From 79c635bb9973748df376664743a7aefad7d34830 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Sat, 30 May 2026 13:57:51 +0700 Subject: [PATCH] fix(tabs): announce the active tab to VoiceOver on tab switch (#1490) --- CHANGELOG.md | 1 + .../MainContentCoordinator+WindowLifecycle.swift | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a74cc19e..e9f8a11c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- VoiceOver announces the active tab title when you switch between window tabs. (#1490) - Escape now dismisses search-based sheets and popovers (database switcher, quick switcher, column and connection pickers). Pressing Escape clears the search text first; a second Escape closes the sheet. (#1490) - Running `EXPLAIN` or `EXPLAIN ANALYZE` typed in the editor now opens the plan viewer instead of squashing the plan into one truncated grid cell. (#1480) - Filtering the data grid keeps you on the keyboard. Applying or clearing a filter returns focus to the grid so you can keep moving through cells, Return applies the filter, and Escape closes the filter panel and returns to the grid. (#1490) diff --git a/TablePro/Views/Main/Extensions/MainContentCoordinator+WindowLifecycle.swift b/TablePro/Views/Main/Extensions/MainContentCoordinator+WindowLifecycle.swift index 540cb75e2..cab08a350 100644 --- a/TablePro/Views/Main/Extensions/MainContentCoordinator+WindowLifecycle.swift +++ b/TablePro/Views/Main/Extensions/MainContentCoordinator+WindowLifecycle.swift @@ -30,6 +30,7 @@ extension MainContentCoordinator { evictionTask = nil syncSidebarToSelectedTab() + announceActiveTabToVoiceOver() Self.lifecycleLogger.debug( "[switch] coordinator.handleWindowDidBecomeKey done connId=\(self.connectionId, privacy: .public) totalMs=\(Int(Date().timeIntervalSince(t0) * 1_000))" @@ -82,6 +83,20 @@ extension MainContentCoordinator { ) } + /// Announce the active tab title to VoiceOver when the window becomes key, + /// so assistive-technology users get the same context the window title gives. + private func announceActiveTabToVoiceOver() { + guard let title = tabManager.selectedTab?.title, !title.isEmpty else { return } + NSAccessibility.post( + element: NSApp as Any, + notification: .announcementRequested, + userInfo: [ + .announcement: title, + .priority: NSAccessibilityPriorityLevel.medium.rawValue, + ] + ) + } + // MARK: - Sidebar Sync /// Update the window-scoped sidebar selection so the active table tab