QtCanpool 3.3.0
载入中...
搜索中...
未找到
dockcontainer.cpp
浏览该文件的文档.
1
6#include "dockcontainer.h"
7#include "dockwindow.h"
8#include "docksplitter.h"
9#include "dockpanel.h"
10#include "dockutils.h"
11#include "dockmanager.h"
13#include "dockoverlay.h"
15#include "docksidebar.h"
16#include "docksidetab.h"
17#include "dockstatereader.h"
18
19#include <QGridLayout>
20#include <QTimer>
21#include <QXmlStreamWriter>
22#include <QApplication>
23#include <QPointer>
24#include <QTextStream>
25
26#if QT_VERSION < 0x050900
27
28inline char toHexLower(uint value)
29{
30 return "0123456789abcdef"[value & 0xF];
31}
32
33QByteArray qByteArrayToHex(const QByteArray &src, char separator)
34{
35 if (src.size() == 0)
36 return QByteArray();
37
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) {
43 hexData[o++] = toHexLower(data[i] >> 4);
44 hexData[o++] = toHexLower(data[i] & 0xf);
45
46 if ((separator) && (o < length))
47 hexData[o++] = separator;
48 }
49 return hex;
50}
51#endif
52
54
55static unsigned int s_zOrderCounter = 0;
56
57static void insertWidgetIntoSplitter(QSplitter *s, QWidget *w, bool append)
58{
59 if (append) {
60 s->addWidget(w);
61 } else {
62 s->insertWidget(0, w);
63 }
64}
65
66static int areaIdToIndex(Qx::DockWidgetArea area)
67{
68 switch (area) {
70 return 0;
72 return 1;
74 return 2;
76 return 3;
78 return 4;
79 default:
80 return 4;
81 }
82}
83
84class DockContainerPrivate
85{
86public:
87 QX_DECLARE_PUBLIC(DockContainer)
88public:
89 DockContainerPrivate();
90
91 void init();
92 void createRootSplitter();
93
94 void onVisibleDockAreaCountChanged();
95 void emitDockAreasRemoved();
96 void emitDockAreasAdded();
97
98 DockSplitter *newSplitter(Qt::Orientation orientation, QWidget *parent = nullptr);
99 void updateSplitterHandles(QSplitter *s);
100 void adjustSplitterSizesOnInsertion(QSplitter *s, qreal lastRatio = 1.0);
101
102 bool widgetResizesWithContainer(QWidget *w);
103 void onDockAreaViewToggled(bool visible);
104
105 DockPanel *addDockWidgetToContainer(Qx::DockWidgetArea area, DockWidget *w);
106 DockPanel *addDockWidgetToPanel(Qx::DockWidgetArea area, DockWidget *w, DockPanel *targetPanel, int index = -1);
107
108 void addDockPanel(DockPanel *np, Qx::DockWidgetArea area = Qx::CenterDockWidgetArea);
109 void addDockPanelsToList(const QList<DockPanel *> ps);
110 void appendPanels(const QList<DockPanel *> ps);
111
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,
115 int tabIndex = 0);
116
117 void moveToContainer(QWidget *widget, 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);
120
121 void dropIntoAutoHideSideBar(DockFloatingContainer *floatingWidget, Qx::DockWidgetArea area);
122 void moveToAutoHideSideBar(QWidget *widget, Qx::DockWidgetArea area, int tabIndex = Qx::DockTabDefaultInsertIndex);
123
124 void saveChildNodesState(QXmlStreamWriter &s, QWidget *w) const;
125 void saveAutoHideWidgetsState(QXmlStreamWriter &s) const;
126
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);
131public:
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;
146};
147
148DockContainerPrivate::DockContainerPrivate()
149{
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));
157 });
158}
159
160void DockContainerPrivate::init()
161{
162 Q_Q(DockContainer);
163
164 m_isFloating = q->floatingWidget() != nullptr;
165
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);
172}
173
174void DockContainerPrivate::createRootSplitter()
175{
176 if (m_rootSplitter) {
177 return;
178 }
179 m_rootSplitter = newSplitter(Qt::Horizontal);
180 // Add it to the center - the 0 and 2 indexes are used for the SideTabBar widgets
181 m_layout->addWidget(m_rootSplitter, 1, 1);
182}
183
188void DockContainerPrivate::onVisibleDockAreaCountChanged()
189{
190 Q_Q(DockContainer);
191 auto panel = q->topLevelDockPanel();
192
193 if (panel) {
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;
199 }
200}
201
202void DockContainerPrivate::emitDockAreasRemoved()
203{
204 Q_Q(DockContainer);
205 onVisibleDockAreaCountChanged();
206 Q_EMIT q->dockAreasRemoved();
207}
208
209void DockContainerPrivate::emitDockAreasAdded()
210{
211 Q_Q(DockContainer);
212 onVisibleDockAreaCountChanged();
213 Q_EMIT q->dockAreasAdded();
214}
215
216DockSplitter *DockContainerPrivate::newSplitter(Qt::Orientation orientation, QWidget *parent)
217{
218 DockSplitter *s = new DockSplitter(orientation, parent);
219 s->setOpaqueResize(DockManager::testConfigFlag(DockManager::OpaqueSplitterResize));
220 s->setChildrenCollapsible(false);
221 return s;
222}
223
228void DockContainerPrivate::updateSplitterHandles(QSplitter *s)
229{
230 if (!m_window->centralWidget() || !s) {
231 return;
232 }
233
234 for (int i = 0; i < s->count(); ++i) {
235 s->setStretchFactor(i, widgetResizesWithContainer(s->widget(i)) ? 1 : 0);
236 }
237}
238
243void DockContainerPrivate::adjustSplitterSizesOnInsertion(QSplitter *s, qreal lastRatio)
244{
245 int areaSize = (s->orientation() == Qt::Horizontal) ? s->width() : s->height();
246 auto splitterSizes = s->sizes();
247
248 qreal totRatio = splitterSizes.size() - 1.0 + lastRatio;
249 for (int i = 0; i < splitterSizes.size() - 1; i++) {
250 splitterSizes[i] = areaSize / totRatio;
251 }
252 splitterSizes.back() = areaSize * lastRatio / totRatio;
253 s->setSizes(splitterSizes);
254}
255
261bool DockContainerPrivate::widgetResizesWithContainer(QWidget *w)
262{
263 if (!m_window->centralWidget()) {
264 return true;
265 }
266
267 auto panel = qobject_cast<DockPanel *>(w);
268 if (panel) {
269 return panel->isCentralWidgetArea();
270 }
271
272 auto innerSplitter = qobject_cast<DockSplitter *>(w);
273 if (innerSplitter) {
274 return innerSplitter->isResizingWithContainer();
275 }
276
277 return false;
278}
279
280void DockContainerPrivate::onDockAreaViewToggled(bool visible)
281{
282 Q_Q(DockContainer);
283 DockPanel *panel = qobject_cast<DockPanel *>(q->sender());
284 m_visibleDockPanelCount += visible ? 1 : -1;
285 onVisibleDockAreaCountChanged();
286 Q_EMIT q->dockAreaViewToggled(panel, visible);
287}
288
289DockPanel *DockContainerPrivate::addDockWidgetToContainer(Qx::DockWidgetArea area, DockWidget *w)
290{
291 Q_Q(DockContainer);
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;
297 return panel;
298}
299
300DockPanel *DockContainerPrivate::addDockWidgetToPanel(Qx::DockWidgetArea area, DockWidget *w, DockPanel *targetPanel,
301 int index)
302{
303 Q_Q(DockContainer);
304 if (area == Qx::CenterDockWidgetArea) {
305 targetPanel->insertDockWidget(index, w);
306 targetPanel->updateTitleBarVisibility();
307 return targetPanel;
308 }
309 DockPanel *np = new DockPanel(m_window, q);
310 np->addDockWidget(w);
311
312 auto param = internal::dockAreaInsertParameters(area);
313 auto targetSplitter = targetPanel->parentSplitter();
314 // use target index instead of parameter index
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);
321 }
322 } else {
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);
333 }
334 }
335 addDockPanelsToList({np});
336 return np;
337}
338
339void DockContainerPrivate::addDockPanel(DockPanel *np, Qx::DockWidgetArea area)
340{
341 auto param = internal::dockAreaInsertParameters(area);
342 if (m_panels.count() <= 1) {
343 m_rootSplitter->setOrientation(param.orientation());
344 }
345 QSplitter *s = m_rootSplitter;
346 if (s->orientation() == param.orientation()) {
347 insertWidgetIntoSplitter(s, np, param.append());
348 updateSplitterHandles(s);
349 if (s->isHidden()) {
350 s->show();
351 }
352 } else {
353 auto ns = newSplitter(param.orientation());
354 if (param.append()) {
355 QLayoutItem *li = m_layout->replaceWidget(s, ns);
356 ns->addWidget(s);
357 ns->addWidget(np);
358 updateSplitterHandles(ns);
359 delete li;
360 } else {
361 ns->addWidget(np);
362 QLayoutItem *li = m_layout->replaceWidget(s, ns);
363 ns->addWidget(s);
364 updateSplitterHandles(ns);
365 delete li;
366 }
367 m_rootSplitter = ns;
368 }
369 addDockPanelsToList({np});
370}
371
372void DockContainerPrivate::addDockPanelsToList(const QList<DockPanel *> ps)
373{
374 int beforeCount = m_panels.count();
375 int newCount = ps.count();
376 appendPanels(ps);
377 for (auto p : ps) {
378 p->titleBarButton(Qx::TitleBarButtonClose)->setVisible(true);
379 p->titleBarButton(Qx::TitleBarButtonAutoHide)->setVisible(true);
380 }
381 if (beforeCount == 1) {
382 m_panels[0]->updateTitleBarVisibility();
383 }
384 if (newCount == 1) {
385 m_panels[0]->updateTitleBarVisibility();
386 }
387 emitDockAreasAdded();
388}
389
390void DockContainerPrivate::appendPanels(const QList<DockPanel *> panels)
391{
392 for (auto *panel : panels) {
393 m_panels.append(panel);
394 }
395 Q_Q(DockContainer);
396 for (auto *panel : panels) {
397 QObject::connect(panel, &DockPanel::viewToggled, q,
398 std::bind(&DockContainerPrivate::onDockAreaViewToggled, this, std::placeholders::_1));
399 ;
400 }
401}
402
403void DockContainerPrivate::dropIntoContainer(DockFloatingContainer *floatingWidget, Qx::DockWidgetArea area)
404{
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;
409
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;
418 delete li;
419 }
420
421 // Now we can insert the floating widget content into this container
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);
431 }
432 } else {
433 insertWidgetIntoSplitter(splitter, floatingSplitter, param.append());
434 }
435
436 m_rootSplitter = splitter;
437 addDockPanelsToList(newPanels);
438
439 // If we dropped the floating widget into the main dock container that does
440 // not contain any dock widgets, then splitter is invisible and we need to
441 // show it to display the docked widgets
442 if (!splitter->isVisible()) {
443 splitter->show();
444 }
445}
446
447void DockContainerPrivate::dropIntoCenterOfPanel(DockFloatingContainer *floatingWidget, DockPanel *targetPanel,
448 int tabIndex)
449{
450 DockContainer *floatingContainer = floatingWidget->dockContainer();
451 auto newDockWidgets = floatingContainer->dockWidgets();
452 auto topLevelPanel = floatingContainer->topLevelDockPanel();
453 int newCurrentIndex = -1;
454 tabIndex = qMax(0, tabIndex);
455
456 // If the floating widget contains only one single dock are, then the
457 // current dock widget of the dock area will also be the future current
458 // dock widget in the drop area.
459 if (topLevelPanel) {
460 newCurrentIndex = topLevelPanel->currentIndex();
461 }
462
463 for (int i = 0; i < newDockWidgets.count(); ++i) {
464 DockWidget *dockWidget = newDockWidgets[i];
465 targetPanel->insertDockWidget(tabIndex + i, dockWidget, false);
466 // If the floating widget contains multiple visible dock areas, then we
467 // simply pick the first visible open dock widget and make it
468 // the current one.
469 if (newCurrentIndex < 0 && !dockWidget->isClosed()) {
470 newCurrentIndex = i;
471 }
472 }
473 targetPanel->setCurrentIndex(newCurrentIndex + tabIndex);
474 targetPanel->updateTitleBarVisibility();
475 return;
476}
477
478void DockContainerPrivate::dropIntoPanel(DockFloatingContainer *floatingWidget, DockPanel *targetPanel,
479 Qx::DockWidgetArea area, int tabIndex)
480{
481 // Dropping into center means all dock widgets in the dropped floating
482 // widget will become tabs of the drop area
483 if (Qx::CenterDockWidgetArea == area) {
484 dropIntoCenterOfPanel(floatingWidget, targetPanel, tabIndex);
485 return;
486 }
487
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);
501 } else {
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);
507 }
508 }
509
510 if (adjustSplitterSizes) {
511 int size = (targetAreaSize - targetSplitter->handleWidth()) / 2;
512 sizes[areaIndex] = size;
513 sizes.insert(areaIndex, size);
514 targetSplitter->setSizes(sizes);
515 }
516 } else {
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);
523 } else {
524 adjustSplitterSizes = (floatingSplitter->count() == 1);
525 while (floatingSplitter->count()) {
526 newSplitter->addWidget(floatingSplitter->widget(0));
527 updateSplitterHandles(newSplitter);
528 }
529 }
530
531 // Save the sizes before insertion and restore it later to prevent
532 // shrinking of existing area
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});
539 }
540 targetSplitter->insertWidget(areaIndex, newSplitter);
541 targetSplitter->setSizes(sizes);
542 updateSplitterHandles(targetSplitter);
543 }
544
545 addDockPanelsToList(newDockpanels);
546}
547
548void DockContainerPrivate::moveToContainer(QWidget *widget, Qx::DockWidgetArea area)
549{
550 Q_Q(DockContainer);
551 DockWidget *droppedDockWidget = qobject_cast<DockWidget *>(widget);
552 DockPanel *droppedPanel = qobject_cast<DockPanel *>(widget);
553 DockPanel *newPanel;
554
555 if (droppedDockWidget) {
556 newPanel = new DockPanel(m_window, q);
557 DockPanel *oldPanel = droppedDockWidget->dockPanel();
558 if (oldPanel) {
559 oldPanel->removeDockWidget(droppedDockWidget);
560 }
561 newPanel->addDockWidget(droppedDockWidget);
562 } else {
563 // We check, if we insert the dropped widget into the same place that
564 // it already has and do nothing, if it is the same place. It would
565 // also work without this check, but it looks nicer with the check
566 // because there will be no layout updates
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) {
571 return;
572 } else if (!insertParam.append() && splitter->firstWidget() == droppedPanel) {
573 return;
574 }
575 }
576 droppedPanel->dockContainer()->removeDockPanel(droppedPanel);
577 newPanel = droppedPanel;
578 }
579
580 addDockPanel(newPanel, area);
581 m_lastAddedPanelCache[areaIdToIndex(area)] = newPanel;
582}
583
584void DockContainerPrivate::moveIntoCenterOfSection(QWidget *widget, DockPanel *targetPanel, int tabIndex)
585{
586 auto droppedDockWidget = qobject_cast<DockWidget *>(widget);
587 auto droppedPanel = qobject_cast<DockPanel *>(widget);
588
589 tabIndex = qMax(0, tabIndex);
590 if (droppedDockWidget) {
591 DockPanel *oldPanel = droppedDockWidget->dockPanel();
592 if (oldPanel == targetPanel) {
593 return;
594 }
595
596 if (oldPanel) {
597 oldPanel->removeDockWidget(droppedDockWidget);
598 }
599 targetPanel->insertDockWidget(tabIndex, droppedDockWidget, true);
600 } else {
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);
606 }
607 targetPanel->setCurrentIndex(tabIndex + newCurrentIndex);
608 droppedPanel->dockContainer()->removeDockPanel(droppedPanel);
609 droppedPanel->deleteLater();
610 }
611
612 targetPanel->updateTitleBarVisibility();
613 return;
614}
615
616void DockContainerPrivate::moveToNewSection(QWidget *widget, DockPanel *targetPanel, Qx::DockWidgetArea area,
617 int tabIndex)
618{
619 Q_Q(DockContainer);
620 // Dropping into center means all dock widgets in the dropped floating
621 // widget will become tabs of the drop area
622 if (Qx::CenterDockWidgetArea == area) {
623 moveIntoCenterOfSection(widget, targetPanel, tabIndex);
624 return;
625 }
626
627 DockWidget *droppedDockWidget = qobject_cast<DockWidget *>(widget);
628 DockPanel *droppedPanel = qobject_cast<DockPanel *>(widget);
629 DockPanel *newPanel;
630 if (droppedDockWidget) {
631 newPanel = new DockPanel(m_window, q);
632 DockPanel *oldPanel = droppedDockWidget->dockPanel();
633 if (oldPanel) {
634 oldPanel->removeDockWidget(droppedDockWidget);
635 }
636 newPanel->addDockWidget(droppedDockWidget);
637 } else {
638 droppedPanel->dockContainer()->removeDockPanel(droppedPanel);
639 newPanel = droppedPanel;
640 }
641
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);
654 } else {
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);
665 }
666 targetSplitter->setSizes(sizes);
667
668 addDockPanelsToList({newPanel});
669}
670
671void DockContainerPrivate::dropIntoAutoHideSideBar(DockFloatingContainer *floatingWidget, Qx::DockWidgetArea area)
672{
673 Q_Q(DockContainer);
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++);
681 }
682 }
683}
684
685void DockContainerPrivate::moveToAutoHideSideBar(QWidget *widget, Qx::DockWidgetArea area, int tabIndex)
686{
687 Q_Q(DockContainer);
688 DockWidget *droppedWidget = qobject_cast<DockWidget *>(widget);
689 DockPanel *droppedPanel = qobject_cast<DockPanel *>(widget);
690 auto sideBarArea = internal::toSideBarArea(area);
691
692 if (droppedWidget) {
693 if (q == droppedWidget->dockContainer()) {
694 droppedWidget->setAutoHide(true, sideBarArea, tabIndex);
695 } else {
696 q->createAndSetupAutoHideContainer(sideBarArea, droppedWidget, tabIndex);
697 }
698 } else {
699 if (q == droppedPanel->dockContainer()) {
700 droppedPanel->setAutoHide(true, sideBarArea, tabIndex);
701 } else {
702 for (const auto dockWidget : droppedPanel->openedDockWidgets()) {
703 if (!dockWidget->features().testFlag(DockWidget::DockWidgetPinnable)) {
704 continue;
705 }
706 q->createAndSetupAutoHideContainer(sideBarArea, dockWidget, tabIndex++);
707 }
708 }
709 }
710}
711
712void DockContainerPrivate::saveChildNodesState(QXmlStreamWriter &s, QWidget *w) const
713{
714 QSplitter *splitter = qobject_cast<QSplitter *>(w);
715 if (splitter) {
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));
721 }
722
723 s.writeStartElement("Sizes");
724 for (auto Size : splitter->sizes()) {
725 s.writeCharacters(QString::number(Size) + " ");
726 }
727 s.writeEndElement(); // Sizes
728
729 s.writeEndElement(); // Splitter
730 } else {
731 DockPanel *panel = qobject_cast<DockPanel *>(w);
732 if (panel) {
733 panel->saveState(s);
734 }
735 }
736}
737
738void DockContainerPrivate::saveAutoHideWidgetsState(QXmlStreamWriter &s) const
739{
740 for (const auto sideTabBar : m_sideTabBarWidgets.values()) {
741 if (!sideTabBar->count()) {
742 continue;
743 }
744
745 sideTabBar->saveState(s);
746 }
747}
748
749bool DockContainerPrivate::restoreChildNodes(DockStateReader &s, QWidget *&createdWidget, bool testing)
750{
751 bool result = true;
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);
759 } else {
760 s.skipCurrentElement();
761 }
762 }
763
764 return result;
765}
766
767bool DockContainerPrivate::restoreSplitter(DockStateReader &s, QWidget *&createdWidget, bool testing)
768{
769 bool ok;
770 QString orientationStr = s.attributes().value("Orientation").toString();
771
772 // Check if the orientation string is right
773 if (!orientationStr.startsWith("|") && !orientationStr.startsWith("-")) {
774 return false;
775 }
776
777 // The "|" shall indicate a vertical splitter handle which in turn means
778 // a Horizontal orientation of the splitter layout.
779 bool horizontalSplitter = orientationStr.startsWith("|");
780 // In version 0 we had a small bug. The "|" indicated a vertical orientation,
781 // but this is wrong, because only the splitter handle is vertical, the
782 // layout of the splitter is a horizontal layout. We fix this here
783 if (s.fileVersion() == 0) {
784 horizontalSplitter = !horizontalSplitter;
785 }
786
787 int orientation = horizontalSplitter ? Qt::Horizontal : Qt::Vertical;
788 int widgetCount = s.attributes().value("Count").toInt(&ok);
789 if (!ok) {
790 return false;
791 }
792
793 QSplitter *splitter = nullptr;
794 if (!testing) {
795 splitter = newSplitter(static_cast<Qt::Orientation>(orientation));
796 }
797 bool visible = false;
798 QList<int> sizes;
799 while (s.readNextStartElement()) {
800 QWidget *childNode = nullptr;
801 bool result = true;
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();
808
809 QTextStream TextStream(&sSizes);
810 while (!TextStream.atEnd()) {
811 int value;
812 TextStream >> value;
813 sizes.append(value);
814 }
815 } else {
816 s.skipCurrentElement();
817 }
818
819 if (!result) {
820 return false;
821 }
822
823 if (testing || !childNode) {
824 continue;
825 }
826
827 splitter->addWidget(childNode);
828 visible |= childNode->isVisibleTo(splitter);
829 }
830 if (!testing) {
831 updateSplitterHandles(splitter);
832 }
833
834 if (sizes.count() != widgetCount) {
835 return false;
836 }
837
838 if (!testing) {
839 if (!splitter->count()) {
840 delete splitter;
841 splitter = nullptr;
842 } else {
843 splitter->setSizes(sizes);
844 splitter->setVisible(visible);
845 }
846 createdWidget = splitter;
847 } else {
848 createdWidget = nullptr;
849 }
850
851 return true;
852}
853
854bool DockContainerPrivate::restoreDockArea(DockStateReader &s, QWidget *&createdWidget, bool testing)
855{
856 Q_Q(DockContainer);
857 DockPanel *panel = nullptr;
858 auto result = DockPanel::restoreState(s, panel, testing, q);
859 if (result && panel) {
860 appendPanels({panel});
861 }
862 createdWidget = panel;
863 return result;
864}
865
866bool DockContainerPrivate::restoreSideBar(DockStateReader &s, QWidget *&createdWidget, bool testing)
867{
868 Q_UNUSED(createdWidget)
869 Q_Q(DockContainer);
870 // Simply ignore side bar auto hide widgets from saved state if
871 // auto hide support is disabled
872 if (!DockManager::testAutoHideConfigFlag(DockManager::AutoHideFeatureEnabled)) {
873 return true;
874 }
875
876 bool ok;
877 auto area = (Qx::DockSideBarArea)s.attributes().value("Area").toInt(&ok);
878 if (!ok) {
879 return false;
880 }
881
882 while (s.readNextStartElement()) {
883 if (s.name() != QLatin1String("Widget")) {
884 continue;
885 }
886
887 auto name = s.attributes().value("Name");
888 if (name.isEmpty()) {
889 return false;
890 }
891
892 bool ok;
893 bool closed = s.attributes().value("Closed").toInt(&ok);
894 if (!ok) {
895 return false;
896 }
897
898 int Size = s.attributes().value("Size").toInt(&ok);
899 if (!ok) {
900 return false;
901 }
902
903 s.skipCurrentElement();
904 DockWidget *dockWidget = m_window->findDockWidget(name.toString());
905 if (!dockWidget || testing) {
906 continue;
907 }
908
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);
915 }
916 } else {
917 autoHideContainer = sideBar->insertDockWidget(-1, dockWidget);
918 }
919 autoHideContainer->setSize(Size);
920 dockWidget->setProperty(internal::ClosedProperty, closed);
921 dockWidget->setProperty(internal::DirtyProperty, false);
922 }
923
924 return true;
925}
926
927/* DockContainer */
928DockContainer::DockContainer(DockWindow *window, QWidget *parent)
929 : Super(parent)
930{
931 Q_ASSERT(window);
933
934 Q_D(DockContainer);
935 d->m_window = window;
936 d->init();
937
938 if (window != this) {
939 d->m_window->registerDockContainer(this);
942 }
943 QXDOCK_SET_BGCOLOR(this);
944}
945
947{
948 Q_D(DockContainer);
949
950 // A DockWindow passes itself as the window (see DockWindow::DockWindow), and
951 // this destructor then runs as part of its base destruction - that is, after
952 // ~DockWindow() released the private data, and with the dynamic type already
953 // degraded to DockContainer. Calling into the derived class in that state is
954 // undefined behaviour, and it would be pointless anyway, because
955 // DockWindow::removeDockContainer() ignores the self case. Only the real
956 // child containers unregister here.
957 if (d->m_window && d->m_window != this) {
958 d->m_window->removeDockContainer(this);
959 }
960
962}
963
971{
972 Q_D(DockContainer);
973 auto topLevelWidget = topLevelDockWidget();
974 DockPanel *oldPanel = w->dockPanel();
975 if (oldPanel) {
976 oldPanel->removeDockWidget(w);
977 }
978 w->setDockWindow(d->m_window);
979
980 DockPanel *panel = nullptr;
981 if (p) {
982 panel = d->addDockWidgetToPanel(area, w, p, index);
983 } else {
984 panel = d->addDockWidgetToContainer(area, w);
985 }
986 if (topLevelWidget) {
987 auto newTopLevelWidget = topLevelDockWidget();
988 // If the container contained only one visible dock widget, then we need
989 // to emit a top level event for this widget because it is not the one and
990 // only visible docked widget anymore
991 if (!newTopLevelWidget) {
992 DockWidget::emitTopLevelEventForWidget(topLevelWidget, false);
993 }
994 }
995 return panel;
996}
997
999{
1000 DockPanel *panel = w->dockPanel();
1001 if (panel) {
1002 panel->removeDockWidget(w);
1003 }
1004}
1005
1006unsigned int DockContainer::zOrderIndex() const
1007{
1008 Q_D(const DockContainer);
1009 return d->m_zOrderIndex;
1010}
1011
1017{
1018 return this->zOrderIndex() > other->zOrderIndex();
1019}
1020
1022{
1023 Q_D(const DockContainer);
1024 return d->m_window;
1025}
1026
1027DockPanel *DockContainer::dockPanelAt(const QPoint &globalPos) const
1028{
1029 Q_D(const DockContainer);
1030
1031 for (const auto &panel : d->m_panels) {
1032 if (panel && panel->isVisible() && panel->rect().contains(panel->mapFromGlobal(globalPos))) {
1033 return panel;
1034 }
1035 }
1036
1037 return nullptr;
1038}
1039
1041{
1042 Q_D(const DockContainer);
1043 return (index < dockPanelCount()) ? d->m_panels[index] : nullptr;
1044}
1045
1050QList<DockPanel *> DockContainer::openedDockPanels() const
1051{
1052 Q_D(const DockContainer);
1053 QList<DockPanel *> result;
1054 for (const auto &p : d->m_panels) {
1055 if (p && !p->isHidden()) {
1056 result.append(p);
1057 }
1058 }
1059 return result;
1060}
1061
1065QList<DockWidget *> DockContainer::openedDockWidgets() const
1066{
1067 Q_D(const DockContainer);
1068 QList<DockWidget *> result;
1069 for (const auto &p : d->m_panels) {
1070 if (p && !p->isHidden()) {
1071 result.append(p->openedDockWidgets());
1072 }
1073 }
1074 return result;
1075}
1076
1083{
1084 Q_D(const DockContainer);
1085 for (auto panel : d->m_panels) {
1086 if (panel && !panel->isHidden()) {
1087 return true;
1088 }
1089 }
1090
1091 return false;
1092}
1093
1101{
1102 auto panels = openedDockPanels();
1103 if (panels.count() != 1) {
1104 return false;
1105 }
1106
1107 return panels[0]->openDockWidgetsCount() == 1;
1108}
1109
1111{
1112 Q_D(const DockContainer);
1113 return d->m_panels.count();
1114}
1115
1117{
1118 Q_D(const DockContainer);
1119 int result = 0;
1120 for (const auto &panel : d->m_panels) {
1121 result += (!panel || panel->isHidden()) ? 0 : 1;
1122 }
1123
1124 return result;
1125}
1126
1134DockWidget::DockWidgetFeatures DockContainer::features() const
1135{
1136 Q_D(const DockContainer);
1137 DockWidget::DockWidgetFeatures f(DockWidget::AllDockWidgetFeatures);
1138 for (const auto &panel : d->m_panels) {
1139 if (!panel) {
1140 continue;
1141 }
1142 f &= panel->features();
1143 }
1144
1145 return f;
1146}
1147
1149{
1150 Q_D(const DockContainer);
1151 return d->m_isFloating;
1152}
1153
1160{
1161 return internal::findParent<DockFloatingContainer *>(this);
1162}
1163
1165{
1166 Q_D(DockContainer);
1167 for (const auto &panel : d->m_panels) {
1168 if (!panel || panel == keepOpenPanel) {
1169 continue;
1170 }
1171
1172 if (!panel->features(Qx::DockBitwiseAnd).testFlag(DockWidget::DockWidgetClosable)) {
1173 continue;
1174 }
1175
1176 // We do not close areas with widgets with custom close handling
1177 if (panel->features(Qx::DockBitwiseOr).testFlag(DockWidget::CustomCloseHandling)) {
1178 continue;
1179 }
1180
1181 panel->closeArea();
1182 }
1183}
1184
1186{
1187 Q_D(const DockContainer);
1188 return d->m_sideTabBarWidgets[area];
1189}
1190
1191QList<DockAutoHideContainer *> DockContainer::autoHideWidgets() const
1192{
1193 Q_D(const DockContainer);
1194 return d->m_autoHideWidgets;
1195}
1196
1204{
1205 Q_D(const DockContainer);
1206 if (!d->m_rootSplitter) {
1207 return QRect();
1208 }
1209
1210 if (d->m_rootSplitter->hasVisibleContent()) {
1211 return d->m_rootSplitter->geometry();
1212 } else {
1213 auto contentRect = this->rect();
1214 contentRect.adjust(autoHideSideBar(Qx::DockSideBarLeft)->sizeHint().width(),
1215 autoHideSideBar(Qx::DockSideBarTop)->sizeHint().height(),
1216 -autoHideSideBar(Qx::DockSideBarRight)->sizeHint().width(),
1217 -autoHideSideBar(Qx::DockSideBarBottom)->sizeHint().height());
1218
1219 return contentRect;
1220 }
1221}
1222
1227{
1228 Q_D(const DockContainer);
1229 return d->m_rootSplitter;
1230}
1231
1233{
1234 Q_D(DockContainer);
1235 d->createRootSplitter();
1236}
1237
1239{
1240 Q_D(DockContainer);
1241 DockContainer *container = panel->dockContainer();
1242 if (container && container != this) {
1243 container->removeDockPanel(panel);
1244 }
1245 d->addDockPanel(panel, area);
1246}
1247
1249{
1250 Q_D(DockContainer);
1251 // If it is an auto hide area, then there is nothing much to do
1252 if (panel->isAutoHide()) {
1253 panel->setAutoHideContainer(nullptr);
1254 return;
1255 }
1256
1257 panel->disconnect(this);
1258 d->m_panels.removeAll(panel);
1259 auto splitter = panel->parentSplitter();
1260
1261 // Remove are from parent splitter and recursively hide tree of parent
1262 // splitters if it has no visible content
1263 panel->setParent(nullptr);
1265
1266 // Remove this panel from cached panels
1267 auto p = std::find(std::begin(d->m_lastAddedPanelCache), std::end(d->m_lastAddedPanelCache), panel);
1268 if (p != std::end(d->m_lastAddedPanelCache)) {
1269 *p = nullptr;
1270 }
1271
1272 // If splitter has more than 1 widgets, we are finished and can leave
1273 if (splitter->count() > 1) {
1274 goto emitAndExit;
1275 }
1276
1277 // If this is the RootSplitter we need to remove empty splitters to
1278 // avoid too many empty splitters
1279 if (splitter == d->m_rootSplitter) {
1280 // If splitter is empty, we are finished
1281 if (!splitter->count()) {
1282 splitter->hide();
1283 goto emitAndExit;
1284 }
1285
1286 QWidget *widget = splitter->widget(0);
1287 auto childSplitter = qobject_cast<DockSplitter *>(widget);
1288 // If the one and only content widget of the splitter is not a splitter
1289 // then we are finished
1290 if (!childSplitter) {
1291 goto emitAndExit;
1292 }
1293
1294 // We replace the superfluous RootSplitter with the childSplitter
1295 childSplitter->setParent(nullptr);
1296 QLayoutItem *li = d->m_layout->replaceWidget(splitter, childSplitter);
1297 d->m_rootSplitter = childSplitter;
1298 delete li;
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);
1304 internal::replaceSplitterWidget(parentSplitter, splitter, widget);
1305 parentSplitter->setSizes(Sizes);
1306 }
1307
1308 delete splitter;
1309 splitter = nullptr;
1310
1311emitAndExit:
1312 updateSplitterHandles(splitter);
1313 DockWidget *topLevelWidget = topLevelDockWidget();
1314
1315 // Updated the title bar visibility of the dock widget if there is only
1316 // one single visible dock widget
1317 DockWidget::emitTopLevelEventForWidget(topLevelWidget, true);
1318 d->emitDockAreasRemoved();
1319}
1320
1324QList<QPointer<DockPanel>> DockContainer::removeAllDockPanels()
1325{
1326 Q_D(DockContainer);
1327 auto result = d->m_panels;
1328 d->m_panels.clear();
1329 return result;
1330}
1331
1337{
1338 auto panel = topLevelDockPanel();
1339 if (!panel) {
1340 return nullptr;
1341 }
1342 auto widgets = panel->openedDockWidgets();
1343 if (widgets.count() != 1) {
1344 return nullptr;
1345 }
1346 return widgets[0];
1347}
1348
1350{
1351 auto panels = openedDockPanels();
1352 if (panels.count() != 1) {
1353 return nullptr;
1354 }
1355 return panels[0];
1356}
1357
1363{
1364 Q_D(const DockContainer);
1365 return d->m_lastAddedPanelCache[areaIdToIndex(area)];
1366}
1367
1376QList<DockWidget *> DockContainer::dockWidgets() const
1377{
1378 Q_D(const DockContainer);
1379 QList<DockWidget *> result;
1380 for (const auto &panel : d->m_panels) {
1381 if (!panel) {
1382 continue;
1383 }
1384 result.append(panel->dockWidgets());
1385 }
1386
1387 return result;
1388}
1389
1395{
1396 Q_D(DockContainer);
1397 d->updateSplitterHandles(splitter);
1398}
1399
1403void DockContainer::dropFloatingWidget(DockFloatingContainer *floatingWidget, const QPoint &targetPos)
1404{
1405 Q_D(DockContainer);
1406 DockWidget *singleDroppedDockWidget = floatingWidget->topLevelDockWidget();
1407 DockWidget *singleDockWidget = topLevelDockWidget();
1408 auto dropArea = Qx::InvalidDockWidgetArea;
1409 auto containerDropArea = d->m_window->containerOverlay()->dropAreaUnderCursor();
1410 bool dropped = false;
1411
1412 DockPanel *panel = dockPanelAt(targetPos);
1413 // mouse is over dock area
1414 if (panel) {
1415 auto dropOverlay = d->m_window->panelOverlay();
1416 dropOverlay->setAllowedAreas(panel->allowedAreas());
1417 dropArea = dropOverlay->showOverlay(panel);
1418 if (containerDropArea != Qx::InvalidDockWidgetArea && containerDropArea != dropArea) {
1419 dropArea = Qx::InvalidDockWidgetArea;
1420 }
1421
1422 if (dropArea != Qx::InvalidDockWidgetArea) {
1423 int tabIndex = d->m_window->panelOverlay()->tabIndexUnderCursor();
1424 d->dropIntoPanel(floatingWidget, panel, dropArea, tabIndex);
1425 dropped = true;
1426 }
1427 }
1428
1429 // mouse is over container or auto hide side bar
1430 if (Qx::InvalidDockWidgetArea == dropArea && Qx::InvalidDockWidgetArea != containerDropArea) {
1431 if (internal::isSideBarArea(containerDropArea)) {
1432 d->dropIntoAutoHideSideBar(floatingWidget, containerDropArea);
1433 } else {
1434 d->dropIntoContainer(floatingWidget, containerDropArea);
1435 }
1436 dropped = true;
1437 }
1438 // Remove the auto hide widgets from the floatingWidget and insert
1439 // them into this widget
1440 for (auto autoHideWidget : floatingWidget->dockContainer()->autoHideWidgets()) {
1441 auto sideBar = autoHideSideBar(autoHideWidget->sideBarArea());
1442 sideBar->addAutoHideWidget(autoHideWidget);
1443 }
1444
1445 if (dropped) {
1447
1448 // If we dropped a floating widget with only one single dock widget, then we
1449 // drop a top level widget that changes from floating to docked now
1450 DockWidget::emitTopLevelEventForWidget(singleDroppedDockWidget, false);
1451
1452 // If there was a top level widget before the drop, then it is not top
1453 // level widget anymore
1454 DockWidget::emitTopLevelEventForWidget(singleDockWidget, false);
1455 }
1456
1457 window()->activateWindow();
1458 if (singleDroppedDockWidget) {
1459 d->m_window->notifyDockAreaRelocation(singleDroppedDockWidget);
1460 }
1461 d->m_window->notifyFloatingWidgetDrop(floatingWidget);
1462}
1463
1471void DockContainer::dropWidget(QWidget *widget, Qx::DockWidgetArea dropArea, DockPanel *targetPanel, int tabIndex)
1472{
1473 Q_D(DockContainer);
1474 DockWidget *singleDockWidget = topLevelDockWidget();
1475 if (targetPanel) {
1476 d->moveToNewSection(widget, targetPanel, dropArea, tabIndex);
1477 } else if (internal::isSideBarArea(dropArea)) {
1478 d->moveToAutoHideSideBar(widget, dropArea, tabIndex);
1479 } else {
1480 d->moveToContainer(widget, dropArea);
1481 }
1482
1483 // If there was a top level widget before the drop, then it is not top
1484 // level widget anymore
1485 DockWidget::emitTopLevelEventForWidget(singleDockWidget, false);
1486
1487 window()->activateWindow();
1488 d->m_window->notifyDockAreaRelocation(widget);
1489}
1490
1492{
1493 Q_D(DockContainer);
1495 return;
1496 }
1497
1498 {
1499 auto area = Qx::DockSideBarLeft;
1500 d->m_sideTabBarWidgets[area] = new DockSideBar(this, area);
1501 d->m_layout->addWidget(d->m_sideTabBarWidgets[area], 1, 0);
1502 }
1503
1504 {
1505 auto area = Qx::DockSideBarRight;
1506 d->m_sideTabBarWidgets[area] = new DockSideBar(this, area);
1507 d->m_layout->addWidget(d->m_sideTabBarWidgets[area], 1, 2);
1508 }
1509
1510 {
1511 auto area = Qx::DockSideBarBottom;
1512 d->m_sideTabBarWidgets[area] = new DockSideBar(this, area);
1513 d->m_layout->addWidget(d->m_sideTabBarWidgets[area], 2, 1);
1514 }
1515
1516 {
1517 auto area = Qx::DockSideBarTop;
1518 d->m_sideTabBarWidgets[area] = new DockSideBar(this, area);
1519 d->m_layout->addWidget(d->m_sideTabBarWidgets[area], 0, 1);
1520 }
1521}
1522
1529 int tabIndex)
1530{
1531 Q_D(DockContainer);
1533 Q_ASSERT_X(false, "DockContainer::createAndSetupAutoHideContainer", "Requested area does not exist in config");
1534 return nullptr;
1535 }
1536 if (d->m_window != w->dockWindow()) {
1537 w->setDockWindow(d->m_window); // Auto hide Dock Container needs a valid dock window
1538 }
1539
1540 return autoHideSideBar(area)->insertDockWidget(tabIndex, w);
1541}
1542
1544{
1545 Q_D(DockContainer);
1546 d->m_autoHideWidgets.append(autoHideWidget);
1547 Q_EMIT autoHideWidgetCreated(autoHideWidget);
1548}
1549
1551{
1552 Q_D(DockContainer);
1553 d->m_autoHideWidgets.removeAll(autoHideWidget);
1554}
1555
1557{
1558 Q_D(DockContainer);
1560 return;
1561 }
1562
1563 if (dockWindow()->isRestoringState()) {
1564 return;
1565 }
1566
1567 auto autoHideTab = qobject_cast<DockSideTab *>(w);
1568 if (autoHideTab) {
1569 switch (e->type()) {
1570 case QEvent::Enter:
1571 if (!autoHideTab->dockWidget()->isVisible()) {
1572 d->m_delayedAutoHideTab = autoHideTab;
1573 d->m_delayedAutoHideShow = true;
1574 d->m_delayedAutoHideTimer.start();
1575 } else {
1576 d->m_delayedAutoHideTimer.stop();
1577 }
1578 break;
1579
1580 case QEvent::MouseButtonPress:
1581 d->m_delayedAutoHideTimer.stop();
1582 break;
1583
1584 case QEvent::Leave:
1585 if (autoHideTab->dockWidget()->isVisible()) {
1586 d->m_delayedAutoHideTab = autoHideTab;
1587 d->m_delayedAutoHideShow = false;
1588 d->m_delayedAutoHideTimer.start();
1589 } else {
1590 d->m_delayedAutoHideTimer.stop();
1591 }
1592 break;
1593
1594 default:
1595 break;
1596 }
1597 return;
1598 }
1599
1600 auto autoHideContainer = qobject_cast<DockAutoHideContainer *>(w);
1601 if (autoHideContainer) {
1602 switch (e->type()) {
1603 case QEvent::Enter:
1604 case QEvent::Hide:
1605 d->m_delayedAutoHideTimer.stop();
1606 break;
1607
1608 case QEvent::Leave:
1609 if (autoHideContainer->isVisible()) {
1610 d->m_delayedAutoHideTab = autoHideContainer->autoHideTab();
1611 d->m_delayedAutoHideShow = false;
1612 d->m_delayedAutoHideTimer.start();
1613 }
1614 break;
1615
1616 default:
1617 break;
1618 }
1619 return;
1620 }
1621}
1622
1623void DockContainer::saveState(QXmlStreamWriter &s) const
1624{
1625 Q_D(const DockContainer);
1626 s.writeStartElement("Container");
1627 s.writeAttribute("Floating", QString::number(isFloating() ? 1 : 0));
1628 if (isFloating()) {
1630 QByteArray geometry = floatingWidget->saveGeometry();
1631#if QT_VERSION < 0x050900
1632 s.writeTextElement("Geometry", qByteArrayToHex(geometry, ' '));
1633#else
1634 s.writeTextElement("Geometry", geometry.toHex(' '));
1635#endif
1636 }
1637 d->saveChildNodesState(s, d->m_rootSplitter);
1638 d->saveAutoHideWidgetsState(s);
1639 s.writeEndElement();
1640}
1641
1643{
1644 Q_D(DockContainer);
1645 bool isFloating = s.attributes().value("Floating").toInt();
1646
1647 QWidget *newRootSplitter{};
1648 if (!testing) {
1649 d->m_visibleDockPanelCount = -1; // invalidate the dock area count
1650 d->m_panels.clear();
1651 std::fill(std::begin(d->m_lastAddedPanelCache), std::end(d->m_lastAddedPanelCache), nullptr);
1652 }
1653
1654 if (isFloating) {
1655 if (!s.readNextStartElement() || s.name() != QLatin1String("Geometry")) {
1656 return false;
1657 }
1658
1659 QByteArray geometryString = s.readElementText(DockStateReader::ErrorOnUnexpectedElement).toLocal8Bit();
1660 QByteArray geometry = QByteArray::fromHex(geometryString);
1661 if (geometry.isEmpty()) {
1662 return false;
1663 }
1664
1665 if (!testing) {
1667 if (floatingWidget) {
1668 floatingWidget->restoreGeometry(geometry);
1669 }
1670 }
1671 }
1672
1673 if (!d->restoreChildNodes(s, newRootSplitter, testing)) {
1674 return false;
1675 }
1676
1677 if (testing) {
1678 return true;
1679 }
1680
1681 // If the root splitter is empty, rostoreChildNodes returns a 0 pointer
1682 // and we need to create a new empty root splitter
1683 if (!newRootSplitter) {
1684 newRootSplitter = d->newSplitter(Qt::Horizontal);
1685 }
1686
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();
1691 delete li;
1692
1693 return true;
1694}
1695
1700{
1701 Q_D(DockContainer);
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;
1707 }
1708
1709 return result;
1710}
1711
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 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)
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 ...
DockWidget * topLevelDockWidget() const
This function returns the first dock widget in the first dock panel.
static bool testAutoHideConfigFlag(AutoHideFlag flag)
DockPanel manages multiple instances of DockWidgets.
Definition dockpanel.h:31
DockSplitter * parentSplitter() const
DockContainer * dockContainer() const
Qx::DockWidgetAreas allowedAreas() const
bool isAutoHide() const
void setAutoHideContainer(DockAutoHideContainer *container)
void removeDockWidget(DockWidget *w)
Side tab bar widget that is shown at the edges of a dock container.
Definition docksidebar.h:32
DockAutoHideContainer * insertDockWidget(int index, DockWidget *w)
Insert dock widget into the side bar.
Extends QXmlStreamReader with file version information
The DockWidget class provides a widget that can be docked inside a DockWindow or floated as a top-lev...
Definition dockwidget.h:31
void setDockWindow(DockWindow *window)
Assigns the dock window that manages this dock widget
static void emitTopLevelEventForWidget(DockWidget *topLevelDockWidget, bool floating)
Call this function to emit a topLevelChanged() signal and to update the dock panel tool bar visibilit...
DockPanel * dockPanel() const
Returns the dock panel this dock widget belongs to or 0 if this dock widget has not been docked yet
DockWindow * dockWindow() const
Returns the dock window that manages the dock widget or 0 if the widget has not been assigned to any ...
The central dock window that maintains the complete docking system.
Definition dockwindow.h:36
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)
Definition dockutils.h:172
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)
DockSideBarArea
@ DockSideBarTop
@ DockSideBarBottom
@ DockSideBarLeft
@ DockSideBarRight
DockWidgetArea
@ BottomDockWidgetArea
@ RightDockWidgetArea
@ LeftDockWidgetArea
@ TopDockWidgetArea
@ InvalidDockWidgetArea
@ CenterDockWidgetArea
@ DockTabDefaultInsertIndex
@ DockBitwiseAnd
@ DockBitwiseOr
@ TitleBarButtonClose
@ TitleBarButtonAutoHide
#define QX_INIT_PRIVATE(Class)
#define QX_DECLARE_PUBLIC(Class)
#define QX_FINI_PRIVATE()
#define QX_DOCK_END_NAMESPACE
#define QX_DOCK_BEGIN_NAMESPACE
const char * name
Definition qxtheme.cpp:25
QColor window