diff --git a/crates/ui-egui/src/layers.rs b/crates/ui-egui/src/layers.rs index f172b309..61eb4162 100644 --- a/crates/ui-egui/src/layers.rs +++ b/crates/ui-egui/src/layers.rs @@ -118,10 +118,10 @@ pub fn rail(app: &mut FigApp, ui: &mut Ui) { Some(tb) => { // Leaving the version history shows the tab rather than collapsing it. if active { - app.ui.left_collapsed = true; + app.ui.close_left_panel(); } else { app.ui.left_tab = tb; - app.ui.left_collapsed = false; + app.ui.open_left_panel(); } app.ui.versions = Default::default(); } @@ -161,7 +161,7 @@ pub fn left_panel(app: &mut FigApp, ui: &mut Ui) { } icons::paint(ui.painter(), hr.shrink(5.0), Icon::Panels, t.icon); if hresp.clicked() { - app.ui.left_collapsed = true; + app.ui.close_left_panel(); } ui.painter().line_segment([head.left_bottom(), head.right_bottom()], Stroke::new(1.0, t.border)); match app.ui.left_tab { @@ -612,7 +612,7 @@ pub(crate) fn start_rename(app: &mut FigApp, ids: &[NodeId]) { let rows = listed_rows(&doc, page, &app.ui.expanded, &app.ui.layer_filter.trim().to_lowercase()); let Some(id) = rows.iter().map(|r| r.id).find(|r| ids.contains(r)) else { return }; app.ui.left_tab = LeftTab::File; - app.ui.left_collapsed = false; + app.ui.open_left_panel(); rename_layer(app, id); } diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index f58ac4ce..b63668bb 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -491,6 +491,22 @@ pub enum MobilePanel { Left, Right, } + +impl UiState { + /// Opens the left panel; a narrow window shows it in place of the right one. + pub fn open_left_panel(&mut self) { + self.left_collapsed = false; + self.mobile_panel = Some(MobilePanel::Left); + } + + /// Closes the left panel. + pub fn close_left_panel(&mut self) { + self.left_collapsed = true; + if self.mobile_panel == Some(MobilePanel::Left) { + self.mobile_panel = None; + } + } +} pub const CANVAS_MIN: f32 = 160.0; /// The widths the side panels are drawn at in `avail` points (the window less the icon rail), from @@ -645,6 +661,8 @@ pub struct UiState { pub left_collapsed: bool, /// The side panel open in a narrow window (a phone), where only one shows at a time. pub mobile_panel: Option, + /// The window was narrow (one side panel at a time) at the last frame. + pub narrow: bool, pub comments: comments::CommentUi, pub versions: versions::VersionUi, /// The branch review dialog, while open. @@ -778,6 +796,7 @@ impl Default for UiState { batch_rename: None, left_collapsed: false, mobile_panel: None, + narrow: false, comments: comments::CommentUi::default(), versions: versions::VersionUi::default(), branch_review: None, @@ -1054,7 +1073,14 @@ impl FigApp { "panel.devMode" => { self.ui.right_tab = if self.ui.right_tab == RightTab::Inspect { RightTab::Design } else { RightTab::Inspect }; } - "panel.leftSidebar" => self.ui.left_collapsed = !self.ui.left_collapsed, + "panel.leftSidebar" => { + let shown = if self.ui.narrow { self.ui.mobile_panel == Some(MobilePanel::Left) } else { !self.ui.left_collapsed }; + if shown { + self.ui.close_left_panel(); + } else { + self.ui.open_left_panel(); + } + } "panel.widths" => { let width = |k: &str| r.data.get(k).and_then(Value::as_f64).map(|w| w as f32); let (left, right) = (width("left"), width("right")); @@ -1069,7 +1095,7 @@ impl FigApp { "panel.layers" => self.ui.left_tab = LeftTab::File, "panel.assets" => { self.ui.left_tab = LeftTab::Assets; - self.ui.left_collapsed = false; + self.ui.open_left_panel(); self.ui.versions = Default::default(); } "panel.versionHistory" => versions::open(self), @@ -1078,7 +1104,7 @@ impl FigApp { } "panel.libraries" => { self.ui.left_tab = LeftTab::Assets; - self.ui.left_collapsed = false; + self.ui.open_left_panel(); self.ui.libraries = true; } "home" => self.ui.home = true, @@ -1635,7 +1661,11 @@ impl FigApp { ui.painter().rect(r, 8.0, fill, egui::Stroke::new(1.0, t.border), egui::StrokeKind::Inside); icons::paint(ui.painter(), r.shrink(10.0), icon, if open { t.accent_on } else { t.icon }); if resp.clicked() { - self.ui.mobile_panel = if open { None } else { Some(side) }; + if side == MobilePanel::Left && !open { + self.ui.open_left_panel(); + } else { + self.ui.mobile_panel = if open { None } else { Some(side) }; + } } ui.add_space(4.0); } @@ -1696,6 +1726,7 @@ impl FigApp { let hidden = self.session.ds().map(|d| d.view.ui_hidden).unwrap_or(false); let narrow = !hidden && ui.available_width() < NARROW_WIDTH; let mobile = self.ui.mobile_panel.filter(|_| narrow); + self.ui.narrow = narrow; // A narrow window shows the right panel when its button asks for it, a board's too. let right_open = (self.right_panel_shown(hidden) && !narrow) || mobile == Some(MobilePanel::Right); if !hidden && (!narrow || mobile == Some(MobilePanel::Left)) { diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index 47593193..8fbd88c3 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -231,7 +231,7 @@ pub fn invoke(app: &mut FigApp, id: &str) { // The file name in the left panel's header turns into its editor, the name selected. "file.rename" => { app.ui.left_tab = crate::LeftTab::File; - app.ui.left_collapsed = false; + app.ui.open_left_panel(); app.ui.rename_file = true; } "text.link" => app.ui.menu_input = Some(crate::dialogs::MenuInput::Link(String::new())), @@ -246,7 +246,7 @@ pub fn invoke(app: &mut FigApp, id: &str) { "tool.set" => app.ui.tool_flyout = Some("move"), "page.select" => { app.ui.left_tab = crate::LeftTab::File; - app.ui.left_collapsed = false; + app.ui.open_left_panel(); app.ui.pages_open = true; } // From a menu the link is pasted into the Join dialog (`collab.join {link}` joins directly). diff --git a/crates/ui-egui/src/panel_width_tests.rs b/crates/ui-egui/src/panel_width_tests.rs index b1e937d5..70bfa927 100644 --- a/crates/ui-egui/src/panel_width_tests.rs +++ b/crates/ui-egui/src/panel_width_tests.rs @@ -254,6 +254,10 @@ fn a_narrow_window_shows_one_side_panel_at_a_time() { let w = canvas_width(&mut app, phone); assert!((1.0..=60.0).contains(&w), "{side:?}: the open panel leaves {w}"); } + // A command that opens the left panel opens it on a phone too, in place of the right one. + app.run("view.assets", json!({})); + let w = canvas_width(&mut app, phone); + assert!((1.0..=60.0).contains(&w) && app.ui.mobile_panel == Some(MobilePanel::Left), "view.assets leaves {w}"); app.ui.mobile_panel = Some(MobilePanel::Right); let wide = canvas_width(&mut app, vec2(1440.0, 900.0)); assert!(wide > 600.0 && wide < 1100.0, "a wide window shows both panels beside the canvas ({wide})"); diff --git a/crates/ui-egui/src/versions.rs b/crates/ui-egui/src/versions.rs index 2d0f9ae2..b3aa879c 100644 --- a/crates/ui-egui/src/versions.rs +++ b/crates/ui-egui/src/versions.rs @@ -50,7 +50,7 @@ impl Form { /// Open the panel (File ▸ Show version history). pub fn open(app: &mut FigApp) { app.ui.versions.open = true; - app.ui.left_collapsed = false; + app.ui.open_left_panel(); app.ui.left_tab = crate::LeftTab::File; }