From 35478ca92598ed3f5223a7e6c0e0192e3412b85c Mon Sep 17 00:00:00 2001 From: wanghongfeng <17621831870@163.com> Date: Fri, 28 Aug 2026 13:31:20 +0800 Subject: [PATCH] Fix TabsAtBottom content swap detaching the tab bar as a top-level window. Look up the current content widget by identity instead of takeAt(1), and keep native widgets parented when takeWidget() would otherwise leave a blank OS window. --- src/DockAreaWidget.cpp | 67 ++++++++++++++++++++++++++++++++++-------- src/DockWidget.cpp | 6 +++- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/src/DockAreaWidget.cpp b/src/DockAreaWidget.cpp index ff40aa5f..d960e690 100644 --- a/src/DockAreaWidget.cpp +++ b/src/DockAreaWidget.cpp @@ -118,6 +118,42 @@ class CDockAreaLayout return m_Widgets.count(); } + /** + * Removes the current content widget from the box layout by widget identity. + * Never use hardcoded takeAt(1): with TabsAtBottom the TabBar shares this + * layout and may sit at index 1, so takeAt(1) would orphan it as a top-level + * window (blank 200x44 popup, workspace tabs disappear). + */ + void removeCurrentWidgetFromBoxLayout() + { + if (!m_CurrentWidget) + return; + + int idx = m_ParentLayout->indexOf(m_CurrentWidget); + if (idx < 0) + { + detachWidget(m_CurrentWidget); + return; + } + + QLayoutItem* LayoutItem = m_ParentLayout->takeAt(idx); + if (!LayoutItem) + return; + + QWidget* taken = LayoutItem->widget(); + if (taken && (qobject_cast(taken) || qobject_cast(taken))) + { + m_ParentLayout->insertWidget(idx, taken); + delete LayoutItem; + detachWidget(m_CurrentWidget); + return; + } + + if (taken) + detachWidget(taken); + delete LayoutItem; + } + /** * Inserts the widget at the given index position into the internal widget * list @@ -150,12 +186,7 @@ class CDockAreaLayout { if (currentWidget() == Widget) { - auto LayoutItem = m_ParentLayout->takeAt(1); - if (LayoutItem) - { - detachWidget(LayoutItem->widget()); - } - delete LayoutItem; + removeCurrentWidgetFromBoxLayout(); m_CurrentWidget = nullptr; m_CurrentIndex = -1; } @@ -195,17 +226,23 @@ class CDockAreaLayout parent->setUpdatesEnabled(false); } - if (m_CurrentWidget) + removeCurrentWidgetFromBoxLayout(); + + int insertAt = 1; + if (m_ParentLayout->count() > 0) { - auto LayoutItem = m_ParentLayout->takeAt(1); - if (LayoutItem) + for (int i = 0; i < m_ParentLayout->count(); ++i) { - detachWidget(LayoutItem->widget()); + QLayoutItem* it = m_ParentLayout->itemAt(i); + QWidget* w = it ? it->widget() : nullptr; + if (qobject_cast(w)) + { + insertAt = i; + break; + } } - delete LayoutItem; } - - m_ParentLayout->insertWidget(1, next); + m_ParentLayout->insertWidget(insertAt, next); if (prev) { prev->hide(); @@ -572,6 +609,10 @@ void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget, { setCurrentIndex(index); DockWidget->setClosedState(false); // Set current index can show the widget without changing the close state, added to keep the close state consistent + if (!DockWidget->isClosed()) + { + TabWidget->setVisible(true); + } } // If this dock area is hidden, then we need to make it visible again // by calling DockWidget->toggleViewInternal(true); diff --git a/src/DockWidget.cpp b/src/DockWidget.cpp index 4b7fae1b..c0c1fdc7 100644 --- a/src/DockWidget.cpp +++ b/src/DockWidget.cpp @@ -495,7 +495,11 @@ QWidget* CDockWidget::takeWidget() if (w) { - w->setParent(nullptr); + // Native 子控件(如 OpenGL)不能 setParent(nullptr),否则会留下空白顶层 OS 窗口 + if (w->internalWinId() && d->DockManager) + w->setParent(d->DockManager); + else + w->setParent(nullptr); } return w; }