mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-02 06:27:11 -06:00
- 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.
84 lines
4.2 KiB
Rust
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));
|
|
}
|