QtCanpool 3.3.0
载入中...
搜索中...
未找到
dockpanel.cpp
浏览该文件的文档.
1
6#include "dockpanel.h"
7#include "dockcontainer.h"
8#include "dockwindow.h"
9#include "docktitlebar.h"
10#include "dockutils.h"
11#include "docksplitter.h"
12#include "docktabbar.h"
13#include "docktab.h"
14#include "dockmanager.h"
16#include "docktitlebar_p.h"
17#include "docklabel.h"
19#include "dockstatereader.h"
20
21#include <QBoxLayout>
22#include <QXmlStreamWriter>
23#include <QPointer>
24#include <QDebug>
25
27
28static const char *const INDEX_PROPERTY = "index";
29
30static bool isAutoHideFeatureEnabled()
31{
32 return DockManager::testAutoHideConfigFlag(DockManager::AutoHideFeatureEnabled);
33}
34
42class DockAreaLayout
43{
44private:
45 QBoxLayout *m_parentLayout;
46 QList<QPointer<QWidget>> m_widgets;
47 int m_currentIndex = -1;
48 QWidget *m_currentWidget = nullptr;
49public:
50 DockAreaLayout(QBoxLayout *parentLayout)
51 : m_parentLayout(parentLayout)
52 {
53 }
54
55 int count() const
56 {
57 return m_widgets.count();
58 }
59
60 void insertWidget(int index, QWidget *w)
61 {
62 w->setParent(nullptr);
63 if (index < 0) {
64 index = m_widgets.count();
65 }
66 m_widgets.insert(index, w);
67 if (m_currentIndex < 0) {
68 setCurrentIndex(index);
69 } else {
70 if (index <= m_currentIndex) {
71 ++m_currentIndex;
72 }
73 }
74 }
75
76 void removeWidget(QWidget *w)
77 {
78 if (currentWidget() == w) {
79 auto layoutItem = m_parentLayout->takeAt(1);
80 if (layoutItem) {
81 layoutItem->widget()->setParent(nullptr);
82 }
83 delete layoutItem;
84 m_currentWidget = nullptr;
85 m_currentIndex = -1;
86 } else if (indexOf(w) < m_currentIndex) {
87 --m_currentIndex;
88 }
89 m_widgets.removeOne(w);
90 }
91
95 QWidget *currentWidget() const
96 {
97 return m_currentWidget;
98 }
99
100 void setCurrentIndex(int index)
101 {
102 QWidget *prev = currentWidget();
103 QWidget *next = widget(index);
104 if (!next || (next == prev && !m_currentWidget)) {
105 return;
106 }
107
108 bool reenableUpdates = false;
109 QWidget *parent = m_parentLayout->parentWidget();
110
111 if (parent && parent->updatesEnabled()) {
112 reenableUpdates = true;
113 parent->setUpdatesEnabled(false);
114 }
115
116 auto layoutItem = m_parentLayout->takeAt(1);
117 if (layoutItem) {
118 layoutItem->widget()->setParent(nullptr);
119 }
120 delete layoutItem;
121
122 m_parentLayout->addWidget(next);
123 if (prev) {
124 prev->hide();
125 }
126 m_currentIndex = index;
127 m_currentWidget = next;
128
129 if (reenableUpdates) {
130 parent->setUpdatesEnabled(true);
131 }
132 }
133
134 int currentIndex() const
135 {
136 return m_currentIndex;
137 }
138
139 bool isEmpty() const
140 {
141 return m_widgets.empty();
142 }
143
144 int indexOf(QWidget *w) const
145 {
146 return m_widgets.indexOf(w);
147 }
148
149 QWidget *widget(int index) const
150 {
151 return (index < m_widgets.size()) ? m_widgets.at(index) : nullptr;
152 }
153
154 QRect geometry() const
155 {
156 return m_widgets.empty() ? QRect() : currentWidget()->geometry();
157 }
158};
159
160static const Qx::DockWidgetAreas s_defaultAllowedAreas = Qx::AllDockAreas;
161
162class DockPanelPrivate
163{
164public:
165 QX_DECLARE_PUBLIC(DockPanel)
166public:
167 DockPanelPrivate();
168 ~DockPanelPrivate();
169 void init();
170
171 DockTabBar *tabBar() const;
172 void updateTitleBarButtonStates();
173 void updateTitleBarButtonVisibility(bool isTopLevel);
174 void updateMinimumSizeHint();
175public:
176 DockWindow *m_window = nullptr;
177 QBoxLayout *m_layout = nullptr;
178 DockAreaLayout *m_contentsLayout = nullptr;
179 DockTitleBar *m_titleBar = nullptr;
180 DockPanel::DockAreaFlags m_flags{DockPanel::DefaultFlags};
181 bool m_updateTitleBarButtons = false;
182 Qx::DockWidgetAreas m_allowedAreas = s_defaultAllowedAreas;
183 DockAutoHideContainer *m_autoHideContainer = nullptr;
184 QSize m_minSizeHint;
185};
186
187DockPanelPrivate::DockPanelPrivate()
188{
189}
190
191DockPanelPrivate::~DockPanelPrivate()
192{
193 delete m_contentsLayout;
194}
195
196void DockPanelPrivate::init()
197{
198 Q_Q(DockPanel);
199
200 m_layout = new QBoxLayout(QBoxLayout::TopToBottom);
201 m_layout->setContentsMargins(0, 0, 0, 0);
202 m_layout->setSpacing(0);
203 q->setLayout(m_layout);
204
205 m_titleBar = new DockTitleBar(q);
206 QObject::connect(m_titleBar, &DockTitleBar::tabBarClicked, q, &DockPanel::setCurrentIndex);
207 QObject::connect(tabBar(), &DockTabBar::tabCloseRequested, q, &DockPanel::onTabCloseRequested);
208 QObject::connect(tabBar(), &DockTabBar::tabMoved, q, &DockPanel::reorderDockWidget);
209 m_layout->addWidget(m_titleBar);
210
211 m_contentsLayout = new DockAreaLayout(m_layout);
212}
213
214DockTabBar *DockPanelPrivate::tabBar() const
215{
216 return m_titleBar->tabBar();
217}
218
219void DockPanelPrivate::updateTitleBarButtonStates()
220{
221 Q_Q(DockPanel);
222 if (q->isHidden()) {
223 m_updateTitleBarButtons = true;
224 return;
225 }
226 if (q->isAutoHide()) {
227 if (DockManager::testAutoHideConfigFlag(DockManager::AutoHideHasCloseButton)) {
228 m_titleBar->button(Qx::TitleBarButtonClose)
229 ->setEnabled(q->features().testFlag(DockWidget::DockWidgetClosable));
230 }
231 } else {
232 m_titleBar->button(Qx::TitleBarButtonUndock)
233 ->setEnabled(q->features().testFlag(DockWidget::DockWidgetFloatable));
234 m_titleBar->button(Qx::TitleBarButtonClose)->setEnabled(q->features().testFlag(DockWidget::DockWidgetClosable));
235 }
236
237 m_titleBar->button(Qx::TitleBarButtonAutoHide)->setEnabled(q->features().testFlag(DockWidget::DockWidgetPinnable));
238 m_titleBar->updateDockWidgetActionsButtons();
239 m_updateTitleBarButtons = false;
240}
241
242void DockPanelPrivate::updateTitleBarButtonVisibility(bool isTopLevel)
243{
244 Q_Q(DockPanel);
245 auto container = q->dockContainer();
246 if (!container) {
247 return;
248 }
249 bool isAutoHide = q->isAutoHide();
250 if (isAutoHide) {
251 bool showCloseButton = DockManager::testAutoHideConfigFlag(DockManager::AutoHideHasCloseButton);
252 m_titleBar->button(Qx::TitleBarButtonClose)->setVisible(showCloseButton);
253 m_titleBar->button(Qx::TitleBarButtonAutoHide)->setVisible(true);
254 m_titleBar->button(Qx::TitleBarButtonUndock)->setVisible(false);
255 m_titleBar->button(Qx::TitleBarButtonTabsMenu)->setVisible(false);
256 } else if (isTopLevel) {
257 m_titleBar->button(Qx::TitleBarButtonClose)->setVisible(!container->isFloating());
258 m_titleBar->button(Qx::TitleBarButtonAutoHide)->setVisible(!container->isFloating());
259 // Undock and tabs should never show when auto hidden
260 m_titleBar->button(Qx::TitleBarButtonUndock)->setVisible(!container->isFloating());
261 m_titleBar->button(Qx::TitleBarButtonTabsMenu)->setVisible(true);
262 } else {
263 m_titleBar->button(Qx::TitleBarButtonClose)->setVisible(true);
264 bool showAutoHideButton = DockManager::testAutoHideConfigFlag(DockManager::DockAreaHasAutoHideButton);
265 m_titleBar->button(Qx::TitleBarButtonAutoHide)->setVisible(showAutoHideButton);
266 m_titleBar->button(Qx::TitleBarButtonUndock)->setVisible(true);
267 m_titleBar->button(Qx::TitleBarButtonTabsMenu)->setVisible(true);
268 }
269}
270
274void DockPanelPrivate::updateMinimumSizeHint()
275{
276 m_minSizeHint = QSize();
277 for (int i = 0; i < m_contentsLayout->count(); ++i) {
278 auto widget = m_contentsLayout->widget(i);
279 m_minSizeHint.setHeight(qMax(m_minSizeHint.height(), widget->minimumSizeHint().height()));
280 m_minSizeHint.setWidth(qMax(m_minSizeHint.width(), widget->minimumSizeHint().width()));
281 }
282}
283
284DockPanel::DockPanel(DockWindow *window, DockContainer *parent)
285 : Super{parent}
286{
288
289 Q_D(DockPanel);
290 d->m_window = window;
291 d->init();
292 QXDOCK_SET_BGCOLOR(this);
293}
294
299
301{
302 Q_D(const DockPanel);
303 return d->m_window;
304}
305
307{
308 // need include header of DockContainer
309 return internal::findParent<DockContainer *>(this);
310}
311
313{
314 // need include header of DockSplitter
315 return internal::findParent<DockSplitter *>(this);
316}
317
319{
320 Q_D(const DockPanel);
321 return d->m_titleBar->geometry();
322}
323
325{
326 Q_D(const DockPanel);
327 return d->m_contentsLayout->geometry();
328}
329
331{
332 Q_D(const DockPanel);
333 return d->m_contentsLayout->count();
334}
335
336QList<DockWidget *> DockPanel::dockWidgets() const
337{
338 Q_D(const DockPanel);
339 QList<DockWidget *> dwList;
340 for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
341 dwList.append(dockWidget(i));
342 }
343 return dwList;
344}
345
347{
348 Q_D(const DockPanel);
349 int cnt = 0;
350 for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
351 DockWidget *w = dockWidget(i);
352 if (w && !w->isClosed()) {
353 ++cnt;
354 }
355 }
356 return cnt;
357}
358
359QList<DockWidget *> DockPanel::openedDockWidgets() const
360{
361 Q_D(const DockPanel);
362 QList<DockWidget *> dwList;
363 for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
364 DockWidget *w = dockWidget(i);
365 if (w && !w->isClosed()) {
366 dwList.append(w);
367 }
368 }
369 return dwList;
370}
371
373{
374 Q_D(const DockPanel);
375 return qobject_cast<DockWidget *>(d->m_contentsLayout->widget(index));
376}
377
383{
384 Q_D(const DockPanel);
385 return d->m_contentsLayout->currentIndex();
386}
387
389{
390 int index = currentIndex();
391 if (index < 0) {
392 return nullptr;
393 }
394 return dockWidget(index);
395}
396
398{
399 if (dockWindow()->isRestoringState()) {
400 return;
401 }
403}
404
414DockWidget::DockWidgetFeatures DockPanel::features(Qx::DockBitwiseOperator mode) const
415{
416 if (mode == Qx::DockBitwiseAnd) {
417 DockWidget::DockWidgetFeatures f(DockWidget::AllDockWidgetFeatures);
418 for (const auto w : dockWidgets()) {
419 f &= w->features();
420 }
421 return f;
422 } else {
423 DockWidget::DockWidgetFeatures f(DockWidget::NoDockWidgetFeatures);
424 for (const auto w : dockWidgets()) {
425 f |= w->features();
426 }
427 return f;
428 }
429}
430
432{
433 Q_D(const DockPanel);
434 return d->m_titleBar->button(which);
435}
436
437void DockPanel::setVisible(bool visible)
438{
439 Super::setVisible(visible);
440 Q_D(DockPanel);
441 if (d->m_updateTitleBarButtons) {
442 d->updateTitleBarButtonStates();
443 }
444}
445
446Qx::DockWidgetAreas DockPanel::allowedAreas() const
447{
448 Q_D(const DockPanel);
449 return d->m_allowedAreas;
450}
451
452void DockPanel::setAllowedAreas(Qx::DockWidgetAreas areas)
453{
454 Q_D(DockPanel);
455 d->m_allowedAreas = areas;
456}
457
459{
460 Q_D(const DockPanel);
461 return d->m_titleBar;
462}
463
469DockPanel::DockAreaFlags DockPanel::dockAreaFlags() const
470{
471 Q_D(const DockPanel);
472 return d->m_flags;
473}
474
479void DockPanel::setDockAreaFlags(DockAreaFlags flags)
480{
481 Q_D(DockPanel);
482 auto changedFlags = d->m_flags ^ flags;
483 d->m_flags = flags;
484 if (changedFlags.testFlag(HideSingleWidgetTitleBar)) {
486 }
487}
488
490{
491 auto flags = dockAreaFlags();
492 internal::setFlag(flags, flag, on);
493 setDockAreaFlags(flags);
494}
495
497{
498 if (dockWidgetsCount() != 1) {
499 return false;
500 }
501
502 return dockWindow()->centralWidget() == dockWidgets().constFirst();
503}
504
506{
507 auto centralWidget = dockWindow()->centralWidget();
508 for (const auto &dockWidget : dockWidgets()) {
509 if (dockWidget == centralWidget) {
510 return true;
511 }
512 }
513
514 return false;
515}
516
518{
519 auto container = dockContainer();
520 if (!container) {
521 return false;
522 }
523
524 return (container->topLevelDockPanel() == this);
525}
526
528{
529 Q_D(DockPanel);
530 auto tabBar = d->tabBar();
531 if (index < 0 || index > (tabBar->count() - 1)) {
532 qWarning() << Q_FUNC_INFO << "Invalid index" << index;
533 return;
534 }
535
536 auto cw = d->m_contentsLayout->currentWidget();
537 auto nw = d->m_contentsLayout->widget(index);
538 if (cw == nw && !nw->isHidden()) {
539 return;
540 }
541
542 Q_EMIT currentChanging(index);
543 tabBar->setCurrentIndex(index);
544 d->m_contentsLayout->setCurrentIndex(index);
545 d->m_contentsLayout->currentWidget()->show();
546 Q_EMIT currentChanged(index);
547}
548
550{
551 auto openWidgets = openedDockWidgets();
552 if (openWidgets.count() == 1 &&
553 (openWidgets[0]->features().testFlag(DockWidget::DockWidgetDeleteOnClose) ||
554 openWidgets[0]->features().testFlag(DockWidget::CustomCloseHandling)) &&
555 !isAutoHide()) {
556 openWidgets[0]->closeDockWidgetInternal();
557 } else {
558 for (auto w : openWidgets) {
559 if ((w->features().testFlag(DockWidget::DockWidgetDeleteOnClose) &&
560 w->features().testFlag(DockWidget::DockWidgetForceCloseWithArea)) ||
561 w->features().testFlag(DockWidget::CustomCloseHandling)) {
562 w->closeDockWidgetInternal();
563 } else if (w->features().testFlag(DockWidget::DockWidgetDeleteOnClose) && isAutoHide()) {
564 w->closeDockWidgetInternal();
565 } else {
566 w->toggleView(false);
567 }
568 }
569 }
570}
571
576
582void DockPanel::setAutoHide(bool enable, Qx::DockSideBarArea area, int tabIndex)
583{
584 Q_D(DockPanel);
585 if (!isAutoHideFeatureEnabled()) {
586 return;
587 }
588
589 if (!enable) {
590 if (isAutoHide()) {
591 d->m_autoHideContainer->moveContentsToParent();
592 }
593 return;
594 }
595
596 // If this is already an auto hide container, then move it to new location
597 if (isAutoHide()) {
598 d->m_autoHideContainer->moveToNewSideBarArea(area, tabIndex);
599 return;
600 }
601
602 auto location = (Qx::DockSideBarNone == area) ? calculateSideBarArea() : area;
603 for (const auto w : openedDockWidgets()) {
604 if (enable == isAutoHide()) {
605 continue;
606 }
607
608 if (!w->features().testFlag(DockWidget::DockWidgetPinnable)) {
609 continue;
610 }
611
612 dockContainer()->createAndSetupAutoHideContainer(location, w, tabIndex++);
613 }
614}
615
621{
622 if (!isAutoHideFeatureEnabled()) {
623 return;
624 }
625
626 setAutoHide(!isAutoHide(), area);
627}
628
630{
631 Q_D(DockPanel);
632 d->m_titleBar->setAreaFloating();
633}
634
636{
637 setVisible(open);
638 Q_EMIT viewToggled(open);
639}
640
642{
644}
645
650void DockPanel::reorderDockWidget(int fromIndex, int toIndex)
651{
652 Q_D(DockPanel);
653 if (fromIndex >= d->m_contentsLayout->count() || fromIndex < 0 || toIndex >= d->m_contentsLayout->count() ||
654 toIndex < 0 || fromIndex == toIndex) {
655 return;
656 }
657
658 auto widget = d->m_contentsLayout->widget(fromIndex);
659 d->m_contentsLayout->removeWidget(widget);
660 d->m_contentsLayout->insertWidget(toIndex, widget);
661 setCurrentIndex(toIndex);
662}
663
664/*
665 * Update the auto hide button checked state based on if it's contained in an auto hide container or not
666 */
668{
669 auto autoHideButton = titleBarButton(Qx::TitleBarButtonAutoHide);
670 autoHideButton->blockSignals(true);
671 autoHideButton->setChecked(isAutoHide());
672 autoHideButton->blockSignals(false);
673}
674
682
701
707{
708 auto container = dockContainer();
709 auto contentRect = container->contentRect();
710
711 int borders = BorderNone; // contains all borders that are touched by the dock ware
712 auto dockAreaTopLeft = mapTo(container, rect().topLeft());
713 auto dockAreaRect = rect();
714 dockAreaRect.moveTo(dockAreaTopLeft);
715 const qreal aspectRatio = dockAreaRect.width() / (qMax(1, dockAreaRect.height()) * 1.0);
716 const qreal sizeRatio = (qreal)contentRect.width() / dockAreaRect.width();
717 static const int minBorderDistance = 16;
718 bool horizontalOrientation = (aspectRatio > 1.0) && (sizeRatio < 3.0);
719
720 // measure border distances - a distance less than 16 px means we touch the border
721 int borderDistance[4];
722
723 int distance = qAbs(contentRect.topLeft().y() - dockAreaRect.topLeft().y());
724 borderDistance[Qx::DockSideBarTop] = (distance < minBorderDistance) ? 0 : distance;
725 if (!borderDistance[Qx::DockSideBarTop]) {
726 borders |= BorderTop;
727 }
728
729 distance = qAbs(contentRect.bottomRight().y() - dockAreaRect.bottomRight().y());
730 borderDistance[Qx::DockSideBarBottom] = (distance < minBorderDistance) ? 0 : distance;
731 if (!borderDistance[Qx::DockSideBarBottom]) {
732 borders |= BorderBottom;
733 }
734
735 distance = qAbs(contentRect.topLeft().x() - dockAreaRect.topLeft().x());
736 borderDistance[Qx::DockSideBarLeft] = (distance < minBorderDistance) ? 0 : distance;
737 if (!borderDistance[Qx::DockSideBarLeft]) {
738 borders |= BorderLeft;
739 }
740
741 distance = qAbs(contentRect.bottomRight().x() - dockAreaRect.bottomRight().x());
742 borderDistance[Qx::DockSideBarRight] = (distance < minBorderDistance) ? 0 : distance;
743 if (!borderDistance[Qx::DockSideBarRight]) {
744 borders |= BorderRight;
745 }
746
747 auto sideBarArea = Qx::DockSideBarRight;
748 switch (borders) {
749 // 1. It's touching all borders
750 case BorderAll:
751 sideBarArea = horizontalOrientation ? Qx::DockSideBarBottom : Qx::DockSideBarRight;
752 break;
753
754 // 2. It's touching 3 borders
756 sideBarArea = Qx::DockSideBarBottom;
757 break;
759 sideBarArea = Qx::DockSideBarTop;
760 break;
762 sideBarArea = Qx::DockSideBarLeft;
763 break;
765 sideBarArea = Qx::DockSideBarRight;
766 break;
767
768 // 3. It's touching horizontal or vertical borders
769 case BorderVertical:
770 sideBarArea = Qx::DockSideBarBottom;
771 break;
772 case BorderHorizontal:
773 sideBarArea = Qx::DockSideBarRight;
774 break;
775
776 // 4. It's in a corner
777 case BorderTopLeft:
778 sideBarArea = horizontalOrientation ? Qx::DockSideBarTop : Qx::DockSideBarLeft;
779 break;
780 case BorderTopRight:
781 sideBarArea = horizontalOrientation ? Qx::DockSideBarTop : Qx::DockSideBarRight;
782 break;
783 case BorderBottomLeft:
784 sideBarArea = horizontalOrientation ? Qx::DockSideBarBottom : Qx::DockSideBarLeft;
785 break;
787 sideBarArea = horizontalOrientation ? Qx::DockSideBarBottom : Qx::DockSideBarRight;
788 break;
789
790 // 5. It's touching only one border
791 case BorderLeft:
792 sideBarArea = Qx::DockSideBarLeft;
793 break;
794 case BorderRight:
795 sideBarArea = Qx::DockSideBarRight;
796 break;
797 case BorderTop:
798 sideBarArea = Qx::DockSideBarTop;
799 break;
800 case BorderBottom:
801 sideBarArea = Qx::DockSideBarBottom;
802 break;
803 }
804
805 return sideBarArea;
806}
807
809{
810 Q_D(DockPanel);
811 if (d->m_titleBar) {
812 d->updateTitleBarButtonStates();
813 }
814}
815
821{
822 Q_D(DockPanel);
823 insertDockWidget(d->m_contentsLayout->count(), w);
824}
825
833void DockPanel::insertDockWidget(int index, DockWidget *w, bool activate)
834{
835 Q_D(DockPanel);
836 if (index < 0 || index > d->m_contentsLayout->count()) {
837 index = d->m_contentsLayout->count();
838 }
839 d->m_contentsLayout->insertWidget(index, w);
840 w->setDockPanel(this);
841 auto tab = w->tab();
842 tab->setDockPanel(this);
843 d->tabBar()->blockSignals(true);
844 d->tabBar()->insertTab(index, tab);
845 d->tabBar()->blockSignals(false);
846 tab->setVisible(!w->isClosed());
847 d->m_titleBar->autoHideTitleLabel()->setText(w->windowTitle());
848 w->setProperty(INDEX_PROPERTY, index);
849 d->m_minSizeHint.setHeight(qMax(d->m_minSizeHint.height(), w->minimumSizeHint().height()));
850 d->m_minSizeHint.setWidth(qMax(d->m_minSizeHint.width(), w->minimumSizeHint().width()));
851 if (activate) {
852 setCurrentIndex(index);
853 // Set current index can show the widget without changing the close state,
854 // added to keep the close state consistent
855 w->setClosedState(false);
856 }
857 // If this dock area is hidden, then we need to make it visible again
858 // by calling DockWidget->toggleViewInternal(true);
859 if (!this->isVisible() && d->m_contentsLayout->count() > 0 && !dockWindow()->isRestoringState()) {
860 // Notes: If d->m_contentsLayout->count() > 1 is required, it will appear
861 // that the toggleViewAction is not checked when there is only one dock widget
862 w->toggleViewInternal(true);
863 }
864 d->updateTitleBarButtonStates();
866}
867
869{
870 if (!w) {
871 return;
872 }
873 // If this dock panel is in a auto hide container, then we can delete
874 // the auto hide container now
875 if (isAutoHide()) {
877 return;
878 }
879
880 auto currentWidget = currentDockWidget();
881 auto nextOpenWidget = (w == currentWidget) ? nextOpenDockWidget(w) : nullptr;
882
883 Q_D(DockPanel);
884 d->m_contentsLayout->removeWidget(w);
885 auto tab = w->tab();
886 tab->hide();
887 d->tabBar()->removeTab(tab);
888 tab->setParent(w);
889 w->setDockPanel(nullptr);
890
891 DockContainer *container = dockContainer();
892 if (nextOpenWidget) {
893 setCurrentDockWidget(nextOpenWidget);
894 } else if (d->m_contentsLayout->isEmpty() && container->dockPanelCount() >= 1) {
895 container->removeDockPanel(this);
896 this->deleteLater();
897 if (container->dockPanelCount() == 0) {
898 if (DockFloatingContainer *floatingContainer = container->floatingWidget()) {
899 floatingContainer->hide();
900 floatingContainer->deleteLater();
901 }
902 }
903 } else if (w == currentWidget) {
905 }
906 d->updateTitleBarButtonStates();
908 d->updateMinimumSizeHint();
909 auto topLevelWidget = container->topLevelDockWidget();
910 if (topLevelWidget) {
911 topLevelWidget->emitTopLevelChanged(true);
912 }
913}
914
922{
923 auto openWidgets = openedDockWidgets();
924
925 if (openWidgets.count() > 1 || (openWidgets.count() == 1 && openWidgets[0] != w)) {
926 if (openWidgets.last() == w) {
927 DockWidget *nextDockWidget = openWidgets[openWidgets.count() - 2];
928 // search backwards for widget with tab
929 for (int i = openWidgets.count() - 2; i >= 0; --i) {
930 auto dw = openWidgets[i];
931 if (!dw->features().testFlag(DockWidget::NoTab)) {
932 return dw;
933 }
934 }
935 // return widget without tab
936 return nextDockWidget;
937 } else {
938 int indexOfDockWidget = openWidgets.indexOf(w);
939 DockWidget *nextDockWidget = openWidgets[indexOfDockWidget + 1];
940 // search forwards for widget with tab
941 for (int i = indexOfDockWidget + 1; i < openWidgets.count(); ++i) {
942 auto dw = openWidgets[i];
943 if (!dw->features().testFlag(DockWidget::NoTab)) {
944 return dw;
945 }
946 }
947
948 // search backwards for widget with tab
949 for (int i = indexOfDockWidget - 1; i >= 0; --i) {
950 auto dw = openWidgets[i];
951 if (!dw->features().testFlag(DockWidget::NoTab)) {
952 return dw;
953 }
954 }
955
956 // return widget without tab
957 return nextDockWidget;
958 }
959 } else {
960 return nullptr;
961 }
962}
963
968{
969 Q_UNUSED(w);
970 Q_UNUSED(open);
972}
973
975{
976 Q_D(const DockPanel);
977 return d->m_contentsLayout->indexOf(w);
978}
979
985{
986 this->toggleView(false);
987
988 // Hide empty parent splitters
989 auto splitter = parentSplitter();
991
992 // Hide empty floating widget
993 DockContainer *container = this->dockContainer();
995 return;
996 }
997
999 auto topLevelWidget = container->topLevelDockWidget();
1000 auto floatingWidget = container->floatingWidget();
1001 if (topLevelWidget) {
1002 if (floatingWidget) {
1003 floatingWidget->updateWindowTitle();
1004 }
1005 DockWidget::emitTopLevelEventForWidget(topLevelWidget, true);
1006 } else if (container->openedDockPanels().isEmpty() && floatingWidget) {
1007 floatingWidget->hide();
1008 }
1009 if (isAutoHide()) {
1010 autoHideContainer()->hide();
1011 }
1012}
1013
1018{
1019 Q_D(DockPanel);
1020 DockContainer *container = dockContainer();
1021 if (!container) {
1022 return;
1023 }
1024 if (!d->m_titleBar) {
1025 return;
1026 }
1027 bool isAutoHide = this->isAutoHide();
1029 bool hidden = container->hasTopLevelDockWidget() && container->isFloating();
1030 hidden |= (d->m_flags.testFlag(HideSingleWidgetTitleBar) && openDockWidgetsCount() == 1);
1031 hidden &= !isAutoHide; // Titlebar must always be visible when auto hidden so it can be dragged
1032 d->m_titleBar->setVisible(!hidden);
1033 }
1034 if (isAutoHideFeatureEnabled()) {
1035 d->m_titleBar->showAutoHideControls(isAutoHide);
1037 }
1038}
1039
1046{
1047 int index = indexOf(w);
1048 if (index < 0) {
1049 return;
1050 }
1051 setCurrentIndex(index);
1052 // Set current index can show the widget without changing the close state, added to keep the close state consistent
1053 w->setClosedState(false);
1054}
1055
1056/*
1057 * Update the title bar button visibility based on if it's top level or not
1058 */
1060{
1061 Q_D(DockPanel);
1062 d->updateTitleBarButtonVisibility(isTopLevel);
1063}
1064
1069{
1070 Q_D(DockPanel);
1071 if (d->m_titleBar) {
1072 d->m_titleBar->markTabsMenuOutdated();
1073 }
1074}
1075
1076#ifdef Q_OS_WIN
1083bool DockPanel::event(QEvent *e)
1084{
1085 switch (e->type()) {
1086 case QEvent::PlatformSurface:
1087 return true;
1088 default:
1089 break;
1090 }
1091
1092 return Super::event(e);
1093}
1094#endif
1095
1097{
1098 Q_D(const DockPanel);
1099 return d->m_autoHideContainer;
1100}
1101
1103{
1104 Q_D(DockPanel);
1105 d->m_autoHideContainer = container;
1108
1109 auto button = d->m_titleBar->button(Qx::TitleBarButtonAutoHide);
1110 TitleBarButton *autoHideButton = qobject_cast<TitleBarButton *>(button);
1111 autoHideButton->setShowInTitleBar(true);
1112}
1113
1115{
1116 Q_D(const DockPanel);
1117 return d->m_autoHideContainer != nullptr;
1118}
1119
1130{
1131 Q_D(const DockPanel);
1132 for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
1133 DockWidget *w = dockWidget(i);
1134 if (w && !w->isClosed()) {
1135 return i;
1136 }
1137 }
1138
1139 return -1;
1140}
1141
1142void DockPanel::saveState(QXmlStreamWriter &s) const
1143{
1144 Q_D(const DockPanel);
1145 s.writeStartElement("Area");
1146 s.writeAttribute("Tabs", QString::number(d->m_contentsLayout->count()));
1147 auto currentDockWidget = this->currentDockWidget();
1148 QString name = currentDockWidget ? currentDockWidget->objectName() : "";
1149 s.writeAttribute("Current", name);
1150
1151 if (d->m_allowedAreas != s_defaultAllowedAreas) {
1152 s.writeAttribute("AllowedAreas", QString::number(d->m_allowedAreas, 16));
1153 }
1154
1155 if (d->m_flags != DefaultFlags) {
1156 s.writeAttribute("Flags", QString::number(d->m_flags, 16));
1157 }
1158 for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
1159 dockWidget(i)->saveState(s);
1160 }
1161 s.writeEndElement();
1162}
1163
1164bool DockPanel::restoreState(DockStateReader &s, DockPanel *&createdWidget, bool testing,
1165 DockContainer *parentContainer)
1166{
1167 bool ok;
1168
1169 QString currentDockWidget = s.attributes().value("Current").toString();
1170
1171 auto dockWindow = parentContainer->dockWindow();
1172 DockPanel *panel = nullptr;
1173 if (!testing) {
1174 panel = new DockPanel(dockWindow, parentContainer);
1175 const auto allowedAreasAttribute = s.attributes().value("AllowedAreas");
1176 if (!allowedAreasAttribute.isEmpty()) {
1177 panel->setAllowedAreas((Qx::DockWidgetArea)allowedAreasAttribute.toInt(nullptr, 16));
1178 }
1179
1180 const auto flagsAttribute = s.attributes().value("Flags");
1181 if (!flagsAttribute.isEmpty()) {
1182 panel->setDockAreaFlags((DockPanel::DockAreaFlags)flagsAttribute.toInt(nullptr, 16));
1183 }
1184 }
1185
1186 while (s.readNextStartElement()) {
1187 if (s.name() != QLatin1String("Widget")) {
1188 continue;
1189 }
1190
1191 auto objectName = s.attributes().value("Name");
1192 if (objectName.isEmpty()) {
1193 return false;
1194 }
1195
1196 bool closed = s.attributes().value("Closed").toInt(&ok);
1197 if (!ok) {
1198 return false;
1199 }
1200
1201 s.skipCurrentElement();
1202 DockWidget *dockWidget = dockWindow->findDockWidget(objectName.toString());
1203 if (!dockWidget || testing) {
1204 continue;
1205 }
1208 }
1209
1210 // We hide the panel here to prevent the short display (the flashing)
1211 // of the dock areas during application startup
1212 panel->hide();
1213 panel->addDockWidget(dockWidget);
1215 dockWidget->setClosedState(closed);
1216 dockWidget->setProperty(internal::ClosedProperty, closed);
1217 dockWidget->setProperty(internal::DirtyProperty, false);
1218 }
1219
1220 if (testing) {
1221 return true;
1222 }
1223
1224 if (!panel->dockWidgetsCount()) {
1225 delete panel;
1226 panel = nullptr;
1227 } else {
1228 panel->setProperty("currentDockWidget", currentDockWidget);
1229 }
1230
1231 createdWidget = panel;
1232 return true;
1233}
1234
1240{
1241 Q_D(const DockPanel);
1242 if (!d->m_minSizeHint.isValid()) {
1243 return Super::minimumSizeHint();
1244 }
1245
1246 if (d->m_titleBar->isVisible()) {
1247 return d->m_minSizeHint + QSize(0, d->m_titleBar->minimumSizeHint().height());
1248 } else {
1249 return d->m_minSizeHint;
1250 }
1251}
1252
Auto hide container for hosting an auto hide dock widget
void cleanupAndDelete()
Cleanups up the side tab widget and then deletes itself
Container that manages a number of dock panels with single dock widgets or tabyfied dock widgets in e...
DockWindow * dockWindow() const
DockWidget * topLevelDockWidget() const
If hasTopLevelDockWidget() returns true, this function returns the one and only visible dock widget.
void closeOtherAreas(DockPanel *keepOpenPanel)
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)
bool hasTopLevelDockWidget() const
This function returns true if this dock panel has only one single visible dock widget.
DockFloatingContainer * floatingWidget() const
If this dock container is in a floating widget, this function returns the floating widget.
DockPanel * topLevelDockPanel() const
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 ...
static bool testConfigFlag(ConfigFlag flag)
DockPanel manages multiple instances of DockWidgets.
Definition dockpanel.h:31
void updateTitleBarButtonVisibility(bool isTopLevel)
int dockWidgetsCount() const
int indexOf(DockWidget *w) const
void updateTitleBarVisibility()
Updates the dock panel layout and components visibility
DockSplitter * parentSplitter() const
DockTitleBar * titleBar() const
DockContainer * dockContainer() const
void toggleAutoHide(Qx::DockSideBarArea area=Qx::DockSideBarNone)
Switches the dock panel to auto hide mode or vice versa depending on its current state.
DockAutoHideContainer * autoHideContainer() const
void internalSetCurrentDockWidget(DockWidget *w)
This is the internal private function for setting the current widget.
QRect contentAreaGeometry() const
DockWidget * currentDockWidget() const
void toggleView(bool open)
void saveState(QXmlStreamWriter &s) const
void insertDockWidget(int index, DockWidget *w, bool activate=true)
Inserts a dock widget into dock panel.
static bool restoreState(DockStateReader &s, DockPanel *&createdWidget, bool testing, DockContainer *parentContainer)
Qx::DockWidgetAreas allowedAreas() const
void setCurrentIndex(int index)
DockWindow * dockWindow() const
DockWidget * dockWidget(int index) const
void updateAutoHideButtonCheckState()
void addDockWidget(DockWidget *w)
Add a new dock widget to dock panel.
void onTabCloseRequested(int index)
void currentChanging(int index)
void updateTitleBarButtonsToolTips()
void setAllowedAreas(Qx::DockWidgetAreas areas)
void markTitleBarMenuOutdated()
Marks tabs menu to update
void setDockAreaFlag(DockAreaFlag flag, bool on)
void toggleDockWidgetView(DockWidget *w, bool open)
Called from dock widget if it is opened or closed
int currentIndex() const
Returns the index of the current active dock widget or -1 if there are is no active dock widget (ie....
DockWidget::DockWidgetFeatures features(Qx::DockBitwiseOperator mode=Qx::DockBitwiseAnd) const
This functions returns the dock widget features of all dock widget in this panel.
bool isAutoHide() const
void setAutoHideContainer(DockAutoHideContainer *container)
void hideAreaWithNoVisibleContent()
Call this function, if you already know, that the dock does not contain any visible content (any open...
DockAreaFlags dockAreaFlags() const
Returns the dock area flags - a combination of flags that configure the appearance and features of th...
QRect titleBarGeometry() const
void onDockWidgetFeaturesChanged()
bool containsCentralWidget() const
int openDockWidgetsCount() const
DockWidget * nextOpenDockWidget(DockWidget *w) const
This is a helper function to get the next open dock widget to activate if the given w will be closed ...
QList< DockWidget * > openedDockWidgets() const
void viewToggled(bool open)
void setAutoHide(bool enable, Qx::DockSideBarArea area=Qx::DockSideBarNone, int tabIndex=-1)
Sets the dock panel into auto hide mode or into normal mode.
QList< DockWidget * > dockWidgets() const
void currentChanged(int index)
QAbstractButton * titleBarButton(Qx::DockTitleBarButton which) const
virtual void setVisible(bool visible) override
void setCurrentDockWidget(DockWidget *w)
void reorderDockWidget(int fromIndex, int toIndex)
Reorder the index position of DockWidget at fromIndx to toIndex if a tab in the tabbar is dragged fro...
void removeDockWidget(DockWidget *w)
bool isCentralWidgetArea() const
int indexOfFirstOpenDockWidget() const
Returns the index of the first open dock widgets in the list of dock widgets.
void setDockAreaFlags(DockAreaFlags flags)
Sets the dock area flags - a combination of flags that configure the appearance and features of the d...
virtual QSize minimumSizeHint() const override
Returns the largest minimumSizeHint() of the dock widgets in this panel.
bool isTopLevelArea() const
Qx::DockSideBarArea calculateSideBarArea() const
Calculate the auto hide side bar are depending on the dock area widget position in the container
Extends QXmlStreamReader with file version information
void setDockPanel(DockPanel *panel)
Definition docktab.cpp:348
Title bar of a dock panel.
The DockWidget class provides a widget that can be docked inside a DockWindow or floated as a top-lev...
Definition dockwidget.h:31
bool isClosed() const
Returns true, if this dock widget is closed.
void setClosedState(bool closed)
virtual QSize minimumSizeHint() const override
void emitTopLevelChanged(bool floating)
Use this function to emit a top level changed event.
static void emitTopLevelEventForWidget(DockWidget *topLevelDockWidget, bool floating)
Call this function to emit a topLevelChanged() signal and to update the dock panel tool bar visibilit...
DockTab * tab() const
Returns the tab widget of this dock widget that is shown in the dock title bar
void setToggleViewActionChecked(bool checked)
This function changes the toggle view action without emitting any signal
void setDockPanel(DockPanel *panel)
If this dock widget is inserted into a dock panel, the dock panel will be registered on this widget v...
DockAutoHideContainer * autoHideContainer() const
Returns the auto hide dock container of this dock widget or 0 if there is none
void requestCloseDockWidget()
Request closing of the dock widget.
void toggleViewInternal(bool open)
Internal toggle view function that does not check if the widget already is in the given state
void saveState(QXmlStreamWriter &s) const
The central dock window that maintains the complete docking system.
Definition dockwindow.h:36
DockWidget * findDockWidget(const QString &objectName) const
Searches for a registered dock widget with the given objectName
DockWidget * centralWidget() const
Title bar button of a dock area that customizes QToolButton appearance/behaviour according to various...
void setShowInTitleBar(bool show)
#define QXDOCK_SET_BGCOLOR(w)
Definition dockutils.h:172
void hideEmptyParentSplitters(DockSplitter *splitter)
This function walks the splitter tree upwards to hides all splitters that do not have visible content
void setToolTip(QObjectPtr obj, const QString &tip)
Definition dockutils.h:56
void setFlag(T &flags, typename T::enum_type flag, bool on=true)
Definition dockutils.h:43
@ BorderHorizontalRight
@ BorderTopLeft
@ BorderBottom
@ BorderTopRight
@ BorderBottomRight
@ BorderHorizontalLeft
@ BorderBottomLeft
@ BorderHorizontal
@ BorderRight
@ BorderVerticalBottom
@ BorderVerticalTop
@ BorderVertical
DockSideBarArea
@ DockSideBarTop
@ DockSideBarBottom
@ DockSideBarLeft
@ DockSideBarRight
@ DockSideBarNone
DockWidgetArea
@ AllDockAreas
DockBitwiseOperator
@ DockBitwiseAnd
DockTitleBarButton
@ TitleBarButtonClose
@ TitleBarButtonAutoHide
@ TitleBarButtonTabsMenu
@ TitleBarButtonUndock
#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
QColor button