[desktop_drop] Fix mouse event blocking for PlatformView in macOS#436
[desktop_drop] Fix mouse event blocking for PlatformView in macOS#436gicha wants to merge 1 commit intoMixinNetwork:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes desktop_drop on macOS where the DropTarget overlay blocks mouse interactions with native PlatformView content by attempting to make the overlay “click-through” except during drag operations.
Changes:
- Makes
DropTargetlayer-backed and transparent. - Adds drag state tracking (
isDragging) and customhitTest(_:)behavior. - Adds mouse event overrides intended to forward events when not dragging; adds additional drag completion state resets.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| override func hitTest(_ point: NSPoint) -> NSView? { | ||
| if !isDragging { | ||
| return nil | ||
| } | ||
| if NSApp.currentEvent?.type == .leftMouseDragged || | ||
| NSApp.currentEvent?.type == .rightMouseDragged { | ||
| return super.hitTest(point) | ||
| } | ||
| return nil | ||
| } |
There was a problem hiding this comment.
hitTest(_:) returns nil whenever isDragging is false, but isDragging is only set to true inside draggingEntered. On macOS, AppKit typically determines the drag destination view via hit-testing; if this view never hit-tests positive at the start of a drag, draggingEntered may never be delivered and drag & drop can stop working entirely. Consider allowing hit-testing for drag sessions regardless of isDragging (e.g., base the decision solely on the current drag event / dragging session), then use isDragging only to control subsequent event handling/state.
| override func mouseDown(with event: NSEvent) { | ||
| if !isDragging && !isDragEvent(event) { | ||
| nextResponder?.mouseDown(with: event) | ||
| } else { | ||
| super.mouseDown(with: event) | ||
| } | ||
| } | ||
|
|
||
| override func mouseUp(with event: NSEvent) { | ||
| if !isDragging && !isDragEvent(event) { | ||
| nextResponder?.mouseUp(with: event) | ||
| } else { | ||
| super.mouseUp(with: event) | ||
| } | ||
| } | ||
|
|
||
| override func mouseDragged(with event: NSEvent) { | ||
| if !isDragging && !isDragEvent(event) { | ||
| nextResponder?.mouseDragged(with: event) | ||
| } else { | ||
| super.mouseDragged(with: event) | ||
| } | ||
| } | ||
|
|
||
| override func mouseMoved(with event: NSEvent) { | ||
| if !isDragging && !isDragEvent(event) { | ||
| nextResponder?.mouseMoved(with: event) | ||
| } else { | ||
| super.mouseMoved(with: event) | ||
| } | ||
| } | ||
|
|
||
| override func rightMouseDown(with event: NSEvent) { | ||
| if !isDragging && !isDragEvent(event) { | ||
| nextResponder?.rightMouseDown(with: event) | ||
| } else { | ||
| super.rightMouseDown(with: event) | ||
| } | ||
| } | ||
|
|
||
| override func rightMouseUp(with event: NSEvent) { | ||
| if !isDragging && !isDragEvent(event) { | ||
| nextResponder?.rightMouseUp(with: event) | ||
| } else { | ||
| super.rightMouseUp(with: event) | ||
| } | ||
| } |
There was a problem hiding this comment.
The mouse event overrides forward to nextResponder, but nextResponder is typically the superview/window—not the actual view under the cursor—so this doesn’t reliably deliver events to underlying PlatformView instances. Also, when hitTest(_:) returns nil (the non-dragging case), these handlers generally won’t be called anyway, making them misleading. Prefer relying on hitTest pass-through alone, or explicitly forwarding to the view returned by window?.contentView?.hitTest(...) if you need manual forwarding.
| override func hitTest(_ point: NSPoint) -> NSView? { | ||
| if !isDragging { | ||
| return nil | ||
| } | ||
| if NSApp.currentEvent?.type == .leftMouseDragged || | ||
| NSApp.currentEvent?.type == .rightMouseDragged { | ||
| return super.hitTest(point) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| private var isDragging = false | ||
|
|
||
| private func isDragEvent(_ event: NSEvent?) -> Bool { | ||
| guard let event = event else { return false } | ||
| return event.type == .leftMouseDragged || | ||
| event.type == .rightMouseDragged || | ||
| event.type == .otherMouseDragged | ||
| } |
There was a problem hiding this comment.
isDragEvent(_:) treats .otherMouseDragged as a drag event, but hitTest(_:) only checks .leftMouseDragged/.rightMouseDragged. This inconsistency can cause different behavior depending on which mouse button initiates the drag. Consider aligning hitTest(_:) with isDragEvent(_:) (or consolidating the logic) so drag detection is consistent.
Problem
The
desktop_dropplugin was blocking mouse events for nativePlatformViewcomponents in macOS Flutter applications. This occurred because theDropTargetview was overlaying the entire screen and intercepting all mouse events, preventing interaction with platform views.Solution
Modified the native macOS implementation to properly handle mouse event forwarding while preserving drag & drop functionality:
Key Changes:
hitTest(_:)to forward mouse events down the responder chain when not handling drag operationsisDraggingvariable andisDragEvent(_:)method to accurately determine when to handle eventsmouseDown,mouseUp,mouseDragged,mouseMoved,rightMouseDown,rightMouseUp) to forward events to the next responder when not in drag modedraggingEnded(_:)method and reset state in all drag completion methodsDropTargettransparent for better compatibility with other viewsTechnical Details:
Testing
Impact
This fix resolves the blocking issue while maintaining full drag & drop capabilities, allowing seamless integration with native platform views in macOS Flutter applications.