dockcontainerwidget.cpp 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459
  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2020 Uwe Kindler
  4. ** Contact: https://www.qt.io/licensing/
  5. **
  6. ** This file is part of Qt Creator.
  7. **
  8. ** Commercial License Usage
  9. ** Licensees holding valid commercial Qt licenses may use this file in
  10. ** accordance with the commercial license agreement provided with the
  11. ** Software or, alternatively, in accordance with the terms contained in
  12. ** a written agreement between you and The Qt Company. For licensing terms
  13. ** and conditions see https://www.qt.io/terms-conditions. For further
  14. ** information use the contact form at https://www.qt.io/contact-us.
  15. **
  16. ** GNU Lesser General Public License Usage
  17. ** Alternatively, this file may be used under the terms of the GNU Lesser
  18. ** General Public License version 2.1 or (at your option) any later version.
  19. ** The licenses are as published by the Free Software Foundation
  20. ** and appearing in the file LICENSE.LGPLv21 included in the packaging
  21. ** of this file. Please review the following information to ensure
  22. ** the GNU Lesser General Public License version 2.1 requirements
  23. ** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  24. **
  25. ** GNU General Public License Usage
  26. ** Alternatively, this file may be used under the terms of the GNU
  27. ** General Public License version 3 or (at your option) any later version
  28. ** approved by the KDE Free Qt Foundation. The licenses are as published by
  29. ** the Free Software Foundation and appearing in the file LICENSE.GPL3
  30. ** included in the packaging of this file. Please review the following
  31. ** information to ensure the GNU General Public License requirements will
  32. ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
  33. **
  34. ****************************************************************************/
  35. #include "dockcontainerwidget.h"
  36. #include "ads_globals.h"
  37. #include "dockareawidget.h"
  38. #include "dockingstatereader.h"
  39. #include "dockmanager.h"
  40. #include "dockoverlay.h"
  41. #include "docksplitter.h"
  42. #include "dockwidget.h"
  43. #include "floatingdockcontainer.h"
  44. #include <QAbstractButton>
  45. #include <QDebug>
  46. #include <QEvent>
  47. #include <QGridLayout>
  48. #include <QList>
  49. #include <QLoggingCategory>
  50. #include <QPointer>
  51. #include <QVariant>
  52. #include <QXmlStreamWriter>
  53. #include <functional>
  54. #include <iostream>
  55. static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
  56. namespace ADS
  57. {
  58. static unsigned int zOrderCounter = 0;
  59. enum eDropMode {
  60. DropModeIntoArea, ///< drop widget into a dock area
  61. DropModeIntoContainer, ///< drop into container
  62. DropModeInvalid ///< invalid mode - do not drop
  63. };
  64. /**
  65. * Converts dock area ID to an index for array access
  66. */
  67. static int areaIdToIndex(DockWidgetArea area)
  68. {
  69. switch (area) {
  70. case LeftDockWidgetArea:
  71. return 0;
  72. case RightDockWidgetArea:
  73. return 1;
  74. case TopDockWidgetArea:
  75. return 2;
  76. case BottomDockWidgetArea:
  77. return 3;
  78. case CenterDockWidgetArea:
  79. return 4;
  80. default:
  81. return 4;
  82. }
  83. }
  84. /**
  85. * Helper function to ease insertion of dock area into splitter
  86. */
  87. static void insertWidgetIntoSplitter(QSplitter *splitter, QWidget *widget, bool append)
  88. {
  89. if (append) {
  90. splitter->addWidget(widget);
  91. } else {
  92. splitter->insertWidget(0, widget);
  93. }
  94. }
  95. /**
  96. * Private data class of DockContainerWidget class (pimpl)
  97. */
  98. class DockContainerWidgetPrivate
  99. {
  100. public:
  101. DockContainerWidget *q;
  102. QPointer<DockManager> m_dockManager;
  103. unsigned int m_zOrderIndex = 0;
  104. QList<DockAreaWidget *> m_dockAreas;
  105. QGridLayout *m_layout = nullptr;
  106. QSplitter *m_rootSplitter = nullptr;
  107. bool m_isFloating = false;
  108. DockAreaWidget *m_lastAddedAreaCache[5];
  109. int m_visibleDockAreaCount = -1;
  110. DockAreaWidget *m_topLevelDockArea = nullptr;
  111. /**
  112. * Private data constructor
  113. */
  114. DockContainerWidgetPrivate(DockContainerWidget *parent);
  115. /**
  116. * Adds dock widget to container and returns the dock area that contains
  117. * the inserted dock widget
  118. */
  119. DockAreaWidget *dockWidgetIntoContainer(DockWidgetArea area, DockWidget *dockWidget);
  120. /**
  121. * Adds dock widget to a existing DockWidgetArea
  122. */
  123. DockAreaWidget *dockWidgetIntoDockArea(DockWidgetArea area,
  124. DockWidget *dockWidget,
  125. DockAreaWidget *targetDockArea);
  126. /**
  127. * Add dock area to this container
  128. */
  129. void addDockArea(DockAreaWidget *newDockWidget, DockWidgetArea area = CenterDockWidgetArea);
  130. /**
  131. * Drop floating widget into container
  132. */
  133. void dropIntoContainer(FloatingDockContainer *floatingWidget, DockWidgetArea area);
  134. /**
  135. * Drop floating widget into dock area
  136. */
  137. void dropIntoSection(FloatingDockContainer *floatingWidget,
  138. DockAreaWidget *targetArea,
  139. DockWidgetArea area);
  140. /**
  141. * Moves the dock widget or dock area given in Widget parameter to a
  142. * new dock widget area
  143. */
  144. void moveToNewSection(QWidget *widget, DockAreaWidget *targetArea, DockWidgetArea area);
  145. /**
  146. * Moves the dock widget or dock area given in Widget parameter to a
  147. * a dock area in container
  148. */
  149. void moveToContainer(QWidget *widget, DockWidgetArea area);
  150. /**
  151. * Creates a new tab for a widget dropped into the center of a section
  152. */
  153. void dropIntoCenterOfSection(FloatingDockContainer *floatingWidget,
  154. DockAreaWidget *targetArea);
  155. /**
  156. * Creates a new tab for a widget dropped into the center of a section
  157. */
  158. void moveIntoCenterOfSection(QWidget *widget, DockAreaWidget *targetArea);
  159. /**
  160. * Adds new dock areas to the internal dock area list
  161. */
  162. void addDockAreasToList(const QList<DockAreaWidget *> newDockAreas);
  163. /**
  164. * Wrapper function for DockAreas append, that ensures that dock area signals
  165. * are properly connected to dock container slots
  166. */
  167. void appendDockAreas(const QList<DockAreaWidget *> newDockAreas);
  168. /**
  169. * Save state of child nodes
  170. */
  171. void saveChildNodesState(QXmlStreamWriter &stream, QWidget *widget);
  172. /**
  173. * Restore state of child nodes.
  174. * \param[in] Stream The data stream that contains the serialized state
  175. * \param[out] CreatedWidget The widget created from parsed data or 0 if
  176. * the parsed widget was an empty splitter
  177. * \param[in] Testing If Testing is true, only the stream data is
  178. * parsed without modifiying anything.
  179. */
  180. bool restoreChildNodes(DockingStateReader &stateReader,
  181. QWidget *&createdWidget,
  182. bool testing);
  183. /**
  184. * Restores a splitter.
  185. * \see restoreChildNodes() for details
  186. */
  187. bool restoreSplitter(DockingStateReader &stateReader, QWidget *&createdWidget, bool testing);
  188. /**
  189. * Restores a dock area.
  190. * \see restoreChildNodes() for details
  191. */
  192. bool restoreDockArea(DockingStateReader &stateReader, QWidget *&createdWidget, bool testing);
  193. /**
  194. * Helper function for recursive dumping of layout
  195. */
  196. void dumpRecursive(int level, QWidget *widget) const;
  197. /**
  198. * Calculate the drop mode from the given target position
  199. */
  200. eDropMode getDropMode(const QPoint &targetPosition);
  201. /**
  202. * Initializes the visible dock area count variable if it is not initialized
  203. * yet
  204. */
  205. void initVisibleDockAreaCount()
  206. {
  207. if (m_visibleDockAreaCount > -1) {
  208. return;
  209. }
  210. m_visibleDockAreaCount = 0;
  211. for (auto dockArea : m_dockAreas) {
  212. m_visibleDockAreaCount += dockArea->isHidden() ? 0 : 1;
  213. }
  214. }
  215. /**
  216. * Access function for the visible dock area counter
  217. */
  218. int visibleDockAreaCount()
  219. {
  220. // Lazy initialization - we initialize the VisibleDockAreaCount variable
  221. // on first use
  222. initVisibleDockAreaCount();
  223. return m_visibleDockAreaCount;
  224. }
  225. /**
  226. * The visible dock area count changes, if dock areas are remove, added or
  227. * when its view is toggled
  228. */
  229. void onVisibleDockAreaCountChanged();
  230. void emitDockAreasRemoved()
  231. {
  232. onVisibleDockAreaCountChanged();
  233. emit q->dockAreasRemoved();
  234. }
  235. void emitDockAreasAdded()
  236. {
  237. onVisibleDockAreaCountChanged();
  238. emit q->dockAreasAdded();
  239. }
  240. /**
  241. * Helper function for creation of new splitter
  242. */
  243. DockSplitter *createSplitter(Qt::Orientation orientation, QWidget *parent = nullptr)
  244. {
  245. auto *splitter = new DockSplitter(orientation, parent);
  246. splitter->setOpaqueResize(
  247. DockManager::configFlags().testFlag(DockManager::OpaqueSplitterResize));
  248. splitter->setChildrenCollapsible(false);
  249. return splitter;
  250. }
  251. void onDockAreaViewToggled(bool visible)
  252. {
  253. DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(q->sender());
  254. m_visibleDockAreaCount += visible ? 1 : -1;
  255. onVisibleDockAreaCountChanged();
  256. emit q->dockAreaViewToggled(dockArea, visible);
  257. }
  258. }; // struct DockContainerWidgetPrivate
  259. DockContainerWidgetPrivate::DockContainerWidgetPrivate(DockContainerWidget *parent)
  260. : q(parent)
  261. {
  262. std::fill(std::begin(m_lastAddedAreaCache), std::end(m_lastAddedAreaCache), nullptr);
  263. }
  264. eDropMode DockContainerWidgetPrivate::getDropMode(const QPoint &targetPosition)
  265. {
  266. DockAreaWidget *dockArea = q->dockAreaAt(targetPosition);
  267. auto dropArea = InvalidDockWidgetArea;
  268. auto containerDropArea = m_dockManager->containerOverlay()->dropAreaUnderCursor();
  269. if (dockArea) {
  270. auto dropOverlay = m_dockManager->dockAreaOverlay();
  271. dropOverlay->setAllowedAreas(dockArea->allowedAreas());
  272. dropArea = dropOverlay->showOverlay(dockArea);
  273. if (containerDropArea != InvalidDockWidgetArea && containerDropArea != dropArea) {
  274. dropArea = InvalidDockWidgetArea;
  275. }
  276. if (dropArea != InvalidDockWidgetArea) {
  277. qCInfo(adsLog) << "Dock Area Drop Content: " << dropArea;
  278. return DropModeIntoArea;
  279. }
  280. }
  281. // mouse is over container
  282. if (InvalidDockWidgetArea == dropArea) {
  283. dropArea = containerDropArea;
  284. qCInfo(adsLog) << "Container Drop Content: " << dropArea;
  285. if (dropArea != InvalidDockWidgetArea) {
  286. return DropModeIntoContainer;
  287. }
  288. }
  289. return DropModeInvalid;
  290. }
  291. void DockContainerWidgetPrivate::onVisibleDockAreaCountChanged()
  292. {
  293. auto topLevelDockArea = q->topLevelDockArea();
  294. if (topLevelDockArea) {
  295. this->m_topLevelDockArea = topLevelDockArea;
  296. topLevelDockArea->titleBarButton(TitleBarButtonUndock)
  297. ->setVisible(false || !q->isFloating());
  298. topLevelDockArea->titleBarButton(TitleBarButtonClose)
  299. ->setVisible(false || !q->isFloating());
  300. } else if (this->m_topLevelDockArea) {
  301. this->m_topLevelDockArea->titleBarButton(TitleBarButtonUndock)->setVisible(true);
  302. this->m_topLevelDockArea->titleBarButton(TitleBarButtonClose)->setVisible(true);
  303. this->m_topLevelDockArea = nullptr;
  304. }
  305. }
  306. void DockContainerWidgetPrivate::dropIntoContainer(FloatingDockContainer *floatingWidget,
  307. DockWidgetArea area)
  308. {
  309. auto insertParam = internal::dockAreaInsertParameters(area);
  310. DockContainerWidget *floatingDockContainer = floatingWidget->dockContainer();
  311. auto newDockAreas = floatingDockContainer
  312. ->findChildren<DockAreaWidget *>(QString(),
  313. Qt::FindChildrenRecursively);
  314. QSplitter *splitter = m_rootSplitter;
  315. if (m_dockAreas.count() <= 1) {
  316. splitter->setOrientation(insertParam.orientation());
  317. } else if (splitter->orientation() != insertParam.orientation()) {
  318. QSplitter *newSplitter = createSplitter(insertParam.orientation());
  319. QLayoutItem *layoutItem = m_layout->replaceWidget(splitter, newSplitter);
  320. newSplitter->addWidget(splitter);
  321. splitter = newSplitter;
  322. delete layoutItem;
  323. }
  324. // Now we can insert the floating widget content into this container
  325. auto floatingSplitter = floatingDockContainer->rootSplitter();
  326. if (floatingSplitter->count() == 1) {
  327. insertWidgetIntoSplitter(splitter, floatingSplitter->widget(0), insertParam.append());
  328. } else if (floatingSplitter->orientation() == insertParam.orientation()) {
  329. while (floatingSplitter->count()) {
  330. insertWidgetIntoSplitter(splitter,
  331. floatingSplitter->widget(0),
  332. insertParam.append());
  333. }
  334. } else {
  335. insertWidgetIntoSplitter(splitter, floatingSplitter, insertParam.append());
  336. }
  337. m_rootSplitter = splitter;
  338. addDockAreasToList(newDockAreas);
  339. // If we dropped the floating widget into the main dock container that does
  340. // not contain any dock widgets, then splitter is invisible and we need to
  341. // show it to display the docked widgets
  342. if (!splitter->isVisible()) {
  343. splitter->show();
  344. }
  345. q->dumpLayout();
  346. }
  347. void DockContainerWidgetPrivate::dropIntoCenterOfSection(FloatingDockContainer *floatingWidget,
  348. DockAreaWidget *targetArea)
  349. {
  350. DockContainerWidget *floatingContainer = floatingWidget->dockContainer();
  351. auto newDockWidgets = floatingContainer->dockWidgets();
  352. auto topLevelDockArea = floatingContainer->topLevelDockArea();
  353. int newCurrentIndex = -1;
  354. // If the floating widget contains only one single dock are, then the
  355. // current dock widget of the dock area will also be the future current
  356. // dock widget in the drop area.
  357. if (topLevelDockArea) {
  358. newCurrentIndex = topLevelDockArea->currentIndex();
  359. }
  360. for (int i = 0; i < newDockWidgets.count(); ++i) {
  361. DockWidget *dockWidget = newDockWidgets[i];
  362. targetArea->insertDockWidget(i, dockWidget, false);
  363. // If the floating widget contains multiple visible dock areas, then we
  364. // simply pick the first visible open dock widget and make it
  365. // the current one.
  366. if (newCurrentIndex < 0 && !dockWidget->isClosed()) {
  367. newCurrentIndex = i;
  368. }
  369. }
  370. targetArea->setCurrentIndex(newCurrentIndex);
  371. targetArea->updateTitleBarVisibility();
  372. return;
  373. }
  374. void DockContainerWidgetPrivate::dropIntoSection(FloatingDockContainer *floatingWidget,
  375. DockAreaWidget *targetArea,
  376. DockWidgetArea area)
  377. {
  378. // Dropping into center means all dock widgets in the dropped floating
  379. // widget will become tabs of the drop area
  380. if (CenterDockWidgetArea == area) {
  381. dropIntoCenterOfSection(floatingWidget, targetArea);
  382. return;
  383. }
  384. auto insertParam = internal::dockAreaInsertParameters(area);
  385. auto newDockAreas = floatingWidget->dockContainer()
  386. ->findChildren<DockAreaWidget *>(QString(),
  387. Qt::FindChildrenRecursively);
  388. QSplitter *targetAreaSplitter = internal::findParent<QSplitter *>(targetArea);
  389. if (!targetAreaSplitter) {
  390. QSplitter *splitter = createSplitter(insertParam.orientation());
  391. m_layout->replaceWidget(targetArea, splitter);
  392. splitter->addWidget(targetArea);
  393. targetAreaSplitter = splitter;
  394. }
  395. int areaIndex = targetAreaSplitter->indexOf(targetArea);
  396. auto widget = floatingWidget->dockContainer()
  397. ->findChild<QWidget *>(QString(), Qt::FindDirectChildrenOnly);
  398. auto floatingSplitter = qobject_cast<QSplitter *>(widget);
  399. if (targetAreaSplitter->orientation() == insertParam.orientation()) {
  400. auto sizes = targetAreaSplitter->sizes();
  401. int targetAreaSize = (insertParam.orientation() == Qt::Horizontal)
  402. ? targetArea->width()
  403. : targetArea->height();
  404. bool adjustSplitterSizes = true;
  405. if ((floatingSplitter->orientation() != insertParam.orientation())
  406. && floatingSplitter->count() > 1) {
  407. targetAreaSplitter->insertWidget(areaIndex + insertParam.insertOffset(), widget);
  408. } else {
  409. adjustSplitterSizes = (floatingSplitter->count() == 1);
  410. int insertIndex = areaIndex + insertParam.insertOffset();
  411. while (floatingSplitter->count()) {
  412. targetAreaSplitter->insertWidget(insertIndex++, floatingSplitter->widget(0));
  413. }
  414. }
  415. if (adjustSplitterSizes) {
  416. int size = (targetAreaSize - targetAreaSplitter->handleWidth()) / 2;
  417. sizes[areaIndex] = size;
  418. sizes.insert(areaIndex, size);
  419. targetAreaSplitter->setSizes(sizes);
  420. }
  421. } else {
  422. QList<int> newSplitterSizes;
  423. QSplitter *newSplitter = createSplitter(insertParam.orientation());
  424. int targetAreaSize = (insertParam.orientation() == Qt::Horizontal)
  425. ? targetArea->width()
  426. : targetArea->height();
  427. bool adjustSplitterSizes = true;
  428. if ((floatingSplitter->orientation() != insertParam.orientation())
  429. && floatingSplitter->count() > 1) {
  430. newSplitter->addWidget(widget);
  431. } else {
  432. adjustSplitterSizes = (floatingSplitter->count() == 1);
  433. while (floatingSplitter->count()) {
  434. newSplitter->addWidget(floatingSplitter->widget(0));
  435. }
  436. }
  437. // Save the sizes before insertion and restore it later to prevent
  438. // shrinking of existing area
  439. auto sizes = targetAreaSplitter->sizes();
  440. insertWidgetIntoSplitter(newSplitter, targetArea, !insertParam.append());
  441. if (adjustSplitterSizes) {
  442. int size = targetAreaSize / 2;
  443. newSplitter->setSizes({size, size});
  444. }
  445. targetAreaSplitter->insertWidget(areaIndex, newSplitter);
  446. targetAreaSplitter->setSizes(sizes);
  447. }
  448. addDockAreasToList(newDockAreas);
  449. q->dumpLayout();
  450. }
  451. void DockContainerWidgetPrivate::moveIntoCenterOfSection(QWidget *widget,
  452. DockAreaWidget *targetArea)
  453. {
  454. auto droppedDockWidget = qobject_cast<DockWidget *>(widget);
  455. auto droppedArea = qobject_cast<DockAreaWidget *>(widget);
  456. if (droppedDockWidget) {
  457. DockAreaWidget *oldDockArea = droppedDockWidget->dockAreaWidget();
  458. if (oldDockArea) {
  459. oldDockArea->removeDockWidget(droppedDockWidget);
  460. }
  461. targetArea->insertDockWidget(0, droppedDockWidget, true);
  462. } else {
  463. QList<DockWidget *> newDockWidgets = droppedArea->dockWidgets();
  464. int newCurrentIndex = droppedArea->currentIndex();
  465. for (int i = 0; i < newDockWidgets.count(); ++i) {
  466. DockWidget *dockWidget = newDockWidgets[i];
  467. targetArea->insertDockWidget(i, dockWidget, false);
  468. }
  469. targetArea->setCurrentIndex(newCurrentIndex);
  470. droppedArea->dockContainer()->removeDockArea(droppedArea);
  471. droppedArea->deleteLater();
  472. }
  473. targetArea->updateTitleBarVisibility();
  474. return;
  475. }
  476. void DockContainerWidgetPrivate::moveToNewSection(QWidget *widget,
  477. DockAreaWidget *targetArea,
  478. DockWidgetArea area)
  479. {
  480. // Dropping into center means all dock widgets in the dropped floating
  481. // widget will become tabs of the drop area
  482. if (CenterDockWidgetArea == area) {
  483. moveIntoCenterOfSection(widget, targetArea);
  484. return;
  485. }
  486. DockWidget *droppedDockWidget = qobject_cast<DockWidget *>(widget);
  487. DockAreaWidget *droppedDockArea = qobject_cast<DockAreaWidget *>(widget);
  488. DockAreaWidget *newDockArea;
  489. if (droppedDockWidget) {
  490. newDockArea = new DockAreaWidget(m_dockManager, q);
  491. DockAreaWidget *oldDockArea = droppedDockWidget->dockAreaWidget();
  492. if (oldDockArea) {
  493. oldDockArea->removeDockWidget(droppedDockWidget);
  494. }
  495. newDockArea->addDockWidget(droppedDockWidget);
  496. } else {
  497. droppedDockArea->dockContainer()->removeDockArea(droppedDockArea);
  498. newDockArea = droppedDockArea;
  499. }
  500. auto insertParam = internal::dockAreaInsertParameters(area);
  501. QSplitter *targetAreaSplitter = internal::findParent<QSplitter *>(targetArea);
  502. int areaIndex = targetAreaSplitter->indexOf(targetArea);
  503. auto sizes = targetAreaSplitter->sizes();
  504. if (targetAreaSplitter->orientation() == insertParam.orientation()) {
  505. int targetAreaSize = (insertParam.orientation() == Qt::Horizontal)
  506. ? targetArea->width()
  507. : targetArea->height();
  508. targetAreaSplitter->insertWidget(areaIndex + insertParam.insertOffset(), newDockArea);
  509. int size = (targetAreaSize - targetAreaSplitter->handleWidth()) / 2;
  510. sizes[areaIndex] = size;
  511. sizes.insert(areaIndex, size);
  512. } else {
  513. auto sizes = targetAreaSplitter->sizes();
  514. int targetAreaSize = (insertParam.orientation() == Qt::Horizontal)
  515. ? targetArea->width()
  516. : targetArea->height();
  517. QSplitter *newSplitter = createSplitter(insertParam.orientation());
  518. newSplitter->addWidget(targetArea);
  519. insertWidgetIntoSplitter(newSplitter, newDockArea, insertParam.append());
  520. int size = targetAreaSize / 2;
  521. newSplitter->setSizes({size, size});
  522. targetAreaSplitter->insertWidget(areaIndex, newSplitter);
  523. }
  524. targetAreaSplitter->setSizes(sizes);
  525. addDockAreasToList({newDockArea});
  526. }
  527. void DockContainerWidgetPrivate::moveToContainer(QWidget *widget, DockWidgetArea area)
  528. {
  529. DockWidget *droppedDockWidget = qobject_cast<DockWidget *>(widget);
  530. DockAreaWidget *droppedDockArea = qobject_cast<DockAreaWidget *>(widget);
  531. DockAreaWidget *newDockArea;
  532. if (droppedDockWidget) {
  533. newDockArea = new DockAreaWidget(m_dockManager, q);
  534. DockAreaWidget *oldDockArea = droppedDockWidget->dockAreaWidget();
  535. if (oldDockArea) {
  536. oldDockArea->removeDockWidget(droppedDockWidget);
  537. }
  538. newDockArea->addDockWidget(droppedDockWidget);
  539. } else {
  540. droppedDockArea->dockContainer()->removeDockArea(droppedDockArea);
  541. newDockArea = droppedDockArea;
  542. }
  543. addDockArea(newDockArea, area);
  544. m_lastAddedAreaCache[areaIdToIndex(area)] = newDockArea;
  545. }
  546. void DockContainerWidgetPrivate::addDockAreasToList(const QList<DockAreaWidget *> newDockAreas)
  547. {
  548. int countBefore = m_dockAreas.count();
  549. int newAreaCount = newDockAreas.count();
  550. appendDockAreas(newDockAreas);
  551. // If the user dropped a floating widget that contains only one single
  552. // visible dock area, then its title bar button TitleBarButtonUndock is
  553. // likely hidden. We need to ensure, that it is visible
  554. for (auto dockArea : newDockAreas) {
  555. dockArea->titleBarButton(TitleBarButtonUndock)->setVisible(true);
  556. dockArea->titleBarButton(TitleBarButtonClose)->setVisible(true);
  557. }
  558. // We need to ensure, that the dock area title bar is visible. The title bar
  559. // is invisible, if the dock are is a single dock area in a floating widget.
  560. if (1 == countBefore) {
  561. m_dockAreas.at(0)->updateTitleBarVisibility();
  562. }
  563. if (1 == newAreaCount) {
  564. m_dockAreas.last()->updateTitleBarVisibility();
  565. }
  566. emitDockAreasAdded();
  567. }
  568. void DockContainerWidgetPrivate::appendDockAreas(const QList<DockAreaWidget *> newDockAreas)
  569. {
  570. m_dockAreas.append(newDockAreas);
  571. for (auto dockArea : newDockAreas) {
  572. QObject::connect(dockArea,
  573. &DockAreaWidget::viewToggled,
  574. q,
  575. std::bind(&DockContainerWidgetPrivate::onDockAreaViewToggled,
  576. this,
  577. std::placeholders::_1));
  578. }
  579. }
  580. void DockContainerWidgetPrivate::saveChildNodesState(QXmlStreamWriter &stream, QWidget *widget)
  581. {
  582. QSplitter *splitter = qobject_cast<QSplitter *>(widget);
  583. if (splitter) {
  584. stream.writeStartElement("splitter");
  585. stream.writeAttribute("orientation",
  586. QVariant::fromValue(splitter->orientation()).toString());
  587. stream.writeAttribute("count", QString::number(splitter->count()));
  588. qCInfo(adsLog) << "NodeSplitter orient: " << splitter->orientation()
  589. << " WidgetCont: " << splitter->count();
  590. for (int i = 0; i < splitter->count(); ++i) {
  591. saveChildNodesState(stream, splitter->widget(i));
  592. }
  593. stream.writeStartElement("sizes");
  594. QStringList sizes;
  595. for (auto size : splitter->sizes()) {
  596. sizes.append(QString::number(size));
  597. }
  598. stream.writeCharacters(sizes.join(" "));
  599. stream.writeEndElement();
  600. stream.writeEndElement();
  601. } else {
  602. DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(widget);
  603. if (dockArea) {
  604. dockArea->saveState(stream);
  605. }
  606. }
  607. }
  608. bool DockContainerWidgetPrivate::restoreSplitter(DockingStateReader &stateReader,
  609. QWidget *&createdWidget,
  610. bool testing)
  611. {
  612. QVariant orientationVar = QVariant(stateReader.attributes().value("orientation").toString());
  613. // Check if the orientation string is convertable
  614. if (!orientationVar.canConvert<Qt::Orientation>()) {
  615. return false;
  616. }
  617. Qt::Orientation orientation = orientationVar.value<Qt::Orientation>();
  618. bool ok;
  619. int widgetCount = stateReader.attributes().value("count").toInt(&ok);
  620. if (!ok) {
  621. return false;
  622. }
  623. qCInfo(adsLog) << "Restore NodeSplitter Orientation: " << orientation
  624. << " WidgetCount: " << widgetCount;
  625. QSplitter *splitter = nullptr;
  626. if (!testing) {
  627. splitter = createSplitter(orientation);
  628. }
  629. bool visible = false;
  630. QList<int> sizes;
  631. while (stateReader.readNextStartElement()) {
  632. QWidget *childNode = nullptr;
  633. bool result = true;
  634. if (stateReader.name() == "splitter") {
  635. result = restoreSplitter(stateReader, childNode, testing);
  636. } else if (stateReader.name() == "area") {
  637. result = restoreDockArea(stateReader, childNode, testing);
  638. } else if (stateReader.name() == "sizes") {
  639. QString size = stateReader.readElementText().trimmed();
  640. qCInfo(adsLog) << "Size: " << size;
  641. QTextStream textStream(&size);
  642. while (!textStream.atEnd()) {
  643. int value;
  644. textStream >> value;
  645. sizes.append(value);
  646. }
  647. } else {
  648. stateReader.skipCurrentElement();
  649. }
  650. if (!result) {
  651. return false;
  652. }
  653. if (testing || !childNode) {
  654. continue;
  655. }
  656. qCInfo(adsLog) << "ChildNode isVisible " << childNode->isVisible() << " isVisibleTo "
  657. << childNode->isVisibleTo(splitter);
  658. splitter->addWidget(childNode);
  659. visible |= childNode->isVisibleTo(splitter);
  660. }
  661. if (sizes.count() != widgetCount) {
  662. return false;
  663. }
  664. if (!testing) {
  665. if (!splitter->count()) {
  666. delete splitter;
  667. splitter = nullptr;
  668. } else {
  669. splitter->setSizes(sizes);
  670. splitter->setVisible(visible);
  671. }
  672. createdWidget = splitter;
  673. } else {
  674. createdWidget = nullptr;
  675. }
  676. return true;
  677. }
  678. bool DockContainerWidgetPrivate::restoreDockArea(DockingStateReader &stateReader,
  679. QWidget *&createdWidget,
  680. bool testing)
  681. {
  682. QString currentDockWidget = stateReader.attributes().value("current").toString();
  683. #ifdef ADS_DEBUG_PRINT
  684. bool ok;
  685. int tabs = stateReader.attributes().value("tabs").toInt(&ok);
  686. if (!ok) {
  687. return false;
  688. }
  689. qCInfo(adsLog) << "Restore NodeDockArea Tabs: " << tabs
  690. << " Current: " << currentDockWidget;
  691. #endif
  692. DockAreaWidget *dockArea = nullptr;
  693. if (!testing) {
  694. dockArea = new DockAreaWidget(m_dockManager, q);
  695. }
  696. while (stateReader.readNextStartElement()) {
  697. if (stateReader.name() != "widget") {
  698. continue;
  699. }
  700. auto objectName = stateReader.attributes().value("name");
  701. if (objectName.isEmpty()) {
  702. qCInfo(adsLog) << "Error: Empty name!";
  703. return false;
  704. }
  705. QVariant closedVar = QVariant(stateReader.attributes().value("closed").toString());
  706. if (!closedVar.canConvert<bool>()) {
  707. return false;
  708. }
  709. bool closed = closedVar.value<bool>();
  710. stateReader.skipCurrentElement();
  711. DockWidget *dockWidget = m_dockManager->findDockWidget(objectName.toString());
  712. if (!dockWidget || testing) {
  713. continue;
  714. }
  715. qCInfo(adsLog) << "Dock Widget found - parent " << dockWidget->parent();
  716. // We hide the DockArea here to prevent the short display (the flashing)
  717. // of the dock areas during application startup
  718. dockArea->hide();
  719. dockArea->addDockWidget(dockWidget);
  720. dockWidget->setToggleViewActionChecked(!closed);
  721. dockWidget->setClosedState(closed);
  722. dockWidget->setProperty(internal::closedProperty, closed);
  723. dockWidget->setProperty(internal::dirtyProperty, false);
  724. }
  725. if (testing) {
  726. return true;
  727. }
  728. if (!dockArea->dockWidgetsCount()) {
  729. delete dockArea;
  730. dockArea = nullptr;
  731. } else {
  732. dockArea->setProperty("currentDockWidget", currentDockWidget);
  733. appendDockAreas({dockArea});
  734. }
  735. createdWidget = dockArea;
  736. return true;
  737. }
  738. bool DockContainerWidgetPrivate::restoreChildNodes(DockingStateReader &stateReader,
  739. QWidget *&createdWidget,
  740. bool testing)
  741. {
  742. bool result = true;
  743. while (stateReader.readNextStartElement()) {
  744. if (stateReader.name() == "splitter") {
  745. result = restoreSplitter(stateReader, createdWidget, testing);
  746. qCInfo(adsLog) << "Splitter";
  747. } else if (stateReader.name() == "area") {
  748. result = restoreDockArea(stateReader, createdWidget, testing);
  749. qCInfo(adsLog) << "DockAreaWidget";
  750. } else {
  751. stateReader.skipCurrentElement();
  752. qCInfo(adsLog) << "Unknown element" << stateReader.name();
  753. }
  754. }
  755. return result;
  756. }
  757. DockAreaWidget *DockContainerWidgetPrivate::dockWidgetIntoContainer(DockWidgetArea area,
  758. DockWidget *dockWidget)
  759. {
  760. DockAreaWidget *newDockArea = new DockAreaWidget(m_dockManager, q);
  761. newDockArea->addDockWidget(dockWidget);
  762. addDockArea(newDockArea, area);
  763. newDockArea->updateTitleBarVisibility();
  764. m_lastAddedAreaCache[areaIdToIndex(area)] = newDockArea;
  765. return newDockArea;
  766. }
  767. void DockContainerWidgetPrivate::addDockArea(DockAreaWidget *newDockArea, DockWidgetArea area)
  768. {
  769. auto insertParam = internal::dockAreaInsertParameters(area);
  770. // As long as we have only one dock area in the splitter we can adjust its orientation
  771. if (m_dockAreas.count() <= 1) {
  772. m_rootSplitter->setOrientation(insertParam.orientation());
  773. }
  774. QSplitter *splitter = m_rootSplitter;
  775. if (splitter->orientation() == insertParam.orientation()) {
  776. insertWidgetIntoSplitter(splitter, newDockArea, insertParam.append());
  777. } else {
  778. QSplitter *newSplitter = createSplitter(insertParam.orientation());
  779. if (insertParam.append()) {
  780. QLayoutItem *layoutItem = m_layout->replaceWidget(splitter, newSplitter);
  781. newSplitter->addWidget(splitter);
  782. newSplitter->addWidget(newDockArea);
  783. delete layoutItem;
  784. } else {
  785. newSplitter->addWidget(newDockArea);
  786. QLayoutItem *layoutItem = m_layout->replaceWidget(splitter, newSplitter);
  787. newSplitter->addWidget(splitter);
  788. delete layoutItem;
  789. }
  790. m_rootSplitter = newSplitter;
  791. }
  792. addDockAreasToList({newDockArea});
  793. }
  794. void DockContainerWidgetPrivate::dumpRecursive(int level, QWidget *widget) const
  795. {
  796. #if defined(QT_DEBUG)
  797. QSplitter *splitter = qobject_cast<QSplitter *>(widget);
  798. QByteArray buf;
  799. buf.fill(' ', level * 4);
  800. if (splitter) {
  801. #ifdef ADS_DEBUG_PRINT
  802. qDebug("%sSplitter %s v: %s c: %s",
  803. buf.data(),
  804. (splitter->orientation() == Qt::Vertical) ? "--" : "|",
  805. splitter->isHidden() ? " " : "v",
  806. QString::number(splitter->count()).toStdString().c_str());
  807. std::cout << buf.data() << "Splitter "
  808. << ((splitter->orientation() == Qt::Vertical) ? "--" : "|") << " "
  809. << (splitter->isHidden() ? " " : "v") << " "
  810. << QString::number(splitter->count()).toStdString() << std::endl;
  811. #endif
  812. for (int i = 0; i < splitter->count(); ++i) {
  813. dumpRecursive(level + 1, splitter->widget(i));
  814. }
  815. } else {
  816. DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(widget);
  817. if (!dockArea) {
  818. return;
  819. }
  820. #ifdef ADS_DEBUG_PRINT
  821. qDebug("%sDockArea", buf.data());
  822. std::cout << buf.data() << (dockArea->isHidden() ? " " : "v")
  823. << (dockArea->openDockWidgetsCount() > 0 ? " " : "c") << " DockArea"
  824. << std::endl;
  825. buf.fill(' ', (level + 1) * 4);
  826. for (int i = 0; i < dockArea->dockWidgetsCount(); ++i) {
  827. std::cout << buf.data() << (i == dockArea->currentIndex() ? "*" : " ");
  828. DockWidget *dockWidget = dockArea->dockWidget(i);
  829. std::cout << (dockWidget->isHidden() ? " " : "v");
  830. std::cout << (dockWidget->isClosed() ? "c" : " ") << " ";
  831. std::cout << dockWidget->windowTitle().toStdString() << std::endl;
  832. }
  833. #endif
  834. }
  835. #else
  836. Q_UNUSED(level)
  837. Q_UNUSED(widget)
  838. #endif
  839. }
  840. DockAreaWidget *DockContainerWidgetPrivate::dockWidgetIntoDockArea(DockWidgetArea area,
  841. DockWidget *dockWidget,
  842. DockAreaWidget
  843. *targetDockArea)
  844. {
  845. if (CenterDockWidgetArea == area) {
  846. targetDockArea->addDockWidget(dockWidget);
  847. targetDockArea->updateTitleBarVisibility();
  848. return targetDockArea;
  849. }
  850. DockAreaWidget *newDockArea = new DockAreaWidget(m_dockManager, q);
  851. newDockArea->addDockWidget(dockWidget);
  852. auto insertParam = internal::dockAreaInsertParameters(area);
  853. QSplitter *targetAreaSplitter = internal::findParent<QSplitter *>(targetDockArea);
  854. int index = targetAreaSplitter->indexOf(targetDockArea);
  855. if (targetAreaSplitter->orientation() == insertParam.orientation()) {
  856. qCInfo(adsLog) << "TargetAreaSplitter->orientation() == InsertParam.orientation()";
  857. targetAreaSplitter->insertWidget(index + insertParam.insertOffset(), newDockArea);
  858. } else {
  859. qCInfo(adsLog) << "TargetAreaSplitter->orientation() != InsertParam.orientation()";
  860. QSplitter *newSplitter = createSplitter(insertParam.orientation());
  861. newSplitter->addWidget(targetDockArea);
  862. insertWidgetIntoSplitter(newSplitter, newDockArea, insertParam.append());
  863. targetAreaSplitter->insertWidget(index, newSplitter);
  864. }
  865. appendDockAreas({newDockArea});
  866. emitDockAreasAdded();
  867. return newDockArea;
  868. }
  869. DockContainerWidget::DockContainerWidget(DockManager *dockManager, QWidget *parent)
  870. : QFrame(parent)
  871. , d(new DockContainerWidgetPrivate(this))
  872. {
  873. d->m_dockManager = dockManager;
  874. d->m_isFloating = floatingWidget() != nullptr;
  875. d->m_layout = new QGridLayout();
  876. d->m_layout->setContentsMargins(0, 1, 0, 1);
  877. d->m_layout->setSpacing(0);
  878. setLayout(d->m_layout);
  879. // The function d->createSplitter() accesses the config flags from dock
  880. // manager which in turn requires a properly constructed dock manager.
  881. // If this dock container is the dock manager, then it is not properly
  882. // constructed yet because this base class constructor is called before
  883. // the constructor of the DockManager private class
  884. if (dockManager != this) {
  885. d->m_dockManager->registerDockContainer(this);
  886. createRootSplitter();
  887. }
  888. }
  889. DockContainerWidget::~DockContainerWidget()
  890. {
  891. if (d->m_dockManager) {
  892. d->m_dockManager->removeDockContainer(this);
  893. }
  894. delete d;
  895. }
  896. DockAreaWidget *DockContainerWidget::addDockWidget(DockWidgetArea area,
  897. DockWidget *dockWidget,
  898. DockAreaWidget *dockAreaWidget)
  899. {
  900. DockAreaWidget *oldDockArea = dockWidget->dockAreaWidget();
  901. if (oldDockArea) {
  902. oldDockArea->removeDockWidget(dockWidget);
  903. }
  904. dockWidget->setDockManager(d->m_dockManager);
  905. if (dockAreaWidget) {
  906. return d->dockWidgetIntoDockArea(area, dockWidget, dockAreaWidget);
  907. } else {
  908. return d->dockWidgetIntoContainer(area, dockWidget);
  909. }
  910. }
  911. void DockContainerWidget::removeDockWidget(DockWidget * dockWidget)
  912. {
  913. DockAreaWidget *area = dockWidget->dockAreaWidget();
  914. if (area) {
  915. area->removeDockWidget(dockWidget);
  916. }
  917. }
  918. unsigned int DockContainerWidget::zOrderIndex() const { return d->m_zOrderIndex; }
  919. bool DockContainerWidget::isInFrontOf(DockContainerWidget *other) const
  920. {
  921. return this->zOrderIndex() > other->zOrderIndex();
  922. }
  923. bool DockContainerWidget::event(QEvent *event)
  924. {
  925. bool result = QWidget::event(event);
  926. if (event->type() == QEvent::WindowActivate) {
  927. d->m_zOrderIndex = ++zOrderCounter;
  928. } else if (event->type() == QEvent::Show && !d->m_zOrderIndex) {
  929. d->m_zOrderIndex = ++zOrderCounter;
  930. }
  931. return result;
  932. }
  933. void DockContainerWidget::addDockArea(DockAreaWidget *dockAreaWidget, DockWidgetArea area)
  934. {
  935. DockContainerWidget *container = dockAreaWidget->dockContainer();
  936. if (container && container != this) {
  937. container->removeDockArea(dockAreaWidget);
  938. }
  939. d->addDockArea(dockAreaWidget, area);
  940. }
  941. void DockContainerWidget::removeDockArea(DockAreaWidget *area)
  942. {
  943. qCInfo(adsLog) << Q_FUNC_INFO;
  944. area->disconnect(this);
  945. d->m_dockAreas.removeAll(area);
  946. DockSplitter *splitter = internal::findParent<DockSplitter *>(area);
  947. // Remove area from parent splitter and recursively hide tree of parent
  948. // splitters if it has no visible content
  949. area->setParent(nullptr);
  950. internal::hideEmptyParentSplitters(splitter);
  951. // Remove this area from cached areas
  952. const auto &cache = d->m_lastAddedAreaCache;
  953. if (auto p = std::find(cache, cache + sizeof(cache) / sizeof(cache[0]), area)) {
  954. d->m_lastAddedAreaCache[std::distance(cache, p)] = nullptr;
  955. }
  956. // If splitter has more than 1 widgets, we are finished and can leave
  957. if (splitter->count() > 1) {
  958. emitAndExit();
  959. return;
  960. }
  961. // If this is the RootSplitter we need to remove empty splitters to
  962. // avoid too many empty splitters
  963. if (splitter == d->m_rootSplitter) {
  964. qCInfo(adsLog) << "Removed from RootSplitter";
  965. // If splitter is empty, we are finished
  966. if (!splitter->count()) {
  967. splitter->hide();
  968. emitAndExit();
  969. return;
  970. }
  971. QWidget *widget = splitter->widget(0);
  972. QSplitter *childSplitter = qobject_cast<QSplitter *>(widget);
  973. // If the one and only content widget of the splitter is not a splitter
  974. // then we are finished
  975. if (!childSplitter) {
  976. emitAndExit();
  977. return;
  978. }
  979. // We replace the superfluous RootSplitter with the ChildSplitter
  980. childSplitter->setParent(nullptr);
  981. QLayoutItem *layoutItem = d->m_layout->replaceWidget(splitter, childSplitter);
  982. d->m_rootSplitter = childSplitter;
  983. delete layoutItem;
  984. qCInfo(adsLog) << "RootSplitter replaced by child splitter";
  985. } else if (splitter->count() == 1) {
  986. qCInfo(adsLog) << "Replacing splitter with content";
  987. QSplitter *parentSplitter = internal::findParent<QSplitter *>(splitter);
  988. auto sizes = parentSplitter->sizes();
  989. QWidget *widget = splitter->widget(0);
  990. widget->setParent(this);
  991. internal::replaceSplitterWidget(parentSplitter, splitter, widget);
  992. parentSplitter->setSizes(sizes);
  993. }
  994. delete splitter;
  995. }
  996. void DockContainerWidget::emitAndExit() const
  997. {
  998. DockWidget *topLevelWidget = topLevelDockWidget();
  999. // Updated the title bar visibility of the dock widget if there is only
  1000. // one single visible dock widget
  1001. DockWidget::emitTopLevelEventForWidget(topLevelWidget, true);
  1002. dumpLayout();
  1003. d->emitDockAreasRemoved();
  1004. }
  1005. DockAreaWidget *DockContainerWidget::dockAreaAt(const QPoint &globalPosition) const
  1006. {
  1007. for (auto dockArea : d->m_dockAreas) {
  1008. if (dockArea->isVisible()
  1009. && dockArea->rect().contains(dockArea->mapFromGlobal(globalPosition))) {
  1010. return dockArea;
  1011. }
  1012. }
  1013. return nullptr;
  1014. }
  1015. DockAreaWidget *DockContainerWidget::dockArea(int index) const
  1016. {
  1017. return (index < dockAreaCount()) ? d->m_dockAreas[index] : nullptr;
  1018. }
  1019. bool DockContainerWidget::isFloating() const { return d->m_isFloating; }
  1020. int DockContainerWidget::dockAreaCount() const { return d->m_dockAreas.count(); }
  1021. int DockContainerWidget::visibleDockAreaCount() const
  1022. {
  1023. int result = 0;
  1024. for (auto dockArea : d->m_dockAreas) {
  1025. result += dockArea->isHidden() ? 0 : 1;
  1026. }
  1027. return result;
  1028. // TODO Cache or precalculate this to speed it up because it is used during
  1029. // movement of floating widget
  1030. //return d->visibleDockAreaCount();
  1031. }
  1032. void DockContainerWidget::dropFloatingWidget(FloatingDockContainer *floatingWidget,
  1033. const QPoint &targetPosition)
  1034. {
  1035. qCInfo(adsLog) << Q_FUNC_INFO;
  1036. DockWidget *singleDroppedDockWidget = floatingWidget->topLevelDockWidget();
  1037. DockWidget *singleDockWidget = topLevelDockWidget();
  1038. DockAreaWidget *dockArea = dockAreaAt(targetPosition);
  1039. auto dropArea = InvalidDockWidgetArea;
  1040. auto containerDropArea = d->m_dockManager->containerOverlay()->dropAreaUnderCursor();
  1041. bool dropped = false;
  1042. if (dockArea) {
  1043. auto dropOverlay = d->m_dockManager->dockAreaOverlay();
  1044. dropOverlay->setAllowedAreas(dockArea->allowedAreas());
  1045. dropArea = dropOverlay->showOverlay(dockArea);
  1046. if (containerDropArea != InvalidDockWidgetArea && containerDropArea != dropArea) {
  1047. dropArea = InvalidDockWidgetArea;
  1048. }
  1049. if (dropArea != InvalidDockWidgetArea) {
  1050. qCInfo(adsLog) << "Dock Area Drop Content: " << dropArea;
  1051. d->dropIntoSection(floatingWidget, dockArea, dropArea);
  1052. dropped = true;
  1053. }
  1054. }
  1055. // mouse is over container
  1056. if (InvalidDockWidgetArea == dropArea) {
  1057. dropArea = containerDropArea;
  1058. qCInfo(adsLog) << "Container Drop Content: " << dropArea;
  1059. if (dropArea != InvalidDockWidgetArea) {
  1060. d->dropIntoContainer(floatingWidget, dropArea);
  1061. dropped = true;
  1062. }
  1063. }
  1064. if (dropped) {
  1065. floatingWidget->deleteLater();
  1066. // If we dropped a floating widget with only one single dock widget, then we
  1067. // drop a top level widget that changes from floating to docked now
  1068. DockWidget::emitTopLevelEventForWidget(singleDroppedDockWidget, false);
  1069. // If there was a top level widget before the drop, then it is not top
  1070. // level widget anymore
  1071. DockWidget::emitTopLevelEventForWidget(singleDockWidget, false);
  1072. }
  1073. }
  1074. void DockContainerWidget::dropWidget(QWidget *widget, const QPoint &targetPosition)
  1075. {
  1076. qCInfo(adsLog) << Q_FUNC_INFO;
  1077. DockWidget *singleDockWidget = topLevelDockWidget();
  1078. DockAreaWidget *dockArea = dockAreaAt(targetPosition);
  1079. auto dropArea = InvalidDockWidgetArea;
  1080. auto containerDropArea = d->m_dockManager->containerOverlay()->dropAreaUnderCursor();
  1081. if (dockArea) {
  1082. auto dropOverlay = d->m_dockManager->dockAreaOverlay();
  1083. dropOverlay->setAllowedAreas(dockArea->allowedAreas());
  1084. dropArea = dropOverlay->showOverlay(dockArea);
  1085. if (containerDropArea != InvalidDockWidgetArea && containerDropArea != dropArea) {
  1086. dropArea = InvalidDockWidgetArea;
  1087. }
  1088. if (dropArea != InvalidDockWidgetArea) {
  1089. qCInfo(adsLog) << "Dock Area Drop Content: " << dropArea;
  1090. d->moveToNewSection(widget, dockArea, dropArea);
  1091. }
  1092. }
  1093. // mouse is over container
  1094. if (InvalidDockWidgetArea == dropArea) {
  1095. dropArea = containerDropArea;
  1096. qCInfo(adsLog) << "Container Drop Content: " << dropArea;
  1097. if (dropArea != InvalidDockWidgetArea) {
  1098. d->moveToContainer(widget, dropArea);
  1099. }
  1100. }
  1101. // If there was a top level widget before the drop, then it is not top
  1102. // level widget anymore
  1103. DockWidget::emitTopLevelEventForWidget(singleDockWidget, false);
  1104. }
  1105. QList<DockAreaWidget *> DockContainerWidget::openedDockAreas() const
  1106. {
  1107. QList<DockAreaWidget *> result;
  1108. for (auto dockArea : d->m_dockAreas) {
  1109. if (!dockArea->isHidden()) {
  1110. result.append(dockArea);
  1111. }
  1112. }
  1113. return result;
  1114. }
  1115. void DockContainerWidget::saveState(QXmlStreamWriter &stream) const
  1116. {
  1117. qCInfo(adsLog) << Q_FUNC_INFO << "isFloating " << isFloating();
  1118. stream.writeStartElement("container");
  1119. stream.writeAttribute("floating", QVariant::fromValue(isFloating()).toString());
  1120. if (isFloating()) {
  1121. FloatingDockContainer *floatingDockContainer = floatingWidget();
  1122. QByteArray geometry = floatingDockContainer->saveGeometry();
  1123. stream.writeTextElement("geometry", QString::fromLatin1(geometry.toBase64()));
  1124. }
  1125. d->saveChildNodesState(stream, d->m_rootSplitter);
  1126. stream.writeEndElement();
  1127. }
  1128. bool DockContainerWidget::restoreState(DockingStateReader &stateReader, bool testing)
  1129. {
  1130. QVariant floatingVar = QVariant(stateReader.attributes().value("floating").toString());
  1131. if (!floatingVar.canConvert<bool>()) {
  1132. return false;
  1133. }
  1134. bool isFloating = floatingVar.value<bool>();
  1135. qCInfo(adsLog) << "Restore DockContainerWidget Floating" << isFloating;
  1136. QWidget *newRootSplitter{};
  1137. if (!testing) {
  1138. d->m_visibleDockAreaCount = -1; // invalidate the dock area count
  1139. d->m_dockAreas.clear();
  1140. std::fill(std::begin(d->m_lastAddedAreaCache),
  1141. std::end(d->m_lastAddedAreaCache),
  1142. nullptr);
  1143. }
  1144. if (isFloating) {
  1145. qCInfo(adsLog) << "Restore floating widget";
  1146. if (!stateReader.readNextStartElement() || stateReader.name() != "geometry") {
  1147. return false;
  1148. }
  1149. QByteArray geometryString = stateReader
  1150. .readElementText(
  1151. DockingStateReader::ErrorOnUnexpectedElement)
  1152. .toLocal8Bit();
  1153. QByteArray geometry = QByteArray::fromBase64(geometryString);
  1154. if (geometry.isEmpty()) {
  1155. return false;
  1156. }
  1157. if (!testing) {
  1158. FloatingDockContainer *floatingDockContainer = floatingWidget();
  1159. floatingDockContainer->restoreGeometry(geometry);
  1160. }
  1161. }
  1162. if (!d->restoreChildNodes(stateReader, newRootSplitter, testing)) {
  1163. return false;
  1164. }
  1165. if (testing) {
  1166. return true;
  1167. }
  1168. // If the root splitter is empty, rostoreChildNodes returns a 0 pointer
  1169. // and we need to create a new empty root splitter
  1170. if (!newRootSplitter) {
  1171. newRootSplitter = d->createSplitter(Qt::Horizontal);
  1172. }
  1173. d->m_layout->replaceWidget(d->m_rootSplitter, newRootSplitter);
  1174. QSplitter *oldRoot = d->m_rootSplitter;
  1175. d->m_rootSplitter = qobject_cast<QSplitter *>(newRootSplitter);
  1176. oldRoot->deleteLater();
  1177. return true;
  1178. }
  1179. QSplitter *DockContainerWidget::rootSplitter() const { return d->m_rootSplitter; }
  1180. void DockContainerWidget::createRootSplitter()
  1181. {
  1182. if (d->m_rootSplitter) {
  1183. return;
  1184. }
  1185. d->m_rootSplitter = d->createSplitter(Qt::Horizontal);
  1186. d->m_layout->addWidget(d->m_rootSplitter);
  1187. }
  1188. void DockContainerWidget::dumpLayout() const
  1189. {
  1190. #if (ADS_DEBUG_LEVEL > 0)
  1191. qDebug("\n\nDumping layout --------------------------");
  1192. std::cout << "\n\nDumping layout --------------------------" << std::endl;
  1193. d->dumpRecursive(0, d->m_rootSplitter);
  1194. qDebug("--------------------------\n\n");
  1195. std::cout << "--------------------------\n\n" << std::endl;
  1196. #endif
  1197. }
  1198. DockAreaWidget *DockContainerWidget::lastAddedDockAreaWidget(DockWidgetArea area) const
  1199. {
  1200. return d->m_lastAddedAreaCache[areaIdToIndex(area)];
  1201. }
  1202. bool DockContainerWidget::hasTopLevelDockWidget() const
  1203. {
  1204. if (!isFloating()) {
  1205. return false;
  1206. }
  1207. auto dockAreas = openedDockAreas();
  1208. if (dockAreas.count() != 1) {
  1209. return false;
  1210. }
  1211. return dockAreas[0]->openDockWidgetsCount() == 1;
  1212. }
  1213. DockWidget *DockContainerWidget::topLevelDockWidget() const
  1214. {
  1215. auto dockArea = topLevelDockArea();
  1216. if (!dockArea) {
  1217. return nullptr;
  1218. }
  1219. auto dockWidgets = dockArea->openedDockWidgets();
  1220. if (dockWidgets.count() != 1) {
  1221. return nullptr;
  1222. }
  1223. return dockWidgets[0];
  1224. }
  1225. DockAreaWidget *DockContainerWidget::topLevelDockArea() const
  1226. {
  1227. if (!isFloating()) {
  1228. return nullptr;
  1229. }
  1230. auto dockAreas = openedDockAreas();
  1231. if (dockAreas.count() != 1) {
  1232. return nullptr;
  1233. }
  1234. return dockAreas[0];
  1235. }
  1236. QList<DockWidget *> DockContainerWidget::dockWidgets() const
  1237. {
  1238. QList<DockWidget *> result;
  1239. for (const auto dockArea : d->m_dockAreas) {
  1240. result.append(dockArea->dockWidgets());
  1241. }
  1242. return result;
  1243. }
  1244. DockWidget::DockWidgetFeatures DockContainerWidget::features() const
  1245. {
  1246. DockWidget::DockWidgetFeatures features(DockWidget::AllDockWidgetFeatures);
  1247. for (const auto dockArea : d->m_dockAreas) {
  1248. features &= dockArea->features();
  1249. }
  1250. return features;
  1251. }
  1252. FloatingDockContainer *DockContainerWidget::floatingWidget() const
  1253. {
  1254. return internal::findParent<FloatingDockContainer *>(this);
  1255. }
  1256. void DockContainerWidget::closeOtherAreas(DockAreaWidget *keepOpenArea)
  1257. {
  1258. for (const auto dockArea : d->m_dockAreas) {
  1259. if (dockArea == keepOpenArea) {
  1260. continue;
  1261. }
  1262. if (!dockArea->features(BitwiseAnd).testFlag(DockWidget::DockWidgetClosable)) {
  1263. continue;
  1264. }
  1265. // We do not close areas with widgets with custom close handling
  1266. if (dockArea->features(BitwiseOr).testFlag(DockWidget::CustomCloseHandling)) {
  1267. continue;
  1268. }
  1269. dockArea->closeArea();
  1270. }
  1271. }
  1272. } // namespace ADS