Files
effectcraft/crates/ui-egui/tests/it/ui_dock_resize.rs
T
aiongg 3eae1747ae D2a: ui-egui integration tests use craft-ui's docking ids, labels and geometry
- Focus and traversal tests use DockArea::hit_id for tabs and dividers; the
  focus outline is checked as craft-ui draws it (inset 2 pt, accent colour)
  while the tab has keyboard focus from traversal.
- Floating resize grips are checked under craft-ui's accessibility label.
- Stack resizes compare rendered heights: craft-ui shares a stack by the
  entries' heights as proportions, so the saved heights are checked as ratios.
- Drops into a split use the side zone depth (SIDE_EDGE_ZONE_DEPTH).
- Panels that need their whole area (scroll audit, automation audit) are
  docked alone: maximizing a stacked panel maximizes its whole stack.
- ui_tabs: dragging by the panel menu button is gone (craft-ui drags by tab,
  drag bar or border); the test docks by the tab. A stacked header's -/+
  button collapses and expands, a click on a closed header opens it; a
  click on an open header no longer collapses it.
- ui_tabs (#302): a panel dropped on a stacked panel joins the stack as its
  own entry below it; craft-ui stack entries hold one panel each.
- ui_panel_focus: a reopened Composition goes back to its own index
  ([Composition, Layer]), craft-ui's restore order.
- ui_menus probes the Effect menu with Remove All, since Effect Controls
  also names a tab.
2026-10-25 05:41:47 +00:00

84 lines
4.2 KiB
Rust

//! Shared pane resizing in the full application, including application shortcut ownership.
use craft_ui::docking::{Divider, DockArea, Hit};
use effectcraft_engine::Session;
use effectcraft_ui_egui::EffectcraftApp;
use effectcraft_ui_egui::dock::{self, Node, PanelKind, SplitSize};
use effectcraft_ui_egui::theme::ThemeKind;
use egui::{Event, Key, Modifiers, PointerButton, Rect, pos2, vec2};
use egui_kittest::Harness;
use serde_json::json;
fn snapshots(h: &mut Harness<'_, EffectcraftApp>, directory: Option<&std::path::Path>, phase: &str) {
let Some(directory) = directory else { return };
std::fs::create_dir_all(directory).unwrap();
for (name, theme) in [("dark", ThemeKind::Dark), ("darker", ThemeKind::Darker), ("light", ThemeKind::Light)] {
let ctx = h.ctx.clone();
h.state_mut().set_theme(&ctx, theme);
h.run_steps(3);
h.render().unwrap().save(directory.join(format!("effectcraft-{phase}-{name}.png"))).unwrap();
}
let ctx = h.ctx.clone();
h.state_mut().set_theme(&ctx, ThemeKind::Dark);
h.run_steps(3);
}
fn first_size(h: &Harness<'_, EffectcraftApp>) -> f32 {
match &h.state().ui.dock.layout.root {
Some(Node::Split { size: SplitSize::FixedFirst(value), .. }) => *value,
ref other => panic!("fixed-size mode changed: {other:?}"),
}
}
#[test]
fn focused_dock_resize_owns_arrows_and_keeps_pointer_dragging() {
let mut session = Session::default();
session.execute("comp.new", json!({"name": "Split test", "width": 160, "height": 90, "duration": 1})).unwrap();
session.execute("layer.newSolid", json!({"name": "Selected", "color": [1.0, 0.0, 0.0, 1.0]})).unwrap();
let mut app = EffectcraftApp::new(session);
app.ui.start_screen = false;
let directory = std::env::var_os("CRAFT_UI_PANES_SNAPSHOT_DIR").map(std::path::PathBuf::from);
let mut builder = Harness::builder().with_size(vec2(1400.0, 900.0)).with_max_steps(10_000);
if directory.is_some() {
builder = builder.wgpu();
}
let mut h = builder.build_eframe(move |_| app);
h.run_steps(4);
snapshots(&mut h, directory.as_deref(), "workspace");
h.state_mut().ui.focused = PanelKind::Composition;
h.state_mut().ui.dock =
dock::docked(dock::hsplit(SplitSize::FixedFirst(400.0), dock::tabs(&[PanelKind::Project], 0), dock::tabs(&[PanelKind::Composition], 0)));
h.run_steps(3);
let before_project = h.state().session.project.clone();
let before_times = h.state().session.state.times.clone();
let id = DockArea::<PanelKind>::hit_id(effectcraft_ui_egui::dock_ui::area_id(), &Hit::Divider(Divider::Split(Vec::new())));
h.ctx.memory_mut(|memory| memory.request_focus(id));
h.run_steps(2);
assert_eq!(h.ctx.memory(|memory| memory.focused()), Some(id));
snapshots(&mut h, directory.as_deref(), "focused-resize");
h.event(Event::Key { key: Key::ArrowRight, physical_key: None, pressed: true, repeat: false, modifiers: Modifiers::NONE });
h.step();
h.event(Event::Key { key: Key::ArrowRight, physical_key: None, pressed: false, repeat: false, modifiers: Modifiers::NONE });
h.step();
assert_eq!(first_size(&h), 408.0);
assert_eq!(h.state().session.state.times, before_times);
assert!(std::sync::Arc::ptr_eq(&h.state().session.project, &before_project), "the viewer shortcut must not nudge a selected layer");
h.run_steps(2);
let element = h.state().auto.find("dock.gutter.").unwrap();
let from = Rect::from_min_size(pos2(element.rect[0], element.rect[1]), vec2(element.rect[2], element.rect[3])).center();
h.event(Event::PointerMoved(from));
h.step();
h.event(Event::PointerButton { pos: from, button: PointerButton::Primary, pressed: true, modifiers: Modifiers::NONE });
h.step();
for offset in [5.0, 10.0, 15.0, 20.0] {
h.event(Event::PointerMoved(from + vec2(offset, 0.0)));
h.step();
}
h.event(Event::PointerButton { pos: from + vec2(20.0, 0.0), button: PointerButton::Primary, pressed: false, modifiers: Modifiers::NONE });
h.step();
assert_eq!(first_size(&h), 428.0);
assert_eq!(h.state().session.state.times, before_times);
assert!(std::sync::Arc::ptr_eq(&h.state().session.project, &before_project));
}