mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-29 12:46:53 -06:00
#1311: craft-ui 352a830 (craft-libs#187 review fixes)
Pin craft-ui to aiongg/craft-libs 352a830. Workspace::set_visible takes the panels' permissions; group_header_width takes the strip width. Tests reach docking controls through DockArea::hit_id and floating_layer instead of rebuilding egui ids. Control names in DOCK_LABELS follow craft-ui's sentence case. Test: dragging a dock divider and then pressing an arrow key nudges the selection and leaves the dock's size alone.
This commit is contained in:
Generated
+1
-1
@@ -849,7 +849,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-ui"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/aiongg/craft-libs?rev=13be52fbaa2c4936c8d7b3d2b5abe6ab72892123#13be52fbaa2c4936c8d7b3d2b5abe6ab72892123"
|
||||
source = "git+https://github.com/aiongg/craft-libs?rev=352a8308c5cc53c2ae31326fa38a4a6bcb0ff9eb#352a8308c5cc53c2ae31326fa38a4a6bcb0ff9eb"
|
||||
dependencies = [
|
||||
"egui",
|
||||
"serde",
|
||||
|
||||
+1
-1
@@ -126,4 +126,4 @@ hayro-jbig2 = { git = "https://github.com/storytold/pdfcraft", rev = "b5467d93a0
|
||||
|
||||
# Docking trial: craft-ui from the craft-libs `docking` branch.
|
||||
[patch."https://github.com/storytold/craft-libs"]
|
||||
craft-ui = { git = "https://github.com/aiongg/craft-libs", rev = "13be52fbaa2c4936c8d7b3d2b5abe6ab72892123" }
|
||||
craft-ui = { git = "https://github.com/aiongg/craft-libs", rev = "352a8308c5cc53c2ae31326fa38a4a6bcb0ff9eb" }
|
||||
|
||||
@@ -217,7 +217,7 @@ mod visual_regressions {
|
||||
harness.run_steps(4);
|
||||
let stem = format!("designcraft-dock-{}-{width}-{scale}x", brightness.id());
|
||||
harness.render().unwrap().save(directory.join(format!("{stem}.png"))).unwrap();
|
||||
let tab = egui::Id::new("designcraft-panel-docking").with(("tab", &"properties".to_string()));
|
||||
let tab = crate::panel_docking::tab_id("properties");
|
||||
let tab = harness.ctx.read_response(tab).unwrap().rect;
|
||||
let region = serde_json::json!({
|
||||
"screen": [width, 640.0], "scale": scale,
|
||||
|
||||
@@ -4002,9 +4002,9 @@ mod tests {
|
||||
}
|
||||
};
|
||||
// The docked panel's tab and a floating panel group.
|
||||
let tab = egui::Id::new("designcraft-panel-docking").with(("tab", &"preflight".to_string()));
|
||||
let tab = crate::panel_docking::tab_id("preflight");
|
||||
let docked = || ctx.read_response(tab).is_some();
|
||||
let group = egui::LayerId::new(egui::Order::Middle, egui::Id::new("designcraft-panel-docking").with(("floating", "preflight")));
|
||||
let group = crate::panel_docking::floating_layer("preflight");
|
||||
let shown = |layer: egui::LayerId| ctx.memory(|m| m.areas().is_visible(&layer));
|
||||
let toggles = [
|
||||
("window.hidePanels", json!({}), json!({})),
|
||||
|
||||
@@ -19,6 +19,25 @@ fn area_id() -> egui::Id {
|
||||
/// The dock area a headless app floats into, before it ever drew a frame.
|
||||
const HEADLESS_AREA: egui::Rect = egui::Rect::from_min_max(egui::Pos2::ZERO, egui::pos2(1400.0, 900.0));
|
||||
|
||||
/// The egui id of a docking control, for tests that press it or read its response.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn hit_id(hit: &craft_ui::docking::Hit<String>) -> egui::Id {
|
||||
craft_ui::docking::DockArea::<String>::hit_id(area_id(), hit)
|
||||
}
|
||||
|
||||
/// The egui id of a panel's tab.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn tab_id(panel: &str) -> egui::Id {
|
||||
hit_id(&craft_ui::docking::Hit::Tab(panel.to_owned()))
|
||||
}
|
||||
|
||||
/// The egui layer of the floating window whose first panel is `panel`.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn floating_layer(panel: &str) -> egui::LayerId {
|
||||
let window = Floating::single(vec![panel.to_owned()], 0, [0.0, 0.0, 1.0, 1.0]);
|
||||
craft_ui::docking::DockArea::<String>::floating_layer(area_id(), &window).unwrap_or_else(egui::LayerId::background)
|
||||
}
|
||||
|
||||
pub(crate) fn cancel_gesture(ctx: &egui::Context) {
|
||||
craft_ui::docking::cancel_drag::<String>(ctx, area_id());
|
||||
}
|
||||
@@ -486,7 +505,7 @@ fn shared_command(app: &mut DesignApp, id: &str, params: &Value) -> Result<Value
|
||||
let mut shown = false;
|
||||
if let Some(panel) = shows.as_ref().filter(|p| !txn.workspace.layout.contains(p)) {
|
||||
let fallback = first_dock_panel(&txn.workspace.layout, panel);
|
||||
txn.workspace.set_visible(panel.clone(), true, fallback).map_err(|e| e.to_string())?;
|
||||
txn.workspace.set_visible(panel.clone(), true, fallback, model_permissions).map_err(|e| e.to_string())?;
|
||||
shown = true;
|
||||
}
|
||||
let returned = match &command {
|
||||
@@ -895,19 +914,19 @@ const DOCK_LABELS: &[(&str, &str)] = &[
|
||||
("dock.panels", "Panels"),
|
||||
("dock.resize", "Resize panels"),
|
||||
("dock.resizeWindow", "Resize panel window"),
|
||||
("dock.moveGroup", "Move Tab Group"),
|
||||
("dock.moveGroup", "Move tab group"),
|
||||
("dock.closeGroup", "Close Tab Group"),
|
||||
("dock.minimize", "Minimize"),
|
||||
("dock.expand", "Expand"),
|
||||
("dock.floatGroup", "Float Tab Group"),
|
||||
("dock.moveGroupTo", "Group Tab Group with"),
|
||||
("dock.moveColumn", "Move Panel Column"),
|
||||
("dock.moveColumn", "Move panel column"),
|
||||
("dock.collapseToIcons", "Collapse to Icons"),
|
||||
("dock.expandPanels", "Expand Panels"),
|
||||
("dock.collapseColumn", "Collapse to Icons"),
|
||||
("dock.expandColumn", "Expand Panels"),
|
||||
("dock.moveWindow", "Move Panel Window"),
|
||||
("dock.closeWindow", "Close Panel Window"),
|
||||
("dock.moveWindow", "Move panel window"),
|
||||
("dock.closeWindow", "Close panel window"),
|
||||
("dock.undock", "Undock Panel"),
|
||||
("dock.undockGroup", "Undock Panel Group"),
|
||||
("dock.closePanelGroup", "Close Panel Group"),
|
||||
@@ -1032,7 +1051,7 @@ impl craft_ui::docking::DockContent<String> for PanelContent<'_> {
|
||||
fn bare(&self, id: &String) -> bool {
|
||||
id == CANVAS
|
||||
}
|
||||
fn group_header_width(&self, _: &[String], _: &String, _: bool) -> f32 {
|
||||
fn group_header_width(&self, _: &[String], _: &String, _: bool, _: f32) -> f32 {
|
||||
24.0
|
||||
}
|
||||
fn group_header(&mut self, ui: &mut egui::Ui, _: &[String], active: &String, _: bool) {
|
||||
|
||||
@@ -87,8 +87,8 @@ fn ordinary_panel_tab_drag_enters_shared_docking_and_can_redock_into_another_tab
|
||||
drag(&mut h, source, egui::pos2(160.0, 140.0));
|
||||
let layout = h.state().ui.docking.as_ref().expect("ordinary tab drag starts shared docking");
|
||||
assert!(layout.floating.iter().flat_map(|w| &w.groups).any(|g| g.panels.contains(&"layers".into())), "{layout:?}");
|
||||
let source = h.ctx.read_response(egui::Id::new("designcraft-panel-docking").with(("tab", &"layers".to_string()))).unwrap().rect.center();
|
||||
let destination = h.ctx.read_response(egui::Id::new("designcraft-panel-docking").with(("tab", &"properties".to_string()))).unwrap().rect.center();
|
||||
let source = h.ctx.read_response(tab_id("layers")).unwrap().rect.center();
|
||||
let destination = h.ctx.read_response(tab_id("properties")).unwrap().rect.center();
|
||||
drag(&mut h, source, destination);
|
||||
let layout = h.state().ui.docking.as_ref().unwrap();
|
||||
assert!(!layout.floating.iter().flat_map(|w| &w.groups).any(|g| g.panels.contains(&"layers".into())));
|
||||
@@ -150,7 +150,7 @@ fn capture_custom_panel_docking_visual_fixtures() {
|
||||
.save(directory.join(format!("designcraft-shared-docking-{}-{language}-{width}-{scale}x.png", theme.id())))
|
||||
.unwrap();
|
||||
if language == "ar" {
|
||||
let tab = egui::Id::new("designcraft-panel-docking").with(("tab", &"swatches".to_string()));
|
||||
let tab = tab_id("swatches");
|
||||
let at = harness.ctx.read_response(tab).unwrap().rect.center();
|
||||
harness.input_mut().events.extend([
|
||||
Event::PointerMoved(at),
|
||||
@@ -277,8 +277,8 @@ fn populated_canvas_keeps_a_real_stroke_edit_after_floating_and_redocking() {
|
||||
std::fs::create_dir_all(directory).unwrap();
|
||||
h.render().unwrap().save(directory.join("designcraft-populated-stroke-floating.png")).unwrap();
|
||||
}
|
||||
let from = h.ctx.read_response(egui::Id::new("designcraft-panel-docking").with(("tab", &"stroke".to_string()))).unwrap().rect.center();
|
||||
let to = h.ctx.read_response(egui::Id::new("designcraft-panel-docking").with(("tab", &"properties".to_string()))).unwrap().rect.center();
|
||||
let from = h.ctx.read_response(tab_id("stroke")).unwrap().rect.center();
|
||||
let to = h.ctx.read_response(tab_id("properties")).unwrap().rect.center();
|
||||
drag(&mut h, from, to);
|
||||
assert!(h.state().ui.docking.as_ref().unwrap().floating.is_empty());
|
||||
assert_eq!(h.state().session.active().unwrap().doc.item(designcraft_doc::ItemId(id)).unwrap().stroke.join, designcraft_doc::Join::Bevel);
|
||||
@@ -439,15 +439,14 @@ fn modal_overlay_blocks_a_real_tab_drag_release() {
|
||||
let mut app = app();
|
||||
app.run("window.panel.float", json!({"panel":"layers"})).unwrap();
|
||||
let mut h = harness(app, vec2(1280.0, 800.0));
|
||||
let area = egui::Id::new("designcraft-panel-docking");
|
||||
let source = h.ctx.read_response(area.with(("tab", &"properties".to_string()))).unwrap().rect.center();
|
||||
let source = h.ctx.read_response(tab_id("properties")).unwrap().rect.center();
|
||||
h.event(Event::PointerMoved(source));
|
||||
h.run_steps(1);
|
||||
h.event(Event::PointerButton { pos: source, button: PointerButton::Primary, pressed: true, modifiers: Modifiers::NONE });
|
||||
h.run_steps(1);
|
||||
h.event(Event::PointerMoved(source - vec2(90.0, 0.0)));
|
||||
h.run_steps(2);
|
||||
assert_eq!(h.ctx.dragged_id(), Some(area.with(("tab", &"properties".to_string()))), "test must start the owned tab gesture");
|
||||
assert_eq!(h.ctx.dragged_id(), Some(tab_id("properties")), "test must start the owned tab gesture");
|
||||
let before = h.state().ui.docking.clone();
|
||||
h.state_mut().ui.dialog = Some(crate::dialogs::Dialog::new("newDocument", json!({})));
|
||||
let destination = egui::pos2(150.0, 150.0);
|
||||
@@ -489,7 +488,7 @@ fn first_story_editor_frame_conservatively_blocks_owned_tab_release() {
|
||||
app.run("window.panel.float", json!({"panel":"layers"})).unwrap();
|
||||
let doc = serde_json::to_value(&app.session.active().unwrap().doc).unwrap();
|
||||
let mut h = crate::test_window::open(app, vec2(1440.0, 900.0));
|
||||
let tab = egui::Id::new("designcraft-panel-docking").with(("tab", &"properties".to_string()));
|
||||
let tab = tab_id("properties");
|
||||
let from = h.ctx.read_response(tab).unwrap().rect.center();
|
||||
h.event(Event::PointerMoved(from));
|
||||
h.run_steps(1);
|
||||
@@ -522,7 +521,7 @@ fn first_story_editor_frame_conservatively_blocks_owned_tab_release() {
|
||||
.find(|p| !editor.contains(*p))
|
||||
.expect("fixture has space outside editor");
|
||||
let before_outside = h.state().app.ui.docking.clone();
|
||||
let tab = egui::Id::new("designcraft-panel-docking").with(("tab", &"layers".to_string()));
|
||||
let tab = tab_id("layers");
|
||||
let from = h.ctx.read_response(tab).unwrap().rect.center();
|
||||
h.event(Event::PointerMoved(from));
|
||||
h.run_steps(1);
|
||||
@@ -729,7 +728,7 @@ fn group_with<'a>(layout: &'a Layout<String>, panel: &str) -> (bool, &'a [String
|
||||
}
|
||||
|
||||
fn drag_bar(h: &Harness<'static, DesignApp>, first: &str) -> egui::Rect {
|
||||
h.ctx.read_response(egui::Id::new("designcraft-panel-docking").with(("drag-bar", Some(&first.to_string())))).expect("the group's drag bar").rect
|
||||
h.ctx.read_response(hit_id(&craft_ui::docking::Hit::DragBar(first.to_string()))).expect("the group's drag bar").rect
|
||||
}
|
||||
|
||||
/// The docked Character Styles | Paragraph Styles group moves as one by its drag bar: dropped on
|
||||
@@ -752,7 +751,7 @@ fn dragging_a_docked_group_by_its_bar_moves_every_tab() {
|
||||
assert!(valid(layout));
|
||||
|
||||
let bar = drag_bar(&h, "characterStyles");
|
||||
let properties = h.ctx.read_response(egui::Id::new("designcraft-panel-docking").with(("tab", &"properties".to_string()))).unwrap().rect.center();
|
||||
let properties = h.ctx.read_response(tab_id("properties")).unwrap().rect.center();
|
||||
drag(&mut h, bar.center(), properties);
|
||||
let layout = h.state().ui.docking.as_ref().unwrap();
|
||||
let (floating, panels, active) = group_with(layout, "characterStyles");
|
||||
@@ -799,9 +798,7 @@ fn default_workspaces_are_the_canvas_beside_an_end_dock() {
|
||||
|
||||
#[test]
|
||||
fn the_dock_is_at_the_right_in_english_and_the_left_in_arabic() {
|
||||
let tab = |h: &Harness<'static, DesignApp>| {
|
||||
h.ctx.read_response(egui::Id::new("designcraft-panel-docking").with(("tab", &"properties".to_string()))).map(|r| r.rect).unwrap()
|
||||
};
|
||||
let tab = |h: &Harness<'static, DesignApp>| h.ctx.read_response(tab_id("properties")).map(|r| r.rect).unwrap();
|
||||
let h = harness(app(), vec2(1280.0, 800.0));
|
||||
assert!(tab(&h).left() > 640.0, "{:?}", tab(&h));
|
||||
let mut arabic = app();
|
||||
@@ -829,7 +826,7 @@ fn ui_docking_reports_the_layout_and_the_focused_panel() {
|
||||
assert!(result["focused"].is_null());
|
||||
let layout: Layout<String> = serde_json::from_value(result["layout"].clone()).unwrap();
|
||||
assert_eq!(layout, default_layout(&h.state().ui));
|
||||
let at = h.ctx.read_response(egui::Id::new("designcraft-panel-docking").with(("tab", &"layers".to_string()))).unwrap().rect.center();
|
||||
let at = h.ctx.read_response(tab_id("layers")).unwrap().rect.center();
|
||||
click(&mut h, at, PointerButton::Primary);
|
||||
let v = docking(h.state_mut());
|
||||
assert_eq!(v["result"]["focused"], "layers", "{v}");
|
||||
@@ -877,8 +874,37 @@ fn moving_or_closing_the_focused_panel_by_command_updates_the_panel_state() {
|
||||
assert_eq!(app.ui.docking_raise, None);
|
||||
}
|
||||
|
||||
/// A divider dragged with the pointer gives the keyboard back: the next arrow key nudges the
|
||||
/// selection and leaves the dock's size alone.
|
||||
#[test]
|
||||
fn arrow_after_dragging_a_dock_divider_nudges_the_selection_not_the_dock() {
|
||||
let mut app = app();
|
||||
app.run("file.new", json!({"width":480,"height":360})).unwrap();
|
||||
let id = app.run("frame.create", json!({"rect":[80,70,330,230],"content":"none"})).unwrap()["id"].as_u64().unwrap();
|
||||
let mut h = Harness::builder().with_size(vec2(1280.0, 800.0)).build_ui_state(
|
||||
|ui, app: &mut DesignApp| {
|
||||
app.logic(ui.ctx());
|
||||
app.ui(ui);
|
||||
},
|
||||
app,
|
||||
);
|
||||
h.run_steps(5);
|
||||
let column = craft_ui::docking::Hit::Divider(craft_ui::docking::Divider::Column("properties".to_string()));
|
||||
let divider = hits(&h).into_iter().find_map(|(hit, rect)| (hit == column).then_some(rect)).expect("the main dock column's divider");
|
||||
drag(&mut h, divider.center(), divider.center() - vec2(40.0, 0.0));
|
||||
let resized = h.state().ui.docking.clone().expect("the drag customizes the layout");
|
||||
let bounds = |h: &Harness<'static, DesignApp>| h.state().session.active().unwrap().doc.item(designcraft_doc::ItemId(id)).unwrap().bounds();
|
||||
let before = bounds(&h);
|
||||
h.event(Event::Key { key: egui::Key::ArrowRight, physical_key: None, pressed: true, repeat: false, modifiers: Modifiers::NONE });
|
||||
h.run_steps(1);
|
||||
h.event(Event::Key { key: egui::Key::ArrowRight, physical_key: None, pressed: false, repeat: false, modifiers: Modifiers::NONE });
|
||||
h.run_steps(2);
|
||||
assert!(bounds(&h).x0 > before.x0, "the arrow nudges the selection");
|
||||
assert_eq!(h.state().ui.docking.as_ref(), Some(&resized), "the arrow doesn't resize the dock");
|
||||
}
|
||||
|
||||
fn hits(h: &Harness<'static, DesignApp>) -> Vec<(craft_ui::docking::Hit<String>, egui::Rect)> {
|
||||
craft_ui::docking::DockArea::<String>::hit_rects(&h.ctx, egui::Id::new("designcraft-panel-docking"))
|
||||
craft_ui::docking::DockArea::<String>::hit_rects(&h.ctx, area_id())
|
||||
}
|
||||
|
||||
/// `window.panel.*` ids and DesignCraft's aliases go through craft-ui's `DockCommand`.
|
||||
@@ -929,6 +955,10 @@ fn shared_panel_commands_float_dock_and_reject_atomically() {
|
||||
assert!(
|
||||
matches!(&app.ui.docking.as_ref().unwrap().root, Some(Node::Split { first, .. }) if matches!(&**first, Node::Dock { side: Side::Start, .. }))
|
||||
);
|
||||
// Docking it at that side again leaves it where it is.
|
||||
let before = app.ui.docking.clone();
|
||||
app.run("window.panel.dock", json!({"panel": "stroke", "side": "Start"})).unwrap();
|
||||
assert_eq!(app.ui.docking, before, "it already leads the start dock's last column");
|
||||
// Close Others and Minimize are shared commands too.
|
||||
app.run("window.panel.closeOthers", json!({"panel": "pages"})).unwrap();
|
||||
assert_eq!(tab_group(app.ui.docking.as_ref().unwrap(), "pages"), ["pages"]);
|
||||
|
||||
Reference in New Issue
Block a user