21#include <QXmlStreamWriter>
22#include <QApplication>
26#if QT_VERSION < 0x050900
30 return "0123456789abcdef"[value & 0xF];
38 const int length = separator ? (src.size() * 3 - 1) : (src.size() * 2);
39 QByteArray hex(length, Qt::Uninitialized);
40 char *hexData = hex.data();
41 const uchar *data =
reinterpret_cast<const uchar *
>(src.data());
42 for (
int i = 0, o = 0; i < src.size(); ++i) {
46 if ((separator) && (o < length))
47 hexData[o++] = separator;
55static unsigned int s_zOrderCounter = 0;
57static void insertWidgetIntoSplitter(QSplitter *s, QWidget *w,
bool append)
62 s->insertWidget(0, w);
84class DockContainerPrivate
89 DockContainerPrivate();
92 void createRootSplitter();
94 void onVisibleDockAreaCountChanged();
95 void emitDockAreasRemoved();
96 void emitDockAreasAdded();
98 DockSplitter *newSplitter(Qt::Orientation orientation, QWidget *parent =
nullptr);
99 void updateSplitterHandles(QSplitter *s);
100 void adjustSplitterSizesOnInsertion(QSplitter *s, qreal lastRatio = 1.0);
102 bool widgetResizesWithContainer(QWidget *w);
103 void onDockAreaViewToggled(
bool visible);
106 DockPanel *addDockWidgetToPanel(
Qx::DockWidgetArea area, DockWidget *w, DockPanel *targetPanel,
int index = -1);
109 void addDockPanelsToList(
const QList<DockPanel *> ps);
110 void appendPanels(
const QList<DockPanel *> ps);
112 void dropIntoContainer(DockFloatingContainer *floatingWidget,
Qx::DockWidgetArea area);
113 void dropIntoCenterOfPanel(DockFloatingContainer *floatingWidget, DockPanel *targetPanel,
int tabIndex = 0);
114 void dropIntoPanel(DockFloatingContainer *floatingWidget, DockPanel *targetPanel,
Qx::DockWidgetArea area,
118 void moveIntoCenterOfSection(QWidget *widget, DockPanel *targetPanel,
int tabIndex = 0);
119 void moveToNewSection(QWidget *widget, DockPanel *targetPanel,
Qx::DockWidgetArea area,
int tabIndex = 0);
121 void dropIntoAutoHideSideBar(DockFloatingContainer *floatingWidget,
Qx::DockWidgetArea area);
124 void saveChildNodesState(QXmlStreamWriter &s, QWidget *w)
const;
125 void saveAutoHideWidgetsState(QXmlStreamWriter &s)
const;
127 bool restoreChildNodes(DockStateReader &s, QWidget *&createdWidget,
bool testing);
128 bool restoreSplitter(DockStateReader &s, QWidget *&createdWidget,
bool testing);
129 bool restoreDockArea(DockStateReader &s, QWidget *&createdWidget,
bool testing);
130 bool restoreSideBar(DockStateReader &s, QWidget *&createdWidget,
bool testing);
132 DockWindow *m_window =
nullptr;
133 QGridLayout *m_layout =
nullptr;
134 DockSplitter *m_rootSplitter =
nullptr;
135 QList<QPointer<DockPanel>> m_panels;
136 DockPanel *m_lastAddedPanelCache[5];
137 DockPanel *m_topLevelPanel =
nullptr;
138 bool m_isFloating =
false;
139 unsigned int m_zOrderIndex = 0;
140 QMap<Qx::DockSideBarArea, DockSideBar *> m_sideTabBarWidgets;
141 QList<DockAutoHideContainer *> m_autoHideWidgets;
142 DockSideTab *m_delayedAutoHideTab;
143 QTimer m_delayedAutoHideTimer;
144 bool m_delayedAutoHideShow =
false;
145 int m_visibleDockPanelCount = -1;
148DockContainerPrivate::DockContainerPrivate()
150 std::fill(std::begin(m_lastAddedPanelCache), std::end(m_lastAddedPanelCache),
nullptr);
151 m_delayedAutoHideTimer.setSingleShot(
true);
152 m_delayedAutoHideTimer.setInterval(500);
153 QObject::connect(&m_delayedAutoHideTimer, &QTimer::timeout, [
this]() {
154 auto GlobalPos = m_delayedAutoHideTab->mapToGlobal(QPoint(0, 0));
155 qApp->sendEvent(m_delayedAutoHideTab,
new QMouseEvent(QEvent::MouseButtonPress, QPoint(0, 0), GlobalPos,
156 Qt::LeftButton, {Qt::LeftButton}, Qt::NoModifier));
160void DockContainerPrivate::init()
164 m_isFloating = q->floatingWidget() !=
nullptr;
166 m_layout =
new QGridLayout();
167 m_layout->setContentsMargins(0, 0, 0, 0);
168 m_layout->setSpacing(0);
169 m_layout->setColumnStretch(1, 1);
170 m_layout->setRowStretch(1, 1);
171 q->setLayout(m_layout);
174void DockContainerPrivate::createRootSplitter()
176 if (m_rootSplitter) {
179 m_rootSplitter = newSplitter(Qt::Horizontal);
181 m_layout->addWidget(m_rootSplitter, 1, 1);
188void DockContainerPrivate::onVisibleDockAreaCountChanged()
191 auto panel = q->topLevelDockPanel();
194 this->m_topLevelPanel = panel;
195 panel->updateTitleBarButtonVisibility(
true);
196 }
else if (this->m_topLevelPanel) {
197 this->m_topLevelPanel->updateTitleBarButtonVisibility(
false);
198 this->m_topLevelPanel =
nullptr;
202void DockContainerPrivate::emitDockAreasRemoved()
205 onVisibleDockAreaCountChanged();
206 Q_EMIT q->dockAreasRemoved();
209void DockContainerPrivate::emitDockAreasAdded()
212 onVisibleDockAreaCountChanged();
213 Q_EMIT q->dockAreasAdded();
216DockSplitter *DockContainerPrivate::newSplitter(Qt::Orientation orientation, QWidget *parent)
218 DockSplitter *s =
new DockSplitter(orientation, parent);
219 s->setOpaqueResize(DockManager::testConfigFlag(DockManager::OpaqueSplitterResize));
220 s->setChildrenCollapsible(
false);
228void DockContainerPrivate::updateSplitterHandles(QSplitter *s)
230 if (!m_window->centralWidget() || !s) {
234 for (
int i = 0; i < s->count(); ++i) {
235 s->setStretchFactor(i, widgetResizesWithContainer(s->widget(i)) ? 1 : 0);
243void DockContainerPrivate::adjustSplitterSizesOnInsertion(QSplitter *s, qreal lastRatio)
245 int areaSize = (s->orientation() == Qt::Horizontal) ? s->width() : s->height();
246 auto splitterSizes = s->sizes();
248 qreal totRatio = splitterSizes.size() - 1.0 + lastRatio;
249 for (
int i = 0; i < splitterSizes.size() - 1; i++) {
250 splitterSizes[i] = areaSize / totRatio;
252 splitterSizes.back() = areaSize * lastRatio / totRatio;
253 s->setSizes(splitterSizes);
261bool DockContainerPrivate::widgetResizesWithContainer(QWidget *w)
263 if (!m_window->centralWidget()) {
267 auto panel = qobject_cast<DockPanel *>(w);
269 return panel->isCentralWidgetArea();
272 auto innerSplitter = qobject_cast<DockSplitter *>(w);
274 return innerSplitter->isResizingWithContainer();
280void DockContainerPrivate::onDockAreaViewToggled(
bool visible)
283 DockPanel *panel = qobject_cast<DockPanel *>(q->sender());
284 m_visibleDockPanelCount += visible ? 1 : -1;
285 onVisibleDockAreaCountChanged();
286 Q_EMIT q->dockAreaViewToggled(panel, visible);
289DockPanel *DockContainerPrivate::addDockWidgetToContainer(
Qx::DockWidgetArea area, DockWidget *w)
292 DockPanel *panel =
new DockPanel(m_window, q);
293 panel->addDockWidget(w);
294 addDockPanel(panel, area);
295 panel->updateTitleBarVisibility();
296 m_lastAddedPanelCache[areaIdToIndex(area)] = panel;
300DockPanel *DockContainerPrivate::addDockWidgetToPanel(
Qx::DockWidgetArea area, DockWidget *w, DockPanel *targetPanel,
305 targetPanel->insertDockWidget(index, w);
306 targetPanel->updateTitleBarVisibility();
309 DockPanel *np =
new DockPanel(m_window, q);
310 np->addDockWidget(w);
312 auto param = internal::dockAreaInsertParameters(area);
313 auto targetSplitter = targetPanel->parentSplitter();
315 index = targetSplitter->indexOf(targetPanel);
316 if (targetSplitter->orientation() == param.orientation()) {
317 targetSplitter->insertWidget(index + param.insertOffset(), np);
318 updateSplitterHandles(targetSplitter);
319 if (DockManager::testConfigFlag(DockManager::EqualSplitOnInsertion)) {
320 adjustSplitterSizesOnInsertion(targetSplitter);
323 auto sizes = targetSplitter->sizes();
324 auto ns = newSplitter(param.orientation());
325 ns->addWidget(targetPanel);
326 insertWidgetIntoSplitter(ns, np, param.append());
327 updateSplitterHandles(ns);
328 targetSplitter->insertWidget(index, ns);
329 updateSplitterHandles(targetSplitter);
330 if (DockManager::testConfigFlag(DockManager::EqualSplitOnInsertion)) {
331 targetSplitter->setSizes(sizes);
332 adjustSplitterSizesOnInsertion(ns);
335 addDockPanelsToList({np});
341 auto param = internal::dockAreaInsertParameters(area);
342 if (m_panels.count() <= 1) {
343 m_rootSplitter->setOrientation(param.orientation());
345 QSplitter *s = m_rootSplitter;
346 if (s->orientation() == param.orientation()) {
347 insertWidgetIntoSplitter(s, np, param.append());
348 updateSplitterHandles(s);
353 auto ns = newSplitter(param.orientation());
354 if (param.append()) {
355 QLayoutItem *li = m_layout->replaceWidget(s, ns);
358 updateSplitterHandles(ns);
362 QLayoutItem *li = m_layout->replaceWidget(s, ns);
364 updateSplitterHandles(ns);
369 addDockPanelsToList({np});
372void DockContainerPrivate::addDockPanelsToList(
const QList<DockPanel *> ps)
374 int beforeCount = m_panels.count();
375 int newCount = ps.count();
381 if (beforeCount == 1) {
382 m_panels[0]->updateTitleBarVisibility();
385 m_panels[0]->updateTitleBarVisibility();
387 emitDockAreasAdded();
390void DockContainerPrivate::appendPanels(
const QList<DockPanel *> panels)
392 for (
auto *panel : panels) {
393 m_panels.append(panel);
396 for (
auto *panel : panels) {
397 QObject::connect(panel, &DockPanel::viewToggled, q,
398 std::bind(&DockContainerPrivate::onDockAreaViewToggled,
this, std::placeholders::_1));
403void DockContainerPrivate::dropIntoContainer(DockFloatingContainer *floatingWidget,
Qx::DockWidgetArea area)
405 auto param = internal::dockAreaInsertParameters(area);
406 DockContainer *floatingDockContainer = floatingWidget->dockContainer();
407 auto newPanels = floatingDockContainer->findChildren<DockPanel *>(QString(), Qt::FindChildrenRecursively);
408 auto splitter = m_rootSplitter;
410 if (m_panels.count() <= 1) {
411 splitter->setOrientation(param.orientation());
412 }
else if (splitter->orientation() != param.orientation()) {
413 auto newSplitter = this->newSplitter(param.orientation());
414 QLayoutItem *li = m_layout->replaceWidget(splitter, newSplitter);
415 newSplitter->addWidget(splitter);
416 updateSplitterHandles(newSplitter);
417 splitter = newSplitter;
422 auto floatingSplitter = floatingDockContainer->rootSplitter();
423 if (floatingSplitter->count() == 1) {
424 insertWidgetIntoSplitter(splitter, floatingSplitter->widget(0), param.append());
425 updateSplitterHandles(splitter);
426 }
else if (floatingSplitter->orientation() == param.orientation()) {
427 int insertIndex = param.append() ? splitter->count() : 0;
428 while (floatingSplitter->count()) {
429 splitter->insertWidget(insertIndex++, floatingSplitter->widget(0));
430 updateSplitterHandles(splitter);
433 insertWidgetIntoSplitter(splitter, floatingSplitter, param.append());
436 m_rootSplitter = splitter;
437 addDockPanelsToList(newPanels);
442 if (!splitter->isVisible()) {
447void DockContainerPrivate::dropIntoCenterOfPanel(DockFloatingContainer *floatingWidget, DockPanel *targetPanel,
450 DockContainer *floatingContainer = floatingWidget->dockContainer();
451 auto newDockWidgets = floatingContainer->dockWidgets();
452 auto topLevelPanel = floatingContainer->topLevelDockPanel();
453 int newCurrentIndex = -1;
454 tabIndex = qMax(0, tabIndex);
460 newCurrentIndex = topLevelPanel->currentIndex();
463 for (
int i = 0; i < newDockWidgets.count(); ++i) {
464 DockWidget *dockWidget = newDockWidgets[i];
465 targetPanel->insertDockWidget(tabIndex + i, dockWidget,
false);
469 if (newCurrentIndex < 0 && !dockWidget->isClosed()) {
473 targetPanel->setCurrentIndex(newCurrentIndex + tabIndex);
474 targetPanel->updateTitleBarVisibility();
478void DockContainerPrivate::dropIntoPanel(DockFloatingContainer *floatingWidget, DockPanel *targetPanel,
484 dropIntoCenterOfPanel(floatingWidget, targetPanel, tabIndex);
488 DockContainer *floatingContainer = floatingWidget->dockContainer();
489 auto param = internal::dockAreaInsertParameters(area);
490 auto newDockpanels = floatingContainer->findChildren<DockPanel *>(QString(), Qt::FindChildrenRecursively);
491 auto targetSplitter = targetPanel->parentSplitter();
492 int areaIndex = targetSplitter->indexOf(targetPanel);
493 auto floatingSplitter = floatingContainer->rootSplitter();
494 if (targetSplitter->orientation() == param.orientation()) {
495 auto sizes = targetSplitter->sizes();
496 int targetAreaSize = (param.orientation() == Qt::Horizontal) ? targetPanel->width() : targetPanel->height();
497 bool adjustSplitterSizes =
true;
498 if ((floatingSplitter->orientation() != param.orientation()) && floatingSplitter->count() > 1) {
499 targetSplitter->insertWidget(areaIndex + param.insertOffset(), floatingSplitter);
500 updateSplitterHandles(targetSplitter);
502 adjustSplitterSizes = (floatingSplitter->count() == 1);
503 int insertIndex = areaIndex + param.insertOffset();
504 while (floatingSplitter->count()) {
505 targetSplitter->insertWidget(insertIndex++, floatingSplitter->widget(0));
506 updateSplitterHandles(targetSplitter);
510 if (adjustSplitterSizes) {
511 int size = (targetAreaSize - targetSplitter->handleWidth()) / 2;
512 sizes[areaIndex] = size;
513 sizes.insert(areaIndex, size);
514 targetSplitter->setSizes(sizes);
517 QSplitter *newSplitter = this->newSplitter(param.orientation());
518 int targetAreaSize = (param.orientation() == Qt::Horizontal) ? targetPanel->width() : targetPanel->height();
519 bool adjustSplitterSizes =
true;
520 if ((floatingSplitter->orientation() != param.orientation()) && floatingSplitter->count() > 1) {
521 newSplitter->addWidget(floatingSplitter);
522 updateSplitterHandles(newSplitter);
524 adjustSplitterSizes = (floatingSplitter->count() == 1);
525 while (floatingSplitter->count()) {
526 newSplitter->addWidget(floatingSplitter->widget(0));
527 updateSplitterHandles(newSplitter);
533 auto sizes = targetSplitter->sizes();
534 insertWidgetIntoSplitter(newSplitter, targetPanel, !param.append());
535 updateSplitterHandles(newSplitter);
536 if (adjustSplitterSizes) {
537 int size = targetAreaSize / 2;
538 newSplitter->setSizes({size, size});
540 targetSplitter->insertWidget(areaIndex, newSplitter);
541 targetSplitter->setSizes(sizes);
542 updateSplitterHandles(targetSplitter);
545 addDockPanelsToList(newDockpanels);
551 DockWidget *droppedDockWidget = qobject_cast<DockWidget *>(widget);
552 DockPanel *droppedPanel = qobject_cast<DockPanel *>(widget);
555 if (droppedDockWidget) {
556 newPanel =
new DockPanel(m_window, q);
557 DockPanel *oldPanel = droppedDockWidget->dockPanel();
559 oldPanel->removeDockWidget(droppedDockWidget);
561 newPanel->addDockWidget(droppedDockWidget);
567 auto splitter = droppedPanel->parentSplitter();
568 auto insertParam = internal::dockAreaInsertParameters(area);
569 if (splitter == m_rootSplitter && insertParam.orientation() == splitter->orientation()) {
570 if (insertParam.append() && splitter->lastWidget() == droppedPanel) {
572 }
else if (!insertParam.append() && splitter->firstWidget() == droppedPanel) {
576 droppedPanel->dockContainer()->removeDockPanel(droppedPanel);
577 newPanel = droppedPanel;
580 addDockPanel(newPanel, area);
581 m_lastAddedPanelCache[areaIdToIndex(area)] = newPanel;
584void DockContainerPrivate::moveIntoCenterOfSection(QWidget *widget, DockPanel *targetPanel,
int tabIndex)
586 auto droppedDockWidget = qobject_cast<DockWidget *>(widget);
587 auto droppedPanel = qobject_cast<DockPanel *>(widget);
589 tabIndex = qMax(0, tabIndex);
590 if (droppedDockWidget) {
591 DockPanel *oldPanel = droppedDockWidget->dockPanel();
592 if (oldPanel == targetPanel) {
597 oldPanel->removeDockWidget(droppedDockWidget);
599 targetPanel->insertDockWidget(tabIndex, droppedDockWidget,
true);
601 QList<DockWidget *> newDockWidgets = droppedPanel->dockWidgets();
602 int newCurrentIndex = droppedPanel->currentIndex();
603 for (
int i = 0; i < newDockWidgets.count(); ++i) {
604 DockWidget *w = newDockWidgets[i];
605 targetPanel->insertDockWidget(tabIndex + i, w,
false);
607 targetPanel->setCurrentIndex(tabIndex + newCurrentIndex);
608 droppedPanel->dockContainer()->removeDockPanel(droppedPanel);
609 droppedPanel->deleteLater();
612 targetPanel->updateTitleBarVisibility();
616void DockContainerPrivate::moveToNewSection(QWidget *widget, DockPanel *targetPanel,
Qx::DockWidgetArea area,
623 moveIntoCenterOfSection(widget, targetPanel, tabIndex);
627 DockWidget *droppedDockWidget = qobject_cast<DockWidget *>(widget);
628 DockPanel *droppedPanel = qobject_cast<DockPanel *>(widget);
630 if (droppedDockWidget) {
631 newPanel =
new DockPanel(m_window, q);
632 DockPanel *oldPanel = droppedDockWidget->dockPanel();
634 oldPanel->removeDockWidget(droppedDockWidget);
636 newPanel->addDockWidget(droppedDockWidget);
638 droppedPanel->dockContainer()->removeDockPanel(droppedPanel);
639 newPanel = droppedPanel;
642 auto insertParam = internal::dockAreaInsertParameters(area);
643 auto targetSplitter = targetPanel->parentSplitter();
644 int panelIndex = targetSplitter->indexOf(targetPanel);
645 auto sizes = targetSplitter->sizes();
646 if (targetSplitter->orientation() == insertParam.orientation()) {
647 int targetPanelSize =
648 (insertParam.orientation() == Qt::Horizontal) ? targetPanel->width() : targetPanel->height();
649 targetSplitter->insertWidget(panelIndex + insertParam.insertOffset(), newPanel);
650 updateSplitterHandles(targetSplitter);
651 int size = (targetPanelSize - targetSplitter->handleWidth()) / 2;
652 sizes[panelIndex] = size;
653 sizes.insert(panelIndex, size);
655 int targetPanelSize =
656 (insertParam.orientation() == Qt::Horizontal) ? targetPanel->width() : targetPanel->height();
657 QSplitter *newSplitter = this->newSplitter(insertParam.orientation());
658 newSplitter->addWidget(targetPanel);
659 insertWidgetIntoSplitter(newSplitter, newPanel, insertParam.append());
660 updateSplitterHandles(newSplitter);
661 int size = targetPanelSize / 2;
662 newSplitter->setSizes({size, size});
663 targetSplitter->insertWidget(panelIndex, newSplitter);
664 updateSplitterHandles(targetSplitter);
666 targetSplitter->setSizes(sizes);
668 addDockPanelsToList({newPanel});
671void DockContainerPrivate::dropIntoAutoHideSideBar(DockFloatingContainer *floatingWidget,
Qx::DockWidgetArea area)
674 auto sideBarArea = internal::toSideBarArea(area);
675 auto newPanels = floatingWidget->findChildren<DockPanel *>(QString(), Qt::FindChildrenRecursively);
676 int tabIndex = m_window->containerOverlay()->tabIndexUnderCursor();
677 for (
auto panel : newPanels) {
678 auto widgets = panel->dockWidgets();
679 for (
auto w : widgets) {
680 q->createAndSetupAutoHideContainer(sideBarArea, w, tabIndex++);
685void DockContainerPrivate::moveToAutoHideSideBar(QWidget *widget,
Qx::DockWidgetArea area,
int tabIndex)
688 DockWidget *droppedWidget = qobject_cast<DockWidget *>(widget);
689 DockPanel *droppedPanel = qobject_cast<DockPanel *>(widget);
690 auto sideBarArea = internal::toSideBarArea(area);
693 if (q == droppedWidget->dockContainer()) {
694 droppedWidget->setAutoHide(
true, sideBarArea, tabIndex);
696 q->createAndSetupAutoHideContainer(sideBarArea, droppedWidget, tabIndex);
699 if (q == droppedPanel->dockContainer()) {
700 droppedPanel->setAutoHide(
true, sideBarArea, tabIndex);
702 for (
const auto dockWidget : droppedPanel->openedDockWidgets()) {
703 if (!dockWidget->features().testFlag(DockWidget::DockWidgetPinnable)) {
706 q->createAndSetupAutoHideContainer(sideBarArea, dockWidget, tabIndex++);
712void DockContainerPrivate::saveChildNodesState(QXmlStreamWriter &s, QWidget *w)
const
714 QSplitter *splitter = qobject_cast<QSplitter *>(w);
716 s.writeStartElement(
"Splitter");
717 s.writeAttribute(
"Orientation", (splitter->orientation() == Qt::Horizontal) ?
"|" :
"-");
718 s.writeAttribute(
"Count", QString::number(splitter->count()));
719 for (
int i = 0; i < splitter->count(); ++i) {
720 saveChildNodesState(s, splitter->widget(i));
723 s.writeStartElement(
"Sizes");
724 for (
auto Size : splitter->sizes()) {
725 s.writeCharacters(QString::number(Size) +
" ");
731 DockPanel *panel = qobject_cast<DockPanel *>(w);
738void DockContainerPrivate::saveAutoHideWidgetsState(QXmlStreamWriter &s)
const
740 for (
const auto sideTabBar : m_sideTabBarWidgets.values()) {
741 if (!sideTabBar->count()) {
745 sideTabBar->saveState(s);
749bool DockContainerPrivate::restoreChildNodes(DockStateReader &s, QWidget *&createdWidget,
bool testing)
752 while (s.readNextStartElement()) {
753 if (s.name() == QLatin1String(
"Splitter")) {
754 result = restoreSplitter(s, createdWidget, testing);
755 }
else if (s.name() == QLatin1String(
"Area")) {
756 result = restoreDockArea(s, createdWidget, testing);
757 }
else if (s.name() == QLatin1String(
"SideBar")) {
758 result = restoreSideBar(s, createdWidget, testing);
760 s.skipCurrentElement();
767bool DockContainerPrivate::restoreSplitter(DockStateReader &s, QWidget *&createdWidget,
bool testing)
770 QString orientationStr = s.attributes().value(
"Orientation").toString();
773 if (!orientationStr.startsWith(
"|") && !orientationStr.startsWith(
"-")) {
779 bool horizontalSplitter = orientationStr.startsWith(
"|");
783 if (s.fileVersion() == 0) {
784 horizontalSplitter = !horizontalSplitter;
787 int orientation = horizontalSplitter ? Qt::Horizontal : Qt::Vertical;
788 int widgetCount = s.attributes().value(
"Count").toInt(&ok);
793 QSplitter *splitter =
nullptr;
795 splitter = newSplitter(
static_cast<Qt::Orientation
>(orientation));
797 bool visible =
false;
799 while (s.readNextStartElement()) {
800 QWidget *childNode =
nullptr;
802 if (s.name() == QLatin1String(
"Splitter")) {
803 result = restoreSplitter(s, childNode, testing);
804 }
else if (s.name() == QLatin1String(
"Area")) {
805 result = restoreDockArea(s, childNode, testing);
806 }
else if (s.name() == QLatin1String(
"Sizes")) {
807 QString sSizes = s.readElementText().trimmed();
809 QTextStream TextStream(&sSizes);
810 while (!TextStream.atEnd()) {
816 s.skipCurrentElement();
823 if (testing || !childNode) {
827 splitter->addWidget(childNode);
828 visible |= childNode->isVisibleTo(splitter);
831 updateSplitterHandles(splitter);
834 if (sizes.count() != widgetCount) {
839 if (!splitter->count()) {
843 splitter->setSizes(sizes);
844 splitter->setVisible(visible);
846 createdWidget = splitter;
848 createdWidget =
nullptr;
854bool DockContainerPrivate::restoreDockArea(DockStateReader &s, QWidget *&createdWidget,
bool testing)
857 DockPanel *panel =
nullptr;
858 auto result = DockPanel::restoreState(s, panel, testing, q);
859 if (result && panel) {
860 appendPanels({panel});
862 createdWidget = panel;
866bool DockContainerPrivate::restoreSideBar(DockStateReader &s, QWidget *&createdWidget,
bool testing)
868 Q_UNUSED(createdWidget)
872 if (!DockManager::testAutoHideConfigFlag(DockManager::AutoHideFeatureEnabled)) {
882 while (s.readNextStartElement()) {
883 if (s.name() != QLatin1String(
"Widget")) {
887 auto name = s.attributes().value(
"Name");
888 if (
name.isEmpty()) {
893 bool closed = s.attributes().value(
"Closed").toInt(&ok);
898 int Size = s.attributes().value(
"Size").toInt(&ok);
903 s.skipCurrentElement();
904 DockWidget *dockWidget = m_window->findDockWidget(
name.toString());
905 if (!dockWidget || testing) {
909 auto sideBar = q->autoHideSideBar(area);
910 DockAutoHideContainer *autoHideContainer;
911 if (dockWidget->isAutoHide()) {
912 autoHideContainer = dockWidget->autoHideContainer();
913 if (autoHideContainer->autoHideSideBar() != sideBar) {
914 sideBar->addAutoHideWidget(autoHideContainer);
917 autoHideContainer = sideBar->insertDockWidget(-1, dockWidget);
919 autoHideContainer->setSize(Size);
920 dockWidget->setProperty(internal::ClosedProperty, closed);
921 dockWidget->setProperty(internal::DirtyProperty,
false);
939 d->m_window->registerDockContainer(
this);
957 if (d->m_window && d->m_window !=
this) {
958 d->m_window->removeDockContainer(
this);
982 panel = d->addDockWidgetToPanel(area, w, p, index);
984 panel = d->addDockWidgetToContainer(area, w);
986 if (topLevelWidget) {
991 if (!newTopLevelWidget) {
1009 return d->m_zOrderIndex;
1031 for (
const auto &panel : d->m_panels) {
1032 if (panel && panel->isVisible() && panel->rect().contains(panel->mapFromGlobal(globalPos))) {
1043 return (index <
dockPanelCount()) ? d->m_panels[index] :
nullptr;
1053 QList<DockPanel *> result;
1054 for (
const auto &p : d->m_panels) {
1055 if (p && !p->isHidden()) {
1068 QList<DockWidget *> result;
1069 for (
const auto &p : d->m_panels) {
1070 if (p && !p->isHidden()) {
1071 result.append(p->openedDockWidgets());
1085 for (
auto panel : d->m_panels) {
1086 if (panel && !panel->isHidden()) {
1103 if (panels.count() != 1) {
1107 return panels[0]->openDockWidgetsCount() == 1;
1113 return d->m_panels.count();
1120 for (
const auto &panel : d->m_panels) {
1121 result += (!panel || panel->isHidden()) ? 0 : 1;
1138 for (
const auto &panel : d->m_panels) {
1142 f &= panel->features();
1151 return d->m_isFloating;
1161 return internal::findParent<DockFloatingContainer *>(
this);
1167 for (
const auto &panel : d->m_panels) {
1168 if (!panel || panel == keepOpenPanel) {
1188 return d->m_sideTabBarWidgets[area];
1194 return d->m_autoHideWidgets;
1206 if (!d->m_rootSplitter) {
1210 if (d->m_rootSplitter->hasVisibleContent()) {
1211 return d->m_rootSplitter->geometry();
1229 return d->m_rootSplitter;
1235 d->createRootSplitter();
1242 if (container && container !=
this) {
1245 d->addDockPanel(panel, area);
1257 panel->disconnect(
this);
1258 d->m_panels.removeAll(panel);
1263 panel->setParent(
nullptr);
1267 auto p = std::find(std::begin(d->m_lastAddedPanelCache), std::end(d->m_lastAddedPanelCache), panel);
1268 if (p != std::end(d->m_lastAddedPanelCache)) {
1273 if (splitter->count() > 1) {
1279 if (splitter == d->m_rootSplitter) {
1281 if (!splitter->count()) {
1286 QWidget *widget = splitter->widget(0);
1287 auto childSplitter = qobject_cast<DockSplitter *>(widget);
1290 if (!childSplitter) {
1295 childSplitter->setParent(
nullptr);
1296 QLayoutItem *li = d->m_layout->replaceWidget(splitter, childSplitter);
1297 d->m_rootSplitter = childSplitter;
1299 }
else if (splitter->count() == 1) {
1300 QSplitter *parentSplitter = internal::findParent<QSplitter *>(splitter);
1301 auto Sizes = parentSplitter->sizes();
1302 QWidget *widget = splitter->widget(0);
1303 widget->setParent(
this);
1305 parentSplitter->setSizes(Sizes);
1318 d->emitDockAreasRemoved();
1327 auto result = d->m_panels;
1328 d->m_panels.clear();
1342 auto widgets = panel->openedDockWidgets();
1343 if (widgets.count() != 1) {
1352 if (panels.count() != 1) {
1365 return d->m_lastAddedPanelCache[areaIdToIndex(area)];
1379 QList<DockWidget *> result;
1380 for (
const auto &panel : d->m_panels) {
1384 result.append(panel->dockWidgets());
1397 d->updateSplitterHandles(splitter);
1409 auto containerDropArea = d->m_window->containerOverlay()->dropAreaUnderCursor();
1410 bool dropped =
false;
1415 auto dropOverlay = d->m_window->panelOverlay();
1417 dropArea = dropOverlay->showOverlay(panel);
1423 int tabIndex = d->m_window->panelOverlay()->tabIndexUnderCursor();
1442 sideBar->addAutoHideWidget(autoHideWidget);
1457 window()->activateWindow();
1458 if (singleDroppedDockWidget) {
1459 d->m_window->notifyDockAreaRelocation(singleDroppedDockWidget);
1476 d->moveToNewSection(widget, targetPanel, dropArea, tabIndex);
1478 d->moveToAutoHideSideBar(widget, dropArea, tabIndex);
1480 d->moveToContainer(widget, dropArea);
1487 window()->activateWindow();
1488 d->m_window->notifyDockAreaRelocation(widget);
1500 d->m_sideTabBarWidgets[area] =
new DockSideBar(
this, area);
1501 d->m_layout->addWidget(d->m_sideTabBarWidgets[area], 1, 0);
1506 d->m_sideTabBarWidgets[area] =
new DockSideBar(
this, area);
1507 d->m_layout->addWidget(d->m_sideTabBarWidgets[area], 1, 2);
1512 d->m_sideTabBarWidgets[area] =
new DockSideBar(
this, area);
1513 d->m_layout->addWidget(d->m_sideTabBarWidgets[area], 2, 1);
1518 d->m_sideTabBarWidgets[area] =
new DockSideBar(
this, area);
1519 d->m_layout->addWidget(d->m_sideTabBarWidgets[area], 0, 1);
1533 Q_ASSERT_X(
false,
"DockContainer::createAndSetupAutoHideContainer",
"Requested area does not exist in config");
1546 d->m_autoHideWidgets.append(autoHideWidget);
1553 d->m_autoHideWidgets.removeAll(autoHideWidget);
1567 auto autoHideTab = qobject_cast<DockSideTab *>(w);
1569 switch (e->type()) {
1571 if (!autoHideTab->dockWidget()->isVisible()) {
1572 d->m_delayedAutoHideTab = autoHideTab;
1573 d->m_delayedAutoHideShow =
true;
1574 d->m_delayedAutoHideTimer.start();
1576 d->m_delayedAutoHideTimer.stop();
1580 case QEvent::MouseButtonPress:
1581 d->m_delayedAutoHideTimer.stop();
1585 if (autoHideTab->dockWidget()->isVisible()) {
1586 d->m_delayedAutoHideTab = autoHideTab;
1587 d->m_delayedAutoHideShow =
false;
1588 d->m_delayedAutoHideTimer.start();
1590 d->m_delayedAutoHideTimer.stop();
1600 auto autoHideContainer = qobject_cast<DockAutoHideContainer *>(w);
1601 if (autoHideContainer) {
1602 switch (e->type()) {
1605 d->m_delayedAutoHideTimer.stop();
1609 if (autoHideContainer->isVisible()) {
1610 d->m_delayedAutoHideTab = autoHideContainer->autoHideTab();
1611 d->m_delayedAutoHideShow =
false;
1612 d->m_delayedAutoHideTimer.start();
1626 s.writeStartElement(
"Container");
1627 s.writeAttribute(
"Floating", QString::number(
isFloating() ? 1 : 0));
1631#if QT_VERSION < 0x050900
1634 s.writeTextElement(
"Geometry", geometry.toHex(
' '));
1637 d->saveChildNodesState(s, d->m_rootSplitter);
1638 d->saveAutoHideWidgetsState(s);
1639 s.writeEndElement();
1645 bool isFloating = s.attributes().value(
"Floating").toInt();
1647 QWidget *newRootSplitter{};
1649 d->m_visibleDockPanelCount = -1;
1650 d->m_panels.clear();
1651 std::fill(std::begin(d->m_lastAddedPanelCache), std::end(d->m_lastAddedPanelCache),
nullptr);
1655 if (!s.readNextStartElement() || s.name() != QLatin1String(
"Geometry")) {
1659 QByteArray geometryString = s.readElementText(DockStateReader::ErrorOnUnexpectedElement).toLocal8Bit();
1660 QByteArray geometry = QByteArray::fromHex(geometryString);
1661 if (geometry.isEmpty()) {
1667 if (floatingWidget) {
1673 if (!d->restoreChildNodes(s, newRootSplitter, testing)) {
1683 if (!newRootSplitter) {
1684 newRootSplitter = d->newSplitter(Qt::Horizontal);
1687 QLayoutItem *li = d->m_layout->replaceWidget(d->m_rootSplitter, newRootSplitter);
1688 auto oldRoot = d->m_rootSplitter;
1689 d->m_rootSplitter = qobject_cast<DockSplitter *>(newRootSplitter);
1690 oldRoot->deleteLater();
1702 bool result = QWidget::event(e);
1703 if (e->type() == QEvent::WindowActivate) {
1704 d->m_zOrderIndex = ++s_zOrderCounter;
1705 }
else if (e->type() == QEvent::Show && !d->m_zOrderIndex) {
1706 d->m_zOrderIndex = ++s_zOrderCounter;
Auto hide container for hosting an auto hide dock widget
Container that manages a number of dock panels with single dock widgets or tabyfied dock widgets in e...
DockPanel * dockPanelAt(const QPoint &globalPos) const
DockPanel * lastAddedDockPanel(Qx::DockWidgetArea area) const
This function returns the last added dock panel for the given area identifier or 0 if no dock panel h...
virtual unsigned int zOrderIndex() const
DockWindow * dockWindow() const
DockPanel * addDockWidget(Qx::DockWidgetArea area, DockWidget *w, DockPanel *p=nullptr, int index=-1)
Adds dockwidget into the given area.
void saveState(QXmlStreamWriter &s) const
QList< DockWidget * > dockWidgets() const
This function returns a list of all dock widgets in this container.
bool isInFrontOf(DockContainer *other) const
This function returns true if this container widgets z order index is higher than the index of the co...
QList< DockWidget * > openedDockWidgets() const
Returns a list for all open dock widgets in all open dock panels
DockWidget * topLevelDockWidget() const
If hasTopLevelDockWidget() returns true, this function returns the one and only visible dock widget.
void createRootSplitter()
void createSideTabBarWidgets()
void removeDockWidget(DockWidget *w)
void dropFloatingWidget(DockFloatingContainer *floatingWidget, const QPoint &targetPos)
Drop floating widget into the container
void closeOtherAreas(DockPanel *keepOpenPanel)
void registerAutoHideWidget(DockAutoHideContainer *autoHideWidget)
void addDockPanel(DockPanel *panel, Qx::DockWidgetArea area=Qx::CenterDockWidgetArea)
bool restoreState(DockStateReader &s, bool testing)
void handleAutoHideWidgetEvent(QEvent *e, QWidget *w)
QList< QPointer< DockPanel > > removeAllDockPanels()
Remove all dock panles and returns the list of removed dock panles
void autoHideWidgetCreated(DockAutoHideContainer *autoHideWidget)
int dockPanelCount() const
DockPanel * dockPanel(int index) const
QRect contentRect() const
Returns the content rectangle mapped to global space.
virtual bool event(QEvent *e) override
Handles activation events to update zOrderIndex
void updateSplitterHandles(QSplitter *splitter)
This function forces the dock container widget to update handles of splitters based on resize modes o...
DockWidget::DockWidgetFeatures features() const
This functions returns the dock widget features of all dock widget in this container.
void removeAutoHideWidget(DockAutoHideContainer *autoHideWidget)
DockAutoHideContainer * createAndSetupAutoHideContainer(Qx::DockSideBarArea area, DockWidget *w, int tabIndex=-1)
Creates and initializes a dockwidget auto hide container into the given area.
void removeDockPanel(DockPanel *panel)
DockSideBar * autoHideSideBar(Qx::DockSideBarArea area) const
QList< DockAutoHideContainer * > autoHideWidgets() const
bool hasTopLevelDockWidget() const
This function returns true if this dock panel has only one single visible dock widget.
DockSplitter * rootSplitter() const
Access function for the internal root splitter
DockFloatingContainer * floatingWidget() const
If this dock container is in a floating widget, this function returns the floating widget.
void dropWidget(QWidget *widget, Qx::DockWidgetArea dropArea, DockPanel *targetPanel, int tabIndex=-1)
Drop a dock panel or a dock widget given in widget parameter.
DockPanel * topLevelDockPanel() const
int visibleDockPanelCount() const
bool hasOpenDockPanels() const
This function returns true, if the container has open dock panels.
QList< DockPanel * > openedDockPanels() const
Returns the list of dock panels that are not closed If all dock widgets in a dock panel are closed,...
This implements a floating widget that is a dock container that accepts docking of dock widgets like ...
DockContainer * dockContainer() const
void finishDropOperation()
DockWidget * topLevelDockWidget() const
This function returns the first dock widget in the first dock panel.
@ AutoHideShowOnMouseOver
static bool testAutoHideConfigFlag(AutoHideFlag flag)
DockPanel manages multiple instances of DockWidgets.
DockSplitter * parentSplitter() const
DockContainer * dockContainer() const
Qx::DockWidgetAreas allowedAreas() const
void setAutoHideContainer(DockAutoHideContainer *container)
void removeDockWidget(DockWidget *w)
Extends QXmlStreamReader with file version information
The central dock window that maintains the complete docking system.
QByteArray qByteArrayToHex(const QByteArray &src, char separator)
char toHexLower(uint value)
Copyleft (C) 2024 maminjie canpool@163.com SPDX-License-Identifier: MulanPSL-2.0
#define QXDOCK_SET_BGCOLOR(w)
void replaceSplitterWidget(QSplitter *splitter, QWidget *from, QWidget *to)
Replace the from widget in the given splitter with the To widget
void hideEmptyParentSplitters(DockSplitter *splitter)
This function walks the splitter tree upwards to hides all splitters that do not have visible content
bool isSideBarArea(Qx::DockWidgetArea area)
@ DockTabDefaultInsertIndex
#define QX_INIT_PRIVATE(Class)
#define QX_DECLARE_PUBLIC(Class)
#define QX_FINI_PRIVATE()
#define QX_DOCK_END_NAMESPACE
#define QX_DOCK_BEGIN_NAMESPACE