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) {
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
}
}
@@ -83,7 +83,7 @@ class ATShortcut {
// 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
// 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)],
id == currentHoldShortcut.id {
let currentModifiers = cocoaToCarbonFlags(ModifierFlags.current)
+68 -18
View File
@@ -38,28 +38,56 @@ class CliServer {
static func executeCommandAndSendReponse(_ rawValue: String) -> Codable {
var output: Codable = ""
DispatchQueue.main.sync {
if rawValue == "--list" {
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
output = executeCommandAndSendReponse_(rawValue)
}
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]
}
@@ -68,11 +96,33 @@ struct JsonWindow: Codable {
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 {
static func detectCommand() -> String? {
let args = CommandLine.arguments
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]
}
}
+2 -2
View File
@@ -86,7 +86,7 @@ class GestureDetector {
static func checkForFingersUp(_ touches: Set<NSTouch>, _ requiredFingers: Int) -> Bool {
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() }
return true
}
@@ -140,7 +140,7 @@ class TriggerSwipeDetector {
let horizontal = Preferences.nextWindowGesture.isHorizontal()
if (updateSwipeStillPossible(horizontal ? absY : absX) && (horizontal ? absX : absY) >= MIN_SWIPE_DISTANCE) {
reset()
DispatchQueue.main.async { App.app.showUiOrCycleSelection(Preferences.gestureIndex) }
DispatchQueue.main.async { App.app.showUiOrCycleSelection(Preferences.gestureIndex, false) }
return true
}
return false
+10 -3
View File
@@ -23,6 +23,7 @@ class App: AppCenterApplication {
var permissionsWindow: PermissionsWindow!
var appIsBeingUsed = false
var shortcutIndex = 0
var forceDoNothingOnRelease = false
private var feedbackWindow: FeedbackWindow!
private var isFirstSummon = true
private var isVeryFirstSummon = true
@@ -73,6 +74,7 @@ class App: AppCenterApplication {
guard appIsBeingUsed else { return } // already hidden
appIsBeingUsed = false
isFirstSummon = true
forceDoNothingOnRelease = false
MouseEvents.toggle(false)
hideThumbnailPanelWithoutChangingKeyWindow()
if !keepPreview {
@@ -156,8 +158,12 @@ class App: AppCenterApplication {
}
}
@objc func showUi() {
showUiOrCycleSelection(0)
func showUi(_ shortcutIndex: Int) {
showUiOrCycleSelection(shortcutIndex, true)
}
@objc func showUiFromShortcut0() {
showUi(0)
}
@objc func showAboutTab() {
@@ -226,7 +232,8 @@ class App: AppCenterApplication {
Applications.refreshBadgesAsync()
}
func showUiOrCycleSelection(_ shortcutIndex: Int) {
func showUiOrCycleSelection(_ shortcutIndex: Int, _ forceDoNothingOnRelease_: Bool) {
forceDoNothingOnRelease = forceDoNothingOnRelease_
Logger.debug(shortcutIndex, self.shortcutIndex, isFirstSummon)
App.app.appIsBeingUsed = true
if isFirstSummon || shortcutIndex != self.shortcutIndex {
+2 -2
View File
@@ -19,7 +19,7 @@ class Menubar {
menu.addItem(NSMenuItem.separator())
menu.addItem(
withTitle: NSLocalizedString("Show", comment: "Menubar option"),
action: #selector(App.app.showUi),
action: #selector(App.app.showUiFromShortcut0),
keyEquivalent: "")
menu.addItem(
withTitle: NSLocalizedString("Preferences…", comment: "Menubar option"),
@@ -67,7 +67,7 @@ class Menubar {
@objc static func statusItemOnClick() {
// NSApp.currentEvent == nil if the icon is "clicked" through VoiceOver
if let type = NSApp.currentEvent?.type, type != .leftMouseDown {
App.app.showUi()
App.app.showUiFromShortcut0()
} else {
statusItem.popUpMenu(Menubar.menu)
}
@@ -9,9 +9,9 @@ class ControlsTab {
"holdShortcut2": { App.app.focusTarget() },
"holdShortcut3": { App.app.focusTarget() },
"focusWindowShortcut": { App.app.focusTarget() },
"nextWindowShortcut": { App.app.showUiOrCycleSelection(0) },
"nextWindowShortcut2": { App.app.showUiOrCycleSelection(1) },
"nextWindowShortcut3": { App.app.showUiOrCycleSelection(2) },
"nextWindowShortcut": { App.app.showUiOrCycleSelection(0, false) },
"nextWindowShortcut2": { App.app.showUiOrCycleSelection(1, false) },
"nextWindowShortcut3": { App.app.showUiOrCycleSelection(2, false) },
"previousWindowShortcut": { App.app.previousWindowShortcutWithRepeatingKey() },
"→": { App.app.cycleSelection(.right) },
"←": { App.app.cycleSelection(.left) },
+1
View File
@@ -4,6 +4,7 @@ class App {
class AppMock {
var appIsBeingUsed = false
var shortcutIndex = 0
var forceDoNothingOnRelease = false
}
static let app = AppMock()
}