feat: new cli commands: --show, --detailed-list (closes #4489)

This commit is contained in:
lwouis committed 2025-06-14 14:18:55 +02:00
1 parent bd162a9e08
commit b0ce899a71
7 files changed
+88 -30

No files matched your search

+2 -2
View File
@@ -57,7 +57,7 @@ class ATShortcut {
if triggerPhase == .down && (!App.app.appIsBeingUsed || index == nil || index == App.app.shortcutIndex) { if triggerPhase == .down && (!App.app.appIsBeingUsed || index == nil || index == App.app.shortcutIndex) {
return true return true
} }
if triggerPhase == .up && App.app.appIsBeingUsed && (index == nil || index == App.app.shortcutIndex) && Preferences.shortcutStyle[App.app.shortcutIndex] == .focusOnRelease { if triggerPhase == .up && App.app.appIsBeingUsed && (index == nil || index == App.app.shortcutIndex) && !App.app.forceDoNothingOnRelease && Preferences.shortcutStyle[App.app.shortcutIndex] == .focusOnRelease {
return true return true
} }
} }
@@ -83,7 +83,7 @@ class ATShortcut {
// Another issue is events being dropped by macOS, which we never receive // Another issue is events being dropped by macOS, which we never receive
// Knowing this, we handle these edge-cases by double checking if holdShortcut is UP, when any shortcut state is UP // Knowing this, we handle these edge-cases by double checking if holdShortcut is UP, when any shortcut state is UP
// If it is, then we trigger the holdShortcut action // If it is, then we trigger the holdShortcut action
if App.app.appIsBeingUsed && Preferences.shortcutStyle[App.app.shortcutIndex] == .focusOnRelease { if App.app.appIsBeingUsed && !App.app.forceDoNothingOnRelease && Preferences.shortcutStyle[App.app.shortcutIndex] == .focusOnRelease {
if let currentHoldShortcut = ControlsTab.shortcuts[Preferences.indexToName("holdShortcut", App.app.shortcutIndex)], if let currentHoldShortcut = ControlsTab.shortcuts[Preferences.indexToName("holdShortcut", App.app.shortcutIndex)],
id == currentHoldShortcut.id { id == currentHoldShortcut.id {
let currentModifiers = cocoaToCarbonFlags(ModifierFlags.current) let currentModifiers = cocoaToCarbonFlags(ModifierFlags.current)
+68 -18
View File
@@ -38,28 +38,56 @@ class CliServer {
static func executeCommandAndSendReponse(_ rawValue: String) -> Codable { static func executeCommandAndSendReponse(_ rawValue: String) -> Codable {
var output: Codable = "" var output: Codable = ""
DispatchQueue.main.sync { DispatchQueue.main.sync {
if rawValue == "--list" { output = executeCommandAndSendReponse_(rawValue)
output = JsonOutput(windows: Windows.list
.filter { !$0.isWindowlessApp }
.map { JsonWindow(id: $0.cgWindowId, title: $0.title) }
)
return
}
if rawValue.hasPrefix("--focus=") {
if let id = CGWindowID(rawValue.dropFirst("--focus=".count)),
let window = (Windows.list.first { $0.cgWindowId == id }) {
window.focus()
output = noOutput
return
}
}
output = error
} }
return output return output
} }
private static func executeCommandAndSendReponse_(_ rawValue: String) -> Codable {
if rawValue == "--list" {
return JsonWindowList(windows: Windows.list
.filter { !$0.isWindowlessApp }
.map { JsonWindow(id: $0.cgWindowId, title: $0.title) }
)
}
if rawValue == "--detailed-list" {
return JsonWindowFullList(windows: Windows.list
.filter { !$0.isWindowlessApp }
.map {
JsonWindowFull(
id: $0.cgWindowId,
title: $0.title,
appName: $0.application.localizedName,
appBundleId: $0.application.bundleIdentifier,
spaceIndexes: $0.spaceIndexes,
lastFocusOrder: $0.lastFocusOrder,
creationOrder: $0.creationOrder,
isTabbed: $0.isTabbed,
isHidden: $0.isHidden,
isFullscreen: $0.isFullscreen,
isMinimized: $0.isMinimized,
isOnAllSpaces: $0.isOnAllSpaces,
position: $0.position,
size: $0.size
)
}
)
}
if rawValue.hasPrefix("--focus="),
let id = CGWindowID(rawValue.dropFirst("--focus=".count)), let window = (Windows.list.first { $0.cgWindowId == id }) {
window.focus()
return noOutput
}
if rawValue.hasPrefix("--show="),
let shortcutIndex = Int(rawValue.dropFirst("--show=".count)), (0...3).contains(shortcutIndex) {
App.app.showUi(shortcutIndex)
return noOutput
}
return error
}
} }
struct JsonOutput: Codable { struct JsonWindowList: Codable {
var windows: [JsonWindow] var windows: [JsonWindow]
} }
@@ -68,11 +96,33 @@ struct JsonWindow: Codable {
var title: String var title: String
} }
struct JsonWindowFullList: Codable {
var windows: [JsonWindowFull]
}
struct JsonWindowFull: Codable {
var id: CGWindowID?
var title: String
// -- additional properties
var appName: String?
var appBundleId: String?
var spaceIndexes: [SpaceIndex]
var lastFocusOrder: Int
var creationOrder: Int
var isTabbed: Bool
var isHidden: Bool
var isFullscreen: Bool
var isMinimized: Bool
var isOnAllSpaces: Bool
var position: CGPoint?
var size: CGSize?
}
class CliClient { class CliClient {
static func detectCommand() -> String? { static func detectCommand() -> String? {
let args = CommandLine.arguments let args = CommandLine.arguments
if args.count == 2 && !args[1].starts(with: "--logs=") { if args.count == 2 && !args[1].starts(with: "--logs=") {
if args[1] == "--list" || args[1].hasPrefix("--focus=") { if args[1] == "--list" || args[1] == "--detailed-list" || args[1].hasPrefix("--focus=") || args[1].hasPrefix("--show=") {
return args[1] return args[1]
} }
} }
+2 -2
View File
@@ -86,7 +86,7 @@ class GestureDetector {
static func checkForFingersUp(_ touches: Set<NSTouch>, _ requiredFingers: Int) -> Bool { static func checkForFingersUp(_ touches: Set<NSTouch>, _ requiredFingers: Int) -> Bool {
if App.app.appIsBeingUsed && touches.count < requiredFingers && App.app.shortcutIndex == Preferences.gestureIndex if App.app.appIsBeingUsed && touches.count < requiredFingers && App.app.shortcutIndex == Preferences.gestureIndex
&& Preferences.shortcutStyle[App.app.shortcutIndex] == .focusOnRelease { && !App.app.forceDoNothingOnRelease && Preferences.shortcutStyle[App.app.shortcutIndex] == .focusOnRelease {
DispatchQueue.main.async { App.app.focusTarget() } DispatchQueue.main.async { App.app.focusTarget() }
return true return true
} }
@@ -140,7 +140,7 @@ class TriggerSwipeDetector {
let horizontal = Preferences.nextWindowGesture.isHorizontal() let horizontal = Preferences.nextWindowGesture.isHorizontal()
if (updateSwipeStillPossible(horizontal ? absY : absX) && (horizontal ? absX : absY) >= MIN_SWIPE_DISTANCE) { if (updateSwipeStillPossible(horizontal ? absY : absX) && (horizontal ? absX : absY) >= MIN_SWIPE_DISTANCE) {
reset() reset()
DispatchQueue.main.async { App.app.showUiOrCycleSelection(Preferences.gestureIndex) } DispatchQueue.main.async { App.app.showUiOrCycleSelection(Preferences.gestureIndex, false) }
return true return true
} }
return false return false
+10 -3
View File
@@ -23,6 +23,7 @@ class App: AppCenterApplication {
var permissionsWindow: PermissionsWindow! var permissionsWindow: PermissionsWindow!
var appIsBeingUsed = false var appIsBeingUsed = false
var shortcutIndex = 0 var shortcutIndex = 0
var forceDoNothingOnRelease = false
private var feedbackWindow: FeedbackWindow! private var feedbackWindow: FeedbackWindow!
private var isFirstSummon = true private var isFirstSummon = true
private var isVeryFirstSummon = true private var isVeryFirstSummon = true
@@ -73,6 +74,7 @@ class App: AppCenterApplication {
guard appIsBeingUsed else { return } // already hidden guard appIsBeingUsed else { return } // already hidden
appIsBeingUsed = false appIsBeingUsed = false
isFirstSummon = true isFirstSummon = true
forceDoNothingOnRelease = false
MouseEvents.toggle(false) MouseEvents.toggle(false)
hideThumbnailPanelWithoutChangingKeyWindow() hideThumbnailPanelWithoutChangingKeyWindow()
if !keepPreview { if !keepPreview {
@@ -156,8 +158,12 @@ class App: AppCenterApplication {
} }
} }
@objc func showUi() { func showUi(_ shortcutIndex: Int) {
showUiOrCycleSelection(0) showUiOrCycleSelection(shortcutIndex, true)
}
@objc func showUiFromShortcut0() {
showUi(0)
} }
@objc func showAboutTab() { @objc func showAboutTab() {
@@ -226,7 +232,8 @@ class App: AppCenterApplication {
Applications.refreshBadgesAsync() Applications.refreshBadgesAsync()
} }
func showUiOrCycleSelection(_ shortcutIndex: Int) { func showUiOrCycleSelection(_ shortcutIndex: Int, _ forceDoNothingOnRelease_: Bool) {
forceDoNothingOnRelease = forceDoNothingOnRelease_
Logger.debug(shortcutIndex, self.shortcutIndex, isFirstSummon) Logger.debug(shortcutIndex, self.shortcutIndex, isFirstSummon)
App.app.appIsBeingUsed = true App.app.appIsBeingUsed = true
if isFirstSummon || shortcutIndex != self.shortcutIndex { if isFirstSummon || shortcutIndex != self.shortcutIndex {
+2 -2
View File
@@ -19,7 +19,7 @@ class Menubar {
menu.addItem(NSMenuItem.separator()) menu.addItem(NSMenuItem.separator())
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("Show", comment: "Menubar option"), withTitle: NSLocalizedString("Show", comment: "Menubar option"),
action: #selector(App.app.showUi), action: #selector(App.app.showUiFromShortcut0),
keyEquivalent: "") keyEquivalent: "")
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("Preferences…", comment: "Menubar option"), withTitle: NSLocalizedString("Preferences…", comment: "Menubar option"),
@@ -67,7 +67,7 @@ class Menubar {
@objc static func statusItemOnClick() { @objc static func statusItemOnClick() {
// NSApp.currentEvent == nil if the icon is "clicked" through VoiceOver // NSApp.currentEvent == nil if the icon is "clicked" through VoiceOver
if let type = NSApp.currentEvent?.type, type != .leftMouseDown { if let type = NSApp.currentEvent?.type, type != .leftMouseDown {
App.app.showUi() App.app.showUiFromShortcut0()
} else { } else {
statusItem.popUpMenu(Menubar.menu) statusItem.popUpMenu(Menubar.menu)
} }
@@ -9,9 +9,9 @@ class ControlsTab {
"holdShortcut2": { App.app.focusTarget() }, "holdShortcut2": { App.app.focusTarget() },
"holdShortcut3": { App.app.focusTarget() }, "holdShortcut3": { App.app.focusTarget() },
"focusWindowShortcut": { App.app.focusTarget() }, "focusWindowShortcut": { App.app.focusTarget() },
"nextWindowShortcut": { App.app.showUiOrCycleSelection(0) }, "nextWindowShortcut": { App.app.showUiOrCycleSelection(0, false) },
"nextWindowShortcut2": { App.app.showUiOrCycleSelection(1) }, "nextWindowShortcut2": { App.app.showUiOrCycleSelection(1, false) },
"nextWindowShortcut3": { App.app.showUiOrCycleSelection(2) }, "nextWindowShortcut3": { App.app.showUiOrCycleSelection(2, false) },
"previousWindowShortcut": { App.app.previousWindowShortcutWithRepeatingKey() }, "previousWindowShortcut": { App.app.previousWindowShortcutWithRepeatingKey() },
"→": { App.app.cycleSelection(.right) }, "→": { App.app.cycleSelection(.right) },
"←": { App.app.cycleSelection(.left) }, "←": { App.app.cycleSelection(.left) },
+1
View File
@@ -4,6 +4,7 @@ class App {
class AppMock { class AppMock {
var appIsBeingUsed = false var appIsBeingUsed = false
var shortcutIndex = 0 var shortcutIndex = 0
var forceDoNothingOnRelease = false
} }
static let app = AppMock() static let app = AppMock()
} }