mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-25 10:17:02 -06:00
Merge pull request #2309 from dotCooCoo/fix/tab-keeps-control-bar-state
Window: a second Tab shows only the bars the first Tab hid
This commit is contained in:
@@ -318,7 +318,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
|
||||
"{pinned?: bool} keep the Contextual Task Bar where it is instead of following the selection (true), let it follow the selection again from where it is (false) or toggle (omitted), as the bar's More Options menu does; returns the new state",
|
||||
),
|
||||
("window.taskBar.reset", "Reset Bar Position", "", "{} unpin the Contextual Task Bar and put it back under the selection"),
|
||||
("window.dock", "Panels", "Tab", "{} show/hide all panels"),
|
||||
(
|
||||
"window.dock",
|
||||
"Panels",
|
||||
"Tab",
|
||||
"{} hide the panels, the toolbar and the Control bar, or show again the ones the last Tab hid (all three when none of them shows and Tab hid none); returns whether it showed them",
|
||||
),
|
||||
(
|
||||
"window.panels",
|
||||
"Panels Except Tools and Control",
|
||||
@@ -1288,13 +1293,29 @@ pub fn run_ui_command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<R
|
||||
Ok(Value::Null)
|
||||
}
|
||||
"window.dock" => {
|
||||
let on = !(app.ui.dock && app.ui.toolbar);
|
||||
app.ui.dock = on;
|
||||
app.ui.toolbar = on;
|
||||
app.ui.control_bar = on;
|
||||
if !on {
|
||||
app.ui.open_panel = None;
|
||||
}
|
||||
let ui = &mut app.ui;
|
||||
// The bars the last Tab hid show again, unless the toolbar or the Control bar was shown
|
||||
// another way since then (Window › Tools, Window › Control). Panels shown with Shift+Tab
|
||||
// in between stay shown. Switching workspaces clears the record (`workspaces::apply`).
|
||||
let hid = ui.tab_hid.take().filter(|h| (h.dock || h.toolbar || h.control_bar) && !ui.toolbar && !ui.control_bar);
|
||||
let on = if let Some(h) = hid {
|
||||
ui.dock |= h.dock;
|
||||
ui.toolbar = h.toolbar;
|
||||
ui.control_bar = h.control_bar;
|
||||
true
|
||||
} else if ui.dock || ui.toolbar || ui.control_bar {
|
||||
ui.tab_hid = Some(crate::state::TabHid { dock: ui.dock, toolbar: ui.toolbar, control_bar: ui.control_bar });
|
||||
ui.dock = false;
|
||||
ui.toolbar = false;
|
||||
ui.control_bar = false;
|
||||
ui.open_panel = None;
|
||||
false
|
||||
} else {
|
||||
ui.dock = true;
|
||||
ui.toolbar = true;
|
||||
ui.control_bar = true;
|
||||
true
|
||||
};
|
||||
Ok(json!(on))
|
||||
}
|
||||
"window.panels" => {
|
||||
|
||||
@@ -861,6 +861,79 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// The second Tab shows again only the bars the first Tab hid: a Control bar that was off stays
|
||||
/// off, and a toolbar hidden with Window › Tools stays hidden, after a restart too. Shift+Tab
|
||||
/// between two Tabs, and Tab between two Shift+Tabs, leave a state the same key goes back from.
|
||||
/// After a switch of workspace, Tab hides and shows that workspace's bars.
|
||||
#[test]
|
||||
fn tab_shows_again_only_the_bars_it_hid() {
|
||||
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
|
||||
app.session.execute("file.new", &json!({"width": 200, "height": 200})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1600.0, 850.0));
|
||||
let run = |app: &mut VectorcraftApp, events: Vec<egui::Event>| {
|
||||
let mut out = ctx.run_ui(egui::RawInput { events, screen_rect: Some(screen), ..Default::default() }, |ui| {
|
||||
app.logic(ui.ctx());
|
||||
app.ui(ui);
|
||||
});
|
||||
out.textures_delta.clear();
|
||||
};
|
||||
let press = |modifiers| egui::Event::Key { key: Key::Tab, physical_key: None, pressed: true, repeat: false, modifiers };
|
||||
let tab = |app: &mut VectorcraftApp, modifiers| {
|
||||
run(app, vec![press(modifiers)]);
|
||||
run(app, vec![]);
|
||||
(app.ui.dock, app.ui.toolbar, app.ui.control_bar)
|
||||
};
|
||||
for _ in 0..3 {
|
||||
run(&mut app, vec![]);
|
||||
}
|
||||
assert_eq!((app.ui.dock, app.ui.toolbar, app.ui.control_bar), (true, true, false), "the Control bar starts off");
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (false, false, false));
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (true, true, false), "the second Tab leaves the Control bar off");
|
||||
app.run("window.toolbar", json!({})).unwrap();
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (false, false, false));
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (true, false, false), "a toolbar hidden with Window › Tools stays hidden");
|
||||
app.run("window.toolbar", json!({})).unwrap();
|
||||
// Hidden by Tab when the app quits: the next Tab after a restart shows the same bars.
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (false, false, false));
|
||||
app.ui = serde_json::from_slice(&serde_json::to_vec(&app.ui).unwrap()).unwrap();
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (true, true, false), "after a restart");
|
||||
// Shift+Tab after Tab shows the panels, and a second Shift+Tab hides them again; Tab then
|
||||
// shows what the first Tab hid.
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (false, false, false));
|
||||
assert_eq!(tab(&mut app, Modifiers::SHIFT), (true, false, false));
|
||||
assert_eq!(tab(&mut app, Modifiers::SHIFT), (false, false, false));
|
||||
assert_eq!(tab(&mut app, Modifiers::SHIFT), (true, false, false));
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (true, true, false), "Tab after Shift+Tab");
|
||||
// Tab after Shift+Tab hides the toolbar, and a second Tab goes back to Shift+Tab's state.
|
||||
assert_eq!(tab(&mut app, Modifiers::SHIFT), (false, true, false));
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (false, false, false));
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (false, true, false), "Shift+Tab's state");
|
||||
// Panels shown with Shift+Tab between two Tabs stay shown.
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (false, false, false));
|
||||
assert_eq!(tab(&mut app, Modifiers::SHIFT), (true, false, false));
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (true, true, false), "the panels Shift+Tab showed");
|
||||
// A bar shown another way after Tab: the next Tab hides it, and the Tab after that shows it again.
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (false, false, false));
|
||||
app.run("window.control", json!({})).unwrap();
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (false, false, false));
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (false, false, true), "only the Control bar");
|
||||
// Nothing shows and Tab hid nothing: Tab shows all three.
|
||||
app.run("window.control", json!({})).unwrap();
|
||||
assert_eq!((app.ui.dock, app.ui.toolbar, app.ui.control_bar), (false, false, false));
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (true, true, true));
|
||||
// After a switch of workspace, Tab hides that workspace's bars and shows them again.
|
||||
app.run("window.toolbar", json!({})).unwrap();
|
||||
app.run("window.control", json!({})).unwrap();
|
||||
app.run("window.workspace.new", json!({"name": "Panels Only"})).unwrap();
|
||||
app.run("window.workspace", json!({"name": "Essentials"})).unwrap();
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (false, false, false));
|
||||
app.run("window.workspace", json!({"name": "Panels Only"})).unwrap();
|
||||
assert_eq!((app.ui.dock, app.ui.toolbar, app.ui.control_bar), (true, false, false));
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (false, false, false), "Tab after a switch of workspace");
|
||||
assert_eq!(tab(&mut app, Modifiers::NONE), (true, false, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_and_paste_events_use_the_system_clipboard() {
|
||||
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
|
||||
|
||||
@@ -325,6 +325,10 @@ pub struct UiState {
|
||||
pub toolbar_pos: Option<[f32; 2]>,
|
||||
pub status_bar: bool,
|
||||
pub dock: bool,
|
||||
/// The bars that Tab (`window.dock`) hid, which the next Tab shows again; none when Tab has not
|
||||
/// hidden them since it last showed them, or a workspace was applied since.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub tab_hid: Option<TabHid>,
|
||||
pub view: ViewFlags,
|
||||
pub dialog: Option<Dialog>,
|
||||
/// Messages to show in a dialog once the open one is closed (`dialogs::confirm::tell`), in
|
||||
@@ -581,6 +585,15 @@ pub struct TaskBarPlace {
|
||||
pub offset: Option<(u64, egui::Vec2)>,
|
||||
}
|
||||
|
||||
/// Which of the panels, the toolbar and the Control bar showed when Tab (`window.dock`) hid them.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct TabHid {
|
||||
pub dock: bool,
|
||||
pub toolbar: bool,
|
||||
pub control_bar: bool,
|
||||
}
|
||||
|
||||
/// A group of panels dragged out of the dock: it floats as a stack of tabs, one panel shown.
|
||||
///
|
||||
/// Groups sharing a `column` float as one set, stacked top to bottom in list order, like the panel
|
||||
@@ -720,6 +733,7 @@ impl Default for UiState {
|
||||
toolbar_pos: None,
|
||||
status_bar: true,
|
||||
dock: true,
|
||||
tab_hid: None,
|
||||
view: ViewFlags::default(),
|
||||
dialog: None,
|
||||
told: vec![],
|
||||
|
||||
@@ -152,6 +152,7 @@ pub fn apply(ui: &mut UiState, w: &Workspace) {
|
||||
ui.toolbar_advanced = w.toolbar_advanced;
|
||||
ui.task_bar = w.task_bar;
|
||||
ui.dock = w.dock;
|
||||
ui.tab_hid = None;
|
||||
ui.dock_tab = w.dock_tab;
|
||||
ui.dock_collapsed = w.dock_collapsed;
|
||||
ui.open_panel = w.open_panel.clone();
|
||||
|
||||
@@ -448,6 +448,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
- In isolation mode the Control bar has an Exit Isolation Mode button (`object.exitIsolation`), except while a pattern is edited. Outside isolation mode, with one group, compound path, path, image, mesh or placed file selected, it has an Isolate Selected Object button (`object.isolate`), which also isolates a path inside a group that Direct Selection selected.
|
||||
- Tab shows and hides the panels (in the Type tool it types a tab) without moving the keyboard focus onto a field, so the next shortcut or letter still works; in dialogs and panel fields it moves between fields.
|
||||
- Shift+Tab (`window.panels`) shows and hides the dock and the floating panels and leaves the toolbar and the Control bar as they are; hiding the panels with Tab or Shift+Tab also closes a panel popped out of the icon column.
|
||||
- The second Tab (`window.dock`) shows again only the bars the first Tab hid, after a restart too: a Control bar that was off stays off, a toolbar hidden with Window › Tools stays hidden, and panels shown with Shift+Tab in between stay shown.
|
||||
- Cmd/Ctrl+A follows the keyboard: in a panel, dialog or search field it selects the field's text, while the Type tool edits it selects all of that text (also `select.all` over MCP), and only otherwise every object. On macOS the menu bar's Select All, Cut, Copy and Paste (keys or clicks) act on a focused field too, and elsewhere the in-window menus' do: the field that had the keyboard when the menu opened gets them, with its selection, and the keyboard back (#516). Not yet: Select All across threaded frames (one frame's text is selected).
|
||||
- Cmd/Ctrl+Alt+Q (`type.resetTracking`) sets the tracking of the selected type, or of the text the Type tool has selected, to 0 and its kerning to Auto (0 in the Character panel), in one undo step; at a caret it resets the kerning of the character before it.
|
||||
- Shortcut sets other apps save as text (`.kys`) open in Import Shortcut Set from File › Open, from Edit › Keyboard Shortcuts › Import… in the desktop app, or from a drop on the window. It matches each entry to a tool, command or panel by name and decodes the file's key and modifier codes where VectorCraft's default shortcuts agree on them; the user answers the rest, and Apply fills the Keyboard Shortcuts dialog's working set. The answers are kept with the preferences for the next import (`shortcutSet.read`, `importMatches.*`).
|
||||
|
||||
Reference in New Issue
Block a user