Skip to content

Commit 2079963

Browse files
committed
fix(toolbar): show real keyboard shortcuts in button tooltips (#1694)
1 parent 8759a94 commit 2079963

7 files changed

Lines changed: 65 additions & 27 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2525
- DuckDB VARIANT columns now show their value as text instead of an empty cell.
2626
- A new database group now appears in the connection list right away instead of only after restarting the app. (#1704)
2727
- The SQL formatter keeps nested indentation for UNION, UNION ALL, INTERSECT, and EXCEPT inside a derived table or CTE, and puts the closing parenthesis of a subquery on its own line instead of collapsing it onto the last SELECT. (#1698)
28+
- Toolbar button tooltips now show each action's real keyboard shortcut and follow your custom bindings, instead of a fixed value. The Switch Connection tooltip showed the wrong shortcut. (#1694)
2829

2930
## [0.51.1] - 2026-06-16
3031

TablePro/Core/Services/Infrastructure/MainWindowToolbar+Buttons.swift

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ struct ConnectionToolbarButton: View {
1717
} label: {
1818
Label("Connection", systemImage: "network")
1919
}
20-
.help(String(localized: "Switch Connection (⌘⌥C)"))
20+
.help(AppSettingsManager.shared.keyboard.shortcutHint(String(localized: "Switch Connection"), for: .switchConnection))
2121
.popover(isPresented: $coordinator.isConnectionSwitcherShown, arrowEdge: .bottom) {
2222
ConnectionSwitcherPopover()
2323
}
@@ -37,7 +37,7 @@ struct DatabaseToolbarButton: View {
3737
} label: {
3838
Label(containerName, systemImage: "cylinder")
3939
}
40-
.help(String(format: String(localized: "Open %@ (⌘K)"), containerName))
40+
.help(AppSettingsManager.shared.keyboard.shortcutHint(String(format: String(localized: "Open %@"), containerName), for: .openDatabase))
4141
.disabled(
4242
state.connectionState != .connected
4343
|| PluginManager.shared.connectionMode(for: state.databaseType) == .fileBased
@@ -89,7 +89,7 @@ struct RefreshToolbarButton: View {
8989
} label: {
9090
Label("Refresh", systemImage: "arrow.clockwise")
9191
}
92-
.help(String(localized: "Refresh (⌘R)"))
92+
.help(AppSettingsManager.shared.keyboard.shortcutHint(String(localized: "Refresh"), for: .refresh))
9393
.disabled(state.connectionState != .connected)
9494
}
9595
}
@@ -104,7 +104,7 @@ struct SaveChangesToolbarButton: View {
104104
} label: {
105105
Label("Save Changes", systemImage: "checkmark.circle.fill")
106106
}
107-
.help(String(localized: "Save Changes (⌘S)"))
107+
.help(AppSettingsManager.shared.keyboard.shortcutHint(String(localized: "Save Changes"), for: .saveChanges))
108108
.disabled(
109109
!state.hasPendingChanges
110110
|| state.connectionState != .connected
@@ -124,7 +124,7 @@ struct QuickSwitcherToolbarButton: View {
124124
} label: {
125125
Label("Quick Switcher", systemImage: "magnifyingglass")
126126
}
127-
.help(String(localized: "Quick Switcher (⇧⌘O)"))
127+
.help(AppSettingsManager.shared.keyboard.shortcutHint(String(localized: "Quick Switcher"), for: .quickSwitcher))
128128
.disabled(state.connectionState != .connected)
129129
}
130130
}
@@ -139,7 +139,7 @@ struct NewTabToolbarButton: View {
139139
} label: {
140140
Label("New Tab", systemImage: "plus.rectangle")
141141
}
142-
.help(String(localized: "New Query Tab (⌘T)"))
142+
.help(AppSettingsManager.shared.keyboard.shortcutHint(String(localized: "New Query Tab"), for: .newTab))
143143
.disabled(state.connectionState != .connected)
144144
}
145145
}
@@ -149,13 +149,14 @@ struct PreviewSQLToolbarButton: View {
149149

150150
var body: some View {
151151
let state = coordinator.toolbarState
152+
let langName = PluginManager.shared.queryLanguageName(for: state.databaseType)
153+
let previewLabel = String(format: String(localized: "Preview %@"), langName)
152154
Button {
153155
coordinator.commandActions?.previewSQL()
154156
} label: {
155-
let langName = PluginManager.shared.queryLanguageName(for: state.databaseType)
156-
Label(String(format: String(localized: "Preview %@"), langName), systemImage: "eye")
157+
Label(previewLabel, systemImage: "eye")
157158
}
158-
.help(String(format: String(localized: "Preview %@ (⌘⇧P)"), PluginManager.shared.queryLanguageName(for: state.databaseType)))
159+
.help(AppSettingsManager.shared.keyboard.shortcutHint(previewLabel, for: .previewSQL))
159160
.disabled(!state.hasDataPendingChanges || state.connectionState != .connected)
160161
}
161162
}
@@ -175,7 +176,7 @@ struct ResultsToolbarButton: View {
175176
: "rectangle.inset.filled"
176177
)
177178
}
178-
.help(String(localized: "Toggle Results (⌘⌥R)"))
179+
.help(AppSettingsManager.shared.keyboard.shortcutHint(String(localized: "Toggle Results"), for: .toggleResults))
179180
.disabled(state.connectionState != .connected || state.isTableTab)
180181
}
181182
}
@@ -205,7 +206,7 @@ struct HistoryToolbarButton: View {
205206
} label: {
206207
Label("History", systemImage: "clock")
207208
}
208-
.help(String(localized: "Toggle Query History (⌘Y)"))
209+
.help(AppSettingsManager.shared.keyboard.shortcutHint(String(localized: "Toggle Query History"), for: .toggleHistory))
209210
}
210211
}
211212

