mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Dynamic Popup/Dropdown shortcut
This commit is contained in:
parent
5321352184
commit
e29745f8f4
6 changed files with 139 additions and 19 deletions
|
|
@ -450,8 +450,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
ClipboardManager.shared.startMonitoring()
|
||||
}
|
||||
|
||||
// Check if window mode is enabled
|
||||
if Defaults[.clipboardWindowMode] {
|
||||
// Determine display mode based on user preference and context
|
||||
let shouldUseWindow = self.shouldUseWindowMode()
|
||||
|
||||
if shouldUseWindow {
|
||||
// Use window interface
|
||||
ClipboardWindowManager.shared.toggleClipboardWindow()
|
||||
} else {
|
||||
|
|
@ -622,6 +624,60 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
NSApplication.shared.terminate(nil)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Clipboard Display Mode Logic
|
||||
|
||||
private func shouldUseWindowMode() -> Bool {
|
||||
let displayMode = Defaults[.clipboardDisplayMode]
|
||||
|
||||
switch displayMode {
|
||||
case .window:
|
||||
return true
|
||||
case .popover:
|
||||
return false
|
||||
case .auto:
|
||||
// Auto mode: use window if any app is in fullscreen, otherwise use popover
|
||||
return isAnyAppInFullscreen()
|
||||
}
|
||||
}
|
||||
|
||||
private func isAnyAppInFullscreen() -> Bool {
|
||||
// Check if any running application is in fullscreen mode
|
||||
let workspace = NSWorkspace.shared
|
||||
|
||||
// Get all visible applications
|
||||
let apps = workspace.runningApplications.filter { app in
|
||||
app.activationPolicy == .regular && !app.isTerminated
|
||||
}
|
||||
|
||||
// Check each application's windows for fullscreen state
|
||||
for app in apps {
|
||||
if app.isActive {
|
||||
// Check if the frontmost application has fullscreen windows
|
||||
let windows = CGWindowListCopyWindowInfo(.optionOnScreenOnly, kCGNullWindowID) as? [[String: Any]] ?? []
|
||||
|
||||
for window in windows {
|
||||
if let ownerPID = window[kCGWindowOwnerPID as String] as? Int32,
|
||||
ownerPID == app.processIdentifier,
|
||||
let bounds = window[kCGWindowBounds as String] as? [String: Any],
|
||||
let width = bounds["Width"] as? CGFloat,
|
||||
let height = bounds["Height"] as? CGFloat {
|
||||
|
||||
// Check if window dimensions match screen dimensions (indicating fullscreen)
|
||||
for screen in NSScreen.screens {
|
||||
let screenFrame = screen.frame
|
||||
if abs(width - screenFrame.width) < 10 && abs(height - screenFrame.height) < 10 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private func showOnboardingWindow() {
|
||||
if onboardingWindowController == nil {
|
||||
let window = NSWindow(
|
||||
|
|
|
|||
|
|
@ -136,6 +136,9 @@
|
|||
},
|
||||
"Appearance" : {
|
||||
|
||||
},
|
||||
"Auto mode intelligently chooses between popover (desktop) and window (fullscreen apps) for the best experience. You can override this with manual selection." : {
|
||||
|
||||
},
|
||||
"Automatically check for updates" : {
|
||||
"localizations" : {
|
||||
|
|
@ -388,6 +391,9 @@
|
|||
},
|
||||
"Disk Write" : {
|
||||
|
||||
},
|
||||
"Display Mode" : {
|
||||
|
||||
},
|
||||
"Download" : {
|
||||
|
||||
|
|
@ -1196,9 +1202,6 @@
|
|||
},
|
||||
"When enabled, the Stats tab will display real-time system performance graphs. This feature requires system permissions and may use additional battery." : {
|
||||
|
||||
},
|
||||
"Window Mode shows clipboard in a moveable window like Raycast with tabs for history and favorites. When disabled, clipboard appears in a popover attached to the Dynamic Island." : {
|
||||
|
||||
},
|
||||
"With %lld graphs enabled, the Dynamic Island will expand horizontally to accommodate all graphs in a single row." : {
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,25 @@ struct DynamicIslandHeader: View {
|
|||
|
||||
if Defaults[.enableClipboardManager] && Defaults[.showClipboardIcon] {
|
||||
Button(action: {
|
||||
// Use the same smart logic as the keyboard shortcut
|
||||
let displayMode = Defaults[.clipboardDisplayMode]
|
||||
let shouldUseWindow: Bool
|
||||
|
||||
switch displayMode {
|
||||
case .window:
|
||||
shouldUseWindow = true
|
||||
case .popover:
|
||||
shouldUseWindow = false
|
||||
case .auto:
|
||||
// For header button clicks, prefer popover since user is already interacting with notch
|
||||
shouldUseWindow = false
|
||||
}
|
||||
|
||||
if shouldUseWindow {
|
||||
ClipboardWindowManager.shared.toggleClipboardWindow()
|
||||
} else {
|
||||
showClipboardPopover.toggle()
|
||||
}
|
||||
}) {
|
||||
Capsule()
|
||||
.fill(.black)
|
||||
|
|
|
|||
|
|
@ -1589,7 +1589,7 @@ struct ClipboardSettings: View {
|
|||
@Default(.enableClipboardManager) var enableClipboardManager
|
||||
@Default(.clipboardHistorySize) var clipboardHistorySize
|
||||
@Default(.showClipboardIcon) var showClipboardIcon
|
||||
@Default(.clipboardWindowMode) var clipboardWindowMode
|
||||
@Default(.clipboardDisplayMode) var clipboardDisplayMode
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
|
|
@ -1612,7 +1612,24 @@ struct ClipboardSettings: View {
|
|||
Section {
|
||||
Defaults.Toggle("Show Clipboard Icon", key: .showClipboardIcon)
|
||||
|
||||
Defaults.Toggle("Window Mode", key: .clipboardWindowMode)
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
HStack {
|
||||
Text("Display Mode")
|
||||
Spacer()
|
||||
Picker("Display Mode", selection: $clipboardDisplayMode) {
|
||||
ForEach(ClipboardDisplayMode.allCases, id: \.self) { mode in
|
||||
Text(mode.displayName).tag(mode)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.menu)
|
||||
.frame(width: 140)
|
||||
}
|
||||
|
||||
Text(clipboardDisplayMode.description)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.padding(.leading, 0)
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("History Size")
|
||||
|
|
@ -1650,7 +1667,7 @@ struct ClipboardSettings: View {
|
|||
} header: {
|
||||
Text("Settings")
|
||||
} footer: {
|
||||
Text("Window Mode shows clipboard in a moveable window like Raycast with tabs for history and favorites. When disabled, clipboard appears in a popover attached to the Dynamic Island.")
|
||||
Text("Auto mode intelligently chooses between popover (desktop) and window (fullscreen apps) for the best experience. You can override this with manual selection.")
|
||||
}
|
||||
|
||||
Section {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@ class ClipboardWindowManager: ObservableObject {
|
|||
|
||||
func showClipboardWindow() {
|
||||
if let existingWindow = clipboardWindow {
|
||||
// Ensure window appears above fullscreen apps
|
||||
existingWindow.level = .screenSaver
|
||||
existingWindow.makeKeyAndOrderFront(nil)
|
||||
existingWindow.orderFrontRegardless() // Force window to front even above fullscreen
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
return
|
||||
}
|
||||
|
|
@ -32,24 +35,24 @@ class ClipboardWindowManager: ObservableObject {
|
|||
|
||||
window.title = "Clipboard Manager"
|
||||
window.isMovableByWindowBackground = true
|
||||
window.level = .floating
|
||||
window.level = .screenSaver // Use screenSaver level to appear above fullscreen apps
|
||||
window.isReleasedWhenClosed = false
|
||||
window.hasShadow = true
|
||||
window.titlebarAppearsTransparent = true
|
||||
window.styleMask.insert(.fullSizeContentView)
|
||||
window.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary] // Allow on all spaces and above fullscreen
|
||||
|
||||
// Set minimum and maximum sizes
|
||||
window.minSize = NSSize(width: 350, height: 250)
|
||||
window.maxSize = NSSize(width: 600, height: 500)
|
||||
|
||||
// Center the window
|
||||
if let screen = NSScreen.main {
|
||||
let screenFrame = screen.visibleFrame
|
||||
// Center the window on the current screen (important for fullscreen apps)
|
||||
let currentScreen = NSScreen.main ?? NSScreen.screens.first!
|
||||
let screenFrame = currentScreen.frame // Use full frame instead of visibleFrame for fullscreen compatibility
|
||||
let windowFrame = window.frame
|
||||
let x = (screenFrame.width - windowFrame.width) / 2 + screenFrame.minX
|
||||
let y = (screenFrame.height - windowFrame.height) / 2 + screenFrame.minY
|
||||
window.setFrameOrigin(NSPoint(x: x, y: y))
|
||||
}
|
||||
|
||||
// Set the content view
|
||||
let contentView = ClipboardWindow()
|
||||
|
|
@ -63,6 +66,7 @@ class ClipboardWindowManager: ObservableObject {
|
|||
|
||||
self.clipboardWindow = window
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
window.orderFrontRegardless() // Force window to front even above fullscreen apps
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,28 @@ enum CalendarSelectionState: Codable, Defaults.Serializable {
|
|||
case selected(Set<String>)
|
||||
}
|
||||
|
||||
enum ClipboardDisplayMode: String, CaseIterable, Codable, Defaults.Serializable {
|
||||
case auto = "auto" // Automatically choose: popover on desktop, window in fullscreen
|
||||
case popover = "popover" // Always use popover (attached to notch)
|
||||
case window = "window" // Always use standalone window
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .auto: return "Auto (Smart)"
|
||||
case .popover: return "Popover"
|
||||
case .window: return "Window"
|
||||
}
|
||||
}
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .auto: return "Automatically chooses the best display mode based on context"
|
||||
case .popover: return "Always shows clipboard attached to the Dynamic Island"
|
||||
case .window: return "Always shows clipboard in a standalone moveable window"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum HideNotchOption: String, Defaults.Serializable {
|
||||
case always
|
||||
case nowPlayingOnly
|
||||
|
|
@ -170,7 +192,7 @@ extension Defaults.Keys {
|
|||
static let enableClipboardManager = Key<Bool>("enableClipboardManager", default: true)
|
||||
static let clipboardHistorySize = Key<Int>("clipboardHistorySize", default: 3)
|
||||
static let showClipboardIcon = Key<Bool>("showClipboardIcon", default: true)
|
||||
static let clipboardWindowMode = Key<Bool>("clipboardWindowMode", default: true)
|
||||
static let clipboardDisplayMode = Key<ClipboardDisplayMode>("clipboardDisplayMode", default: .auto)
|
||||
|
||||
// MARK: Keyboard Shortcuts
|
||||
static let enableShortcuts = Key<Bool>("enableShortcuts", default: true)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue