mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-02 16:29:34 -06:00
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:
co-authored by
Claude Fable 5.1
Brandon Thomas
parent
1f0acd2bbd
commit
e65ce42000
@@ -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()
|
||||
|
||||
@@ -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)"),
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,7 +288,6 @@ status = "draft — needs native review"
|
||||
"Code" = "الكود"
|
||||
"Code syntax" = "صياغة الكود"
|
||||
"Collapse" = "طي"
|
||||
"Collapse panel" = "طي اللوحة"
|
||||
"Collection" = "مجموعة"
|
||||
"Collections" = "المجموعات"
|
||||
"Color" = "اللون"
|
||||
|
||||
@@ -275,7 +275,6 @@ status = "draft — needs native review"
|
||||
"Code" = "কোড"
|
||||
"Code syntax" = "কোড সিনট্যাক্স"
|
||||
"Collapse" = "সংকুচিত করুন"
|
||||
"Collapse panel" = "প্যানেল সংকুচিত করুন"
|
||||
"Collection" = "সংগ্রহ"
|
||||
"Collections" = "সংগ্রহসমূহ"
|
||||
"Color" = "রঙ"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -270,7 +270,6 @@ status = "draft — needs native review"
|
||||
"Code" = "コード"
|
||||
"Code syntax" = "コード構文"
|
||||
"Collapse" = "折りたたむ"
|
||||
"Collapse panel" = "パネルを折りたたむ"
|
||||
"Collection" = "コレクション"
|
||||
"Collections" = "コレクション"
|
||||
"Color" = "カラー"
|
||||
|
||||
@@ -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" = "ფერი"
|
||||
|
||||
@@ -260,7 +260,6 @@ status = "draft — needs native review"
|
||||
"Code" = "코드"
|
||||
"Code syntax" = "코드 구문"
|
||||
"Collapse" = "접기"
|
||||
"Collapse panel" = "패널 접기"
|
||||
"Collection" = "컬렉션"
|
||||
"Collections" = "컬렉션"
|
||||
"Color" = "색상"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -272,7 +272,6 @@ name = "Русский"
|
||||
"Code" = "Код"
|
||||
"Code syntax" = "Синтаксис кода"
|
||||
"Collapse" = "Свернуть"
|
||||
"Collapse panel" = "Свернуть панель"
|
||||
"Collection" = "Коллекция"
|
||||
"Collections" = "Коллекции"
|
||||
"Color" = "Цвет"
|
||||
|
||||
@@ -293,7 +293,6 @@ name = "Svenska"
|
||||
"Code" = "Kod"
|
||||
"Code syntax" = "Kodsyntax"
|
||||
"Collapse" = "Komprimera"
|
||||
"Collapse panel" = "Komprimera panel"
|
||||
"Collection" = "Samling"
|
||||
"Collections" = "Samlingar"
|
||||
"Color" = "Färg"
|
||||
|
||||
@@ -273,7 +273,6 @@ status = "draft — needs native review"
|
||||
"Code" = "โค้ด"
|
||||
"Code syntax" = "ไวยากรณ์โค้ด"
|
||||
"Collapse" = "ยุบ"
|
||||
"Collapse panel" = "ยุบแผง"
|
||||
"Collection" = "คอลเลกชัน"
|
||||
"Collections" = "คอลเลกชัน"
|
||||
"Color" = "สี"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -261,7 +261,6 @@ status = "draft — needs native review"
|
||||
"Code" = "代码"
|
||||
"Code syntax" = "代码语法"
|
||||
"Collapse" = "折叠"
|
||||
"Collapse panel" = "折叠面板"
|
||||
"Collection" = "集合"
|
||||
"Collections" = "集合"
|
||||
"Color" = "颜色"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user