Fix add button. Fix hotkeys in settings. Add Workspace hotkeys
This commit is contained in:
@@ -5,7 +5,7 @@ import Combine
|
||||
/// Manages global and local hotkeys.
|
||||
///
|
||||
/// The toggle hotkey uses Carbon's `RegisterEventHotKey` which works
|
||||
/// system-wide without Accessibility permission. Tab-level hotkeys
|
||||
/// system-wide without Accessibility permission. Notch-scoped hotkeys
|
||||
/// use a local `NSEvent` monitor (only fires when our app is active).
|
||||
@MainActor
|
||||
class HotkeyManager {
|
||||
@@ -19,21 +19,29 @@ class HotkeyManager {
|
||||
var onCloseTab: (() -> Void)?
|
||||
var onNextTab: (() -> Void)?
|
||||
var onPreviousTab: (() -> Void)?
|
||||
var onNextWorkspace: (() -> Void)?
|
||||
var onPreviousWorkspace: (() -> Void)?
|
||||
var onDetachTab: (() -> Void)?
|
||||
var onApplySizePreset: ((TerminalSizePreset) -> Void)?
|
||||
var onSwitchToTab: ((Int) -> Void)?
|
||||
var onSwitchToWorkspace: ((WorkspaceID) -> Void)?
|
||||
|
||||
/// Tab-level hotkeys only fire when the notch is open.
|
||||
/// Notch-scoped hotkeys only fire when the notch is open.
|
||||
var isNotchOpen: Bool = false
|
||||
|
||||
private var hotKeyRef: EventHotKeyRef?
|
||||
private var eventHandlerRef: EventHandlerRef?
|
||||
private var localMonitor: Any?
|
||||
private let settingsProvider: TerminalSessionConfigurationProviding
|
||||
private let workspaceRegistry: WorkspaceRegistry
|
||||
private var settingsCancellable: AnyCancellable?
|
||||
|
||||
init(settingsProvider: TerminalSessionConfigurationProviding? = nil) {
|
||||
init(
|
||||
settingsProvider: TerminalSessionConfigurationProviding? = nil,
|
||||
workspaceRegistry: WorkspaceRegistry? = nil
|
||||
) {
|
||||
self.settingsProvider = settingsProvider ?? AppSettingsController.shared
|
||||
self.workspaceRegistry = workspaceRegistry ?? WorkspaceRegistry.shared
|
||||
}
|
||||
|
||||
// MARK: - Resolved bindings from typed runtime settings
|
||||
@@ -53,6 +61,12 @@ class HotkeyManager {
|
||||
private var prevTabBinding: HotkeyBinding {
|
||||
settingsProvider.hotkeySettings.previousTab
|
||||
}
|
||||
private var nextWorkspaceBinding: HotkeyBinding {
|
||||
settingsProvider.hotkeySettings.nextWorkspace
|
||||
}
|
||||
private var previousWorkspaceBinding: HotkeyBinding {
|
||||
settingsProvider.hotkeySettings.previousWorkspace
|
||||
}
|
||||
private var detachBinding: HotkeyBinding {
|
||||
settingsProvider.hotkeySettings.detachTab
|
||||
}
|
||||
@@ -173,7 +187,7 @@ class HotkeyManager {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Local monitor (tab-level hotkeys, only when our app is active)
|
||||
// MARK: - Local monitor (notch-level hotkeys, only when our app is active)
|
||||
|
||||
private func installLocalMonitor() {
|
||||
localMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
|
||||
@@ -189,9 +203,9 @@ class HotkeyManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles tab-level hotkeys. Returns true if the event was consumed.
|
||||
/// Handles notch-scoped hotkeys. Returns true if the event was consumed.
|
||||
private func handleLocalKeyEvent(_ event: NSEvent) -> Bool {
|
||||
// Tab hotkeys only when the notch is open and focused
|
||||
// Local shortcuts only fire when the notch is open and focused.
|
||||
guard isNotchOpen else { return false }
|
||||
|
||||
if newTabBinding.matches(event) {
|
||||
@@ -210,10 +224,25 @@ class HotkeyManager {
|
||||
onPreviousTab?()
|
||||
return true
|
||||
}
|
||||
if nextWorkspaceBinding.matches(event) {
|
||||
onNextWorkspace?()
|
||||
return true
|
||||
}
|
||||
if previousWorkspaceBinding.matches(event) {
|
||||
onPreviousWorkspace?()
|
||||
return true
|
||||
}
|
||||
if detachBinding.matches(event) {
|
||||
onDetachTab?()
|
||||
return true
|
||||
}
|
||||
for summary in workspaceRegistry.workspaceSummaries {
|
||||
guard let binding = summary.hotkey else { continue }
|
||||
if binding.matches(event) {
|
||||
onSwitchToWorkspace?(summary.id)
|
||||
return true
|
||||
}
|
||||
}
|
||||
for preset in sizePresets {
|
||||
guard let binding = preset.hotkey else { continue }
|
||||
if binding.matches(event) {
|
||||
|
||||
@@ -9,6 +9,7 @@ final class ScreenManager: ObservableObject {
|
||||
static let shared = ScreenManager()
|
||||
|
||||
private let screenRegistry = ScreenRegistry.shared
|
||||
private let workspaceRegistry = WorkspaceRegistry.shared
|
||||
private let windowCoordinator = WindowCoordinator()
|
||||
private lazy var orchestrator = NotchOrchestrator(screenRegistry: screenRegistry, host: self)
|
||||
|
||||
@@ -55,6 +56,12 @@ final class ScreenManager: ObservableObject {
|
||||
hotkeyManager.onPreviousTab = { [weak self] in
|
||||
MainActor.assumeIsolated { self?.activeWorkspace().previousTab() }
|
||||
}
|
||||
hotkeyManager.onNextWorkspace = { [weak self] in
|
||||
MainActor.assumeIsolated { self?.switchWorkspace(offset: 1) }
|
||||
}
|
||||
hotkeyManager.onPreviousWorkspace = { [weak self] in
|
||||
MainActor.assumeIsolated { self?.switchWorkspace(offset: -1) }
|
||||
}
|
||||
hotkeyManager.onDetachTab = { [weak self] in
|
||||
MainActor.assumeIsolated { self?.detachActiveTab() }
|
||||
}
|
||||
@@ -64,6 +71,9 @@ final class ScreenManager: ObservableObject {
|
||||
hotkeyManager.onSwitchToTab = { [weak self] index in
|
||||
MainActor.assumeIsolated { self?.activeWorkspace().switchToTab(at: index) }
|
||||
}
|
||||
hotkeyManager.onSwitchToWorkspace = { [weak self] workspaceID in
|
||||
MainActor.assumeIsolated { self?.switchActiveScreen(to: workspaceID) }
|
||||
}
|
||||
|
||||
hotkeyManager.start()
|
||||
}
|
||||
@@ -92,6 +102,33 @@ final class ScreenManager: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
private func switchWorkspace(offset: Int) {
|
||||
guard let screenID = screenRegistry.activeScreenID() else { return }
|
||||
let currentWorkspaceID = screenRegistry.screenContext(for: screenID)?.workspaceID ?? workspaceRegistry.defaultWorkspaceID
|
||||
let nextWorkspaceID = offset >= 0
|
||||
? workspaceRegistry.nextWorkspaceID(after: currentWorkspaceID)
|
||||
: workspaceRegistry.previousWorkspaceID(before: currentWorkspaceID)
|
||||
|
||||
guard let nextWorkspaceID else { return }
|
||||
switchScreen(screenID, to: nextWorkspaceID)
|
||||
}
|
||||
|
||||
private func switchActiveScreen(to workspaceID: WorkspaceID) {
|
||||
guard let screenID = screenRegistry.activeScreenID() else { return }
|
||||
switchScreen(screenID, to: workspaceID)
|
||||
}
|
||||
|
||||
private func switchScreen(_ screenID: ScreenID, to workspaceID: WorkspaceID) {
|
||||
screenRegistry.assignWorkspace(workspaceID, to: screenID)
|
||||
|
||||
guard let context = screenRegistry.screenContext(for: screenID),
|
||||
context.notchState == .open else {
|
||||
return
|
||||
}
|
||||
|
||||
orchestrator.open(screenID: screenID)
|
||||
}
|
||||
|
||||
func applySizePreset(_ preset: TerminalSizePreset) {
|
||||
guard let context = screenRegistry.allScreens().first(where: { $0.notchState == .open }) else {
|
||||
AppSettingsController.shared.update {
|
||||
|
||||
Reference in New Issue
Block a user