Skip to content

[desktop_drop] Fix mouse event blocking for PlatformView in macOS#436

Open
gicha wants to merge 1 commit intoMixinNetwork:mainfrom
Perilla-Labs:main
Open

[desktop_drop] Fix mouse event blocking for PlatformView in macOS#436
gicha wants to merge 1 commit intoMixinNetwork:mainfrom
Perilla-Labs:main

Conversation

@gicha
Copy link
Copy Markdown

@gicha gicha commented Sep 16, 2025

Problem

The desktop_drop plugin was blocking mouse events for native PlatformView components in macOS Flutter applications. This occurred because the DropTarget view 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:

  • Enhanced hitTest handling: Override hitTest(_:) to forward mouse events down the responder chain when not handling drag operations
  • Drag state tracking: Added isDragging variable and isDragEvent(_:) method to accurately determine when to handle events
  • Mouse event forwarding: Override all mouse event handlers (mouseDown, mouseUp, mouseDragged, mouseMoved, rightMouseDown, rightMouseUp) to forward events to the next responder when not in drag mode
  • Proper drag completion: Added draggingEnded(_:) method and reset state in all drag completion methods
  • Visual improvements: Made DropTarget transparent for better compatibility with other views

Technical Details:

  • Events are only intercepted during actual drag operations
  • All other mouse events are passed through to underlying views
  • Platform views can now receive and handle mouse events normally
  • Drag & drop functionality remains fully intact

Testing

  • Drag & drop functionality works correctly
  • Platform views respond to mouse events
  • Various interaction types work (clicks, dragging, context menus)
  • No regression in existing functionality

Impact

This fix resolves the blocking issue while maintaining full drag & drop capabilities, allowing seamless integration with native platform views in macOS Flutter applications.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 DropTarget layer-backed and transparent.
  • Adds drag state tracking (isDragging) and custom hitTest(_:) 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.

Comment on lines +95 to +104
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
}
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +232 to +278
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)
}
}
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +95 to +113
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
}
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants