Fixes handling of native / alien widgets when (un)docking (#820)

* Initial plan

* Fix drawing artifacts when content widgets call winId()

When a content widget (e.g. VTK/OpenGL) calls winId() to get a native
window handle, Qt propagates native-window creation up the ancestor chain.
This makes CDockWidget, CDockAreaWidget, and CDockContainerWidget all
become native OS windows unexpectedly. Reparenting these native dock
widgets during floating or resizing then causes drawing artifacts.

Fix: set Qt::WA_DontCreateNativeAncestors on the content widget in
CDockWidget::setWidget() to stop native-window creation from propagating
up into the dock hierarchy.

Co-authored-by: RubendeBruin <34062862+RubendeBruin@users.noreply.github.com>

* Fix docking system to handle mix of native and alien Qt widgets

Three targeted fixes for the native/alien widget mixing problem:

1. Revert WA_DontCreateNativeAncestors from CDockWidget::setWidget().
   Setting it on the content widget created a broken native-inside-alien
   hierarchy: the QScrollArea (alien) could not clip its native child, so
   content escaped the scrollbox bounds; and alien CAutoHideDockContainer
   could not appear above the native content widget.
   Allowing natural propagation means QScrollArea also goes native, and OS
   parent/child window clipping works correctly.

2. Fix CDockContainerWidget::removeDockArea to use internalWinId() instead
   of testAttribute(WA_NativeWindow). WA_NativeWindow is only set when a
   widget is explicitly made native; widgets that became native through
   upward propagation from a descendant calling winId() have a real native
   window handle (internalWinId() != 0) but WA_NativeWindow may be unset.
   Setting their parent to nullptr would create a free-floating invisible
   OS window and cause drawing artifacts.

3. Fix CAutoHideDockContainer::collapseView: when the parent dock container
   is native but the auto-hide panel is still alien, promote the panel to a
   native window (winId()) before raise(). The OS always renders native
   child windows above a parent window's painted alien content, so an alien
   auto-hide panel can never appear above native sibling dock areas. Once
   the panel has its own OS-level window, raise() operates on OS Z-order
   and correctly places it above native content siblings.

Co-authored-by: RubendeBruin <34062862+RubendeBruin@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
Ruben de Bruin
2026-03-10 18:34:08 +01:00
committed by GitHub
parent e2c960238d
commit ee3a526adb
2 changed files with 23 additions and 3 deletions

View File

@@ -482,6 +482,17 @@ void CAutoHideDockContainer::collapseView(bool Enable)
{ {
updateSize(); updateSize();
d->updateResizeHandleSizeLimitMax(); d->updateResizeHandleSizeLimitMax();
// If the parent dock container has native child windows (e.g. because
// an OpenGL or VTK content widget called winId()), this panel is an
// alien (non-native) widget and will always be obscured by those native
// sibling windows regardless of Qt's paint order. Native OS windows are
// rendered above the parent's painted (alien) content by the windowing
// system. To allow raise() to use OS-level Z-order and appear on top,
// this panel must first be promoted to a native window itself.
if (parentWidget() && parentWidget()->internalWinId() && !internalWinId())
{
winId();
}
raise(); raise();
show(); show();
d->DockWidget->dockManager()->setDockWidgetFocused(d->DockWidget); d->DockWidget->dockManager()->setDockWidgetFocused(d->DockWidget);

View File

@@ -1573,9 +1573,18 @@ void CDockContainerWidget::removeDockArea(CDockAreaWidget* area)
d->DockAreas.removeAll(area); d->DockAreas.removeAll(area);
auto Splitter = area->parentSplitter(); auto Splitter = area->parentSplitter();
// Remove are from parent splitter and recursively hide tree of parent // Remove area from parent splitter and recursively hide tree of parent
// splitters if it has no visible content // splitters if it has no visible content.
if (area->testAttribute(Qt::WA_NativeWindow)) // Use internalWinId() rather than testAttribute(WA_NativeWindow) because
// WA_NativeWindow is only set when a widget is *explicitly* made native
// (e.g. winId() called directly on it). Widgets that became native through
// propagation from a child calling winId() (e.g. a VTK/OpenGL widget) also
// hold a real native window handle but may not have WA_NativeWindow set.
// Setting the parent of such a native window to nullptr would make it an
// invisible top-level OS window, causing drawing artifacts. Reparent to
// the dock manager instead so the window stays off-screen but within the
// application's window hierarchy.
if (area->internalWinId())
area->setParent(d->DockManager); area->setParent(d->DockManager);
else else
area->setParent(nullptr); area->setParent(nullptr);