mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-02 16:29:34 -06:00
M1: leaving Design ends the pen path and goes back to Move (fixes #307)
This commit is contained in:
@@ -122,6 +122,8 @@ mod panel_reorder_tests;
|
||||
#[cfg(test)]
|
||||
mod panel_width_tests;
|
||||
#[cfg(test)]
|
||||
mod pen_mode_tests;
|
||||
#[cfg(test)]
|
||||
mod perf_tests;
|
||||
#[cfg(test)]
|
||||
mod plugin_panel_tests;
|
||||
@@ -280,6 +282,8 @@ pub struct UiState {
|
||||
pub left_width: f32,
|
||||
pub right_width: f32,
|
||||
pub right_tab: RightTab,
|
||||
/// The tab of the previous frame, to see the right panel change mode.
|
||||
pub last_right_tab: RightTab,
|
||||
pub left_tab: LeftTab,
|
||||
pub pages_open: bool,
|
||||
pub expanded: BTreeSet<NodeId>,
|
||||
@@ -400,6 +404,7 @@ impl Default for UiState {
|
||||
left_width: PANEL_DEFAULT,
|
||||
right_width: PANEL_DEFAULT,
|
||||
right_tab: RightTab::Design,
|
||||
last_right_tab: RightTab::Design,
|
||||
left_tab: LeftTab::File,
|
||||
pages_open: true,
|
||||
expanded: BTreeSet::new(),
|
||||
@@ -610,6 +615,19 @@ impl FigApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// Leaving Design ends a pen path as Escape does and goes back to Move: Prototype and Inspect
|
||||
/// don't draw (#307). Checked as the frame starts (the control channel) and after the panels
|
||||
/// and shortcuts (the mode switcher, the tabs, Shift+E/Shift+D), before the canvas sees more.
|
||||
fn end_pen_outside_design(&mut self) {
|
||||
if self.ui.right_tab == self.ui.last_right_tab {
|
||||
return;
|
||||
}
|
||||
self.ui.last_right_tab = self.ui.right_tab;
|
||||
if self.ui.right_tab != RightTab::Design && self.session.tool.tool == figcraft_engine::tools::ToolId::Pen {
|
||||
self.run("tool.set", json!({"tool": "move"}));
|
||||
}
|
||||
}
|
||||
|
||||
/// Once a frame, after the panels: a preview nothing asked for this frame ends.
|
||||
fn end_unwanted_text_preview(&mut self, ctx: &egui::Context) {
|
||||
if !std::mem::take(&mut self.ui.text_preview_wanted) {
|
||||
@@ -984,6 +1002,7 @@ impl FigApp {
|
||||
widgets::set_nudge(&ctx, self.session.prefs.nudge_small, self.session.prefs.nudge_big);
|
||||
// The Inspect tab measures on hover without Alt.
|
||||
self.session.tool.redlines = self.ui.right_tab == RightTab::Inspect;
|
||||
self.end_pen_outside_design();
|
||||
if self.ui.present.is_some() {
|
||||
present::show(self, ui);
|
||||
// What went wrong while presenting (a widget's plugin failing) shows over it.
|
||||
@@ -1086,6 +1105,7 @@ impl FigApp {
|
||||
variable_ui::picker(self, &ctx);
|
||||
style_picker::show(self, &ctx);
|
||||
self.end_unwanted_text_preview(&ctx);
|
||||
self.end_pen_outside_design();
|
||||
dialogs::show(self, &ctx);
|
||||
self.toast_ui(&ctx);
|
||||
// A rename started after the layers panel drew (Cmd/Ctrl+R, a double-click) opens its field
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
//! Leaving Design with a pen path open (#307): the path ends as Escape ends it, the tool goes back
|
||||
//! to Move, and the canvas of Prototype or Inspect takes no more pen points. Driven by the panel
|
||||
//! keys and by the toolbar's mode switcher.
|
||||
|
||||
use egui::accesskit::Role;
|
||||
use egui::{Event, Key, Modifiers, Pos2, pos2};
|
||||
use figcraft_engine::Session;
|
||||
use figcraft_engine::tools::ToolId;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::RightTab;
|
||||
use crate::layers_drag_tests::Ui;
|
||||
|
||||
fn press(u: &mut Ui, key: Key, modifiers: Modifiers) {
|
||||
u.frame(vec![
|
||||
Event::Key { key, physical_key: None, pressed: true, repeat: false, modifiers },
|
||||
Event::Key { key, physical_key: None, pressed: false, repeat: false, modifiers },
|
||||
]);
|
||||
}
|
||||
|
||||
fn click(u: &mut Ui, p: Pos2) {
|
||||
u.button(p, true);
|
||||
u.button(p, false);
|
||||
u.frame(vec![]);
|
||||
}
|
||||
|
||||
fn layers(u: &Ui) -> usize {
|
||||
let doc = u.app.session.doc().unwrap();
|
||||
doc.node(u.app.session.page().unwrap()).unwrap().children.len()
|
||||
}
|
||||
|
||||
/// The middle of the toolbar's mode button named `label`, found in the AccessKit tree.
|
||||
fn mode_button(u: &mut Ui, label: &str) -> Pos2 {
|
||||
u.ctx.enable_accesskit();
|
||||
u.frame(vec![]);
|
||||
let out = u.frame_output(vec![]);
|
||||
let nodes = out.platform_output.accesskit_update.expect("an AccessKit update").nodes;
|
||||
let n = nodes.iter().map(|(_, n)| n).find(|n| n.role() == Role::Button && n.label() == Some(label)).expect(label);
|
||||
let b = n.bounds().expect("bounds");
|
||||
pos2(((b.x0 + b.x1) / 2.0) as f32, ((b.y0 + b.y1) / 2.0) as f32)
|
||||
}
|
||||
|
||||
/// A pen path with two anchors still open on an empty page.
|
||||
fn drawing() -> Ui {
|
||||
let mut u = Ui::new(Session::new());
|
||||
u.app.run("tool.set", json!({"tool": "pen"}));
|
||||
click(&mut u, pos2(600.0, 400.0));
|
||||
click(&mut u, pos2(700.0, 450.0));
|
||||
assert_eq!(u.app.session.tool.pen.as_ref().map(|p| p.points.len()), Some(2), "the path is open");
|
||||
u
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn switching_mode_ends_the_pen_path_as_escape_does() {
|
||||
let mut esc = drawing();
|
||||
press(&mut esc, Key::Escape, Modifiers::NONE);
|
||||
assert!(esc.app.session.tool.pen.is_none());
|
||||
let finished = layers(&esc);
|
||||
assert_eq!(finished, 1, "Escape keeps the two-anchor path");
|
||||
|
||||
// Shift+E is Prototype, Shift+D is Inspect.
|
||||
for key in [Key::E, Key::D] {
|
||||
let mut u = drawing();
|
||||
press(&mut u, key, Modifiers::SHIFT);
|
||||
assert_ne!(u.app.ui.right_tab, RightTab::Design, "{key:?}");
|
||||
assert!(u.app.session.tool.pen.is_none(), "{key:?}: the path is finished");
|
||||
assert_eq!(u.app.session.tool.tool, ToolId::Move, "{key:?}: back to Move");
|
||||
assert_eq!(layers(&u), finished, "{key:?}: the path is kept as Escape keeps it");
|
||||
// The canvas takes no more pen points.
|
||||
click(&mut u, pos2(800.0, 300.0));
|
||||
assert!(u.app.session.tool.pen.is_none(), "{key:?}: a click starts no path");
|
||||
assert_eq!(layers(&u), finished, "{key:?}: a click adds nothing");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_mode_switcher_ends_the_pen_path_too() {
|
||||
let finished = {
|
||||
let mut esc = drawing();
|
||||
press(&mut esc, Key::Escape, Modifiers::NONE);
|
||||
layers(&esc)
|
||||
};
|
||||
// Design is already the mode while the pen draws: pressing it still leaves the pen, as it leaves
|
||||
// the pencil of Draw.
|
||||
for (label, tab) in [("Prototype", RightTab::Prototype), ("Inspect", RightTab::Inspect), ("Design", RightTab::Design)] {
|
||||
let mut u = drawing();
|
||||
let at = mode_button(&mut u, label);
|
||||
click(&mut u, at);
|
||||
assert_eq!(u.app.ui.right_tab, tab, "{label}");
|
||||
assert!(u.app.session.tool.pen.is_none(), "{label}: the path is finished");
|
||||
assert_eq!(u.app.session.tool.tool, ToolId::Move, "{label}: back to Move");
|
||||
assert_eq!(layers(&u), finished, "{label}: the path is kept as Escape keeps it");
|
||||
}
|
||||
}
|
||||
@@ -174,7 +174,7 @@ pub fn show(app: &mut FigApp, ui: &mut Ui) {
|
||||
}
|
||||
1 => {
|
||||
app.ui.right_tab = crate::RightTab::Design;
|
||||
if app.session.tool.tool == ToolId::Pencil {
|
||||
if matches!(app.session.tool.tool, ToolId::Pencil | ToolId::Pen) {
|
||||
app.run("tool.set", json!({"tool": "move"}));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user