From ef14d755347307fad1c1144cef332e6ce63272ac Mon Sep 17 00:00:00 2001 From: audiosyncrasy <4401511+Audiosyncrasy@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:54:40 -0400 Subject: [PATCH 1/2] Edit tools: F6+F7 / F7+F8 and Cmd/Ctrl+7 select the Smart Tool; Cmd/Ctrl+6 selects the Pencil --- crates/engine/src/cmd/edit.rs | 2 +- crates/ui-egui/src/shortcuts.rs | 31 +++++++++++++++++------ crates/ui-egui/tests/it/edit_modes.rs | 17 +++++++++++++ crates/ui-egui/tests/it/pro_tools_keys.rs | 18 ++++++++++++- 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/crates/engine/src/cmd/edit.rs b/crates/engine/src/cmd/edit.rs index 5536668b..08be4bcc 100644 --- a/crates/engine/src/cmd/edit.rs +++ b/crates/engine/src/cmd/edit.rs @@ -165,7 +165,7 @@ pub fn specs() -> Vec { always, shuffle_lock ), - cmd!(noundo "edit.tool", "Edit Tool", [], Some("F5-F10"), "{tool: zoom|trim|selector|grabber|scrubber|pencil|smart}", always, |e, p| { + cmd!(noundo "edit.tool", "Edit Tool", [], Some("F5-F10, F6+F7, F7+F8, Cmd+6-7"), "{tool: zoom|trim|selector|grabber|scrubber|pencil|smart}", always, |e, p| { let t = str_param(p, "tool").and_then(soundcraft_model::Tool::from_id).ok_or_else(|| bad("edit.tool", "unknown tool"))?; e.session_mut().edit.tool = t; Ok(json!({"tool": t})) diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs index 4aa47a9e..d504273e 100644 --- a/crates/ui-egui/src/shortcuts.rs +++ b/crates/ui-egui/src/shortcuts.rs @@ -151,14 +151,13 @@ fn fixed(app: &mut SoundApp, key: Key, m: Modifiers, mac: bool) -> bool { true } Key::F5 | Key::F6 | Key::F7 | Key::F8 | Key::F9 | Key::F10 if plain => { - let tool = match key { - Key::F5 => "zoom", - Key::F6 => "trim", - Key::F7 => "selector", - Key::F8 => "grabber", - Key::F9 => "scrubber", - _ => "pencil", - }; + let _ = app.run("edit.tool", json!({"tool": tool_key(app, key)})); + true + } + // Cmd+6 / Cmd+7 (Ctrl off macOS) are Pencil and the Smart Tool. Cmd+1–5 keep their window and Session + // keys: egui reports the numeric keypad's digits as the main keyboard's, so the two can't be told apart. + Key::Num6 | Key::Num7 if mods_match(Modifiers::COMMAND, m, mac) => { + let tool = if key == Key::Num6 { "pencil" } else { "smart" }; let _ = app.run("edit.tool", json!({"tool": tool})); true } @@ -265,6 +264,22 @@ fn edit_mode_key(app: &mut SoundApp, pressed: Key) { let _ = app.run("edit.mode", json!({"mode": mode, "snap_to_grid": snap})); } +/// F5–F10 choose the Edit tools; F7 with F6 or F8 held, in either order, chooses the Smart Tool. +fn tool_key(app: &SoundApp, pressed: Key) -> &'static str { + let ctx = app.docking_context.as_ref(); + let held = |k: Key| ctx.is_some_and(|c| c.input(|i| i.key_down(k))); + match pressed { + Key::F6 | Key::F8 if held(Key::F7) => "smart", + Key::F7 if held(Key::F6) || held(Key::F8) => "smart", + Key::F5 => "zoom", + Key::F6 => "trim", + Key::F7 => "selector", + Key::F8 => "grabber", + Key::F9 => "scrubber", + _ => "pencil", + } +} + /// What choosing Grid picks: in Grid the other Grid mode, from another mode the one used last. pub(crate) fn grid_mode(app: &SoundApp) -> &'static str { use soundcraft_model::EditMode; diff --git a/crates/ui-egui/tests/it/edit_modes.rs b/crates/ui-egui/tests/it/edit_modes.rs index ab7e05e1..f9c648ce 100644 --- a/crates/ui-egui/tests/it/edit_modes.rs +++ b/crates/ui-egui/tests/it/edit_modes.rs @@ -281,6 +281,23 @@ fn shuffle_drags_leave_locked_clips_in_the_selection_out_of_it() { assert_eq!(starts(&app, &ids), vec![0, SEC]); } +#[test] +fn f6_f7_and_f7_f8_in_either_order_choose_the_smart_tool() { + let (mut app, ctx, _, _) = app("slip", "grabber", &[]); + let tool = |app: &SoundApp| app.engine.session().edit.tool.id(); + for (first, second) in [(Key::F6, Key::F7), (Key::F7, Key::F6), (Key::F7, Key::F8), (Key::F8, Key::F7)] { + frame(&ctx, &mut app, key(Key::F5)); + frame(&ctx, &mut app, vec![key_event(first, true, Modifiers::NONE)]); + frame(&ctx, &mut app, key(second)); + frame(&ctx, &mut app, vec![key_event(first, false, Modifiers::NONE)]); + assert_eq!(tool(&app), "smart", "{first:?}+{second:?}"); + } + for (k, want) in [(Key::F6, "trim"), (Key::F7, "selector"), (Key::F8, "grabber")] { + frame(&ctx, &mut app, key(k)); + assert_eq!(tool(&app), want, "{k:?} alone"); + } +} + #[test] fn f4_pressed_before_f1_leaves_the_kind_of_grid_alone() { let (mut app, ctx, _, _) = app("grid", "grabber", &[]); diff --git a/crates/ui-egui/tests/it/pro_tools_keys.rs b/crates/ui-egui/tests/it/pro_tools_keys.rs index c87ede65..f1fac7a5 100644 --- a/crates/ui-egui/tests/it/pro_tools_keys.rs +++ b/crates/ui-egui/tests/it/pro_tools_keys.rs @@ -1,5 +1,5 @@ //! Pro Tools keys for commands that had none: Grid and Nudge values, Commands Focus nudging and selection -//! keys, playlists, F12, Ctrl+Tab off macOS and the window keys. +//! keys, playlists, F12, Ctrl+Tab off macOS, the window keys and the Cmd+6/7 tool keys. use egui::os::OperatingSystem; use egui::{Event, Key, Modifiers, Pos2, RawInput, Rect, vec2}; @@ -259,3 +259,19 @@ fn shift_tab_extends_the_selection_and_tab_to_transient_stops_on_hits() { assert_eq!(start(&app), clip.start, "{os:?}"); } } + +#[test] +fn cmd_6_and_7_choose_the_pencil_and_the_smart_tool() { + for (os, cmd) in OSES { + let (ctx, mut app) = rig(os); + app.run("edit.tool", json!({"tool": "grabber"})).unwrap(); + press(&ctx, &mut app, Key::Num6, cmd); + assert_eq!(app.engine.session().edit.tool.id(), "pencil", "{os:?}"); + if os == OperatingSystem::Mac { + press(&ctx, &mut app, Key::Num7, Modifiers::CTRL); + assert_eq!(app.engine.session().edit.tool.id(), "pencil", "the Mac's Control+7 is not Cmd+7"); + } + press(&ctx, &mut app, Key::Num7, cmd); + assert_eq!(app.engine.session().edit.tool.id(), "smart", "{os:?}"); + } +} From bf56b66b5c8dedc6d24fef7412de4aa1f42f5a66 Mon Sep 17 00:00:00 2001 From: audiosyncrasy <4401511+Audiosyncrasy@users.noreply.github.com> Date: Sat, 24 Oct 2026 00:41:55 -0400 Subject: [PATCH 2/2] Test: cover F5, F9 and F10 alone in the tool-key test --- crates/ui-egui/tests/it/edit_modes.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/tests/it/edit_modes.rs b/crates/ui-egui/tests/it/edit_modes.rs index f9c648ce..9a515b10 100644 --- a/crates/ui-egui/tests/it/edit_modes.rs +++ b/crates/ui-egui/tests/it/edit_modes.rs @@ -292,7 +292,8 @@ fn f6_f7_and_f7_f8_in_either_order_choose_the_smart_tool() { frame(&ctx, &mut app, vec![key_event(first, false, Modifiers::NONE)]); assert_eq!(tool(&app), "smart", "{first:?}+{second:?}"); } - for (k, want) in [(Key::F6, "trim"), (Key::F7, "selector"), (Key::F8, "grabber")] { + let alone = [(Key::F5, "zoom"), (Key::F6, "trim"), (Key::F7, "selector"), (Key::F8, "grabber"), (Key::F9, "scrubber"), (Key::F10, "pencil")]; + for (k, want) in alone { frame(&ctx, &mut app, key(k)); assert_eq!(tool(&app), want, "{k:?} alone"); }