diff --git a/CHANGELOG.md b/CHANGELOG.md index dee8192c3..b46a4c98f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - `Cmd+C` now copies the focused cell value when one row is selected and a cell has focus; with multiple rows selected, or when no cell is focused, it still copies row(s) as TSV. `Cmd+Shift+C` now always copies row(s) as TSV. "Copy with Headers" stays in the Edit menu and row context menu without a default shortcut (#1332) -- `Cmd+F` toggles the data-grid filter panel when the grid is focused; the SQL editor's Find panel still opens with `Cmd+F` when the editor is focused. The old `Cmd+Shift+F` shortcut for filters is removed +- `Cmd+F` toggles the filter panel when viewing a table, and opens the Find panel in the SQL editor. The old `Cmd+Shift+F` shortcut for filters is removed ### Fixed diff --git a/TablePro/Models/UI/KeyboardShortcutModels.swift b/TablePro/Models/UI/KeyboardShortcutModels.swift index 11605e159..2aaa58709 100644 --- a/TablePro/Models/UI/KeyboardShortcutModels.swift +++ b/TablePro/Models/UI/KeyboardShortcutModels.swift @@ -499,6 +499,7 @@ struct KeyboardSettings: Codable, Equatable { // View .toggleTableBrowser: KeyCombo(key: "0", command: true), .toggleInspector: KeyCombo(key: "i", command: true, option: true), + .toggleFilters: KeyCombo(key: "f", command: true), .toggleHistory: KeyCombo(key: "y", command: true), .toggleResults: KeyCombo(key: "r", command: true, option: true), .previousResultTab: KeyCombo(key: "[", command: true, option: true), diff --git a/TablePro/TableProApp.swift b/TablePro/TableProApp.swift index b22c6add7..6d2d4b5b3 100644 --- a/TablePro/TableProApp.swift +++ b/TablePro/TableProApp.swift @@ -106,6 +106,23 @@ struct PasteboardCommands: Commands { // MARK: - App Menu Commands +/// Where `Cmd+F` resolves in the current context. The data-grid filter lives in +/// the View menu and the editor's Find lives in the Edit menu. Only the item +/// matching the current route binds `Cmd+F`; the other drops it. Two items +/// sharing one key equivalent makes SwiftUI dedupe the shortcut and AppKit bind +/// it to the disabled item, so the live owner must be unique. +enum CommandFRoute { + case inspectorFilter + case tableFilter + case editorFind + + static func resolve(isInspector: Bool, isTableTab: Bool) -> CommandFRoute { + if isInspector { return .inspectorFilter } + if isTableTab { return .tableFilter } + return .editorFind + } +} + /// All menu commands extracted into a separate Commands struct so that AppState /// changes only re-evaluate the menu items — NOT the Scene body / WindowGroups. struct AppMenuCommands: Commands { @@ -152,6 +169,10 @@ struct AppMenuCommands: Commands { NSApp.keyWindow?.windowController is InspectorWindowController } + private var commandFRoute: CommandFRoute { + CommandFRoute.resolve(isInspector: keyWindowIsInspector, isTableTab: actions?.isTableTab == true) + } + var body: some Commands { // Custom About window + Check for Updates + MCP status CommandGroup(replacing: .appInfo) { @@ -468,16 +489,17 @@ struct AppMenuCommands: Commands { Divider() Button(String(localized: "Find...")) { - if keyWindowIsInspector { + switch commandFRoute { + case .inspectorFilter: NSApp.sendAction(#selector(InspectorViewController.toggleInspectorFilter(_:)), to: nil, from: nil) - } else if NSApp.keyWindow?.firstResponder is KeyHandlingTableView, - actions?.isTableTab == true { - actions?.toggleFilterPanel() - } else { + case .editorFind: EditorEventRouter.shared.showFindPanelForKeyWindow() + case .tableFilter: + break } } - .keyboardShortcut("f", modifiers: .command) + .optionalKeyboardShortcut(commandFRoute == .tableFilter ? nil : KeyboardShortcut("f", modifiers: .command)) + .disabled(commandFRoute == .tableFilter) Divider() @@ -521,8 +543,8 @@ struct AppMenuCommands: Commands { Button("Toggle Filters") { actions?.toggleFilterPanel() } - .optionalKeyboardShortcut(shortcut(for: .toggleFilters)) - .disabled(!(actions?.isConnected ?? false) || !(actions?.isTableTab ?? false)) + .optionalKeyboardShortcut(commandFRoute == .tableFilter ? shortcut(for: .toggleFilters) : nil) + .disabled(commandFRoute != .tableFilter || !(actions?.isConnected ?? false)) Button("Toggle History") { actions?.toggleHistoryPanel() diff --git a/TableProTests/Views/CommandFRouteTests.swift b/TableProTests/Views/CommandFRouteTests.swift new file mode 100644 index 000000000..dcc70ea8f --- /dev/null +++ b/TableProTests/Views/CommandFRouteTests.swift @@ -0,0 +1,29 @@ +// +// CommandFRouteTests.swift +// TableProTests +// +// Tests for CommandFRoute, which decides where Cmd+F resolves so exactly one +// menu item owns the shortcut per context. +// + +@testable import TablePro +import Testing + +@Suite("CommandFRouteTests") +struct CommandFRouteTests { + @Test("Inspector window takes priority over tab type") + func inspectorWins() { + #expect(CommandFRoute.resolve(isInspector: true, isTableTab: true) == .inspectorFilter) + #expect(CommandFRoute.resolve(isInspector: true, isTableTab: false) == .inspectorFilter) + } + + @Test("Table tab routes to the View-menu filter toggle") + func tableTabFilters() { + #expect(CommandFRoute.resolve(isInspector: false, isTableTab: true) == .tableFilter) + } + + @Test("Everything else routes to the editor Find panel") + func otherwiseFinds() { + #expect(CommandFRoute.resolve(isInspector: false, isTableTab: false) == .editorFind) + } +} diff --git a/docs/features/filtering.mdx b/docs/features/filtering.mdx index 9bcbff9b6..5d4c1456b 100644 --- a/docs/features/filtering.mdx +++ b/docs/features/filtering.mdx @@ -5,7 +5,7 @@ description: Filter table data with 18 operators, raw SQL, and saved presets # Filtering -Press `Cmd+F` with the data grid focused to open the filter panel. Type a raw SQL WHERE clause and press `Enter` to filter. Raw SQL is the default mode. +Press `Cmd+F` while viewing a table to open the filter panel. Type a raw SQL WHERE clause and press `Enter` to filter. Raw SQL is the default mode. Each row has a column picker, operator, value field, and **+**/**−** buttons. Multiple rows combine with **AND** or **OR** (toggle in the header). Click **Apply** or press `Enter` to activate. Click **Unset** to clear all. diff --git a/docs/features/keyboard-shortcuts.mdx b/docs/features/keyboard-shortcuts.mdx index 11aa941b1..9c0584479 100644 --- a/docs/features/keyboard-shortcuts.mdx +++ b/docs/features/keyboard-shortcuts.mdx @@ -71,7 +71,7 @@ TablePro is keyboard-driven. Most actions have shortcuts, and most menu shortcut | Action | Shortcut | |--------|----------| -| Find (SQL editor focused) | `Cmd+F` | +| Find (in SQL editor) | `Cmd+F` | | Find and replace | `Cmd+Option+F` | | Find next | `Cmd+G` | | Find previous | `Cmd+Shift+G` | @@ -151,7 +151,7 @@ Inherits the standard `NSDocument` shortcuts. | Save | `Cmd+S` | | Save As | `Cmd+Shift+S` | | Revert to Saved | `Cmd+Option+S` | -| Toggle filter bar | `Cmd+F` | +| Toggle filter panel | `Cmd+F` | | Copy selected rows as TSV | `Cmd+C` | | Paste TSV as new rows | `Cmd+V` | | Add secondary sort column | `Shift+Click` column header | @@ -255,7 +255,7 @@ Vim keys only apply in the SQL editor. The data grid and other panels keep their | Action | Shortcut | |--------|----------| -| Toggle filter panel (data grid focused) | `Cmd+F` | +| Toggle filter panel (table tab) | `Cmd+F` | | Apply filters | `Enter` (in value field) | ## Table Structure