Files
cadcraft/crates/ui-egui/tests/quickcalc_toggle.rs
T
aiongg b901656669 Panels: use craft-ui's shared docking (#2093)
* Pin craft-libs at 420d21a

* Pin craft-libs at 16fb3d4

* Use craft-ui's shared docking workspace and ui.dock commands

Replace the local Workspace, its validation, apply, visibility and the
ui.dock parser with craft_ui::docking::Workspace and DockOperation.
CADCraft keeps its panels, the drawing view's rules, the default layout
and the workspace file. Add tests for loading a v1 workspace file and for
dragging a panel group by its bar.

* Pin craft-libs at 6e7f285

* Docking: reveal panels through craft-ui's set_visible, which reopens groups and places new panels itself

* Docking: palette switches regroup a panel whose recorded anchor is hidden instead of opening it alone
2026-10-24 20:23:39 -04:00

149 lines
6.9 KiB
Rust

//! QuickCalc's menu and platform shortcut toggle only its panel, preserving drafting and calculator
//! state. Explicit opens remain idempotent and programmatic calculations do not open the palette.
use cadcraft_engine::Session;
use cadcraft_ui_egui::{CadApp, Services, docking::Panel, menus};
use egui::{Key, Modifiers, accesskit::Role, vec2};
use egui_kittest::Harness;
use egui_kittest::kittest::{NodeT, Queryable, by};
use serde_json::{Value, json};
fn harness() -> Harness<'static, CadApp> {
let mut app = CadApp::new(Session::default(), Services::default());
app.ui.start_tab = false;
app.ui.in_window_menu = false;
let mut h = Harness::builder().with_size(vec2(1280.0, 800.0)).build_ui_state(
|ui, app: &mut CadApp| {
app.logic(&ui.ctx().clone());
app.ui(ui);
},
app,
);
h.run_steps(5);
h
}
/// In these tests QuickCalc floats alone; scope to its body to avoid the Properties text fields.
/// Its first text input is the expression, before conversion values and variable definitions.
fn expression<'a>(h: &'a Harness<'_, CadApp>) -> egui_kittest::Node<'a> {
h.get_by_role_and_label(Role::Tab, "QuickCalc").parent().unwrap().get_all_by_role(Role::TextInput).next().unwrap()
}
#[test]
fn platform_shortcut_toggles_with_expression_focus_and_preserves_drafting() {
for (os, modifiers) in [
(egui::os::OperatingSystem::Windows, Modifiers { ctrl: true, command: true, ..Modifiers::NONE }),
(egui::os::OperatingSystem::Mac, Modifiers { mac_cmd: true, command: true, ..Modifiers::NONE }),
] {
let mut h = harness();
h.ctx.set_os(os);
let drawing = h.state().session.doc().unwrap().clone();
h.state_mut().start("line");
h.state_mut().cmd.buffer = "12,34".into();
h.key_press_modifiers(modifiers, Key::Num8);
h.run_steps(5);
assert!(h.state().ui.docking.layout.contains(&Panel::QuickCalc));
let floating = h.state().ui.docking.layout.floating.iter().find(|group| group.contains(&Panel::QuickCalc)).unwrap().rect;
expression(&h).click();
h.run_steps(3);
expression(&h).type_text("2+3");
h.run_steps(3);
assert!(h.ctx.egui_wants_keyboard_input());
h.key_press_modifiers(modifiers, Key::Num8);
h.run_steps(5);
assert!(!h.state().ui.docking.layout.contains(&Panel::QuickCalc), "text focus must not swallow the toggle");
h.key_press_modifiers(modifiers, Key::Num8);
h.run_steps(5);
assert_eq!(h.get(by().role(Role::TextInput).value("2+3")).value().as_deref(), Some("2+3"));
assert_eq!(h.state().ui.docking.layout.floating.iter().find(|group| group.contains(&Panel::QuickCalc)).unwrap().rect, floating);
assert_eq!(h.state().session.running.as_ref().unwrap().id, "line");
assert_eq!(h.state().cmd.buffer, "12,34");
assert_eq!(h.state().session.doc().unwrap(), &drawing);
}
}
#[test]
fn window_menu_toggle_preserves_calculator_history_and_command_line_input() {
let mut h = harness();
let (id, _, _, shortcut) = menus::UI_COMMANDS.iter().find(|(_, label, path, _)| *label == "QuickCalc" && !path.is_empty()).unwrap();
assert_eq!(*id, "ui.toggle.quickcalc");
assert_eq!(*shortcut, Some("Cmd+8"));
// Both the native menu and egui menu dispatch through activate().
menus::activate(h.state_mut(), id);
h.run_steps(5);
expression(&h).click();
h.run_steps(3);
expression(&h).type_text("2+3");
h.run_steps(3);
h.get_by_role_and_label(Role::Button, "=").click();
h.run_steps(3);
assert!(h.query_all(by().value("2+3")).next().is_some());
h.state_mut().start("line");
h.state_mut().cmd.buffer = "12,34".into();
menus::activate(h.state_mut(), id);
h.run_steps(5);
assert!(!h.state().ui.docking.layout.contains(&Panel::QuickCalc));
menus::activate(h.state_mut(), id);
h.run_steps(5);
assert!(h.query_all(by().value("2+3")).next().is_some(), "closing must not discard calculation history");
assert_eq!(h.get(by().role(Role::TextInput).value("5")).value().as_deref(), Some("5"));
assert_eq!(h.state().session.running.as_ref().unwrap().id, "line");
assert_eq!(h.state().cmd.buffer, "12,34");
}
#[test]
fn toggle_restores_floating_or_docked_location_without_hiding_other_panels() {
for floating in [false, true] {
let mut app = CadApp::new(Session::default(), Services::default());
app.run("ui.panel.quickcalc", Value::Null).unwrap();
let params = if floating {
json!({"operation":"float", "panel":"quickCalc", "rect":[80,120,360,480]})
} else {
json!({"operation":"move", "panel":"quickCalc", "target":"toolSets", "zone":"center"})
};
app.run("ui.dock", params).unwrap();
let layout = app.ui.docking.layout.clone();
app.run("ui.toggle.quickcalc", json!({})).unwrap();
assert!(!app.ui.docking.layout.contains(&Panel::QuickCalc));
for panel in [Panel::Canvas, Panel::Layers, Panel::Properties, Panel::ToolSets] {
assert!(app.ui.docking.layout.contains(&panel));
}
app.run("ui.toggle.quickcalc", json!({})).unwrap();
assert_eq!(app.ui.docking.layout, layout);
}
}
#[test]
fn explicit_opens_still_reveal_and_calculations_do_not_open_the_palette() {
let mut app = CadApp::new(Session::default(), Services::default());
let value = app.run("cal", json!({"expr":"2+3"})).unwrap();
assert_eq!(value["value"], 5.0);
assert!(app.cmdline(r#"quickcalc {"expr":"2+3"}"#).is_none());
assert!(!app.ui.docking.layout.contains(&Panel::QuickCalc));
assert!(menus::is_ui_command("ui.panel.quickcalc"));
assert!(app.cmdline("QUICKCALC").is_none());
let layout = app.ui.docking.layout.clone();
for command in ["QC", "QUICKCALC", "ui.panel.quickcalc"] {
assert!(app.cmdline(command).is_none());
assert_eq!(app.ui.docking.layout, layout, "{command} must not toggle an open palette");
}
app.run("ui.dock", json!({"operation":"move", "panel":"quickCalc", "target":"toolSets", "zone":"center"})).unwrap();
app.run("ui.dock", json!({"operation":"activate", "panel":"toolSets"})).unwrap();
app.run("ui.panel.quickcalc", Value::Null).unwrap();
let mut workspace = app.ui.docking.clone();
cadcraft_ui_egui::docking::reveal_panel(&mut workspace, Panel::QuickCalc).unwrap();
assert_eq!(app.ui.docking.layout, workspace.layout, "explicit open must activate an inactive tab");
}
#[test]
fn quickcalc_shortcut_does_not_bypass_the_unsaved_drawing_prompt() {
let mut h = harness();
h.state_mut().run("circle", json!({"center":[0,0], "radius":1})).unwrap();
assert!(h.state_mut().request_quit());
assert!(h.state().closing.is_some());
h.key_press_modifiers(Modifiers::COMMAND, Key::Num8);
h.run_steps(4);
assert!(!h.state().ui.docking.layout.contains(&Panel::QuickCalc));
assert!(h.state().closing.is_some());
}