M7: Minimize collapses the navigation bar and both sidebars, the right one back while something is selected (#1835) (#1865)

* M7: Minimize collapses the navigation bar and both sidebars, the right one back while something is selected (#1835)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* M7: a narrow window ignores Minimize, and a rename started while minimized waits without repaints

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
Trevor Brown
2026-11-02 05:31:32 -05:00
committed by GitHub
co-authored by Claude Fable 5.1 Brandon Thomas
parent 1f0acd2bbd
commit e65ce42000
35 changed files with 161 additions and 36 deletions
+7
View File
@@ -321,6 +321,13 @@ pub fn specs() -> Vec<CommandSpec> {
spec!("view.snapObjects", "Snap to objects", "{on?}", false, |s, p| toggle(s, p, "view.snapObjects")),
spec!("view.snapPixel", "Snap to pixel grid", "{on?}", false, |s, p| toggle(s, p, "view.snapPixel")),
spec!("view.toggleUi", "Show/Hide UI", "{on?}", false, |s, p| flag(s, p, |v| &mut v.ui_hidden)),
spec!(
"view.minimize",
"Minimize the interface",
"{on?} collapses the navigation bar and both sidebars and keeps the toolbar (Shift+Cmd/Ctrl+\\, the Minimize button by the file name); the right sidebar shows while something is selected",
false,
|s, p| flag(s, p, |v| &mut v.ui_minimized)
),
spec!("view.fullscreen", "Full screen", "{on?} the window fills the screen (the desktop app; a no-op headless)", false, |s, p| {
s.request_ui("window.fullscreen", json!({"on": boolean(p, "on")}));
ok()
+1 -1
View File
@@ -225,7 +225,7 @@ pub static KEYS: &[Binding] = &[
k(View, "view.prototype", &["Alt+9"]).label("Prototype panel"),
k(View, "view.designPrototype", &["Shift+E"]),
k(View, "view.devMode", &["Shift+D"]).label("Dev Mode on/off"),
k(View, "view.leftSidebar", &["Shift+Cmd+\\"]),
k(View, "view.minimize", &["Shift+Cmd+\\"]),
k(View, "view.nextRegion", &["F6"]).win(&["F6", "Ctrl+F6"]).label("Focus the next region (toolbar, layers, canvas, properties)").both(),
k(View, "view.previousRegion", &["Shift+F6"]).label("Focus the previous region").both(),
k(View, "view.contextMenu", &["Shift+F10", "Menu"]).label("Context menu of the selection (the focused layer row or the canvas)"),
+1 -1
View File
@@ -145,7 +145,7 @@ static ROWS: &[Row] = &[
doc(View, "Outline mode", &["Shift+Cmd+O"], &[], Bound("view.outlines"), ""),
doc(View, "Pixel preview", &["Ctrl+P"], &["Ctrl+Alt+P"], Bound("view.pixelPreview"), ""),
doc(View, "Others' cursors in a live session", &["Alt+Cmd+\\"], &["Ctrl+Alt+\\"], Bound("view.multiplayerCursors"), "new command"),
doc(View, "Hide the side panels (minimize the interface)", &["Shift+Cmd+\\"], &[], Bound("view.leftSidebar"), ""),
doc(View, "Minimize the interface: the navigation bar and both sidebars", &["Shift+Cmd+\\"], &[], Bound("view.minimize"), "the toolbar stays; the right sidebar shows while something is selected"),
doc(View, "Layers panel", &["Alt+1"], &["Alt+1", "Ctrl+1"], Bound("view.layers"), "one article writes Ctrl+1 for Windows, another Alt+1; both bound there"),
doc(View, "Interface scale: make larger", &["Alt+Shift+Cmd+="], &[], Bound("pref.uiScaleUp"), "Accessibility at Figma, Adjust the scale of the Figma UI"),
doc(View, "Interface scale: make smaller", &["Alt+Shift+Cmd+-"], &[], Bound("pref.uiScaleDown"), "Accessibility at Figma"),
+4
View File
@@ -38,6 +38,9 @@ pub struct ViewState {
pub snap_objects: bool,
pub snap_pixel: bool,
pub ui_hidden: bool,
/// Minimized interface (Shift+Cmd/Ctrl+\): the navigation bar and both sidebars are collapsed
/// and the toolbar stays; the right sidebar shows while something is selected.
pub ui_minimized: bool,
}
impl Default for ViewState {
@@ -67,6 +70,7 @@ impl Default for ViewState {
snap_objects: true,
snap_pixel: true,
ui_hidden: false,
ui_minimized: false,
}
}
}
-1
View File
@@ -288,7 +288,6 @@ status = "draft — needs native review"
"Code" = "الكود"
"Code syntax" = "صياغة الكود"
"Collapse" = "طي"
"Collapse panel" = "طي اللوحة"
"Collection" = "مجموعة"
"Collections" = "المجموعات"
"Color" = "اللون"
-1
View File
@@ -275,7 +275,6 @@ status = "draft — needs native review"
"Code" = "কোড"
"Code syntax" = "কোড সিনট্যাক্স"
"Collapse" = "সংকুচিত করুন"
"Collapse panel" = "প্যানেল সংকুচিত করুন"
"Collection" = "সংগ্রহ"
"Collections" = "সংগ্রহসমূহ"
"Color" = "রঙ"
-1
View File
@@ -288,7 +288,6 @@ status = "draft — needs native review"
"Code" = "Kod"
"Code syntax" = "Sintaksa koda"
"Collapse" = "Skupi"
"Collapse panel" = "Skupi panel"
"Collection" = "Kolekcija"
"Collections" = "Kolekcije"
"Color" = "Boja"
-1
View File
@@ -265,7 +265,6 @@ status = "draft — needs native review"
"Code" = "Kód"
"Code syntax" = "Syntaxe kódu"
"Collapse" = "Sbalit"
"Collapse panel" = "Sbalit panel"
"Collection" = "Kolekce"
"Collections" = "Kolekce"
"Color" = "Barva"
-1
View File
@@ -263,7 +263,6 @@ status = "draft — needs native review"
"Code" = "Code"
"Code syntax" = "Code-Syntax"
"Collapse" = "Einklappen"
"Collapse panel" = "Bereich einklappen"
"Collection" = "Sammlung"
"Collections" = "Sammlungen"
"Color" = "Farbe"
+1 -1
View File
@@ -293,7 +293,6 @@ name = "English"
"Code" = "Code"
"Code syntax" = "Code syntax"
"Collapse" = "Collapse"
"Collapse panel" = "Collapse panel"
"Collection" = "Collection"
"Collections" = "Collections"
"Color" = "Color"
@@ -536,6 +535,7 @@ name = "English"
"Exclusion" = "Exclusion"
"Exit focus view (Esc)" = "Exit focus view (Esc)"
"Expand" = "Expand"
"Expand the interface" = "Expand the interface"
"Export" = "Export"
"Export all" = "Export all"
"Export frames and layers as PNG, JPG, SVG or PDF" = "Export frames and layers as PNG, JPG, SVG or PDF"
-1
View File
@@ -293,7 +293,6 @@ status = "draft — needs native review"
"Code" = "Código"
"Code syntax" = "Sintaxis de código"
"Collapse" = "Contraer"
"Collapse panel" = "Contraer panel"
"Collection" = "Colección"
"Collections" = "Colecciones"
"Color" = "Color"
-1
View File
@@ -281,7 +281,6 @@ status = "draft — needs native review"
"Code or invite link" = "Koodi tai kutsulinkki"
"Code syntax" = "Koodin syntaksi"
"Collapse" = "Tiivistä muunnokset"
"Collapse panel" = "Supista paneeli"
"Collection" = "Kokoelma"
"Collections" = "Kokoelmat"
"Color" = "Väri"
-1
View File
@@ -269,7 +269,6 @@ status = "draft — needs native review"
"Code" = "Code"
"Code syntax" = "Syntaxe du code"
"Collapse" = "Réduire"
"Collapse panel" = "Réduire le panneau"
"Collection" = "Collection"
"Collections" = "Collections"
"Color" = "Couleur"
-1
View File
@@ -281,7 +281,6 @@ status = "draft — needs native review"
"Code" = "Kode"
"Code syntax" = "Sintaks kode"
"Collapse" = "Ciutkan"
"Collapse panel" = "Ciutkan panel"
"Collection" = "Koleksi"
"Collections" = "Koleksi"
"Color" = "Warna"
-1
View File
@@ -259,7 +259,6 @@ status = "draft — needs native review"
"Code" = "Codice"
"Code syntax" = "Sintassi del codice"
"Collapse" = "Comprimi"
"Collapse panel" = "Comprimi pannello"
"Collection" = "Collezione"
"Collections" = "Collezioni"
"Color" = "Colore"
-1
View File
@@ -270,7 +270,6 @@ status = "draft — needs native review"
"Code" = "コード"
"Code syntax" = "コード構文"
"Collapse" = "折りたたむ"
"Collapse panel" = "パネルを折りたたむ"
"Collection" = "コレクション"
"Collections" = "コレクション"
"Color" = "カラー"
-1
View File
@@ -279,7 +279,6 @@ status = "draft — reviewed by a native speaker, awaiting a maintainer's approv
"Code" = "კოდი"
"Code syntax" = "კოდის სინტაქსი"
"Collapse" = "ჩაკეცვა"
"Collapse panel" = "პანელის ჩაკეცვა"
"Collection" = "კოლექცია"
"Collections" = "კოლექციები"
"Color" = "ფერი"
-1
View File
@@ -260,7 +260,6 @@ status = "draft — needs native review"
"Code" = "코드"
"Code syntax" = "코드 구문"
"Collapse" = "접기"
"Collapse panel" = "패널 접기"
"Collection" = "컬렉션"
"Collections" = "컬렉션"
"Color" = "색상"
-1
View File
@@ -271,7 +271,6 @@ status = "draft — needs native review"
"Code or invite link" = "Code of uitnodigingslink"
"Code syntax" = "Code-syntaxis"
"Collapse" = "Inklappen"
"Collapse panel" = "Paneel samenvouwen"
"Collection" = "Verzameling"
"Collections" = "Verzamelingen"
"Color" = "Kleur"
-1
View File
@@ -275,7 +275,6 @@ name = "Polski"
"Code" = "Kod"
"Code syntax" = "Składnia kodu"
"Collapse" = "Zwiń"
"Collapse panel" = "Zwiń panel"
"Collection" = "Kolekcja"
"Collections" = "Kolekcje"
"Color" = "Kolor"
-1
View File
@@ -258,7 +258,6 @@ status = "draft — needs native review"
"Code" = "Código"
"Code syntax" = "Sintaxe de código"
"Collapse" = "Recolher"
"Collapse panel" = "Recolher painel"
"Collection" = "Coleção"
"Collections" = "Coleções"
"Color" = "Cor"
-1
View File
@@ -272,7 +272,6 @@ name = "Русский"
"Code" = "Код"
"Code syntax" = "Синтаксис кода"
"Collapse" = "Свернуть"
"Collapse panel" = "Свернуть панель"
"Collection" = "Коллекция"
"Collections" = "Коллекции"
"Color" = "Цвет"
-1
View File
@@ -293,7 +293,6 @@ name = "Svenska"
"Code" = "Kod"
"Code syntax" = "Kodsyntax"
"Collapse" = "Komprimera"
"Collapse panel" = "Komprimera panel"
"Collection" = "Samling"
"Collections" = "Samlingar"
"Color" = "Färg"
-1
View File
@@ -273,7 +273,6 @@ status = "draft — needs native review"
"Code" = "โค้ด"
"Code syntax" = "ไวยากรณ์โค้ด"
"Collapse" = "ยุบ"
"Collapse panel" = "ยุบแผง"
"Collection" = "คอลเลกชัน"
"Collections" = "คอลเลกชัน"
"Color" = "สี"
-1
View File
@@ -269,7 +269,6 @@ name = "Türkçe"
"Code" = "Kod"
"Code syntax" = "Kod söz dizimi"
"Collapse" = "Daralt"
"Collapse panel" = "Paneli daralt"
"Collection" = "Koleksiyon"
"Collections" = "Koleksiyonlar"
"Color" = "Renk"
-1
View File
@@ -256,7 +256,6 @@ status = "draft — needs native review"
"Code or invite link" = "Mã hoặc liên kết mời"
"Code syntax" = "Cú pháp mã"
"Collapse" = "Thu gọn"
"Collapse panel" = "Thu gọn bảng"
"Collection" = "Bộ sưu tập"
"Collections" = "Bộ sưu tập"
"Color" = "Màu"
-1
View File
@@ -261,7 +261,6 @@ status = "draft — needs native review"
"Code" = "代码"
"Code syntax" = "代码语法"
"Collapse" = "折叠"
"Collapse panel" = "折叠面板"
"Collection" = "集合"
"Collections" = "集合"
"Color" = "颜色"
+17 -5
View File
@@ -274,12 +274,21 @@ impl FigApp {
return;
}
let hidden = self.session.ds().map(|d| d.view.ui_hidden).unwrap_or(false);
// Minimized (Shift+Cmd/Ctrl+\, the Minimize button by the file name): "collapses the
// navigation bar, left sidebar, and right sidebar"; "If you select an object while the
// panels are minimized, the left sidebar remains minimized while the right sidebar expands
// [...] Once all objects are deselected, the right panel minimizes again" (Explore the
// navigation bar and left sidebar). The toolbar stays; Hide UI takes everything.
let narrow = !hidden && ui.available_width() < NARROW_WIDTH;
// A narrow window has no sidebars beside the canvas to take: its panels open from their
// buttons as before, and the interface is minimized again once the window is wide.
let minimized = !hidden && !narrow && self.session.ds().is_ok_and(|d| d.view.ui_minimized);
let right_minimized = minimized && self.session.selection().is_empty();
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)) {
let right_open = (self.right_panel_shown(hidden) && !narrow && !right_minimized) || mobile == Some(MobilePanel::Right);
if !hidden && !minimized && (!narrow || mobile == Some(MobilePanel::Left)) {
egui::Panel::left("left_rail")
.exact_size(layers::RAIL_WIDTH)
.resizable(false)
@@ -290,7 +299,7 @@ impl FigApp {
});
}
// The panels are as wide as asked, less in a window too small for them and the canvas.
let left_open = !hidden && if narrow { mobile == Some(MobilePanel::Left) } else { !self.ui.left_collapsed };
let left_open = !hidden && !minimized && if narrow { mobile == Some(MobilePanel::Left) } else { !self.ui.left_collapsed };
let (left_w, right_w) = if narrow {
let w = (ui.available_width() - MOBILE_STRIP).max(0.0);
(if left_open { w } else { 0.0 }, w)
@@ -353,6 +362,8 @@ impl FigApp {
if narrow {
let x = if mobile == Some(MobilePanel::Left) { ctx.content_rect().max.x - MOBILE_STRIP + 4.0 } else { ctx.content_rect().min.x + 4.0 };
self.mobile_buttons(&ctx, egui::pos2(x, rows.min + 8.0));
} else if minimized {
self.expand_button(&ctx, egui::pos2(ctx.content_rect().min.x + 8.0, rows.min + 8.0));
}
// The panels' edges, over the canvas and the panels (drawn after them: they take the press).
for (name, x, shown, grows) in edges {
@@ -385,8 +396,9 @@ impl FigApp {
updates_ui::show(self, &ctx);
self.toast_ui(&ctx);
// A rename started after the layers panel drew (Cmd/Ctrl+R, a double-click) opens its field
// next frame.
if self.ui.rename.is_some_and(|r| r.start) && !hidden && !self.ui.left_collapsed && self.ui.left_tab == LeftTab::File {
// next frame; while the panel isn't drawn (the interface hidden or minimized, the panel
// closed in a narrow window) the rename waits for it without asking for frames.
if self.ui.rename.is_some_and(|r| r.start) && left_open && self.ui.left_tab == LeftTab::File {
ctx.request_repaint();
}
a11y::end(self, &ctx);
+1
View File
@@ -102,6 +102,7 @@ impl FigApp {
"importReport": self.ui.import_report.as_ref().map(import_report::Report::inspect),
});
state["homeSearch"] = json!(self.ui.home_view.file_query);
state["panels"]["minimized"] = json!(self.session.ds().is_ok_and(|d| d.view.ui_minimized));
// Set apart from the literal above, which is at the macro recursion limit.
state["uiScale"] = json!({"choice": self.session.prefs.ui_scale, "zoom": self.ui.ui_scale.zoom, "drawn": self.ui.ui_scale.drawn, "limited": self.ui.ui_scale.limited});
state["pendingOpens"] = json!(self.ui.pending_opens.len());
+16
View File
@@ -247,6 +247,22 @@ impl FigApp {
});
}
/// The way back from a minimized interface by mouse: a small Panels button at the top left of
/// the canvas, where the navigation bar was (the shortcut works too).
pub(super) fn expand_button(&mut self, ctx: &egui::Context, at: egui::Pos2) {
use a11y::Tip as _;
let t = theme::Tokens::get(ctx);
egui::Area::new(egui::Id::unique("expand_interface")).order(egui::Order::Foreground).fixed_pos(at).show(ctx, |ui| {
let (r, resp) = ui.allocate_exact_size(egui::vec2(32.0, 32.0), egui::Sense::click());
let resp = resp.tip(i18n::tr("Expand the interface"));
ui.painter().rect(r, 8.0, t.panel, egui::Stroke::new(1.0, t.border), egui::StrokeKind::Inside);
icons::paint(ui.painter(), r.shrink(8.0), icons::Icon::Panels, t.icon);
if resp.clicked() {
self.run("view.minimize", serde_json::json!({}));
}
});
}
/// Draw the toast: start its lifetime on the frame **after** the first drawn frame, request
/// that frame and the frame that hides it, expire and remove it at the deadline, and place it
/// at the bottom centre.
+17
View File
@@ -513,3 +513,20 @@ fn a_discarded_sizing_pass_sets_up_and_counts_one_frame_only() {
assert_eq!(passes, 2, "egui reran the frame");
assert_eq!(app.frame_stats.frames, frames + 1, "one frame was drawn, not one per pass");
}
#[test]
fn a_rename_started_while_minimized_waits_for_the_layers_panel_quietly() {
let (mut h, _) = document();
let r = h.run("shape.rectangle", json!({"x": 500, "y": 500, "width": 40, "height": 40}));
h.run("view.minimize", json!({"on": true}));
h.settle();
// What Cmd/Ctrl+R leaves: the field opens when the layers panel next draws, which a minimized
// interface doesn't do; no frame is asked for meanwhile.
h.app.ui.rename = Some(crate::panels::layers::Rename { id: id(&r), start: true });
h.assert_idle("a rename started while the interface is minimized");
h.run("view.minimize", json!({"on": false}));
for _ in 0..3 {
h.frame(vec![]);
}
assert!(h.app.ui.rename.is_some_and(|r| !r.start), "the field opened once the interface expanded");
}
+84 -1
View File
@@ -160,7 +160,7 @@ fn the_command_sets_and_resets_the_widths() {
u.app.run("view.panelWidths", json!({"left": 320, "right": 400}));
u.frame(vec![]);
assert_eq!(widths(&u), (320.0, 400.0));
assert_eq!(u.app.inspect()["panels"], json!({"left": 320.0, "right": 400.0, "leftCollapsed": false}));
assert_eq!(u.app.inspect()["panels"], json!({"left": 320.0, "right": 400.0, "leftCollapsed": false, "minimized": false}));
u.app.run("view.panelWidths", json!({"left": 5000}));
assert_eq!(widths(&u), (*LEFT_PANEL.end(), 400.0), "within its limits; the other panel as it was");
for junk in [json!({"left": "wide"}), json!({"left": null, "right": null}), json!({})] {
@@ -210,6 +210,58 @@ fn minimize_ui_hides_the_panels_and_keeps_their_widths() {
assert_eq!(edges(&u), (56.0, shown.1));
}
/// The middle of the accessible control with `role` and `label`, if one shows.
fn control(u: &mut Ui, role: egui::accesskit::Role, label: &str) -> Option<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 && n.label() == Some(label))?;
let b = n.bounds()?;
Some(pos2(((b.x0 + b.x1) / 2.0) as f32, ((b.y0 + b.y1) / 2.0) as f32))
}
/// "You can click Minimize next to the file name to collapse the navigation bar, left sidebar,
/// and right sidebar" (Explore the navigation bar and left sidebar; Shift+Cmd/Ctrl+\ too): the
/// rail and both sidebars go, the toolbar and the widths stay. "If you select an object while
/// the panels are minimized, the left sidebar remains minimized while the right sidebar expands
/// [...] Once all objects are deselected, the right panel minimizes again." The Expand button at
/// the top left puts everything back (#1835).
#[test]
fn minimize_collapses_the_rail_and_both_sidebars_and_the_right_one_follows_the_selection() {
use egui::accesskit::Role;
let (mut u, _) = ui();
u.app.run("view.panelWidths", json!({"left": 320, "right": 400}));
u.app.run("select.set", json!({"ids": []}));
u.frame(vec![]);
let shown = edges(&u);
let minimize = control(&mut u, Role::Button, "Minimize").expect("the Minimize button by the file name");
u.button(minimize, true);
u.button(minimize, false);
u.frame(vec![]);
assert_eq!(edges(&u), (0.0, 1440.0), "no rail and no sidebars: the canvas has the window");
assert!(control(&mut u, Role::Toolbar, "Tools").is_some(), "the toolbar stays");
assert_eq!(widths(&u), (320.0, 400.0), "the widths are kept");
// A selection brings the right sidebar, the left stays away; deselecting takes it again.
u.app.run("select.all", json!({}));
u.frame(vec![]);
assert!(edges(&u).0 == 0.0 && edges(&u).1 < 1440.0, "the right sidebar shows for the selection: {:?}", edges(&u));
u.app.run("select.set", json!({"ids": []}));
u.frame(vec![]);
assert_eq!(edges(&u), (0.0, 1440.0), "and goes once nothing is selected");
// Expand puts the interface back as it was.
let expand = control(&mut u, Role::Button, "Expand the interface").expect("the Expand button at the top left");
u.button(expand, true);
u.button(expand, false);
u.frame(vec![]);
assert_eq!((edges(&u), widths(&u)), (shown, (320.0, 400.0)), "back as it was");
assert!(control(&mut u, Role::Button, "Expand the interface").is_none());
// Hide UI hides the toolbar as well, as before.
u.app.run("view.toggleUi", json!({}));
assert!(control(&mut u, Role::Toolbar, "Tools").is_none(), "Hide UI takes the toolbar too");
u.app.run("view.toggleUi", json!({}));
}
#[test]
fn the_smallest_window_keeps_its_canvas_whatever_the_widths() {
for asked in [(240.0, 240.0), (480.0, 480.0), (480.0, 240.0), (200.0, 480.0)] {
@@ -231,6 +283,37 @@ fn the_smallest_window_keeps_its_canvas_whatever_the_widths() {
}
}
/// Minimize takes nothing from a phone-sized window, which has no sidebars beside the canvas: its
/// panels open from their buttons as before, and the interface is minimized again once the window
/// is wide.
#[test]
fn a_narrow_window_keeps_its_panel_buttons_while_minimized() {
use crate::MobilePanel;
let ctx = egui::Context::default();
let mut app = FigApp::new(Session::new(), Box::new(NoServices));
let mut k = 0.0;
let mut canvas_width = |app: &mut FigApp, size: egui::Vec2| {
for _ in 0..4 {
k += 1.0 / 60.0;
let input = RawInput { screen_rect: Some(Rect::from_min_size(Pos2::ZERO, size)), time: Some(k), ..Default::default() };
ctx.run_ui(input, |ui| app.ui(ui)).textures_delta.clear();
}
app.canvas.rect.width()
};
let phone = vec2(390.0, 844.0);
app.run("view.minimize", json!({"on": true}));
assert!(canvas_width(&mut app, phone) >= 380.0, "closed panels leave the whole width");
app.ui.mobile_panel = Some(MobilePanel::Left);
let w = canvas_width(&mut app, phone);
assert!((1.0..=60.0).contains(&w), "the Layers button opens the left panel while minimized: {w}");
app.ui.mobile_panel = None;
let wide = canvas_width(&mut app, vec2(1440.0, 900.0));
assert!(wide >= 1439.0, "wide again, the interface is minimized: the canvas has the window ({wide})");
app.run("view.minimize", json!({"on": false}));
let wide = canvas_width(&mut app, vec2(1440.0, 900.0));
assert!(wide > 600.0 && wide < 1100.0, "expanded, both panels are beside the canvas again ({wide})");
}
/// A phone-sized window: the canvas has the whole width, and each side panel opens alone over it,
/// leaving the strip with the panel buttons. A wide window ignores the choice.
#[test]
+4 -2
View File
@@ -159,13 +159,15 @@ pub fn left_panel(app: &mut FigApp, ui: &mut Ui) {
ui.painter().text(pos2(head.min.x + 16.0, head.min.y + 46.0), Align2::LEFT_CENTER, tr("Drafts"), regular(11.0), t.text_secondary);
}
let hr = Rect::from_center_size(pos2(head.max.x - 24.0, head.min.y + 24.0), vec2(24.0, 24.0));
let hresp = ui.interact(hr, ui.scope_id().with("hideui"), Sense::click()).tip(tr("Collapse panel"));
// Minimize, by the file name: "click Minimize next to the file name to collapse the
// navigation bar, left sidebar, and right sidebar" (Explore the navigation bar and left sidebar).
let hresp = ui.interact(hr, ui.scope_id().with("hideui"), Sense::click()).tip(tr("Minimize"));
if hresp.hovered() {
ui.painter().rect_filled(hr, CornerRadius::same(5), t.hover);
}
icons::paint(ui.painter(), hr.shrink(5.0), Icon::Panels, t.icon);
if hresp.clicked() {
app.ui.close_left_panel();
app.run("view.minimize", json!({}));
}
ui.painter().line_segment([head.left_bottom(), head.right_bottom()], Stroke::new(1.0, t.border));
match app.ui.left_tab {
+5 -1
View File
@@ -124,8 +124,12 @@ fn panel_keys_and_cmd_k() {
press(&mut u, Key::D, Modifiers::SHIFT);
assert_eq!(u.app.ui.right_tab, RightTab::Design);
let collapsed = u.app.ui.left_collapsed;
let minimized = |u: &Ui| u.app.session.ds().unwrap().view.ui_minimized;
press(&mut u, Key::Backslash, Modifiers::SHIFT | Modifiers::COMMAND);
assert_eq!(u.app.ui.left_collapsed, !collapsed, "Shift+Cmd+\\ toggles the left sidebar");
assert!(minimized(&u), "Shift+Cmd+\\ minimizes the interface");
assert_eq!(u.app.ui.left_collapsed, collapsed, "without collapsing the left sidebar for good");
press(&mut u, Key::Backslash, Modifiers::SHIFT | Modifiers::COMMAND);
assert!(!minimized(&u), "and back");
press(&mut u, Key::K, Modifiers::COMMAND);
assert!(u.app.ui.palette.is_some(), "Cmd+K opens Actions on the canvas");
}
+3 -2
View File
File diff suppressed because one or more lines are too long