diff --git a/boringNotch/ContentView.swift b/boringNotch/ContentView.swift index 461410f3..7e6c5060 100644 --- a/boringNotch/ContentView.swift +++ b/boringNotch/ContentView.swift @@ -27,6 +27,9 @@ struct ContentView: View { @State private var isHovering: Bool = false @State private var anyDropDebounceTask: Task? + @Default(.boringShelf) var boringShelf + @Default(.expandedDragDetection) var expandedDragDetection + @State private var gestureProgress: CGFloat = .zero @State private var haptics: Bool = false @@ -394,7 +397,22 @@ struct ContentView: View { .opacity(gestureProgress != 0 ? 1.0 - min(abs(gestureProgress) * 0.1, 0.3) : 1.0) } } - .onDrop(of: [.fileURL, .url, .utf8PlainText, .plainText, .data], delegate: GeneralDropTargetDelegate(isTargeted: $vm.generalDropTargeting)) + // Disable general drop target if shelf or detection is disabled + .onDrop(of: [.fileURL, .url, .utf8PlainText, .plainText, .data], delegate: GeneralDropTargetDelegate(isTargeted: Binding( + get: { + // Return true only if BOTH shelf AND detection are enabled + return boringShelf && expandedDragDetection && vm.generalDropTargeting + }, + set: { newValue in + // Only update the actual model IF shelf AND detection are enabled + if boringShelf && expandedDragDetection { + vm.generalDropTargeting = newValue + } else { + // If disabled, force target to false to be safe + vm.generalDropTargeting = false + } + } + ))) } @ViewBuilder @@ -521,7 +539,7 @@ struct ContentView: View { @ViewBuilder var dragDetector: some View { - if Defaults[.boringShelf] && vm.notchState == .closed { + if boringShelf && expandedDragDetection && vm.notchState == .closed { Color.clear .frame(maxWidth: .infinity, maxHeight: .infinity) .contentShape(Rectangle()) diff --git a/boringNotch/Localizable.xcstrings b/boringNotch/Localizable.xcstrings index 905fcd34..6ab2af04 100644 --- a/boringNotch/Localizable.xcstrings +++ b/boringNotch/Localizable.xcstrings @@ -6212,6 +6212,10 @@ } } }, + "Detection area" : { + "comment" : "A label for the picker that lets the user select the detection area for drag-and-drop functionality.", + "isCommentAutoGenerated" : true + }, "Disable" : { "extractionState" : "stale", "localizations" : { @@ -7215,6 +7219,10 @@ } } }, + "Enable drag detection" : { + "comment" : "A toggle that enables or disables drag detection in the shelf.", + "isCommentAutoGenerated" : true + }, "Enable gestures" : { "localizations" : { "ar" : { @@ -7817,6 +7825,7 @@ } }, "Expanded drag detection area" : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { diff --git a/boringNotch/boringNotchApp.swift b/boringNotch/boringNotchApp.swift index f6a82044..0085d2f0 100644 --- a/boringNotch/boringNotchApp.swift +++ b/boringNotch/boringNotchApp.swift @@ -186,7 +186,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { private func setupDragDetectors() { cleanupDragDetectors() - guard Defaults[.expandedDragDetection] else { return } + guard Defaults[.boringShelf] && Defaults[.expandedDragDetection] else { return } if Defaults[.showOnAllDisplays] { for screen in NSScreen.screens { @@ -205,19 +205,29 @@ class AppDelegate: NSObject, NSApplicationDelegate { private func setupDragDetectorForScreen(_ screen: NSScreen) { guard let uuid = screen.displayUUID else { return } - + let screenFrame = screen.frame - let notchHeight = openNotchSize.height - let notchWidth = openNotchSize.width - - // Create notch region at the top-center of the screen where an open notch would occupy + let closedNotchSize = getClosedNotchSize(screenUUID: uuid) + + var notchHeight: CGFloat + var notchWidth: CGFloat + + switch Defaults[.dragDetectionArea] { + case .openNotch: + notchHeight = openNotchSize.height + notchWidth = openNotchSize.width + case .closedNotch: + notchHeight = closedNotchSize.height + notchWidth = closedNotchSize.width + } + let notchRegion = CGRect( x: screenFrame.midX - notchWidth / 2, y: screenFrame.maxY - notchHeight, width: notchWidth, height: notchHeight ) - + let detector = DragDetector(notchRegion: notchRegion) detector.onDragEntersNotchRegion = { [weak self] in @@ -345,6 +355,14 @@ class AppDelegate: NSObject, NSApplicationDelegate { } }) + observers.append(NotificationCenter.default.addObserver( + forName: Notification.Name.boringShelfChanged, object: nil, queue: nil + ) { [weak self] _ in + Task { @MainActor in + self?.setupDragDetectors() + } + }) + // Use closure-based observers for DistributedNotificationCenter and keep tokens for removal screenLockedObserver = DistributedNotificationCenter.default().addObserver( forName: NSNotification.Name(rawValue: "com.apple.screenIsLocked"), diff --git a/boringNotch/components/Settings/Views/ShelfSettingsView.swift b/boringNotch/components/Settings/Views/ShelfSettingsView.swift index 600b7fdb..05aa133a 100644 --- a/boringNotch/components/Settings/Views/ShelfSettingsView.swift +++ b/boringNotch/components/Settings/Views/ShelfSettingsView.swift @@ -10,9 +10,11 @@ import SwiftUI struct Shelf: View { + @Default(.boringShelf) var boringShelf: Bool @Default(.shelfTapToOpen) var shelfTapToOpen: Bool @Default(.quickShareProvider) var quickShareProvider @Default(.expandedDragDetection) var expandedDragDetection: Bool + @Default(.dragDetectionArea) var dragDetectionArea: DragDetectionArea @StateObject private var quickShareService = QuickShareService.shared private var selectedProvider: QuickShareProvider? { @@ -25,11 +27,17 @@ struct Shelf: View { Defaults.Toggle(key: .boringShelf) { Text("Enable shelf") } + .onChange(of: boringShelf) { + NotificationCenter.default.post( + name: Notification.Name.boringShelfChanged, + object: nil + ) + } Defaults.Toggle(key: .openShelfByDefault) { Text("Open shelf by default if items are present") } Defaults.Toggle(key: .expandedDragDetection) { - Text("Expanded drag detection area") + Text("Enable drag detection") } .onChange(of: expandedDragDetection) { NotificationCenter.default.post( @@ -37,6 +45,18 @@ struct Shelf: View { object: nil ) } + Picker("Detection area", selection: $dragDetectionArea) { + ForEach(DragDetectionArea.allCases) { area in + Text(area.rawValue).tag(area) + } + } + .disabled(!expandedDragDetection) + .onChange(of: dragDetectionArea) { + NotificationCenter.default.post( + name: Notification.Name.expandedDragDetectionChanged, + object: nil + ) + } Defaults.Toggle(key: .copyOnDrag) { Text("Copy items on drag") } diff --git a/boringNotch/models/Constants.swift b/boringNotch/models/Constants.swift index 0e4b9f58..91750770 100644 --- a/boringNotch/models/Constants.swift +++ b/boringNotch/models/Constants.swift @@ -43,6 +43,7 @@ extension Notification.Name { // MARK: - Shelf static let expandedDragDetectionChanged = Notification.Name("expandedDragDetectionChanged") + static let boringShelfChanged = Notification.Name("boringShelfChanged") // MARK: - System static let accessibilityAuthorizationChanged = Notification.Name("accessibilityAuthorizationChanged") @@ -72,6 +73,13 @@ enum SneakPeekStyle: String, CaseIterable, Identifiable, Defaults.Serializable { var id: String { self.rawValue } } +enum DragDetectionArea: String, CaseIterable, Identifiable, Defaults.Serializable { + case openNotch = "Expanded area" + case closedNotch = "Notch only" + + var id: String { self.rawValue } +} + // Action to perform when Option (⌥) is held while pressing media keys enum OptionKeyAction: String, CaseIterable, Identifiable, Defaults.Serializable { case openSettings = "Open System Settings" @@ -191,7 +199,8 @@ extension Defaults.Keys { static let copyOnDrag = Key("copyOnDrag", default: false) static let autoRemoveShelfItems = Key("autoRemoveShelfItems", default: false) static let expandedDragDetection = Key("expandedDragDetection", default: true) - + static let dragDetectionArea = Key("dragDetectionArea", default: .openNotch) + // MARK: Calendar static let calendarSelectionState = Key("calendarSelectionState", default: .all) static let hideAllDayEvents = Key("hideAllDayEvents", default: false) diff --git a/boringNotch/observers/DragDetector.swift b/boringNotch/observers/DragDetector.swift index 9eab0777..c72661ef 100644 --- a/boringNotch/observers/DragDetector.swift +++ b/boringNotch/observers/DragDetector.swift @@ -46,7 +46,7 @@ final class DragDetector { .string ] let isValid = dragPasteboard.pasteboardItems?.allSatisfy { item in - item.types.allSatisfy { validTypes.contains($0) } + item.types.contains { validTypes.contains($0) } } return isValid ?? false } @@ -79,7 +79,7 @@ final class DragDetector { if self.isContentDragging { let mouseLocation = NSEvent.mouseLocation self.onDragMove?(mouseLocation) - + // Track notch region entry/exit let containsMouse = self.notchRegion.contains(mouseLocation) if containsMouse && !self.hasEnteredNotchRegion {