From 7137fcd255787b65a8132ef144c2a5ce715b9439 Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Thu, 17 Jul 2025 22:47:04 +0530 Subject: [PATCH] BugFix: MouseOver and Add Feature: Clipboard settings --- DynamicIsland/DynamicIslandApp.swift | 35 +++++++++++++++++++ DynamicIsland/Localizable.xcstrings | 8 ++++- .../Notch/ClipboardHistoryPopover.swift | 13 +++++++ .../Notch/DynamicIslandHeader.swift | 8 ++++- .../components/Settings/SettingsView.swift | 8 ++++- DynamicIsland/managers/ClipboardManager.swift | 7 +++- DynamicIsland/models/Constants.swift | 1 + 7 files changed, 76 insertions(+), 4 deletions(-) diff --git a/DynamicIsland/DynamicIslandApp.swift b/DynamicIsland/DynamicIslandApp.swift index 40db3ca..ab73c2e 100644 --- a/DynamicIsland/DynamicIslandApp.swift +++ b/DynamicIsland/DynamicIslandApp.swift @@ -268,6 +268,41 @@ class AppDelegate: NSObject, NSApplicationDelegate { TimerManager.shared.startDemoTimer(duration: 300) } + KeyboardShortcuts.onKeyDown(for: .clipboardHistoryPanel) { [weak self] in + guard let self = self else { return } + + // Only open clipboard if the feature is enabled + guard Defaults[.enableClipboardManager] else { return } + + // Find the appropriate view model based on mouse location + let mouseLocation = NSEvent.mouseLocation + var viewModel = self.vm + + if Defaults[.showOnAllDisplays] { + for screen in NSScreen.screens { + if screen.frame.contains(mouseLocation) { + if let screenViewModel = self.viewModels[screen] { + viewModel = screenViewModel + break + } + } + } + } + + // Open the notch if it's closed + if viewModel.notchState == .closed { + viewModel.open() + } + + // Start clipboard monitoring if not already running + if !ClipboardManager.shared.isMonitoring { + ClipboardManager.shared.startMonitoring() + } + + // Post notification to trigger clipboard popover + NotificationCenter.default.post(name: NSNotification.Name("ToggleClipboardPopover"), object: nil) + } + if !Defaults[.showOnAllDisplays] { let viewModel = self.vm let window = createDynamicIslandWindow( diff --git a/DynamicIsland/Localizable.xcstrings b/DynamicIsland/Localizable.xcstrings index e4984b9..19538a6 100644 --- a/DynamicIsland/Localizable.xcstrings +++ b/DynamicIsland/Localizable.xcstrings @@ -52,6 +52,9 @@ }, "5 items" : { + }, + "7 items" : { + }, "10 items" : { @@ -653,7 +656,7 @@ "Mirror shape" : { }, - "Monitor clipboard changes and keep a history of recent copies. The clipboard button will appear in the header next to the settings gear." : { + "Monitor clipboard changes and keep a history of recent copies. Use Cmd+Shift+V to quickly access clipboard history." : { }, "Monitoring Status" : { @@ -999,6 +1002,9 @@ }, "System Performance" : { + }, + "The clipboard icon appears in the header next to the settings gear when enabled. Use the global shortcut Cmd+Shift+V or click the icon to access history." : { + }, "TheDynamicIsland" : { diff --git a/DynamicIsland/components/Notch/ClipboardHistoryPopover.swift b/DynamicIsland/components/Notch/ClipboardHistoryPopover.swift index 73dc37e..03f65b5 100644 --- a/DynamicIsland/components/Notch/ClipboardHistoryPopover.swift +++ b/DynamicIsland/components/Notch/ClipboardHistoryPopover.swift @@ -10,6 +10,7 @@ import SwiftUI struct ClipboardHistoryPopover: View { @ObservedObject var clipboardManager = ClipboardManager.shared @Binding var isPresented: Bool + @EnvironmentObject var vm: DynamicIslandViewModel var body: some View { VStack(alignment: .leading, spacing: 0) { @@ -56,6 +57,18 @@ struct ClipboardHistoryPopover: View { .stroke(Color.gray.opacity(0.2), lineWidth: 1) ) .shadow(color: .black.opacity(0.3), radius: 10, x: 0, y: 5) + .onHover { hovering in + // Close popover and notch when mouse exits + if !hovering { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + isPresented = false + // Also close the notch if no other popovers are active + if vm.notchState == .open && !vm.isBatteryPopoverActive { + vm.close() + } + } + } + } } @ViewBuilder diff --git a/DynamicIsland/components/Notch/DynamicIslandHeader.swift b/DynamicIsland/components/Notch/DynamicIslandHeader.swift index 13115dd..4a0fbee 100644 --- a/DynamicIsland/components/Notch/DynamicIslandHeader.swift +++ b/DynamicIsland/components/Notch/DynamicIslandHeader.swift @@ -65,7 +65,7 @@ struct DynamicIslandHeader: View { .buttonStyle(PlainButtonStyle()) } - if Defaults[.enableClipboardManager] { + if Defaults[.enableClipboardManager] && Defaults[.showClipboardIcon] { Button(action: { showClipboardPopover.toggle() }) { @@ -138,6 +138,12 @@ struct DynamicIslandHeader: View { } .foregroundColor(.gray) .environmentObject(vm) + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("ToggleClipboardPopover"))) { _ in + // Only toggle if clipboard is enabled + if Defaults[.enableClipboardManager] { + showClipboardPopover.toggle() + } + } } } diff --git a/DynamicIsland/components/Settings/SettingsView.swift b/DynamicIsland/components/Settings/SettingsView.swift index f261ec0..9c2e710 100644 --- a/DynamicIsland/components/Settings/SettingsView.swift +++ b/DynamicIsland/components/Settings/SettingsView.swift @@ -1309,6 +1309,7 @@ struct ClipboardSettings: View { @ObservedObject var clipboardManager = ClipboardManager.shared @Default(.enableClipboardManager) var enableClipboardManager @Default(.clipboardHistorySize) var clipboardHistorySize + @Default(.showClipboardIcon) var showClipboardIcon var body: some View { Form { @@ -1324,17 +1325,20 @@ struct ClipboardSettings: View { } header: { Text("Clipboard Manager") } footer: { - Text("Monitor clipboard changes and keep a history of recent copies. The clipboard button will appear in the header next to the settings gear.") + Text("Monitor clipboard changes and keep a history of recent copies. Use Cmd+Shift+V to quickly access clipboard history.") } if enableClipboardManager { Section { + Defaults.Toggle("Show Clipboard Icon", key: .showClipboardIcon) + HStack { Text("History Size") Spacer() Picker("History Size", selection: $clipboardHistorySize) { Text("3 items").tag(3) Text("5 items").tag(5) + Text("7 items").tag(7) Text("10 items").tag(10) } .pickerStyle(.menu) @@ -1356,6 +1360,8 @@ struct ClipboardSettings: View { } } header: { Text("Settings") + } footer: { + Text("The clipboard icon appears in the header next to the settings gear when enabled. Use the global shortcut Cmd+Shift+V or click the icon to access history.") } Section { diff --git a/DynamicIsland/managers/ClipboardManager.swift b/DynamicIsland/managers/ClipboardManager.swift index b6ede38..e4b4e7f 100644 --- a/DynamicIsland/managers/ClipboardManager.swift +++ b/DynamicIsland/managers/ClipboardManager.swift @@ -10,6 +10,7 @@ import SwiftUI import Combine import Foundation import UniformTypeIdentifiers +import Defaults // Clipboard item data structure struct ClipboardItem: Identifiable, Codable { @@ -152,7 +153,11 @@ class ClipboardManager: ObservableObject { private var timer: Timer? private var lastChangeCount: Int = 0 - private let maxHistoryItems = 3 + + // Use configurable history size from settings + private var maxHistoryItems: Int { + return Defaults[.clipboardHistorySize] + } // Directory for storing clipboard data files static let clipboardDataDirectory: URL = { diff --git a/DynamicIsland/models/Constants.swift b/DynamicIsland/models/Constants.swift index 120e444..e04f7e5 100644 --- a/DynamicIsland/models/Constants.swift +++ b/DynamicIsland/models/Constants.swift @@ -160,6 +160,7 @@ extension Defaults.Keys { // MARK: Clipboard Feature static let enableClipboardManager = Key("enableClipboardManager", default: true) static let clipboardHistorySize = Key("clipboardHistorySize", default: 3) + static let showClipboardIcon = Key("showClipboardIcon", default: true) // Helper to determine the default media controller based on macOS version static var defaultMediaController: MediaControllerType {