collective

Browse / github.com/storytold/vectorcraft / patch

UI: Shift-click document tab close box closes all

Close all documents with Shift+click on a tab's close button **Summary.** Add the Adobe Illustrator gesture for closing every open document at once: hold **Shift** and click the **×** on a document tab. Today the × closes one document at a time, and closing everything means going through **File › Close All** (`Ctrl+Alt+W`). **Motivation.** A working session easily ends with a dozen-plus tabs — variants of a poster, exported fragments, a few imports. Closing them one by one is the slowest part of wrapping up, and each dirty document asks its own "save changes?" question. Illustrator solves this with a single Shift+click on the tab, and that muscle memor

Status
open
Score
0.00
Adopters
0 other operators
Author
MiQ71 (imported from pull request #3974, open)
Base
75dacb4fec73dac539538c91f92d41e09b255d23
Size
+48 −2 in 1 file
Review
pending
Shared
2026-10-28
Id
d6e009e82056ef32a14be1ad

How it went

No other operator has reported using it yet.

Diff

raw · crates/ui-egui/src/chrome.rs

diff --git a/crates/ui-egui/src/chrome.rs b/crates/ui-egui/src/chrome.rs--- a/crates/ui-egui/src/chrome.rs+++ b/crates/ui-egui/src/chrome.rs@@ -482,6 +482,7 @@ pub fn doc_tabs(app: &mut VectorcraftApp, ui: &mut Ui) {     let mut x = strip.left();     let mut activate = None;     let mut close = None;+    let mut close_all = false;     let mut hovering_art = false;     for (i, ((_, galley), w)) in tabs.iter().zip(&widths).enumerate().skip(first) {         if x + w > strip.left() + room && Some(i) != active {@@ -505,7 +506,11 @@ pub fn doc_tabs(app: &mut VectorcraftApp, ui: &mut Ui) {         ui.painter().galley(egui::pos2(title_left, r.center().y - galley.size().y / 2.0), galley.clone(), t.text);         hovering_art |= crate::document_drag::hover_tab(app, &resp, i, xr.expand(3.0));         if xresp.clicked() {-            close = Some(i);+            if ui.input(|input| input.modifiers.shift) {+                close_all = true;+            } else {+                close = Some(i);+            }         } else if resp.clicked() {             activate = Some(i);         }@@ -528,7 +533,11 @@ pub fn doc_tabs(app: &mut VectorcraftApp, ui: &mut Ui) {             }         });     }-    if let Some(i) = close {+    if close_all {+        if let Err(e) = crate::unsaved::close_all(app, "closeAll") {+            app.status(e);+        }+    } else if let Some(i) = close {         if let Err(e) = crate::unsaved::close(app, i) {             app.status(e);         }@@ -1565,6 +1574,43 @@ mod tests {         assert!(super::CLOSE_BEFORE_TITLE == cfg!(target_os = "macos"));     } +    /// #3900: Shift-clicking any document tab's close box uses Close All; a plain click keeps+    /// closing only that tab (covered by `a_tabs_close_box_sits_where_the_platform_puts_it`).+    #[test]+    fn shift_clicking_a_tabs_close_box_closes_all_documents() {+        let mut app = crate::VectorcraftApp::new(Session::new(), Default::default());+        for _ in 0..3 {+            app.session.execute("file.new", &json!({"width": 100, "height": 100})).unwrap();+        }+        let ctx = egui::Context::default();+        crate::theme::install_fonts(&ctx);+        let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1600.0, 200.0));+        let frame = |app: &mut crate::VectorcraftApp, events: Vec<egui::Event>| {+            let mut out = ctx.run_ui(egui::RawInput { screen_rect: Some(screen), events, ..Default::default() }, |ui| super::doc_tabs(app, ui));+            out.textures_delta.clear();+            let mut titles = vec![];+            for c in &out.shapes {+                if let egui::Shape::Text(t) = &c.shape+                    && t.visual_bounding_rect().width() > 30.0+                {+                    titles.push(t.visual_bounding_rect());+                }+            }+            titles.sort_by(|a, b| a.left().total_cmp(&b.left()));+            titles+        };+        let titles = frame(&mut app, vec![]);+        assert_eq!(titles.len(), 3);+        let first = titles[0];+        let x = if super::CLOSE_BEFORE_TITLE { first.left() - 16.0 } else { first.right() + 20.0 };+        let at = egui::pos2(x, first.center().y);+        let shift = egui::Modifiers { shift: true, ..Default::default() };+        let press = |pressed| egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed, modifiers: shift };+        frame(&mut app, vec![egui::Event::PointerMoved(at), egui::Event::ModifiersChanged(shift), press(true)]);+        frame(&mut app, vec![press(false)]);+        assert!(app.session.documents().is_empty(), "Shift+click on a tab × must run Close All");+    }+     /// One headless frame of the status bar; returns the artboard navigator's buttons, left to     /// right: first, previous, next, last.     fn navigator(app: &mut crate::VectorcraftApp, ctx: &egui::Context, events: Vec<egui::Event>) -> Vec<egui::Rect> {