Skip to content

wxGUI: fix orphaned drag image windows when dragging layers in layer tree on macOS - #7725

Merged
petrasovaa merged 3 commits into
OSGeo:mainfrom
christianbraun:wxgui-fix-toc-drag-phantom-windows
Jul 17, 2026
Merged

petrasovaa merged 3 commits into
OSGeo:mainfrom
christianbraun:wxgui-fix-toc-drag-phantom-windows

Conversation

@christianbraun

Copy link
Copy Markdown
Contributor

Description

Every drag of a layer in the Layer Manager tree (TOC) on macOS leaves an
orphaned borderless window behind. The windows are invisible on the desktop
but pile up in Mission Control/Exposé as untitled "python" windows, one per
drag, and are never released for the lifetime of the session.

Root cause

The bug is not in GRASS code. The chain is:

  1. LayerTree is based on wx.lib.agw.customtreectrl.CustomTreeCtrl, whose
    internal OnMouse handler shows the drag animation through a
    wx.DragImage.
  2. On macOS wx.DragImage is the generic implementation, which draws via
    wxOverlay.
  3. The Cocoa wxOverlayImpl::Reset() releases its borderless NSWindow
    without ever ordering it out of the screen (there is a
    // todo : don't dispose, only hide and reposition on next run comment at
    the exact spot in src/osx/cocoa/overlay.mm). A visible NSWindow stays
    alive on screen, so every overlay use leaks one phantom window.

This is wxWidgets/wxWidgets#26380, reproducible with the plain wxWidgets
Drawing sample. It affects wxWidgets 3.2.x and 3.3.x including current
nightly and is still open, so upgrading the bundled wxPython does not fix
it
— GRASS needs a workaround on its side.

The leak was verified empirically against wxPython 4.2.4 / wxWidgets 3.2.8
(osx-cocoa) by replicating customtreectrl.OnMouse's exact drag-image
lifecycle and counting the process's on-screen windows via
CGWindowListCopyWindowInfo: 3 drags → 3 leaked windows with stock
wx.DragImage, 0 with this patch.

Changes

All in gui/wxpython/lmgr/layertree.py, active on macOS only
(sys.platform == "darwin").

…tree on macOS

Every layer drag in the Layer Manager tree leaves an orphaned borderless
window on screen on macOS (visible in Mission Control), because
wx.DragImage renders through wxOverlay whose Cocoa implementation never
orders its NSWindow out of screen on Reset() (wxWidgets#26380, still open).

Replace the drag image on macOS with a wx.PopupWindow-based ghost that is
explicitly destroyed when the drag ends, and add an EVT_MOUSE_CAPTURE_LOST
handler to clean up when the capture is stolen mid-drag (e.g. by a Mission
Control space switch), in which case EVT_TREE_END_DRAG never fires.
@github-actions github-actions Bot added GUI wxGUI related Python Related code is in Python labels Jul 15, 2026
@petrasovaa petrasovaa added macOS macOS specific bug Something isn't working labels Jul 15, 2026
Comment thread gui/wxpython/lmgr/layertree.py Resolved
Comment thread gui/wxpython/lmgr/layertree.py Resolved
Add a TODO to remove the _MacSafeDragImage workaround once a wxPython
release includes the wxWidgets/wxWidgets#26380 fix.

Remove the EVT_MOUSE_CAPTURE_LOST handler: the event is only generated
by the MSW, GTK, and Qt ports (wxWindowBase::NotifyCaptureLost callers
in wxWidgets 3.2.8 and master), never by the osx port, so the handler
could never fire on macOS.

@petrasovaa petrasovaa left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks!

@petrasovaa
petrasovaa enabled auto-merge (squash) July 16, 2026 09:57
@petrasovaa
petrasovaa merged commit f778540 into OSGeo:main Jul 17, 2026
26 checks passed
@github-actions github-actions Bot added this to the 8.6.0 milestone Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working GUI wxGUI related macOS macOS specific Python Related code is in Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants