Skip to content

Commit 8d5bd3d

Browse files
committed
Menu: new Select top-level submenu with select/deselect items (M8)
- Add a Select submenu between Edit and View on both macOS and Linux. - Move Select all (⌘A) and Deselect all (⌘⇧A) out of Edit into Select. - Add Select files… and Deselect files… items with NO menu accelerators (bare +/- aren't valid macOS menu accelerators; the keystroke binding lives in FilePane's keydown handler from M7). - Register the four IDs in menu_items.rs, mod.rs::menu_id_to_command and command_id_to_menu_id, plus shortcuts-store.ts::menuCommands. - Decision: Cmdr's selectAll operates on files, not text, so the Select menu is the more honest home. Edit retains Cut/Copy/Paste for text.
1 parent ea79d1c commit 8d5bd3d

8 files changed

Lines changed: 177 additions & 67 deletions

File tree

apps/desktop/src-tauri/src/menu/CLAUDE.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,14 @@ template auto-tinting). However, **full-color non-template images do render corr
146146

147147
## Menu structure
148148

149-
Both platforms share: File, Edit, View (with Sort by and Zoom submenus), Go, Tab, Help.
149+
Both platforms share: File, Edit, Select, View (with Sort by and Zoom submenus), Go, Tab, Help.
150+
151+
The **Select** submenu (between Edit and View) holds the four selection commands: `Select all` (⌘A), `Deselect all`
152+
(⌘⇧A), `Select files…` (no menu accelerator), and `Deselect files…` (no menu accelerator). The two `` items open the
153+
Selection dialog (see `apps/desktop/src/lib/selection-dialog/CLAUDE.md`); their keystrokes (bare `+` / `-`) are bound in
154+
`FilePane`'s keydown handler because macOS menu accelerators always carry the ⌘ modifier and bare `+` / `-` aren't
155+
valid accelerator strings. The items are still registered in `MenuState.items` so a user-customized shortcut could flow
156+
into the menu via the generic update path.
150157

151158
The **Zoom** submenu (`build_zoom_submenu`) holds the text-size presets (75/100/125/150 %) plus Zoom in (`Cmd+Plus`) /
152159
Zoom out (`Cmd+Minus`) / 100 % (`Cmd+0`). Items are `App`-scoped so the keyboard accelerators fire in any focused window.
@@ -186,6 +193,13 @@ also Window and Help.
186193
**Decision**: Per-pane View submenus (`View > Left pane > …`, `View > Right pane > …`) with the accelerator following the active pane.
187194
**Why**: The previous single Full/Brief pair always targeted the active pane, but that scope was invisible in the menu, so testers were slow to figure out how to change the inactive pane's view. Nesting each pane's Full/Brief items inside its own submenu makes the scope obvious without cluttering the View root. The accelerator is attached only to the active pane's pair (and migrates on focus change via `rebuild_view_mode_items`) so the shortcut remains accurate: pressing ⌘1 always affects the active pane, and the visible binding sits next to the items it actually targets.
188195

196+
**Decision**: `Select all` and `Deselect all` live in the new `Select` top-level menu, not in `Edit`.
197+
**Why**: macOS convention puts them under `Edit`, but Cmdr's `selection.selectAll` operates on files, not on text. The
198+
`Select` menu is the honest home for file-selection commands, and it groups them with the new `Select files…` /
199+
`Deselect files…` dialog openers (M8 of the selection-dialog plan). `Edit` retains the text-edit operations
200+
(Cut/Copy/Paste/Move here/Copy path/Copy filename/Search files) plus Undo/Redo. Don't move them back without re-reading
201+
this entry and the selection-dialog plan's M8 § "Menu structure" rationale.
202+
189203
**Decision**: SF Symbol icons only on the menu bar, not on context menus.
190204
**Why**: Tauri doesn't support SF Symbols natively. For the menu bar, we walk `NSApplication.mainMenu()` post-construction via objc2 FFI and set SF Symbols directly on `NSMenuItem` objects, producing true template images that auto-tint correctly. Context menus don't get icons because Tauri doesn't expose the raw `NSMenu` pointer, and the alternative (rasterized bitmaps via `IconMenuItem`) produces visually poor results (no template tinting, wrong size/weight).
191205

@@ -205,10 +219,10 @@ also Window and Help.
205219
ensures text clipboard works natively in all windows. Undo and Redo remain PredefinedMenuItems
206220
since they only apply to text fields.
207221
- **⌘A dual routing**: "Select all" uses ⌘A as a native menu accelerator (so it's visible in the
208-
Edit menu). Since macOS intercepts it before the webview, the frontend's `handleCommandExecute`
209-
checks `document.activeElement`: if it's an input/textarea, it calls `.select()` for text
210-
selection; otherwise it selects files. This avoids PredefinedMenuItem::select_all which would
211-
conflict with the custom MenuItem.
222+
Select menu — moved out of Edit in M8 of the selection-dialog plan). Since macOS intercepts it before the webview,
223+
the frontend's `handleCommandExecute` checks `document.activeElement`: if it's an input/textarea, it calls `.select()`
224+
for text selection; otherwise it selects files. This avoids PredefinedMenuItem::select_all which would conflict with
225+
the custom MenuItem.
212226
- **Pin tab label**: `pin_tab` in MenuState is updated dynamically by the frontend to show
213227
"Pin tab" or "Unpin tab" based on the active tab's state.
214228
- **Reopen closed tab item**: The Tab submenu includes "Reopen closed tab" (⌘⇧T on macOS) between

apps/desktop/src-tauri/src/menu/linux.rs

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ use super::menu_items::{
1111
};
1212
use super::{
1313
ABOUT_ID, CHECK_FOR_UPDATES_ID, CLOSE_OTHER_TABS_ID, CLOSE_TAB_ID, COMMAND_PALETTE_ID, COPY_FILENAME_ID,
14-
COPY_PATH_ID, DESELECT_ALL_ID, EDIT_COPY_ID, EDIT_CUT_ID, EDIT_ID, EDIT_PASTE_ID, EDIT_PASTE_MOVE_ID,
15-
ENTER_LICENSE_KEY_ID, FILE_COPY_ID, FILE_DELETE_ID, FILE_DELETE_PERMANENTLY_ID, FILE_MOVE_ID, FILE_NEW_FOLDER_ID,
16-
FILE_VIEW_ID, GET_INFO_ID, GO_BACK_ID, GO_FORWARD_ID, GO_PARENT_ID, HELP_SEND_ERROR_REPORT_ID, MenuItems,
17-
NEW_TAB_ID, NEXT_TAB_ID, OPEN_ID, PIN_TAB_MENU_ID, PREV_TAB_ID, QUICK_LOOK_ID, RENAME_ID, REOPEN_CLOSED_TAB_ID,
18-
SEARCH_FILES_ID, SELECT_ALL_ID, SETTINGS_ID, SHOW_HIDDEN_FILES_ID, SHOW_IN_FINDER_ID, SORT_BY_EXTENSION_ID,
19-
SORT_BY_MODIFIED_ID, SORT_BY_NAME_ID, SORT_BY_SIZE_ID, SWAP_PANES_ID, SWITCH_PANE_ID, VIEW_MODE_BRIEF_LEFT_ID,
20-
VIEW_MODE_BRIEF_RIGHT_ID, VIEW_MODE_FULL_LEFT_ID, VIEW_MODE_FULL_RIGHT_ID, ViewMode,
14+
COPY_PATH_ID, DESELECT_ALL_ID, DESELECT_FILES_ID, EDIT_COPY_ID, EDIT_CUT_ID, EDIT_ID, EDIT_PASTE_ID,
15+
EDIT_PASTE_MOVE_ID, ENTER_LICENSE_KEY_ID, FILE_COPY_ID, FILE_DELETE_ID, FILE_DELETE_PERMANENTLY_ID, FILE_MOVE_ID,
16+
FILE_NEW_FOLDER_ID, FILE_VIEW_ID, GET_INFO_ID, GO_BACK_ID, GO_FORWARD_ID, GO_PARENT_ID, HELP_SEND_ERROR_REPORT_ID,
17+
MenuItems, NEW_TAB_ID, NEXT_TAB_ID, OPEN_ID, PIN_TAB_MENU_ID, PREV_TAB_ID, QUICK_LOOK_ID, RENAME_ID,
18+
REOPEN_CLOSED_TAB_ID, SEARCH_FILES_ID, SELECT_ALL_ID, SELECT_FILES_ID, SETTINGS_ID, SHOW_HIDDEN_FILES_ID,
19+
SHOW_IN_FINDER_ID, SORT_BY_EXTENSION_ID, SORT_BY_MODIFIED_ID, SORT_BY_NAME_ID, SORT_BY_SIZE_ID, SWAP_PANES_ID,
20+
SWITCH_PANE_ID, VIEW_MODE_BRIEF_LEFT_ID, VIEW_MODE_BRIEF_RIGHT_ID, VIEW_MODE_FULL_LEFT_ID, VIEW_MODE_FULL_RIGHT_ID,
21+
ViewMode,
2122
};
2223

