File indexing completed on 2024-06-02 03:47:29
0001 /******************************************************************************* 0002 ** Qt Advanced Docking System 0003 ** Copyright (C) 2017 Uwe Kindler 0004 ** 0005 ** This library is free software; you can redistribute it and/or 0006 ** modify it under the terms of the GNU Lesser General Public 0007 ** License as published by the Free Software Foundation; either 0008 ** version 2.1 of the License, or (at your option) any later version. 0009 ** 0010 ** This library is distributed in the hope that it will be useful, 0011 ** but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 ** Lesser General Public License for more details. 0014 ** 0015 ** You should have received a copy of the GNU Lesser General Public 0016 ** License along with this library; If not, see <http://www.gnu.org/licenses/>. 0017 ******************************************************************************/ 0018 0019 0020 //============================================================================ 0021 /// \file DockContainerWidget.cpp 0022 /// \author Uwe Kindler 0023 /// \date 24.02.2017 0024 /// \brief Implementation of CDockContainerWidget class 0025 //============================================================================ 0026 0027 0028 //============================================================================ 0029 // INCLUDES 0030 //============================================================================ 0031 #include "DockContainerWidget.h" 0032 0033 #include <QEvent> 0034 #include <QList> 0035 #include <QGridLayout> 0036 #include <QPointer> 0037 #include <QVariant> 0038 #include <QDebug> 0039 #include <QXmlStreamWriter> 0040 #include <QAbstractButton> 0041 #include <QLabel> 0042 #include <QTimer> 0043 #include <QMetaObject> 0044 #include <QMetaType> 0045 #include <QApplication> 0046 0047 #include "DockManager.h" 0048 #include "DockAreaWidget.h" 0049 #include "DockWidget.h" 0050 #include "DockingStateReader.h" 0051 #include "FloatingDockContainer.h" 0052 #include "DockOverlay.h" 0053 #include "ads_globals.h" 0054 #include "DockSplitter.h" 0055 #include "DockWidgetTab.h" 0056 #include "DockAreaTitleBar.h" 0057 #include "DockFocusController.h" 0058 #include "AutoHideDockContainer.h" 0059 #include "AutoHideSideBar.h" 0060 #include "AutoHideTab.h" 0061 0062 #include <functional> 0063 #include <iostream> 0064 0065 #if QT_VERSION < 0x050900 0066 0067 inline char toHexLower(uint value) 0068 { 0069 return "0123456789abcdef"[value & 0xF]; 0070 } 0071 0072 QByteArray qByteArrayToHex(const QByteArray& src, char separator) 0073 { 0074 if(src.size() == 0) 0075 return QByteArray(); 0076 0077 const int length = separator ? (src.size() * 3 - 1) : (src.size() * 2); 0078 QByteArray hex(length, Qt::Uninitialized); 0079 char *hexData = hex.data(); 0080 const uchar *data = reinterpret_cast<const uchar *>(src.data()); 0081 for (int i = 0, o = 0; i < src.size(); ++i) { 0082 hexData[o++] = toHexLower(data[i] >> 4); 0083 hexData[o++] = toHexLower(data[i] & 0xf); 0084 0085 if ((separator) && (o < length)) 0086 hexData[o++] = separator; 0087 } 0088 return hex; 0089 } 0090 #endif 0091 0092 namespace ads 0093 { 0094 static unsigned int zOrderCounter = 0; 0095 0096 enum eDropMode 0097 { 0098 DropModeIntoArea,///< drop widget into a dock area 0099 DropModeIntoContainer,///< drop into container 0100 DropModeInvalid///< invalid mode - do not drop 0101 }; 0102 0103 /** 0104 * Converts dock area ID to an index for array access 0105 */ 0106 static int areaIdToIndex(DockWidgetArea area) 0107 { 0108 switch (area) 0109 { 0110 case LeftDockWidgetArea: return 0; 0111 case RightDockWidgetArea: return 1; 0112 case TopDockWidgetArea: return 2; 0113 case BottomDockWidgetArea: return 3; 0114 case CenterDockWidgetArea: return 4; 0115 default: 0116 return 4; 0117 } 0118 } 0119 0120 /** 0121 * Helper function to ease insertion of dock area into splitter 0122 */ 0123 static void insertWidgetIntoSplitter(QSplitter* Splitter, QWidget* widget, bool Append) 0124 { 0125 if (Append) 0126 { 0127 Splitter->addWidget(widget); 0128 } 0129 else 0130 { 0131 Splitter->insertWidget(0, widget); 0132 } 0133 } 0134 0135 /** 0136 * Private data class of CDockContainerWidget class (pimpl) 0137 */ 0138 class DockContainerWidgetPrivate 0139 { 0140 public: 0141 CDockContainerWidget* _this; 0142 QPointer<CDockManager> DockManager; 0143 unsigned int zOrderIndex = 0; 0144 QList<QPointer<CDockAreaWidget>> DockAreas; 0145 QList<CAutoHideDockContainer*> AutoHideWidgets; 0146 QMap<SideBarLocation, CAutoHideSideBar*> SideTabBarWidgets; 0147 QGridLayout* Layout = nullptr; 0148 CDockSplitter* RootSplitter = nullptr; 0149 bool isFloating = false; 0150 CDockAreaWidget* LastAddedAreaCache[5]; 0151 int VisibleDockAreaCount = -1; 0152 CDockAreaWidget* TopLevelDockArea = nullptr; 0153 QTimer DelayedAutoHideTimer; 0154 CAutoHideTab* DelayedAutoHideTab; 0155 bool DelayedAutoHideShow = false; 0156 0157 /** 0158 * Private data constructor 0159 */ 0160 DockContainerWidgetPrivate(CDockContainerWidget* _public); 0161 0162 /** 0163 * Adds dock widget to container and returns the dock area that contains 0164 * the inserted dock widget 0165 */ 0166 CDockAreaWidget* addDockWidgetToContainer(DockWidgetArea area, CDockWidget* Dockwidget); 0167 0168 /** 0169 * Adds dock widget to a existing DockWidgetArea 0170 */ 0171 CDockAreaWidget* addDockWidgetToDockArea(DockWidgetArea area, CDockWidget* Dockwidget, 0172 CDockAreaWidget* TargetDockArea, int Index = -1); 0173 0174 /** 0175 * Add dock area to this container 0176 */ 0177 void addDockArea(CDockAreaWidget* NewDockWidget, DockWidgetArea area = CenterDockWidgetArea); 0178 0179 /** 0180 * Drop floating widget into container 0181 */ 0182 void dropIntoContainer(CFloatingDockContainer* FloatingWidget, DockWidgetArea area); 0183 0184 /** 0185 * Drop floating widget into auto hide side bar 0186 */ 0187 void dropIntoAutoHideSideBar(CFloatingDockContainer* FloatingWidget, DockWidgetArea area); 0188 0189 /** 0190 * Creates a new tab for a widget dropped into the center of a section 0191 */ 0192 void dropIntoCenterOfSection(CFloatingDockContainer* FloatingWidget, 0193 CDockAreaWidget* TargetArea, int TabIndex = 0); 0194 0195 /** 0196 * Drop floating widget into dock area 0197 */ 0198 void dropIntoSection(CFloatingDockContainer* FloatingWidget, 0199 CDockAreaWidget* TargetArea, DockWidgetArea area, int TabIndex = 0); 0200 0201 /** 0202 * Moves the dock widget or dock area given in Widget parameter to a 0203 * new dock widget area 0204 */ 0205 void moveToNewSection(QWidget* Widget, CDockAreaWidget* TargetArea, DockWidgetArea area, 0206 int TabIndex = 0); 0207 0208 /** 0209 * Moves the dock widget or dock area given in Widget parameter to a 0210 * a dock area in container 0211 */ 0212 void moveToContainer(QWidget* Widgett, DockWidgetArea area); 0213 0214 /** 0215 * Creates a new tab for a widget dropped into the center of a section 0216 */ 0217 void moveIntoCenterOfSection(QWidget* Widget, CDockAreaWidget* TargetArea, int TabIndex = 0); 0218 0219 /** 0220 * Moves the dock widget or dock area given in Widget parameter to 0221 * a auto hide sidebar area 0222 */ 0223 void moveToAutoHideSideBar(QWidget* Widget, DockWidgetArea area, int TabIndex = TabDefaultInsertIndex); 0224 0225 0226 /** 0227 * Adds new dock areas to the internal dock area list 0228 */ 0229 void addDockAreasToList(const QList<CDockAreaWidget*> NewDockAreas); 0230 0231 /** 0232 * Wrapper function for DockAreas append, that ensures that dock area signals 0233 * are properly connected to dock container slots 0234 */ 0235 void appendDockAreas(const QList<CDockAreaWidget*> NewDockAreas); 0236 0237 /** 0238 * Save state of child nodes 0239 */ 0240 void saveChildNodesState(QXmlStreamWriter& Stream, QWidget* Widget); 0241 0242 /** 0243 * Save state of auto hide widgets 0244 */ 0245 void saveAutoHideWidgetsState(QXmlStreamWriter& Stream); 0246 0247 /** 0248 * Restore state of child nodes. 0249 * \param[in] Stream The data stream that contains the serialized state 0250 * \param[out] CreatedWidget The widget created from parsed data or 0 if 0251 * the parsed widget was an empty splitter 0252 * \param[in] Testing If Testing is true, only the stream data is 0253 * parsed without modifying anything. 0254 */ 0255 bool restoreChildNodes(CDockingStateReader& Stream, QWidget*& CreatedWidget, 0256 bool Testing); 0257 0258 /** 0259 * Restores a splitter. 0260 * \see restoreChildNodes() for details 0261 */ 0262 bool restoreSplitter(CDockingStateReader& Stream, QWidget*& CreatedWidget, 0263 bool Testing); 0264 0265 /** 0266 * Restores a dock area. 0267 * \see restoreChildNodes() for details 0268 */ 0269 bool restoreDockArea(CDockingStateReader& Stream, QWidget*& CreatedWidget, 0270 bool Testing); 0271 0272 /** 0273 * Restores a auto hide side bar 0274 */ 0275 bool restoreSideBar(CDockingStateReader& Stream, QWidget*& CreatedWidget, 0276 bool Testing); 0277 0278 /** 0279 * Helper function for recursive dumping of layout 0280 */ 0281 void dumpRecursive(int level, QWidget* widget); 0282 0283 /** 0284 * Calculate the drop mode from the given target position 0285 */ 0286 eDropMode getDropMode(const QPoint& TargetPos); 0287 0288 /** 0289 * Initializes the visible dock area count variable if it is not initialized 0290 * yet 0291 */ 0292 void initVisibleDockAreaCount() 0293 { 0294 if (VisibleDockAreaCount > -1) 0295 { 0296 return; 0297 } 0298 0299 VisibleDockAreaCount = 0; 0300 for (auto DockArea : DockAreas) 0301 { 0302 if (!DockArea) 0303 { 0304 continue; 0305 } 0306 VisibleDockAreaCount += (DockArea->isHidden() ? 0 : 1); 0307 } 0308 } 0309 0310 /** 0311 * Access function for the visible dock area counter 0312 */ 0313 int& visibleDockAreaCount() 0314 { 0315 // Lazy initialisation - we initialize the VisibleDockAreaCount variable 0316 // on first use 0317 initVisibleDockAreaCount(); 0318 return VisibleDockAreaCount; 0319 } 0320 0321 /** 0322 * The visible dock area count changes, if dock areas are remove, added or 0323 * when its view is toggled 0324 */ 0325 void onVisibleDockAreaCountChanged(); 0326 0327 void emitDockAreasRemoved() 0328 { 0329 onVisibleDockAreaCountChanged(); 0330 Q_EMIT _this->dockAreasRemoved(); 0331 } 0332 0333 void emitDockAreasAdded() 0334 { 0335 onVisibleDockAreaCountChanged(); 0336 Q_EMIT _this->dockAreasAdded(); 0337 } 0338 0339 /** 0340 * Helper function for creation of new splitter 0341 */ 0342 CDockSplitter* newSplitter(Qt::Orientation orientation, QWidget* parent = nullptr) 0343 { 0344 CDockSplitter* s = new CDockSplitter(orientation, parent); 0345 s->setOpaqueResize(CDockManager::testConfigFlag(CDockManager::OpaqueSplitterResize)); 0346 s->setChildrenCollapsible(false); 0347 return s; 0348 } 0349 0350 /** 0351 * Ensures equal distribution of the sizes of a splitter if an dock widget 0352 * is inserted from code 0353 */ 0354 void adjustSplitterSizesOnInsertion(QSplitter* Splitter, qreal LastRatio = 1.0) 0355 { 0356 int AreaSize = (Splitter->orientation() == Qt::Horizontal) ? Splitter->width() : Splitter->height(); 0357 auto SplitterSizes = Splitter->sizes(); 0358 0359 qreal TotRatio = SplitterSizes.size() - 1.0 + LastRatio; 0360 for(int i = 0; i < SplitterSizes.size() -1; i++) 0361 { 0362 SplitterSizes[i] = AreaSize / TotRatio; 0363 } 0364 SplitterSizes.back() = AreaSize * LastRatio / TotRatio; 0365 Splitter->setSizes(SplitterSizes); 0366 } 0367 0368 /** 0369 * This function forces the dock container widget to update handles of splitters 0370 * based if a central widget exists. 0371 */ 0372 void updateSplitterHandles(QSplitter* splitter); 0373 0374 /** 0375 * If no central widget exists, the widgets resize with the container. 0376 * If a central widget exists, the widgets surrounding the central widget 0377 * do not resize its height or width. 0378 */ 0379 bool widgetResizesWithContainer(QWidget* widget); 0380 0381 // private slots: ------------------------------------------------------------ 0382 void onDockAreaViewToggled(bool Visible) 0383 { 0384 CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(_this->sender()); 0385 VisibleDockAreaCount += Visible ? 1 : -1; 0386 onVisibleDockAreaCountChanged(); 0387 Q_EMIT _this->dockAreaViewToggled(DockArea, Visible); 0388 } 0389 }; // struct DockContainerWidgetPrivate 0390 0391 0392 //============================================================================ 0393 DockContainerWidgetPrivate::DockContainerWidgetPrivate(CDockContainerWidget* _public) : 0394 _this(_public) 0395 { 0396 std::fill(std::begin(LastAddedAreaCache),std::end(LastAddedAreaCache), nullptr); 0397 DelayedAutoHideTimer.setSingleShot(true); 0398 DelayedAutoHideTimer.setInterval(500); 0399 QObject::connect(&DelayedAutoHideTimer, &QTimer::timeout, [this](){ 0400 auto GlobalPos = DelayedAutoHideTab->mapToGlobal(QPoint(0, 0)); 0401 qApp->sendEvent(DelayedAutoHideTab, new QMouseEvent(QEvent::MouseButtonPress, 0402 QPoint(0, 0), GlobalPos, Qt::LeftButton, {Qt::LeftButton}, Qt::NoModifier)); 0403 }); 0404 } 0405 0406 0407 //============================================================================ 0408 eDropMode DockContainerWidgetPrivate::getDropMode(const QPoint& TargetPos) 0409 { 0410 CDockAreaWidget* DockArea = _this->dockAreaAt(TargetPos); 0411 auto dropArea = InvalidDockWidgetArea; 0412 auto ContainerDropArea = DockManager->containerOverlay()->dropAreaUnderCursor(); 0413 0414 if (DockArea) 0415 { 0416 auto dropOverlay = DockManager->dockAreaOverlay(); 0417 dropOverlay->setAllowedAreas(DockArea->allowedAreas()); 0418 dropArea = dropOverlay->showOverlay(DockArea); 0419 if (ContainerDropArea != InvalidDockWidgetArea && 0420 ContainerDropArea != dropArea) 0421 { 0422 dropArea = InvalidDockWidgetArea; 0423 } 0424 0425 if (dropArea != InvalidDockWidgetArea) 0426 { 0427 ADS_PRINT("Dock Area Drop Content: " << dropArea); 0428 return DropModeIntoArea; 0429 } 0430 } 0431 0432 // mouse is over container 0433 if (InvalidDockWidgetArea == dropArea) 0434 { 0435 dropArea = ContainerDropArea; 0436 ADS_PRINT("Container Drop Content: " << dropArea); 0437 if (dropArea != InvalidDockWidgetArea) 0438 { 0439 return DropModeIntoContainer; 0440 } 0441 } 0442 0443 return DropModeInvalid; 0444 } 0445 0446 0447 //============================================================================ 0448 void DockContainerWidgetPrivate::onVisibleDockAreaCountChanged() 0449 { 0450 auto TopLevelDockArea = _this->topLevelDockArea(); 0451 0452 if (TopLevelDockArea) 0453 { 0454 this->TopLevelDockArea = TopLevelDockArea; 0455 TopLevelDockArea->updateTitleBarButtonVisibility(true); 0456 } 0457 else if (this->TopLevelDockArea) 0458 { 0459 this->TopLevelDockArea->updateTitleBarButtonVisibility(false); 0460 this->TopLevelDockArea = nullptr; 0461 } 0462 } 0463 0464 0465 //============================================================================ 0466 void DockContainerWidgetPrivate::dropIntoContainer(CFloatingDockContainer* FloatingWidget, 0467 DockWidgetArea area) 0468 { 0469 auto InsertParam = internal::dockAreaInsertParameters(area); 0470 CDockContainerWidget* FloatingDockContainer = FloatingWidget->dockContainer(); 0471 auto NewDockAreas = FloatingDockContainer->findChildren<CDockAreaWidget*>( 0472 QString(), Qt::FindChildrenRecursively); 0473 auto Splitter = RootSplitter; 0474 0475 if (DockAreas.count() <= 1) 0476 { 0477 Splitter->setOrientation(InsertParam.orientation()); 0478 } 0479 else if (Splitter->orientation() != InsertParam.orientation()) 0480 { 0481 auto NewSplitter = newSplitter(InsertParam.orientation()); 0482 QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter); 0483 NewSplitter->addWidget(Splitter); 0484 updateSplitterHandles(NewSplitter); 0485 Splitter = NewSplitter; 0486 delete li; 0487 } 0488 0489 // Now we can insert the floating widget content into this container 0490 auto FloatingSplitter = FloatingDockContainer->rootSplitter(); 0491 if (FloatingSplitter->count() == 1) 0492 { 0493 insertWidgetIntoSplitter(Splitter, FloatingSplitter->widget(0), InsertParam.append()); 0494 updateSplitterHandles(Splitter); 0495 } 0496 else if (FloatingSplitter->orientation() == InsertParam.orientation()) 0497 { 0498 int InsertIndex = InsertParam.append() ? Splitter->count() : 0; 0499 while (FloatingSplitter->count()) 0500 { 0501 Splitter->insertWidget(InsertIndex++, FloatingSplitter->widget(0)); 0502 updateSplitterHandles(Splitter); 0503 } 0504 } 0505 else 0506 { 0507 insertWidgetIntoSplitter(Splitter, FloatingSplitter, InsertParam.append()); 0508 } 0509 0510 RootSplitter = Splitter; 0511 addDockAreasToList(NewDockAreas); 0512 0513 // If we dropped the floating widget into the main dock container that does 0514 // not contain any dock widgets, then splitter is invisible and we need to 0515 // show it to display the docked widgets 0516 if (!Splitter->isVisible()) 0517 { 0518 Splitter->show(); 0519 } 0520 _this->dumpLayout(); 0521 } 0522 0523 0524 //============================================================================ 0525 void DockContainerWidgetPrivate::dropIntoAutoHideSideBar(CFloatingDockContainer* FloatingWidget, DockWidgetArea area) 0526 { 0527 auto SideBarLocation = internal::toSideBarLocation(area); 0528 auto NewDockAreas = FloatingWidget->findChildren<CDockAreaWidget*>( 0529 QString(), Qt::FindChildrenRecursively); 0530 int TabIndex = DockManager->containerOverlay()->tabIndexUnderCursor(); 0531 for (auto DockArea : NewDockAreas) 0532 { 0533 auto DockWidgets = DockArea->dockWidgets(); 0534 for (auto DockWidget : DockWidgets) 0535 { 0536 _this->createAndSetupAutoHideContainer(SideBarLocation, DockWidget, TabIndex++); 0537 } 0538 } 0539 } 0540 0541 0542 //============================================================================ 0543 void DockContainerWidgetPrivate::dropIntoCenterOfSection( 0544 CFloatingDockContainer* FloatingWidget, CDockAreaWidget* TargetArea, int TabIndex) 0545 { 0546 CDockContainerWidget* FloatingContainer = FloatingWidget->dockContainer(); 0547 auto NewDockWidgets = FloatingContainer->dockWidgets(); 0548 auto TopLevelDockArea = FloatingContainer->topLevelDockArea(); 0549 int NewCurrentIndex = -1; 0550 TabIndex = qMax(0, TabIndex); 0551 0552 // If the floating widget contains only one single dock are, then the 0553 // current dock widget of the dock area will also be the future current 0554 // dock widget in the drop area. 0555 if (TopLevelDockArea) 0556 { 0557 NewCurrentIndex = TopLevelDockArea->currentIndex(); 0558 } 0559 0560 for (int i = 0; i < NewDockWidgets.count(); ++i) 0561 { 0562 CDockWidget* DockWidget = NewDockWidgets[i]; 0563 TargetArea->insertDockWidget(TabIndex + i, DockWidget, false); 0564 // If the floating widget contains multiple visible dock areas, then we 0565 // simply pick the first visible open dock widget and make it 0566 // the current one. 0567 if (NewCurrentIndex < 0 && !DockWidget->isClosed()) 0568 { 0569 NewCurrentIndex = i; 0570 } 0571 } 0572 TargetArea->setCurrentIndex(NewCurrentIndex + TabIndex); 0573 TargetArea->updateTitleBarVisibility(); 0574 return; 0575 } 0576 0577 0578 //============================================================================ 0579 void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* FloatingWidget, 0580 CDockAreaWidget* TargetArea, DockWidgetArea area, int TabIndex) 0581 { 0582 // Dropping into center means all dock widgets in the dropped floating 0583 // widget will become tabs of the drop area 0584 if (CenterDockWidgetArea == area) 0585 { 0586 dropIntoCenterOfSection(FloatingWidget, TargetArea, TabIndex); 0587 return; 0588 } 0589 0590 CDockContainerWidget* FloatingContainer = FloatingWidget->dockContainer(); 0591 auto InsertParam = internal::dockAreaInsertParameters(area); 0592 auto NewDockAreas = FloatingContainer->findChildren<CDockAreaWidget*>( 0593 QString(), Qt::FindChildrenRecursively); 0594 auto TargetAreaSplitter = TargetArea->parentSplitter(); 0595 int AreaIndex = TargetAreaSplitter->indexOf(TargetArea); 0596 auto FloatingSplitter = FloatingContainer->rootSplitter(); 0597 if (TargetAreaSplitter->orientation() == InsertParam.orientation()) 0598 { 0599 auto Sizes = TargetAreaSplitter->sizes(); 0600 int TargetAreaSize = (InsertParam.orientation() == Qt::Horizontal) ? TargetArea->width() : TargetArea->height(); 0601 bool AdjustSplitterSizes = true; 0602 if ((FloatingSplitter->orientation() != InsertParam.orientation()) && FloatingSplitter->count() > 1) 0603 { 0604 TargetAreaSplitter->insertWidget(AreaIndex + InsertParam.insertOffset(), FloatingSplitter); 0605 updateSplitterHandles(TargetAreaSplitter); 0606 } 0607 else 0608 { 0609 AdjustSplitterSizes = (FloatingSplitter->count() == 1); 0610 int InsertIndex = AreaIndex + InsertParam.insertOffset(); 0611 while (FloatingSplitter->count()) 0612 { 0613 TargetAreaSplitter->insertWidget(InsertIndex++, FloatingSplitter->widget(0)); 0614 updateSplitterHandles(TargetAreaSplitter); 0615 } 0616 } 0617 0618 if (AdjustSplitterSizes) 0619 { 0620 int Size = (TargetAreaSize - TargetAreaSplitter->handleWidth()) / 2; 0621 Sizes[AreaIndex] = Size; 0622 Sizes.insert(AreaIndex, Size); 0623 TargetAreaSplitter->setSizes(Sizes); 0624 } 0625 } 0626 else 0627 { 0628 QSplitter* NewSplitter = newSplitter(InsertParam.orientation()); 0629 int TargetAreaSize = (InsertParam.orientation() == Qt::Horizontal) ? TargetArea->width() : TargetArea->height(); 0630 bool AdjustSplitterSizes = true; 0631 if ((FloatingSplitter->orientation() != InsertParam.orientation()) && FloatingSplitter->count() > 1) 0632 { 0633 NewSplitter->addWidget(FloatingSplitter); 0634 updateSplitterHandles(NewSplitter); 0635 } 0636 else 0637 { 0638 AdjustSplitterSizes = (FloatingSplitter->count() == 1); 0639 while (FloatingSplitter->count()) 0640 { 0641 NewSplitter->addWidget(FloatingSplitter->widget(0)); 0642 updateSplitterHandles(NewSplitter); 0643 } 0644 } 0645 0646 // Save the sizes before insertion and restore it later to prevent 0647 // shrinking of existing area 0648 auto Sizes = TargetAreaSplitter->sizes(); 0649 insertWidgetIntoSplitter(NewSplitter, TargetArea, !InsertParam.append()); 0650 updateSplitterHandles(NewSplitter); 0651 if (AdjustSplitterSizes) 0652 { 0653 int Size = TargetAreaSize / 2; 0654 NewSplitter->setSizes({Size, Size}); 0655 } 0656 TargetAreaSplitter->insertWidget(AreaIndex, NewSplitter); 0657 TargetAreaSplitter->setSizes(Sizes); 0658 updateSplitterHandles(TargetAreaSplitter); 0659 } 0660 0661 addDockAreasToList(NewDockAreas); 0662 _this->dumpLayout(); 0663 } 0664 0665 0666 //============================================================================ 0667 void DockContainerWidgetPrivate::moveIntoCenterOfSection(QWidget* Widget, CDockAreaWidget* TargetArea, 0668 int TabIndex) 0669 { 0670 auto DroppedDockWidget = qobject_cast<CDockWidget*>(Widget); 0671 auto DroppedArea = qobject_cast<CDockAreaWidget*>(Widget); 0672 0673 TabIndex = qMax(0, TabIndex); 0674 if (DroppedDockWidget) 0675 { 0676 CDockAreaWidget* OldDockArea = DroppedDockWidget->dockAreaWidget(); 0677 if (OldDockArea == TargetArea) 0678 { 0679 return; 0680 } 0681 0682 if (OldDockArea) 0683 { 0684 OldDockArea->removeDockWidget(DroppedDockWidget); 0685 } 0686 TargetArea->insertDockWidget(TabIndex, DroppedDockWidget, true); 0687 } 0688 else 0689 { 0690 QList<CDockWidget*> NewDockWidgets = DroppedArea->dockWidgets(); 0691 int NewCurrentIndex = DroppedArea->currentIndex(); 0692 for (int i = 0; i < NewDockWidgets.count(); ++i) 0693 { 0694 CDockWidget* DockWidget = NewDockWidgets[i]; 0695 TargetArea->insertDockWidget(TabIndex + i, DockWidget, false); 0696 } 0697 TargetArea->setCurrentIndex(TabIndex + NewCurrentIndex); 0698 DroppedArea->dockContainer()->removeDockArea(DroppedArea); 0699 DroppedArea->deleteLater(); 0700 } 0701 0702 TargetArea->updateTitleBarVisibility(); 0703 return; 0704 } 0705 0706 0707 //============================================================================ 0708 void DockContainerWidgetPrivate::moveToNewSection(QWidget* Widget, CDockAreaWidget* TargetArea, DockWidgetArea area, 0709 int TabIndex) 0710 { 0711 // Dropping into center means all dock widgets in the dropped floating 0712 // widget will become tabs of the drop area 0713 if (CenterDockWidgetArea == area) 0714 { 0715 moveIntoCenterOfSection(Widget, TargetArea, TabIndex); 0716 return; 0717 } 0718 0719 0720 CDockWidget* DroppedDockWidget = qobject_cast<CDockWidget*>(Widget); 0721 CDockAreaWidget* DroppedDockArea = qobject_cast<CDockAreaWidget*>(Widget); 0722 CDockAreaWidget* NewDockArea; 0723 if (DroppedDockWidget) 0724 { 0725 NewDockArea = new CDockAreaWidget(DockManager, _this); 0726 CDockAreaWidget* OldDockArea = DroppedDockWidget->dockAreaWidget(); 0727 if (OldDockArea) 0728 { 0729 OldDockArea->removeDockWidget(DroppedDockWidget); 0730 } 0731 NewDockArea->addDockWidget(DroppedDockWidget); 0732 } 0733 else 0734 { 0735 DroppedDockArea->dockContainer()->removeDockArea(DroppedDockArea); 0736 NewDockArea = DroppedDockArea; 0737 } 0738 0739 auto InsertParam = internal::dockAreaInsertParameters(area); 0740 auto TargetAreaSplitter = TargetArea->parentSplitter(); 0741 int AreaIndex = TargetAreaSplitter->indexOf(TargetArea); 0742 auto Sizes = TargetAreaSplitter->sizes(); 0743 if (TargetAreaSplitter->orientation() == InsertParam.orientation()) 0744 { 0745 int TargetAreaSize = (InsertParam.orientation() == Qt::Horizontal) ? TargetArea->width() : TargetArea->height(); 0746 TargetAreaSplitter->insertWidget(AreaIndex + InsertParam.insertOffset(), NewDockArea); 0747 updateSplitterHandles(TargetAreaSplitter); 0748 int Size = (TargetAreaSize - TargetAreaSplitter->handleWidth()) / 2; 0749 Sizes[AreaIndex] = Size; 0750 Sizes.insert(AreaIndex, Size); 0751 } 0752 else 0753 { 0754 int TargetAreaSize = (InsertParam.orientation() == Qt::Horizontal) ? TargetArea->width() : TargetArea->height(); 0755 QSplitter* NewSplitter = newSplitter(InsertParam.orientation()); 0756 NewSplitter->addWidget(TargetArea); 0757 insertWidgetIntoSplitter(NewSplitter, NewDockArea, InsertParam.append()); 0758 updateSplitterHandles(NewSplitter); 0759 int Size = TargetAreaSize / 2; 0760 NewSplitter->setSizes({Size, Size}); 0761 TargetAreaSplitter->insertWidget(AreaIndex, NewSplitter); 0762 updateSplitterHandles(TargetAreaSplitter); 0763 } 0764 TargetAreaSplitter->setSizes(Sizes); 0765 0766 addDockAreasToList({NewDockArea}); 0767 } 0768 0769 0770 //============================================================================ 0771 void DockContainerWidgetPrivate::moveToAutoHideSideBar(QWidget* Widget, DockWidgetArea area, int TabIndex) 0772 { 0773 CDockWidget* DroppedDockWidget = qobject_cast<CDockWidget*>(Widget); 0774 CDockAreaWidget* DroppedDockArea = qobject_cast<CDockAreaWidget*>(Widget); 0775 auto SideBarLocation = internal::toSideBarLocation(area); 0776 0777 if (DroppedDockWidget) 0778 { 0779 if (_this == DroppedDockWidget->dockContainer()) 0780 { 0781 DroppedDockWidget->setAutoHide(true, SideBarLocation, TabIndex); 0782 } 0783 else 0784 { 0785 _this->createAndSetupAutoHideContainer(SideBarLocation, DroppedDockWidget, TabIndex); 0786 } 0787 } 0788 else 0789 { 0790 if (_this == DroppedDockArea->dockContainer()) 0791 { 0792 DroppedDockArea->setAutoHide(true, SideBarLocation, TabIndex); 0793 } 0794 else 0795 { 0796 for (const auto DockWidget : DroppedDockArea->openedDockWidgets()) 0797 { 0798 if (!DockWidget->features().testFlag( 0799 CDockWidget::DockWidgetPinnable)) 0800 { 0801 continue; 0802 } 0803 0804 _this->createAndSetupAutoHideContainer(SideBarLocation, 0805 DockWidget, TabIndex++); 0806 } 0807 } 0808 } 0809 } 0810 0811 0812 //============================================================================ 0813 void DockContainerWidgetPrivate::updateSplitterHandles( QSplitter* splitter ) 0814 { 0815 if (!DockManager->centralWidget() || !splitter) 0816 { 0817 return; 0818 } 0819 0820 for (int i = 0; i < splitter->count(); ++i) 0821 { 0822 splitter->setStretchFactor(i, widgetResizesWithContainer(splitter->widget(i)) ? 1 : 0); 0823 } 0824 } 0825 0826 0827 //============================================================================ 0828 bool DockContainerWidgetPrivate::widgetResizesWithContainer(QWidget* widget) 0829 { 0830 if (!DockManager->centralWidget()) 0831 { 0832 return true; 0833 } 0834 0835 auto Area = qobject_cast<CDockAreaWidget*>(widget); 0836 if(Area) 0837 { 0838 return Area->isCentralWidgetArea(); 0839 } 0840 0841 auto innerSplitter = qobject_cast<CDockSplitter*>(widget); 0842 if (innerSplitter) 0843 { 0844 return innerSplitter->isResizingWithContainer(); 0845 } 0846 0847 return false; 0848 } 0849 0850 0851 0852 //============================================================================ 0853 void DockContainerWidgetPrivate::moveToContainer(QWidget* Widget, DockWidgetArea area) 0854 { 0855 CDockWidget* DroppedDockWidget = qobject_cast<CDockWidget*>(Widget); 0856 CDockAreaWidget* DroppedDockArea = qobject_cast<CDockAreaWidget*>(Widget); 0857 CDockAreaWidget* NewDockArea; 0858 0859 if (DroppedDockWidget) 0860 { 0861 NewDockArea = new CDockAreaWidget(DockManager, _this); 0862 CDockAreaWidget* OldDockArea = DroppedDockWidget->dockAreaWidget(); 0863 if (OldDockArea) 0864 { 0865 OldDockArea->removeDockWidget(DroppedDockWidget); 0866 } 0867 NewDockArea->addDockWidget(DroppedDockWidget); 0868 } 0869 else 0870 { 0871 // We check, if we insert the dropped widget into the same place that 0872 // it already has and do nothing, if it is the same place. It would 0873 // also work without this check, but it looks nicer with the check 0874 // because there will be no layout updates 0875 auto Splitter = DroppedDockArea->parentSplitter(); 0876 auto InsertParam = internal::dockAreaInsertParameters(area); 0877 if (Splitter == RootSplitter && InsertParam.orientation() == Splitter->orientation()) 0878 { 0879 if (InsertParam.append() && Splitter->lastWidget() == DroppedDockArea) 0880 { 0881 return; 0882 } 0883 else if (!InsertParam.append() && Splitter->firstWidget() == DroppedDockArea) 0884 { 0885 return; 0886 } 0887 } 0888 DroppedDockArea->dockContainer()->removeDockArea(DroppedDockArea); 0889 NewDockArea = DroppedDockArea; 0890 } 0891 0892 addDockArea(NewDockArea, area); 0893 LastAddedAreaCache[areaIdToIndex(area)] = NewDockArea; 0894 } 0895 0896 0897 //============================================================================ 0898 void DockContainerWidgetPrivate::addDockAreasToList(const QList<CDockAreaWidget*> NewDockAreas) 0899 { 0900 int CountBefore = DockAreas.count(); 0901 int NewAreaCount = NewDockAreas.count(); 0902 appendDockAreas(NewDockAreas); 0903 // If the user dropped a floating widget that contains only one single 0904 // visible dock area, then its title bar button TitleBarButtonUndock is 0905 // likely hidden. We need to ensure, that it is visible 0906 for (auto DockArea : NewDockAreas) 0907 { 0908 DockArea->titleBarButton(TitleBarButtonClose)->setVisible(true); 0909 DockArea->titleBarButton(TitleBarButtonAutoHide)->setVisible(true); 0910 } 0911 0912 // We need to ensure, that the dock area title bar is visible. The title bar 0913 // is invisible, if the dock are is a single dock area in a floating widget. 0914 if (1 == CountBefore) 0915 { 0916 DockAreas.at(0)->updateTitleBarVisibility(); 0917 } 0918 0919 if (1 == NewAreaCount) 0920 { 0921 DockAreas.last()->updateTitleBarVisibility(); 0922 } 0923 0924 emitDockAreasAdded(); 0925 } 0926 0927 0928 //============================================================================ 0929 void DockContainerWidgetPrivate::appendDockAreas(const QList<CDockAreaWidget*> NewDockAreas) 0930 { 0931 for (auto *newDockArea : NewDockAreas) 0932 { 0933 DockAreas.append(newDockArea); 0934 } 0935 for (auto DockArea : NewDockAreas) 0936 { 0937 QObject::connect(DockArea, 0938 &CDockAreaWidget::viewToggled, 0939 _this, 0940 std::bind(&DockContainerWidgetPrivate::onDockAreaViewToggled, this, std::placeholders::_1)); 0941 } 0942 } 0943 0944 0945 //============================================================================ 0946 void DockContainerWidgetPrivate::saveChildNodesState(QXmlStreamWriter& s, QWidget* Widget) 0947 { 0948 QSplitter* Splitter = qobject_cast<QSplitter*>(Widget); 0949 if (Splitter) 0950 { 0951 s.writeStartElement("Splitter"); 0952 s.writeAttribute("Orientation", (Splitter->orientation() == Qt::Horizontal) ? "|" : "-"); 0953 s.writeAttribute("Count", QString::number(Splitter->count())); 0954 ADS_PRINT("NodeSplitter orient: " << Splitter->orientation() 0955 << " WidgetCont: " << Splitter->count()); 0956 for (int i = 0; i < Splitter->count(); ++i) 0957 { 0958 saveChildNodesState(s, Splitter->widget(i)); 0959 } 0960 0961 s.writeStartElement("Sizes"); 0962 for (auto Size : Splitter->sizes()) 0963 { 0964 s.writeCharacters(QString::number(Size) + QStringLiteral(" ")); 0965 } 0966 s.writeEndElement(); 0967 s.writeEndElement(); 0968 } 0969 else 0970 { 0971 CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(Widget); 0972 if (DockArea) 0973 { 0974 DockArea->saveState(s); 0975 } 0976 } 0977 } 0978 0979 0980 //============================================================================ 0981 void DockContainerWidgetPrivate::saveAutoHideWidgetsState(QXmlStreamWriter& s) 0982 { 0983 for (const auto sideTabBar : SideTabBarWidgets.values()) 0984 { 0985 if (!sideTabBar->count()) 0986 { 0987 continue; 0988 } 0989 0990 sideTabBar->saveState(s); 0991 } 0992 } 0993 0994 0995 //============================================================================ 0996 bool DockContainerWidgetPrivate::restoreSplitter(CDockingStateReader& s, 0997 QWidget*& CreatedWidget, bool Testing) 0998 { 0999 bool Ok; 1000 QString OrientationStr = s.attributes().value("Orientation").toString(); 1001 1002 // Check if the orientation string is right 1003 if (!OrientationStr.startsWith(QStringLiteral("|")) && !OrientationStr.startsWith(QStringLiteral("-"))) 1004 { 1005 return false; 1006 } 1007 1008 // The "|" shall indicate a vertical splitter handle which in turn means 1009 // a Horizontal orientation of the splitter layout. 1010 bool HorizontalSplitter = OrientationStr.startsWith(QStringLiteral("|")); 1011 // In version 0 we had a small bug. The "|" indicated a vertical orientation, 1012 // but this is wrong, because only the splitter handle is vertical, the 1013 // layout of the splitter is a horizontal layout. We fix this here 1014 if (s.fileVersion() == 0) 1015 { 1016 HorizontalSplitter = !HorizontalSplitter; 1017 } 1018 1019 int Orientation = HorizontalSplitter ? Qt::Horizontal : Qt::Vertical; 1020 int WidgetCount = s.attributes().value("Count").toInt(&Ok); 1021 if (!Ok) 1022 { 1023 return false; 1024 } 1025 ADS_PRINT("Restore NodeSplitter Orientation: " << Orientation << 1026 " WidgetCount: " << WidgetCount); 1027 QSplitter* Splitter = nullptr; 1028 if (!Testing) 1029 { 1030 Splitter = newSplitter(static_cast<Qt::Orientation>(Orientation)); 1031 } 1032 bool Visible = false; 1033 QList<int> Sizes; 1034 while (s.readNextStartElement()) 1035 { 1036 QWidget* ChildNode = nullptr; 1037 bool Result = true; 1038 if (s.name() == QLatin1String("Splitter")) 1039 { 1040 Result = restoreSplitter(s, ChildNode, Testing); 1041 } 1042 else if (s.name() == QLatin1String("Area")) 1043 { 1044 Result = restoreDockArea(s, ChildNode, Testing); 1045 } 1046 else if (s.name() == QLatin1String("Sizes")) 1047 { 1048 QString sSizes = s.readElementText().trimmed(); 1049 ADS_PRINT("Sizes: " << sSizes); 1050 QTextStream TextStream(&sSizes); 1051 while (!TextStream.atEnd()) 1052 { 1053 int value; 1054 TextStream >> value; 1055 Sizes.append(value); 1056 } 1057 } 1058 else 1059 { 1060 s.skipCurrentElement(); 1061 } 1062 1063 if (!Result) 1064 { 1065 return false; 1066 } 1067 1068 if (Testing || !ChildNode) 1069 { 1070 continue; 1071 } 1072 1073 ADS_PRINT("ChildNode isVisible " << ChildNode->isVisible() 1074 << " isVisibleTo " << ChildNode->isVisibleTo(Splitter)); 1075 Splitter->addWidget(ChildNode); 1076 Visible |= ChildNode->isVisibleTo(Splitter); 1077 } 1078 if(!Testing) 1079 { 1080 updateSplitterHandles(Splitter); 1081 } 1082 1083 if (Sizes.count() != WidgetCount) 1084 { 1085 return false; 1086 } 1087 1088 if (!Testing) 1089 { 1090 if (!Splitter->count()) 1091 { 1092 delete Splitter; 1093 Splitter = nullptr; 1094 } 1095 else 1096 { 1097 Splitter->setSizes(Sizes); 1098 Splitter->setVisible(Visible); 1099 } 1100 CreatedWidget = Splitter; 1101 } 1102 else 1103 { 1104 CreatedWidget = nullptr; 1105 } 1106 1107 return true; 1108 } 1109 1110 1111 //============================================================================ 1112 bool DockContainerWidgetPrivate::restoreDockArea(CDockingStateReader& s, 1113 QWidget*& CreatedWidget, bool Testing) 1114 { 1115 CDockAreaWidget* DockArea = nullptr; 1116 auto Result = CDockAreaWidget::restoreState(s, DockArea, Testing, _this); 1117 if (Result && DockArea) 1118 { 1119 appendDockAreas({DockArea}); 1120 } 1121 CreatedWidget = DockArea; 1122 return Result; 1123 } 1124 1125 1126 //============================================================================ 1127 bool DockContainerWidgetPrivate::restoreSideBar(CDockingStateReader& s, 1128 QWidget*& CreatedWidget, bool Testing) 1129 { 1130 Q_UNUSED(CreatedWidget) 1131 // Simply ignore side bar auto hide widgets from saved state if 1132 // auto hide support is disabled 1133 if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled)) 1134 { 1135 return true; 1136 } 1137 1138 bool Ok; 1139 auto Area = (ads::SideBarLocation)s.attributes().value("Area").toInt(&Ok); 1140 if (!Ok) 1141 { 1142 return false; 1143 } 1144 1145 while (s.readNextStartElement()) 1146 { 1147 if (s.name() != QLatin1String("Widget")) 1148 { 1149 continue; 1150 } 1151 1152 auto Name = s.attributes().value("Name"); 1153 if (Name.isEmpty()) 1154 { 1155 return false; 1156 } 1157 1158 bool Ok; 1159 bool Closed = s.attributes().value("Closed").toInt(&Ok); 1160 if (!Ok) 1161 { 1162 return false; 1163 } 1164 1165 int Size = s.attributes().value("Size").toInt(&Ok); 1166 if (!Ok) 1167 { 1168 return false; 1169 } 1170 1171 s.skipCurrentElement(); 1172 CDockWidget* DockWidget = DockManager->findDockWidget(Name.toString()); 1173 if (!DockWidget || Testing) 1174 { 1175 continue; 1176 } 1177 1178 auto SideBar = _this->autoHideSideBar(Area); 1179 CAutoHideDockContainer* AutoHideContainer; 1180 if (DockWidget->isAutoHide()) 1181 { 1182 AutoHideContainer = DockWidget->autoHideDockContainer(); 1183 if (AutoHideContainer->autoHideSideBar() != SideBar) 1184 { 1185 SideBar->addAutoHideWidget(AutoHideContainer); 1186 } 1187 } 1188 else 1189 { 1190 AutoHideContainer = SideBar->insertDockWidget(-1, DockWidget); 1191 } 1192 AutoHideContainer->setSize(Size); 1193 DockWidget->setProperty(internal::ClosedProperty, Closed); 1194 DockWidget->setProperty(internal::DirtyProperty, false); 1195 } 1196 1197 return true; 1198 } 1199 1200 1201 //============================================================================ 1202 bool DockContainerWidgetPrivate::restoreChildNodes(CDockingStateReader& s, 1203 QWidget*& CreatedWidget, bool Testing) 1204 { 1205 bool Result = true; 1206 while (s.readNextStartElement()) 1207 { 1208 if (s.name() == QLatin1String("Splitter")) 1209 { 1210 Result = restoreSplitter(s, CreatedWidget, Testing); 1211 ADS_PRINT("Splitter"); 1212 } 1213 else if (s.name() == QLatin1String("Area")) 1214 { 1215 Result = restoreDockArea(s, CreatedWidget, Testing); 1216 ADS_PRINT("DockAreaWidget"); 1217 } 1218 else if (s.name() == QLatin1String("SideBar")) 1219 { 1220 Result = restoreSideBar(s, CreatedWidget, Testing); 1221 ADS_PRINT("SideBar"); 1222 } 1223 else 1224 { 1225 s.skipCurrentElement(); 1226 ADS_PRINT("Unknown element"); 1227 } 1228 } 1229 1230 return Result; 1231 } 1232 1233 1234 //============================================================================ 1235 CDockAreaWidget* DockContainerWidgetPrivate::addDockWidgetToContainer(DockWidgetArea area, 1236 CDockWidget* Dockwidget) 1237 { 1238 CDockAreaWidget* NewDockArea = new CDockAreaWidget(DockManager, _this); 1239 NewDockArea->addDockWidget(Dockwidget); 1240 addDockArea(NewDockArea, area); 1241 NewDockArea->updateTitleBarVisibility(); 1242 LastAddedAreaCache[areaIdToIndex(area)] = NewDockArea; 1243 return NewDockArea; 1244 } 1245 1246 1247 //============================================================================ 1248 void DockContainerWidgetPrivate::addDockArea(CDockAreaWidget* NewDockArea, DockWidgetArea area) 1249 { 1250 auto InsertParam = internal::dockAreaInsertParameters(area); 1251 // As long as we have only one dock area in the splitter we can adjust 1252 // its orientation 1253 if (DockAreas.count() <= 1) 1254 { 1255 RootSplitter->setOrientation(InsertParam.orientation()); 1256 } 1257 1258 QSplitter* Splitter = RootSplitter; 1259 if (Splitter->orientation() == InsertParam.orientation()) 1260 { 1261 insertWidgetIntoSplitter(Splitter, NewDockArea, InsertParam.append()); 1262 updateSplitterHandles(Splitter); 1263 if (Splitter->isHidden()) 1264 { 1265 Splitter->show(); 1266 } 1267 } 1268 else 1269 { 1270 auto NewSplitter = newSplitter(InsertParam.orientation()); 1271 if (InsertParam.append()) 1272 { 1273 QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter); 1274 NewSplitter->addWidget(Splitter); 1275 NewSplitter->addWidget(NewDockArea); 1276 updateSplitterHandles(NewSplitter); 1277 delete li; 1278 } 1279 else 1280 { 1281 NewSplitter->addWidget(NewDockArea); 1282 QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter); 1283 NewSplitter->addWidget(Splitter); 1284 updateSplitterHandles(NewSplitter); 1285 delete li; 1286 } 1287 RootSplitter = NewSplitter; 1288 } 1289 1290 addDockAreasToList({NewDockArea}); 1291 } 1292 1293 1294 //============================================================================ 1295 void DockContainerWidgetPrivate::dumpRecursive(int level, QWidget* widget) 1296 { 1297 #if defined(QT_DEBUG) 1298 QSplitter* Splitter = qobject_cast<QSplitter*>(widget); 1299 QByteArray buf; 1300 buf.fill(' ', level * 4); 1301 if (Splitter) 1302 { 1303 #ifdef ADS_DEBUG_PRINT 1304 qDebug("%sSplitter %s v: %s c: %s", 1305 (const char*)buf, 1306 (Splitter->orientation() == Qt::Vertical) ? "--" : "|", 1307 Splitter->isHidden() ? " " : "v", 1308 QString::number(Splitter->count()).toStdString().c_str()); 1309 std::cout << (const char*)buf << "Splitter " 1310 << ((Splitter->orientation() == Qt::Vertical) ? "--" : "|") << " " 1311 << (Splitter->isHidden() ? " " : "v") << " " 1312 << QString::number(Splitter->count()).toStdString() << std::endl; 1313 #endif 1314 for (int i = 0; i < Splitter->count(); ++i) 1315 { 1316 dumpRecursive(level + 1, Splitter->widget(i)); 1317 } 1318 } 1319 else 1320 { 1321 CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(widget); 1322 if (!DockArea) 1323 { 1324 return; 1325 } 1326 #ifdef ADS_DEBUG_PRINT 1327 qDebug("%sDockArea", (const char*)buf); 1328 std::cout << (const char*)buf 1329 << (DockArea->isHidden() ? " " : "v") 1330 << (DockArea->openDockWidgetsCount() > 0 ? " " : "c") 1331 << " DockArea " << "[hs: " << DockArea->sizePolicy().horizontalStretch() << ", vs: " << DockArea->sizePolicy().verticalStretch() << "]" << std::endl; 1332 buf.fill(' ', (level + 1) * 4); 1333 for (int i = 0; i < DockArea->dockWidgetsCount(); ++i) 1334 { 1335 std::cout << (const char*)buf << (i == DockArea->currentIndex() ? "*" : " "); 1336 CDockWidget* DockWidget = DockArea->dockWidget(i); 1337 std::cout << (DockWidget->isHidden() ? " " : "v"); 1338 std::cout << (DockWidget->isClosed() ? "c" : " ") << " "; 1339 std::cout << DockWidget->windowTitle().toStdString() << std::endl; 1340 } 1341 #endif 1342 } 1343 #else 1344 Q_UNUSED(level); 1345 Q_UNUSED(widget); 1346 #endif 1347 } 1348 1349 1350 //============================================================================ 1351 CDockAreaWidget* DockContainerWidgetPrivate::addDockWidgetToDockArea(DockWidgetArea area, 1352 CDockWidget* Dockwidget, CDockAreaWidget* TargetDockArea, int Index) 1353 { 1354 if (CenterDockWidgetArea == area) 1355 { 1356 TargetDockArea->insertDockWidget(Index, Dockwidget); 1357 TargetDockArea->updateTitleBarVisibility(); 1358 return TargetDockArea; 1359 } 1360 1361 CDockAreaWidget* NewDockArea = new CDockAreaWidget(DockManager, _this); 1362 NewDockArea->addDockWidget(Dockwidget); 1363 auto InsertParam = internal::dockAreaInsertParameters(area); 1364 1365 auto TargetAreaSplitter = TargetDockArea->parentSplitter(); 1366 int index = TargetAreaSplitter ->indexOf(TargetDockArea); 1367 if (TargetAreaSplitter->orientation() == InsertParam.orientation()) 1368 { 1369 ADS_PRINT("TargetAreaSplitter->orientation() == InsertParam.orientation()"); 1370 TargetAreaSplitter->insertWidget(index + InsertParam.insertOffset(), NewDockArea); 1371 updateSplitterHandles(TargetAreaSplitter); 1372 // do nothing, if flag is not enabled 1373 if (CDockManager::testConfigFlag(CDockManager::EqualSplitOnInsertion)) 1374 { 1375 adjustSplitterSizesOnInsertion(TargetAreaSplitter); 1376 } 1377 } 1378 else 1379 { 1380 ADS_PRINT("TargetAreaSplitter->orientation() != InsertParam.orientation()"); 1381 auto TargetAreaSizes = TargetAreaSplitter->sizes(); 1382 auto NewSplitter = newSplitter(InsertParam.orientation()); 1383 NewSplitter->addWidget(TargetDockArea); 1384 1385 insertWidgetIntoSplitter(NewSplitter, NewDockArea, InsertParam.append()); 1386 updateSplitterHandles(NewSplitter); 1387 TargetAreaSplitter->insertWidget(index, NewSplitter); 1388 updateSplitterHandles(TargetAreaSplitter); 1389 if (CDockManager::testConfigFlag(CDockManager::EqualSplitOnInsertion)) 1390 { 1391 TargetAreaSplitter->setSizes(TargetAreaSizes); 1392 adjustSplitterSizesOnInsertion(NewSplitter); 1393 } 1394 } 1395 1396 addDockAreasToList({NewDockArea}); 1397 return NewDockArea; 1398 } 1399 1400 1401 //============================================================================ 1402 CDockContainerWidget::CDockContainerWidget(CDockManager* DockManager, QWidget *parent) : 1403 QFrame(parent), 1404 d(new DockContainerWidgetPrivate(this)) 1405 { 1406 d->DockManager = DockManager; 1407 d->isFloating = floatingWidget() != nullptr; 1408 1409 d->Layout = new QGridLayout(); 1410 d->Layout->setContentsMargins(0, 0, 0, 0); 1411 d->Layout->setSpacing(0); 1412 d->Layout->setColumnStretch(1, 1); 1413 d->Layout->setRowStretch(1, 1); 1414 setLayout(d->Layout); 1415 1416 // The function d->newSplitter() accesses the config flags from dock 1417 // manager which in turn requires a properly constructed dock manager. 1418 // If this dock container is the dock manager, then it is not properly 1419 // constructed yet because this base class constructor is called before 1420 // the constructor of the DockManager private class 1421 if (DockManager != this) 1422 { 1423 d->DockManager->registerDockContainer(this); 1424 createRootSplitter(); 1425 createSideTabBarWidgets(); 1426 } 1427 } 1428 1429 1430 //============================================================================ 1431 CDockContainerWidget::~CDockContainerWidget() 1432 { 1433 if (d->DockManager) 1434 { 1435 d->DockManager->removeDockContainer(this); 1436 } 1437 1438 delete d; 1439 } 1440 1441 1442 //============================================================================ 1443 CDockAreaWidget* CDockContainerWidget::addDockWidget(DockWidgetArea area, CDockWidget* Dockwidget, 1444 CDockAreaWidget* DockAreaWidget, int Index) 1445 { 1446 auto TopLevelDockWidget = topLevelDockWidget(); 1447 CDockAreaWidget* OldDockArea = Dockwidget->dockAreaWidget(); 1448 if (OldDockArea) 1449 { 1450 OldDockArea->removeDockWidget(Dockwidget); 1451 } 1452 1453 Dockwidget->setDockManager(d->DockManager); 1454 CDockAreaWidget* DockArea; 1455 if (DockAreaWidget) 1456 { 1457 DockArea = d->addDockWidgetToDockArea(area, Dockwidget, DockAreaWidget, Index); 1458 } 1459 else 1460 { 1461 DockArea = d->addDockWidgetToContainer(area, Dockwidget); 1462 } 1463 1464 if (TopLevelDockWidget) 1465 { 1466 auto NewTopLevelDockWidget = topLevelDockWidget(); 1467 // If the container contained only one visible dock widget, the we need 1468 // to emit a top level event for this widget because it is not the one and 1469 // only visible docked widget anymore 1470 if (!NewTopLevelDockWidget) 1471 { 1472 CDockWidget::emitTopLevelEventForWidget(TopLevelDockWidget, false); 1473 } 1474 } 1475 return DockArea; 1476 } 1477 1478 1479 //============================================================================ 1480 CAutoHideDockContainer* CDockContainerWidget::createAndSetupAutoHideContainer( 1481 SideBarLocation area, CDockWidget* DockWidget, int TabIndex) 1482 { 1483 if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled)) 1484 { 1485 Q_ASSERT_X(false, "CDockContainerWidget::createAndInitializeDockWidgetOverlayContainer", 1486 "Requested area does not exist in config"); 1487 return nullptr; 1488 } 1489 if (d->DockManager != DockWidget->dockManager()) 1490 { 1491 DockWidget->setDockManager(d->DockManager); // Auto hide Dock Container needs a valid dock manager 1492 } 1493 1494 return autoHideSideBar(area)->insertDockWidget(TabIndex, DockWidget); 1495 } 1496 1497 1498 //============================================================================ 1499 void CDockContainerWidget::removeDockWidget(CDockWidget* Dockwidget) 1500 { 1501 CDockAreaWidget* Area = Dockwidget->dockAreaWidget(); 1502 if (Area) 1503 { 1504 Area->removeDockWidget(Dockwidget); 1505 } 1506 } 1507 1508 //============================================================================ 1509 unsigned int CDockContainerWidget::zOrderIndex() const 1510 { 1511 return d->zOrderIndex; 1512 } 1513 1514 1515 //============================================================================ 1516 bool CDockContainerWidget::isInFrontOf(CDockContainerWidget* Other) const 1517 { 1518 return this->zOrderIndex() > Other->zOrderIndex(); 1519 } 1520 1521 1522 //============================================================================ 1523 bool CDockContainerWidget::event(QEvent *e) 1524 { 1525 bool Result = QWidget::event(e); 1526 if (e->type() == QEvent::WindowActivate) 1527 { 1528 d->zOrderIndex = ++zOrderCounter; 1529 } 1530 else if (e->type() == QEvent::Show && !d->zOrderIndex) 1531 { 1532 d->zOrderIndex = ++zOrderCounter; 1533 } 1534 1535 return Result; 1536 } 1537 1538 1539 //============================================================================ 1540 QList<CAutoHideDockContainer*> CDockContainerWidget::autoHideWidgets() const 1541 { 1542 return d->AutoHideWidgets; 1543 } 1544 1545 1546 //============================================================================ 1547 void CDockContainerWidget::addDockArea(CDockAreaWidget* DockAreaWidget, 1548 DockWidgetArea area) 1549 { 1550 CDockContainerWidget* Container = DockAreaWidget->dockContainer(); 1551 if (Container && Container != this) 1552 { 1553 Container->removeDockArea(DockAreaWidget); 1554 } 1555 1556 d->addDockArea(DockAreaWidget, area); 1557 } 1558 1559 1560 //============================================================================ 1561 void CDockContainerWidget::removeDockArea(CDockAreaWidget* area) 1562 { 1563 ADS_PRINT("CDockContainerWidget::removeDockArea"); 1564 // If it is an auto hide area, then there is nothing much to do 1565 if (area->isAutoHide()) 1566 { 1567 area->setAutoHideDockContainer(nullptr); 1568 return; 1569 } 1570 1571 area->disconnect(this); 1572 d->DockAreas.removeAll(area); 1573 auto Splitter = area->parentSplitter(); 1574 1575 // Remove are from parent splitter and recursively hide tree of parent 1576 // splitters if it has no visible content 1577 area->setParent(nullptr); 1578 internal::hideEmptyParentSplitters(Splitter); 1579 1580 // Remove this area from cached areas 1581 auto p = std::find(std::begin(d->LastAddedAreaCache), std::end(d->LastAddedAreaCache), area); 1582 if (p != std::end(d->LastAddedAreaCache)) { 1583 *p = nullptr; 1584 } 1585 1586 // If splitter has more than 1 widgets, we are finished and can leave 1587 if (Splitter->count() > 1) 1588 { 1589 goto emitAndExit; 1590 } 1591 1592 // If this is the RootSplitter we need to remove empty splitters to 1593 // avoid too many empty splitters 1594 if (Splitter == d->RootSplitter) 1595 { 1596 ADS_PRINT("Removed from RootSplitter"); 1597 // If splitter is empty, we are finished 1598 if (!Splitter->count()) 1599 { 1600 Splitter->hide(); 1601 goto emitAndExit; 1602 } 1603 1604 QWidget* widget = Splitter->widget(0); 1605 auto ChildSplitter = qobject_cast<CDockSplitter*>(widget); 1606 // If the one and only content widget of the splitter is not a splitter 1607 // then we are finished 1608 if (!ChildSplitter) 1609 { 1610 goto emitAndExit; 1611 } 1612 1613 // We replace the superfluous RootSplitter with the ChildSplitter 1614 ChildSplitter->setParent(nullptr); 1615 QLayoutItem* li = d->Layout->replaceWidget(Splitter, ChildSplitter); 1616 d->RootSplitter = ChildSplitter; 1617 delete li; 1618 ADS_PRINT("RootSplitter replaced by child splitter"); 1619 } 1620 else if (Splitter->count() == 1) 1621 { 1622 ADS_PRINT("Replacing splitter with content"); 1623 QSplitter* ParentSplitter = internal::findParent<QSplitter*>(Splitter); 1624 auto Sizes = ParentSplitter->sizes(); 1625 QWidget* widget = Splitter->widget(0); 1626 widget->setParent(this); 1627 internal::replaceSplitterWidget(ParentSplitter, Splitter, widget); 1628 ParentSplitter->setSizes(Sizes); 1629 } 1630 1631 delete Splitter; 1632 Splitter = nullptr; 1633 1634 emitAndExit: 1635 updateSplitterHandles(Splitter); 1636 CDockWidget* TopLevelWidget = topLevelDockWidget(); 1637 1638 // Updated the title bar visibility of the dock widget if there is only 1639 // one single visible dock widget 1640 CDockWidget::emitTopLevelEventForWidget(TopLevelWidget, true); 1641 dumpLayout(); 1642 d->emitDockAreasRemoved(); 1643 } 1644 1645 1646 //============================================================================ 1647 CDockAreaWidget* CDockContainerWidget::dockAreaAt(const QPoint& GlobalPos) const 1648 { 1649 for (const auto& DockArea : d->DockAreas) 1650 { 1651 if (DockArea && DockArea->isVisible() && DockArea->rect().contains(DockArea->mapFromGlobal(GlobalPos))) 1652 { 1653 return DockArea; 1654 } 1655 } 1656 1657 return nullptr; 1658 } 1659 1660 1661 //============================================================================ 1662 CDockAreaWidget* CDockContainerWidget::dockArea(int Index) const 1663 { 1664 return (Index < dockAreaCount()) ? d->DockAreas[Index] : nullptr; 1665 } 1666 1667 1668 //============================================================================ 1669 bool CDockContainerWidget::isFloating() const 1670 { 1671 return d->isFloating; 1672 } 1673 1674 1675 //============================================================================ 1676 int CDockContainerWidget::dockAreaCount() const 1677 { 1678 return d->DockAreas.count(); 1679 } 1680 1681 1682 //============================================================================ 1683 int CDockContainerWidget::visibleDockAreaCount() const 1684 { 1685 int Result = 0; 1686 for (auto DockArea : d->DockAreas) 1687 { 1688 Result += (!DockArea || DockArea->isHidden()) ? 0 : 1; 1689 } 1690 1691 return Result; 1692 1693 // TODO Cache or precalculate this to speed it up because it is used during 1694 // movement of floating widget 1695 //return d->visibleDockAreaCount(); 1696 } 1697 1698 1699 //============================================================================ 1700 void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWidget, 1701 const QPoint& TargetPos) 1702 { 1703 ADS_PRINT("CDockContainerWidget::dropFloatingWidget"); 1704 CDockWidget* SingleDroppedDockWidget = FloatingWidget->topLevelDockWidget(); 1705 CDockWidget* SingleDockWidget = topLevelDockWidget(); 1706 auto dropArea = InvalidDockWidgetArea; 1707 auto ContainerDropArea = d->DockManager->containerOverlay()->dropAreaUnderCursor(); 1708 bool Dropped = false; 1709 1710 CDockAreaWidget* DockArea = dockAreaAt(TargetPos); 1711 // mouse is over dock area 1712 if (DockArea) 1713 { 1714 auto dropOverlay = d->DockManager->dockAreaOverlay(); 1715 dropOverlay->setAllowedAreas(DockArea->allowedAreas()); 1716 dropArea = dropOverlay->showOverlay(DockArea); 1717 if (ContainerDropArea != InvalidDockWidgetArea && 1718 ContainerDropArea != dropArea) 1719 { 1720 dropArea = InvalidDockWidgetArea; 1721 } 1722 1723 if (dropArea != InvalidDockWidgetArea) 1724 { 1725 ADS_PRINT("Dock Area Drop Content: " << dropArea); 1726 int TabIndex = d->DockManager->dockAreaOverlay()->tabIndexUnderCursor(); 1727 d->dropIntoSection(FloatingWidget, DockArea, dropArea, TabIndex); 1728 Dropped = true; 1729 } 1730 } 1731 1732 // mouse is over container or auto hide side bar 1733 if (InvalidDockWidgetArea == dropArea && InvalidDockWidgetArea != ContainerDropArea) 1734 { 1735 if (internal::isSideBarArea(ContainerDropArea)) 1736 { 1737 ADS_PRINT("Container Drop Content: " << ContainerDropArea); 1738 d->dropIntoAutoHideSideBar(FloatingWidget, ContainerDropArea); 1739 } 1740 else 1741 { 1742 ADS_PRINT("Container Drop Content: " << ContainerDropArea); 1743 d->dropIntoContainer(FloatingWidget, ContainerDropArea); 1744 } 1745 Dropped = true; 1746 } 1747 1748 // Remove the auto hide widgets from the FloatingWidget and insert 1749 // them into this widget 1750 for (auto AutohideWidget : FloatingWidget->dockContainer()->autoHideWidgets()) 1751 { 1752 auto SideBar = autoHideSideBar(AutohideWidget->sideBarLocation()); 1753 SideBar->addAutoHideWidget(AutohideWidget); 1754 } 1755 1756 if (Dropped) 1757 { 1758 // Fix https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/351 1759 FloatingWidget->hideAndDeleteLater(); 1760 1761 // If we dropped a floating widget with only one single dock widget, then we 1762 // drop a top level widget that changes from floating to docked now 1763 CDockWidget::emitTopLevelEventForWidget(SingleDroppedDockWidget, false); 1764 1765 // If there was a top level widget before the drop, then it is not top 1766 // level widget anymore 1767 CDockWidget::emitTopLevelEventForWidget(SingleDockWidget, false); 1768 } 1769 1770 window()->activateWindow(); 1771 if (SingleDroppedDockWidget) 1772 { 1773 d->DockManager->notifyWidgetOrAreaRelocation(SingleDroppedDockWidget); 1774 } 1775 d->DockManager->notifyFloatingWidgetDrop(FloatingWidget); 1776 } 1777 1778 1779 //============================================================================ 1780 void CDockContainerWidget::dropWidget(QWidget* Widget, DockWidgetArea DropArea, CDockAreaWidget* TargetAreaWidget, 1781 int TabIndex) 1782 { 1783 CDockWidget* SingleDockWidget = topLevelDockWidget(); 1784 if (TargetAreaWidget) 1785 { 1786 d->moveToNewSection(Widget, TargetAreaWidget, DropArea, TabIndex); 1787 } 1788 else if (internal::isSideBarArea(DropArea)) 1789 { 1790 d->moveToAutoHideSideBar(Widget, DropArea, TabIndex); 1791 } 1792 else 1793 { 1794 d->moveToContainer(Widget, DropArea); 1795 } 1796 1797 // If there was a top level widget before the drop, then it is not top 1798 // level widget anymore 1799 CDockWidget::emitTopLevelEventForWidget(SingleDockWidget, false); 1800 1801 window()->activateWindow(); 1802 d->DockManager->notifyWidgetOrAreaRelocation(Widget); 1803 } 1804 1805 1806 //============================================================================ 1807 QList<CDockAreaWidget*> CDockContainerWidget::openedDockAreas() const 1808 { 1809 QList<CDockAreaWidget*> Result; 1810 for (auto DockArea : d->DockAreas) 1811 { 1812 if (DockArea && !DockArea->isHidden()) 1813 { 1814 Result.append(DockArea); 1815 } 1816 } 1817 1818 return Result; 1819 } 1820 1821 1822 //============================================================================ 1823 QList<CDockWidget*> CDockContainerWidget::openedDockWidgets() const 1824 { 1825 QList<CDockWidget*> DockWidgetList; 1826 for (auto DockArea : d->DockAreas) 1827 { 1828 if (DockArea && !DockArea->isHidden()) 1829 { 1830 DockWidgetList.append(DockArea->openedDockWidgets()); 1831 } 1832 } 1833 1834 return DockWidgetList; 1835 } 1836 1837 1838 //============================================================================ 1839 bool CDockContainerWidget::hasOpenDockAreas() const 1840 { 1841 for (auto DockArea : d->DockAreas) 1842 { 1843 if (DockArea && !DockArea->isHidden()) 1844 { 1845 return true; 1846 } 1847 } 1848 1849 return false; 1850 } 1851 1852 1853 //============================================================================ 1854 void CDockContainerWidget::saveState(QXmlStreamWriter& s) const 1855 { 1856 ADS_PRINT("CDockContainerWidget::saveState isFloating " 1857 << isFloating()); 1858 1859 s.writeStartElement("Container"); 1860 s.writeAttribute("Floating", QString::number(isFloating() ? 1 : 0)); 1861 if (isFloating()) 1862 { 1863 CFloatingDockContainer* FloatingWidget = floatingWidget(); 1864 QByteArray Geometry = FloatingWidget->saveGeometry(); 1865 #if QT_VERSION < 0x050900 1866 s.writeTextElement("Geometry", qByteArrayToHex(Geometry, ' ')); 1867 #else 1868 s.writeTextElement("Geometry", Geometry.toHex(' ')); 1869 #endif 1870 } 1871 d->saveChildNodesState(s, d->RootSplitter); 1872 d->saveAutoHideWidgetsState(s); 1873 s.writeEndElement(); 1874 } 1875 1876 1877 //============================================================================ 1878 bool CDockContainerWidget::restoreState(CDockingStateReader& s, bool Testing) 1879 { 1880 bool IsFloating = s.attributes().value("Floating").toInt(); 1881 ADS_PRINT("Restore CDockContainerWidget Floating" << IsFloating); 1882 1883 QWidget* NewRootSplitter {}; 1884 if (!Testing) 1885 { 1886 d->VisibleDockAreaCount = -1;// invalidate the dock area count 1887 d->DockAreas.clear(); 1888 std::fill(std::begin(d->LastAddedAreaCache),std::end(d->LastAddedAreaCache), nullptr); 1889 } 1890 1891 if (IsFloating) 1892 { 1893 ADS_PRINT("Restore floating widget"); 1894 if (!s.readNextStartElement() || s.name() != QLatin1String("Geometry")) 1895 { 1896 return false; 1897 } 1898 1899 QByteArray GeometryString = s.readElementText(CDockingStateReader::ErrorOnUnexpectedElement).toLocal8Bit(); 1900 QByteArray Geometry = QByteArray::fromHex(GeometryString); 1901 if (Geometry.isEmpty()) 1902 { 1903 return false; 1904 } 1905 1906 if (!Testing) 1907 { 1908 CFloatingDockContainer* FloatingWidget = floatingWidget(); 1909 if (FloatingWidget) 1910 { 1911 FloatingWidget->restoreGeometry(Geometry); 1912 } 1913 } 1914 } 1915 1916 if (!d->restoreChildNodes(s, NewRootSplitter, Testing)) 1917 { 1918 return false; 1919 } 1920 1921 if (Testing) 1922 { 1923 return true; 1924 } 1925 1926 // If the root splitter is empty, rostoreChildNodes returns a 0 pointer 1927 // and we need to create a new empty root splitter 1928 if (!NewRootSplitter) 1929 { 1930 NewRootSplitter = d->newSplitter(Qt::Horizontal); 1931 } 1932 1933 QLayoutItem* li = d->Layout->replaceWidget(d->RootSplitter, NewRootSplitter); 1934 auto OldRoot = d->RootSplitter; 1935 d->RootSplitter = qobject_cast<CDockSplitter*>(NewRootSplitter); 1936 OldRoot->deleteLater(); 1937 delete li; 1938 1939 return true; 1940 } 1941 1942 1943 //============================================================================ 1944 CDockSplitter* CDockContainerWidget::rootSplitter() const 1945 { 1946 return d->RootSplitter; 1947 } 1948 1949 1950 //============================================================================ 1951 void CDockContainerWidget::createRootSplitter() 1952 { 1953 if (d->RootSplitter) 1954 { 1955 return; 1956 } 1957 d->RootSplitter = d->newSplitter(Qt::Horizontal); 1958 d->Layout->addWidget(d->RootSplitter, 1, 1); // Add it to the center - the 0 and 2 indexes are used for the SideTabBar widgets 1959 } 1960 1961 1962 //============================================================================ 1963 void CDockContainerWidget::createSideTabBarWidgets() 1964 { 1965 if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled)) 1966 { 1967 return; 1968 } 1969 1970 { 1971 auto Area = SideBarLocation::SideBarLeft; 1972 d->SideTabBarWidgets[Area] = new CAutoHideSideBar(this, Area); 1973 d->Layout->addWidget(d->SideTabBarWidgets[Area], 1, 0); 1974 } 1975 1976 { 1977 auto Area = SideBarLocation::SideBarRight; 1978 d->SideTabBarWidgets[Area] = new CAutoHideSideBar(this, Area); 1979 d->Layout->addWidget(d->SideTabBarWidgets[Area], 1, 2); 1980 } 1981 1982 { 1983 auto Area = SideBarLocation::SideBarBottom; 1984 d->SideTabBarWidgets[Area] = new CAutoHideSideBar(this, Area); 1985 d->Layout->addWidget(d->SideTabBarWidgets[Area], 2, 1); 1986 } 1987 1988 { 1989 auto Area = SideBarLocation::SideBarTop; 1990 d->SideTabBarWidgets[Area] = new CAutoHideSideBar(this, Area); 1991 d->Layout->addWidget(d->SideTabBarWidgets[Area], 0, 1); 1992 } 1993 } 1994 1995 1996 //============================================================================ 1997 void CDockContainerWidget::dumpLayout() 1998 { 1999 #if (ADS_DEBUG_LEVEL > 0) 2000 qDebug("\n\nDumping layout --------------------------"); 2001 std::cout << "\n\nDumping layout --------------------------" << std::endl; 2002 d->dumpRecursive(0, d->RootSplitter); 2003 qDebug("--------------------------\n\n"); 2004 std::cout << "--------------------------\n\n" << std::endl; 2005 #endif 2006 } 2007 2008 2009 //============================================================================ 2010 CDockAreaWidget* CDockContainerWidget::lastAddedDockAreaWidget(DockWidgetArea area) const 2011 { 2012 return d->LastAddedAreaCache[areaIdToIndex(area)]; 2013 } 2014 2015 2016 //============================================================================ 2017 bool CDockContainerWidget::hasTopLevelDockWidget() const 2018 { 2019 auto DockAreas = openedDockAreas(); 2020 if (DockAreas.count() != 1) 2021 { 2022 return false; 2023 } 2024 2025 return DockAreas[0]->openDockWidgetsCount() == 1; 2026 } 2027 2028 2029 //============================================================================ 2030 CDockWidget* CDockContainerWidget::topLevelDockWidget() const 2031 { 2032 auto TopLevelDockArea = topLevelDockArea(); 2033 if (!TopLevelDockArea) 2034 { 2035 return nullptr; 2036 } 2037 2038 auto DockWidgets = TopLevelDockArea->openedDockWidgets(); 2039 if (DockWidgets.count() != 1) 2040 { 2041 return nullptr; 2042 } 2043 2044 return DockWidgets[0]; 2045 2046 } 2047 2048 2049 //============================================================================ 2050 CDockAreaWidget* CDockContainerWidget::topLevelDockArea() const 2051 { 2052 auto DockAreas = openedDockAreas(); 2053 if (DockAreas.count() != 1) 2054 { 2055 return nullptr; 2056 } 2057 2058 return DockAreas[0]; 2059 } 2060 2061 2062 //============================================================================ 2063 QList<CDockWidget*> CDockContainerWidget::dockWidgets() const 2064 { 2065 QList<CDockWidget*> Result; 2066 for (const auto& DockArea : d->DockAreas) 2067 { 2068 if (!DockArea) 2069 { 2070 continue; 2071 } 2072 Result.append(DockArea->dockWidgets()); 2073 } 2074 2075 return Result; 2076 } 2077 2078 //============================================================================ 2079 void CDockContainerWidget::updateSplitterHandles(QSplitter* splitter) 2080 { 2081 d->updateSplitterHandles(splitter); 2082 } 2083 2084 //============================================================================ 2085 void CDockContainerWidget::registerAutoHideWidget(CAutoHideDockContainer* AutohideWidget) 2086 { 2087 d->AutoHideWidgets.append(AutohideWidget); 2088 Q_EMIT autoHideWidgetCreated(AutohideWidget); 2089 ADS_PRINT("d->AutoHideWidgets.count() " << d->AutoHideWidgets.count()); 2090 } 2091 2092 //============================================================================ 2093 void CDockContainerWidget::removeAutoHideWidget(CAutoHideDockContainer* AutohideWidget) 2094 { 2095 d->AutoHideWidgets.removeAll(AutohideWidget); 2096 } 2097 2098 //============================================================================ 2099 CDockWidget::DockWidgetFeatures CDockContainerWidget::features() const 2100 { 2101 CDockWidget::DockWidgetFeatures Features(CDockWidget::AllDockWidgetFeatures); 2102 for (const auto& DockArea : d->DockAreas) 2103 { 2104 if (!DockArea) 2105 { 2106 continue; 2107 } 2108 Features &= DockArea->features(); 2109 } 2110 2111 return Features; 2112 } 2113 2114 2115 //============================================================================ 2116 CFloatingDockContainer* CDockContainerWidget::floatingWidget() const 2117 { 2118 return internal::findParent<CFloatingDockContainer*>(this); 2119 } 2120 2121 2122 //============================================================================ 2123 void CDockContainerWidget::closeOtherAreas(CDockAreaWidget* KeepOpenArea) 2124 { 2125 for (const auto& DockArea : d->DockAreas) 2126 { 2127 if (!DockArea || DockArea == KeepOpenArea) 2128 { 2129 continue; 2130 } 2131 2132 if (!DockArea->features(BitwiseAnd).testFlag(CDockWidget::DockWidgetClosable)) 2133 { 2134 continue; 2135 } 2136 2137 // We do not close areas with widgets with custom close handling 2138 if (DockArea->features(BitwiseOr).testFlag(CDockWidget::CustomCloseHandling)) 2139 { 2140 continue; 2141 } 2142 2143 DockArea->closeArea(); 2144 } 2145 } 2146 2147 //============================================================================ 2148 CAutoHideSideBar* CDockContainerWidget::autoHideSideBar(SideBarLocation area) const 2149 { 2150 return d->SideTabBarWidgets[area]; 2151 } 2152 2153 2154 //============================================================================ 2155 QRect CDockContainerWidget::contentRect() const 2156 { 2157 if (!d->RootSplitter) 2158 { 2159 return QRect(); 2160 } 2161 2162 if (d->RootSplitter->hasVisibleContent()) 2163 { 2164 return d->RootSplitter->geometry(); 2165 } 2166 else 2167 { 2168 auto ContentRect = this->rect(); 2169 ContentRect.adjust( 2170 autoHideSideBar(SideBarLeft)->sizeHint().width(), 2171 autoHideSideBar(SideBarTop)->sizeHint().height(), 2172 -autoHideSideBar(SideBarRight)->sizeHint().width(), 2173 -autoHideSideBar(SideBarBottom)->sizeHint().height()); 2174 2175 return ContentRect; 2176 } 2177 } 2178 2179 2180 //=========================================================================== 2181 QRect CDockContainerWidget::contentRectGlobal() const 2182 { 2183 if (!d->RootSplitter) 2184 { 2185 return QRect(); 2186 } 2187 return internal::globalGeometry(d->RootSplitter); 2188 } 2189 2190 2191 //=========================================================================== 2192 CDockManager* CDockContainerWidget::dockManager() const 2193 { 2194 return d->DockManager; 2195 } 2196 2197 2198 //=========================================================================== 2199 void CDockContainerWidget::handleAutoHideWidgetEvent(QEvent* e, QWidget* w) 2200 { 2201 if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideShowOnMouseOver)) 2202 { 2203 return; 2204 } 2205 2206 if (dockManager()->isRestoringState()) 2207 { 2208 return; 2209 } 2210 2211 auto AutoHideTab = qobject_cast<CAutoHideTab*>(w); 2212 if (AutoHideTab) 2213 { 2214 switch (e->type()) 2215 { 2216 case QEvent::Enter: 2217 if (!AutoHideTab->dockWidget()->isVisible()) 2218 { 2219 d->DelayedAutoHideTab = AutoHideTab; 2220 d->DelayedAutoHideShow = true; 2221 d->DelayedAutoHideTimer.start(); 2222 } 2223 else 2224 { 2225 d->DelayedAutoHideTimer.stop(); 2226 } 2227 break; 2228 2229 case QEvent::MouseButtonPress: 2230 d->DelayedAutoHideTimer.stop(); 2231 break; 2232 2233 case QEvent::Leave: 2234 if (AutoHideTab->dockWidget()->isVisible()) 2235 { 2236 d->DelayedAutoHideTab = AutoHideTab; 2237 d->DelayedAutoHideShow = false; 2238 d->DelayedAutoHideTimer.start(); 2239 } 2240 else 2241 { 2242 d->DelayedAutoHideTimer.stop(); 2243 } 2244 break; 2245 2246 default: 2247 break; 2248 } 2249 return; 2250 } 2251 2252 auto AutoHideContainer = qobject_cast<CAutoHideDockContainer*>(w); 2253 if (AutoHideContainer) 2254 { 2255 switch (e->type()) 2256 { 2257 case QEvent::Enter: 2258 case QEvent::Hide: 2259 d->DelayedAutoHideTimer.stop(); 2260 break; 2261 2262 case QEvent::Leave: 2263 if (AutoHideContainer->isVisible()) 2264 { 2265 d->DelayedAutoHideTab = AutoHideContainer->autoHideTab(); 2266 d->DelayedAutoHideShow = false; 2267 d->DelayedAutoHideTimer.start(); 2268 } 2269 break; 2270 2271 default: 2272 break; 2273 } 2274 return; 2275 return; 2276 } 2277 } 2278 } // namespace ads 2279 2280 //--------------------------------------------------------------------------- 2281 // EOF DockContainerWidget.cpp