-
-
Notifications
You must be signed in to change notification settings - Fork 550
feat: make drag-and-drop trigger area configurable & fix shelf disable behavior #1056
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 2 commits
b872ae3
2f0772b
10eac9a
df5b3be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't the existing drop target already cover the closed notch? In this case a global monitor shouldn't be necessary. In general i would like to avoid the global monitor, since it false positives with Safari tabs and is probably less performant than a drop target. |
||
| 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"), | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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,18 +27,36 @@ 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") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Creating a new setting while still using an older variable is generally bad practice. In the current version, this value only disabled drag detection outside of the closed notch window, but now it is being used to disable all drag detection. If this code gets deployed, it will apply the old setting value to the new unrelated setting, causing unexpected behaviour for users by disabling dragging entirely. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| .onChange(of: expandedDragDetection) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| NotificationCenter.default.post( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: Notification.Name.expandedDragDetectionChanged, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While we eventually need to move away from using notifications for these settings, I’m not requesting a change here since this pattern is still consistent.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for now this is good. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: Notification.Name.expandedDragDetectionChanged, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| object: nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+48
to
+59
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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 | |
| ) | |
| } | |
| if expandedDragDetection { | |
| Picker("Detection area", selection: $dragDetectionArea) { | |
| ForEach(DragDetectionArea.allCases) { area in | |
| Text(area.rawValue).tag(area) | |
| } | |
| } | |
| .onChange(of: dragDetectionArea) { | |
| NotificationCenter.default.post( | |
| name: Notification.Name.expandedDragDetectionChanged, | |
| object: nil | |
| ) | |
| } | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When expandedDragDetection is false we early‑return from setupDragDetectors(), so dragDetectionArea isn’t used at runtime; the disabled picker only shows (and preserves) which area will be used once detection is turned back on.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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) } | ||||||
|
||||||
| item.types.contains { validTypes.contains($0) } | |
| item.types.allSatisfy { validTypes.contains($0) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, changing from allSatisfy to contains is intentional.
Pasteboard items (e.g. from Finder) usually include many extra types (dyn., com.apple.finder., etc.) in addition to .fileURL / URL / string.
With allSatisfy, a single non-whitelisted type caused us to reject otherwise valid drags, which is why drag detection stopped working reliably after the first drag during my tests.
Uh oh!
There was an error while loading. Please reload this page.