@@ -219,7 +220,7 @@ struct ExportToolbarButton: View {
219220
} label: {
220221
Label("Export", systemImage: "square.and.arrow.up")
221222
}
222-
.help(String(localized: "Export Data (⌘⇧E)"))
223+
.help(AppSettingsManager.shared.keyboard.shortcutHint(String(localized: "Export Data"), for: .export))
223224
.disabled(state.connectionState != .connected)
224225
}
225226
}
@@ -238,7 +239,7 @@ struct ImportToolbarButton: View {
238239
} label: {
239240
Label("Import", systemImage: "square.and.arrow.down")
240241
}
241-
.help(String(localized: "Import Data (⌘⇧I)"))
242+
.help(AppSettingsManager.shared.keyboard.shortcutHint(String(localized: "Import Data"), for: .importData))
242243
.disabled(isDisabled || formats.isEmpty)
243244
} else {
244245
Menu {
@@ -250,7 +251,7 @@ struct ImportToolbarButton: View {
250251
} label: {
251252
Label("Import", systemImage: "square.and.arrow.down")
252253
}
253-
.help(String(localized: "Import Data (⌘⇧I)"))
254+
.help(AppSettingsManager.shared.keyboard.shortcutHint(String(localized: "Import Data"), for: .importData))
254255
.disabled(isDisabled)
255256
}
256257
}

TablePro/Models/UI/KeyboardShortcutModels.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,15 @@ struct KeyboardSettings: Codable, Equatable {
375375
return KeyboardShortcut(equivalent, modifiers: key.eventModifiers)
376376
}
377377

378+
/// A tooltip/help string that appends the action's resolved shortcut, e.g.
379+
/// "Switch Connection (⌃⌘C)". Returns just the label when the shortcut is
380+
/// cleared or unset. Reflects user overrides because it resolves through
381+
/// `shortcut(for:)`.
382+
func shortcutHint(_ label: String, for action: ShortcutAction) -> String {
383+
guard let key = shortcut(for: action), !key.isCleared else { return label }
384+
return "\(label) (\(key.displayString))"
385+
}
386+
378387
// MARK: - Default Shortcuts
379388