2324
/// Linux menu: builds all menus from scratch, matching the macOS menu structure.
@@ -91,8 +92,6 @@ pub(crate) fn build_menu_linux<R: Runtime>(
9192
let edit_copy_item = MenuItem::with_id(app, EDIT_COPY_ID, "&Copy", true, Some("Ctrl+C"))?;
9293
let edit_paste_item = MenuItem::with_id(app, EDIT_PASTE_ID, "&Paste", true, Some("Ctrl+V"))?;
9394
let edit_paste_move_item = MenuItem::with_id(app, EDIT_PASTE_MOVE_ID, "&Move here", true, Some("Ctrl+Alt+V"))?;
94-
let select_all_item = MenuItem::with_id(app, SELECT_ALL_ID, "Select &all", true, Some("Cmd+A"))?;
95-
let deselect_all_item = MenuItem::with_id(app, DESELECT_ALL_ID, "D&eselect all", true, Some("Cmd+Shift+A"))?;
9695
let copy_path_item = MenuItem::with_id(app, COPY_PATH_ID, "Cop&y path", true, Some(copy_path_accelerator()))?;
9796
let copy_filename_item = MenuItem::with_id(app, COPY_FILENAME_ID, "Copy file&name", true, None::<&str>)?;
9897
let search_files_item = MenuItem::with_id(app, SEARCH_FILES_ID, "&Search files", true, Some("Cmd+F"))?;
@@ -121,9 +120,6 @@ pub(crate) fn build_menu_linux<R: Runtime>(
121120
&edit_paste_item,
122121
&edit_paste_move_item,
123122
&PredefinedMenuItem::separator(app)?,
124-
&select_all_item,
125-
&deselect_all_item,
126-
&PredefinedMenuItem::separator(app)?,
127123
&copy_path_item,
128124
&copy_filename_item,
129125
&PredefinedMenuItem::separator(app)?,
@@ -136,6 +132,29 @@ pub(crate) fn build_menu_linux<R: Runtime>(
136132
)?;
137133
menu.append(&edit_menu)?;
138134

135+
// --- Select menu ---
136+
// Lives between Edit and View, matching the macOS layout. Holds the four selection
137+
// commands. The two `…` dialog openers carry no accelerator: the keystroke binding
138+
// (bare `+` / `-`) lives in FilePane's keydown handler.
139+
let select_all_item = MenuItem::with_id(app, SELECT_ALL_ID, "Select &all", true, Some("Cmd+A"))?;
140+
let deselect_all_item = MenuItem::with_id(app, DESELECT_ALL_ID, "D&eselect all", true, Some("Cmd+Shift+A"))?;
141+
let select_files_item = MenuItem::with_id(app, SELECT_FILES_ID, "Select &files\u{2026}", true, None::<&str>)?;
142+
let deselect_files_item = MenuItem::with_id(app, DESELECT_FILES_ID, "Dese&lect files\u{2026}", true, None::<&str>)?;
143+
144+
let select_menu = Submenu::with_items(
145+
app,
146+
"&Select",
147+
true,
148+
&[
149+
&select_all_item,
150+
&deselect_all_item,
151+
&PredefinedMenuItem::separator(app)?,
152+
&select_files_item,
153+
&deselect_files_item,
154+
],
155+
)?;
156+
menu.append(&select_menu)?;
157+
139158
// --- View menu ---
140159
// View > Left pane > {Full, Brief} and View > Right pane > {Full, Brief}.
141160
// Both pairs always exist; only the active pane's pair carries the keyboard
@@ -340,26 +359,33 @@ pub(crate) fn build_menu_linux<R: Runtime>(
340359
register_item(&mut items, QUICK_LOOK_ID, &quick_look_item, &file_menu, 14);
341360

342361
// Edit menu positions: cut(0), copy(1), paste(2), move_here(3), sep(4),
343-
// select_all(5), deselect_all(6), sep(7), copy_path(8), copy_filename(9),
344-
// sep(10), search_files(11), sep(12), settings(13), license(14), check_for_updates(15)
362+
// copy_path(5), copy_filename(6), sep(7), search_files(8), sep(9), settings(10),
363+
// license(11), check_for_updates(12)
345364
register_item(&mut items, EDIT_CUT_ID, &edit_cut_item, &edit_menu, 0);
346365
register_item(&mut items, EDIT_COPY_ID, &edit_copy_item, &edit_menu, 1);
347366
register_item(&mut items, EDIT_PASTE_ID, &edit_paste_item, &edit_menu, 2);
348367
register_item(&mut items, EDIT_PASTE_MOVE_ID, &edit_paste_move_item, &edit_menu, 3);
349-
register_item(&mut items, SELECT_ALL_ID, &select_all_item, &edit_menu, 5);
350-
register_item(&mut items, DESELECT_ALL_ID, &deselect_all_item, &edit_menu, 6);
351-
register_item(&mut items, COPY_PATH_ID, &copy_path_item, &edit_menu, 8);
352-
register_item(&mut items, COPY_FILENAME_ID, &copy_filename_item, &edit_menu, 9);
353-
register_item(&mut items, SEARCH_FILES_ID, &search_files_item, &edit_menu, 11);
354-
register_item(&mut items, SETTINGS_ID, &settings_item, &edit_menu, 13);
368+
register_item(&mut items, COPY_PATH_ID, &copy_path_item, &edit_menu, 5);
369+
register_item(&mut items, COPY_FILENAME_ID, &copy_filename_item, &edit_menu, 6);
370+
register_item(&mut items, SEARCH_FILES_ID, &search_files_item, &edit_menu, 8);
371+
register_item(&mut items, SETTINGS_ID, &settings_item, &edit_menu, 10);
355372
register_item(
356373
&mut items,
357374
CHECK_FOR_UPDATES_ID,
358375
&check_for_updates_item,
359376
&edit_menu,
360-
15,
377+
12,
361378
);
362379

380+
// Select menu positions: select_all(0), deselect_all(1), sep(2), select_files(3),
381+
// deselect_files(4). The two dialog openers carry no accelerator; bare `+` / `-` are
382+
// bound in FilePane's keydown handler. The items are still registered so a future
383+
// user-customized shortcut could flow into the menu via the generic update path.
384+
register_item(&mut items, SELECT_ALL_ID, &select_all_item, &select_menu, 0);
385+
register_item(&mut items, DESELECT_ALL_ID, &deselect_all_item, &select_menu, 1);
386+
register_item(&mut items, SELECT_FILES_ID, &select_files_item, &select_menu, 3);
387+
register_item(&mut items, DESELECT_FILES_ID, &deselect_files_item, &select_menu, 4);
388+
363389
// View menu positions: left_pane_submenu(0), right_pane_submenu(1), sep(2), hidden(3),
364390
// sort(4), zoom(5), sep(6), switch(7), swap(8), sep(9), palette(10)
365391
register_item(&mut items, SWITCH_PANE_ID, &switch_pane_item, &view_submenu, 7);

apps/desktop/src-tauri/src/menu/macos.rs

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ use super::menu_items::{
1414
};
1515
use super::{
1616
ABOUT_ID, CHECK_FOR_UPDATES_ID, CLOSE_OTHER_TABS_ID, CLOSE_TAB_ID, COMMAND_PALETTE_ID, COPY_FILENAME_ID,
17-
COPY_PATH_ID, DESELECT_ALL_ID, EDIT_COPY_ID, EDIT_CUT_ID, EDIT_ID, EDIT_PASTE_ID, EDIT_PASTE_MOVE_ID,
18-
ENTER_LICENSE_KEY_ID, FILE_COPY_ID, FILE_DELETE_ID, FILE_DELETE_PERMANENTLY_ID, FILE_MOVE_ID, FILE_NEW_FOLDER_ID,
19-
FILE_VIEW_ID, GET_INFO_ID, GO_BACK_ID, GO_FORWARD_ID, GO_PARENT_ID, HELP_SEND_ERROR_REPORT_ID, MenuItems,
20-
NEW_TAB_ID, NEXT_TAB_ID, OPEN_ID, PIN_TAB_MENU_ID, PREV_TAB_ID, QUICK_LOOK_ID, RENAME_ID, REOPEN_CLOSED_TAB_ID,
21-
SEARCH_FILES_ID, SELECT_ALL_ID, SETTINGS_ID, SHOW_HIDDEN_FILES_ID, SHOW_IN_FINDER_ID, SORT_BY_EXTENSION_ID,
22-
SORT_BY_MODIFIED_ID, SORT_BY_NAME_ID, SORT_BY_SIZE_ID, SWAP_PANES_ID, SWITCH_PANE_ID, VIEW_MODE_BRIEF_LEFT_ID,
23-
VIEW_MODE_BRIEF_RIGHT_ID, VIEW_MODE_FULL_LEFT_ID, VIEW_MODE_FULL_RIGHT_ID, ViewMode,
17+
COPY_PATH_ID, DESELECT_ALL_ID, DESELECT_FILES_ID, EDIT_COPY_ID, EDIT_CUT_ID, EDIT_ID, EDIT_PASTE_ID,
18+
EDIT_PASTE_MOVE_ID, ENTER_LICENSE_KEY_ID, FILE_COPY_ID, FILE_DELETE_ID, FILE_DELETE_PERMANENTLY_ID, FILE_MOVE_ID,
19+
FILE_NEW_FOLDER_ID, FILE_VIEW_ID, GET_INFO_ID, GO_BACK_ID, GO_FORWARD_ID, GO_PARENT_ID, HELP_SEND_ERROR_REPORT_ID,
20+
MenuItems, NEW_TAB_ID, NEXT_TAB_ID, OPEN_ID, PIN_TAB_MENU_ID, PREV_TAB_ID, QUICK_LOOK_ID, RENAME_ID,
21+
REOPEN_CLOSED_TAB_ID, SEARCH_FILES_ID, SELECT_ALL_ID, SELECT_FILES_ID, SETTINGS_ID, SHOW_HIDDEN_FILES_ID,
22+
SHOW_IN_FINDER_ID, SORT_BY_EXTENSION_ID, SORT_BY_MODIFIED_ID, SORT_BY_NAME_ID, SORT_BY_SIZE_ID, SWAP_PANES_ID,
23+
SWITCH_PANE_ID, VIEW_MODE_BRIEF_LEFT_ID, VIEW_MODE_BRIEF_RIGHT_ID, VIEW_MODE_FULL_LEFT_ID, VIEW_MODE_FULL_RIGHT_ID,
24+
ViewMode,
2425
};
2526

2627
pub(crate) fn build_menu_macos<R: Runtime>(
@@ -136,8 +137,6 @@ pub(crate) fn build_menu_macos<R: Runtime>(
136137
let edit_copy_item = MenuItem::with_id(app, EDIT_COPY_ID, "Copy", true, Some("Cmd+C"))?;
137138
let edit_paste_item = MenuItem::with_id(app, EDIT_PASTE_ID, "Paste", true, Some("Cmd+V"))?;
138139
let edit_paste_move_item = MenuItem::with_id(app, EDIT_PASTE_MOVE_ID, "Move here", true, Some("Alt+Cmd+V"))?;
139-
let select_all_item = MenuItem::with_id(app, SELECT_ALL_ID, "Select all", true, Some("Cmd+A"))?;
140-
let deselect_all_item = MenuItem::with_id(app, DESELECT_ALL_ID, "Deselect all", true, Some("Cmd+Shift+A"))?;
141140
let copy_path_item = MenuItem::with_id(app, COPY_PATH_ID, "Copy path", true, Some(copy_path_accelerator()))?;
142141
let copy_filename_item = MenuItem::with_id(app, COPY_FILENAME_ID, "Copy filename", true, None::<&str>)?;
143142
let search_files_item = MenuItem::with_id(app, SEARCH_FILES_ID, "Search files", true, Some("Cmd+F"))?;
@@ -155,9 +154,6 @@ pub(crate) fn build_menu_macos<R: Runtime>(
155154
&edit_paste_item,
156155
&edit_paste_move_item,
157156
&PredefinedMenuItem::separator(app)?,
158-
&select_all_item,
159-
&deselect_all_item,
160-
&PredefinedMenuItem::separator(app)?,
161157
&copy_path_item,
162158
&copy_filename_item,
163159
&PredefinedMenuItem::separator(app)?,
@@ -166,6 +162,31 @@ pub(crate) fn build_menu_macos<R: Runtime>(
166162
)?;
167163
menu.append(&edit_menu)?;
168164

165+
// --- Select menu ---
166+
// Lives between Edit and View. Holds the selection commands: Select all / Deselect all
167+
// (formerly in Edit), and the two new pattern-based dialog openers.
168+
// The dialog openers carry no menu accelerator: macOS menu accelerators always carry
169+
// a modifier (Cmd), and the bare `+` / `-` keystrokes are bound in FilePane's keydown
170+
// handler instead. The labels show no accelerator badge as a result.
171+
let select_all_item = MenuItem::with_id(app, SELECT_ALL_ID, "Select all", true, Some("Cmd+A"))?;
172+
let deselect_all_item = MenuItem::with_id(app, DESELECT_ALL_ID, "Deselect all", true, Some("Cmd+Shift+A"))?;
173+
let select_files_item = MenuItem::with_id(app, SELECT_FILES_ID, "Select files\u{2026}", true, None::<&str>)?;
174+
let deselect_files_item = MenuItem::with_id(app, DESELECT_FILES_ID, "Deselect files\u{2026}", true, None::<&str>)?;
175+
176+
let select_menu = Submenu::with_items(
177+
app,
178+
"Select",
179+
true,
180+
&[
181+
&select_all_item,
182+
&deselect_all_item,
183+
&PredefinedMenuItem::separator(app)?,
184+
&select_files_item,
185+
&deselect_files_item,
186+
],
187+
)?;
188+
menu.append(&select_menu)?;
189+
169190
// --- View menu ---
170191
// View > Left pane > {Full, Brief} and View > Right pane > {Full, Brief}.
171192
// Both pairs always exist; only the active pane's pair carries the keyboard
@@ -364,17 +385,24 @@ pub(crate) fn build_menu_macos<R: Runtime>(
364385
register_item(&mut items, QUICK_LOOK_ID, &quick_look_item, &file_menu, 14);
365386

366387
// Edit menu positions: undo(0), redo(1), sep(2), cut(3), copy(4), paste(5), move_here(6),
367-
// sep(7), select_all(8), deselect_all(9), sep(10), copy_path(11), copy_filename(12),
368-
// sep(13), search_files(14)
388+
// sep(7), copy_path(8), copy_filename(9), sep(10), search_files(11)
369389
register_item(&mut items, EDIT_CUT_ID, &edit_cut_item, &edit_menu, 3);
370390
register_item(&mut items, EDIT_COPY_ID, &edit_copy_item, &edit_menu, 4);
371391
register_item(&mut items, EDIT_PASTE_ID, &edit_paste_item, &edit_menu, 5);
372392
register_item(&mut items, EDIT_PASTE_MOVE_ID, &edit_paste_move_item, &edit_menu, 6);
373-
register_item(&mut items, SELECT_ALL_ID, &select_all_item, &edit_menu, 8);
374-
register_item(&mut items, DESELECT_ALL_ID, &deselect_all_item, &edit_menu, 9);
375-
register_item(&mut items, COPY_PATH_ID, &copy_path_item, &edit_menu, 11);
376-
register_item(&mut items, COPY_FILENAME_ID, &copy_filename_item, &edit_menu, 12);
377-
register_item(&mut items, SEARCH_FILES_ID, &search_files_item, &edit_menu, 14);
393+
register_item(&mut items, COPY_PATH_ID, &copy_path_item, &edit_menu, 8);
394+
register_item(&mut items, COPY_FILENAME_ID, &copy_filename_item, &edit_menu, 9);
395+
register_item(&mut items, SEARCH_FILES_ID, &search_files_item, &edit_menu, 11);
396+
397+
// Select menu positions: select_all(0), deselect_all(1), sep(2), select_files(3),
398+
// deselect_files(4). The two `…` items carry no accelerator: bare `+`/`-` aren't valid
399+
// macOS menu accelerators (those always carry Cmd), so the keystroke binding lives in
400+
// FilePane's keydown handler. The items are still registered so a future user-customized
401+
// shortcut could flow into the menu via the generic update path.
402+
register_item(&mut items, SELECT_ALL_ID, &select_all_item, &select_menu, 0);
403+
register_item(&mut items, DESELECT_ALL_ID, &deselect_all_item, &select_menu, 1);
404+
register_item(&mut items, SELECT_FILES_ID, &select_files_item, &select_menu, 3);
405+
register_item(&mut items, DESELECT_FILES_ID, &deselect_files_item, &select_menu, 4);
378406

379407
// View menu positions: full(0), brief(1), sep(2), hidden(3), sort(4), zoom(5), sep(6),
380408
// switch(7), swap(8), sep(9), command(10)
@@ -572,12 +600,16 @@ fn set_macos_menu_icons_inner() {
572600
("Copy", "document.on.document"),
573601
("Paste", "clipboard"),
574602
("Move here", "document.on.clipboard"),
575-
("Select all", "checkmark.circle"),
576-
("Deselect all", "circle"),
577603
("Copy path", "link"),
578604
("Copy filename", "textformat"),
579605
("Search files", "magnifyingglass"),
580606
],
607+
"Select" => &[
608+
("Select all", "checkmark.circle"),
609+
("Deselect all", "circle"),
610+
("Select files\u{2026}", "plus.circle"),
611+
("Deselect files\u{2026}", "minus.circle"),
612+
],
581613
"View" => {
582614
// Also apply icons to the "Sort by" submenu items
583615
apply_sf_symbols_to_nested_submenu(

0 commit comments

Comments
 (0)