380389
/// Default shortcuts, applied when the user has no override. An action absent

TablePro/Views/Components/PaginationControlsView.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,7 @@ struct PaginationControlsView: View {
139139
}
140140

141141
private func helpText(_ label: String, for shortcut: ShortcutAction) -> String {
142-
guard let combo = AppSettingsManager.shared.keyboard.shortcut(for: shortcut), !combo.isCleared else {
143-
return label
144-
}
145-
return "\(label) (\(combo.displayString))"
142+
AppSettingsManager.shared.keyboard.shortcutHint(label, for: shortcut)
146143
}
147144

148145
private var pageIndicator: some View {

TablePro/Views/Editor/QueryEditorView.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,7 @@ struct QueryEditorView: View {
142142
// MARK: - Helpers
143143

144144
private func shortcutHint(_ label: String, for action: ShortcutAction) -> String {
145-
guard let combo = AppSettingsManager.shared.keyboard.shortcut(for: action), !combo.isCleared else {
146-
return label
147-
}
148-
return "\(label) (\(combo.displayString))"
145+
AppSettingsManager.shared.keyboard.shortcutHint(label, for: action)
149146
}
150147

151148
@ViewBuilder

TablePro/Views/Main/Child/MainStatusBarView.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,7 @@ struct MainStatusBarView: View {
6161
}
6262

6363
private func helpText(_ label: String, shortcut action: ShortcutAction) -> String {
64-
guard let combo = AppSettingsManager.shared.keyboard.shortcut(for: action),
65-
!combo.isCleared else {
66-
return label
67-
}
68-
return "\(label) (\(combo.displayString))"
64+
AppSettingsManager.shared.keyboard.shortcutHint(label, for: action)
6965
}
7066

7167
private var columnsAccessibilityLabel: String {

TableProTests/Models/KeyboardShortcutTests.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,3 +251,40 @@ struct KeyboardSettingsMigrationTests {
251251
#expect(settings.shortcut(for: .executeQuery)?.isCleared == true)
252252
}
253253
}
254+
255+
@Suite("Shortcut hint")
256+
struct ShortcutHintTests {
257+
@Test("Switch Connection default hint shows Control+Command+C")
258+
func switchConnectionDefaultHint() {
259+
let hint = KeyboardSettings.default.shortcutHint(
260+
String(localized: "Switch Connection"),
261+
for: .switchConnection
262+
)
263+
#expect(hint == "Switch Connection (⌃⌘C)")
264+
}
265+
266+
@Test("Hint reflects a user override")
267+
func hintReflectsOverride() {
268+
var settings = KeyboardSettings.default
269+
settings.setShortcut(.character("j", command: true), for: .switchConnection)
270+
let hint = settings.shortcutHint(String(localized: "Switch Connection"), for: .switchConnection)
271+
#expect(hint == "Switch Connection (⌘J)")
272+
}
273+
274+
@Test("Cleared shortcut shows label only")
275+
func clearedShortcutShowsLabelOnly() {
276+
var settings = KeyboardSettings.default
277+
settings.clearShortcut(for: .switchConnection)
278+
let hint = settings.shortcutHint(String(localized: "Switch Connection"), for: .switchConnection)
279+
#expect(hint == "Switch Connection")
280+
}
281+
282+
@Test("Action without a default shows label only")
283+
func unsetShortcutShowsLabelOnly() {
284+
let hint = KeyboardSettings.default.shortcutHint(
285+
String(localized: "Manage Connections"),
286+
for: .manageConnections
287+
)
288+
#expect(hint == "Manage Connections")
289+
}
290+
}

0 commit comments

Comments
 (0)