dockwidget.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  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 "dockwidget.h"
  36. #include "ads_globals.h"
  37. #include "dockareawidget.h"
  38. #include "dockcomponentsfactory.h"
  39. #include "dockcontainerwidget.h"
  40. #include "dockmanager.h"
  41. #include "docksplitter.h"
  42. #include "dockwidgettab.h"
  43. #include "floatingdockcontainer.h"
  44. #include <QAction>
  45. #include <QBoxLayout>
  46. #include <QEvent>
  47. #include <QLoggingCategory>
  48. #include <QPointer>
  49. #include <QScrollArea>
  50. #include <QSplitter>
  51. #include <QStack>
  52. #include <QTextStream>
  53. #include <QToolBar>
  54. #include <QXmlStreamWriter>
  55. static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
  56. namespace ADS
  57. {
  58. /**
  59. * Private data class of DockWidget class (pimpl)
  60. */
  61. struct DockWidgetPrivate
  62. {
  63. DockWidget *q = nullptr;
  64. QBoxLayout *m_layout = nullptr;
  65. QWidget *m_widget = nullptr;
  66. DockWidgetTab *m_tabWidget = nullptr;
  67. DockWidget::DockWidgetFeatures m_features = DockWidget::DefaultDockWidgetFeatures;
  68. DockManager *m_dockManager = nullptr;
  69. DockAreaWidget *m_dockArea = nullptr;
  70. QAction *m_toggleViewAction = nullptr;
  71. bool m_closed = false;
  72. QScrollArea *m_scrollArea = nullptr;
  73. QToolBar *m_toolBar = nullptr;
  74. Qt::ToolButtonStyle m_toolBarStyleDocked = Qt::ToolButtonIconOnly;
  75. Qt::ToolButtonStyle m_toolBarStyleFloating = Qt::ToolButtonTextUnderIcon;
  76. QSize m_toolBarIconSizeDocked = QSize(16, 16);
  77. QSize m_toolBarIconSizeFloating = QSize(24, 24);
  78. bool m_isFloatingTopLevel = false;
  79. QList<QAction *> m_titleBarActions;
  80. /**
  81. * Private data constructor
  82. */
  83. DockWidgetPrivate(DockWidget *parent);
  84. /**
  85. * Show dock widget
  86. */
  87. void showDockWidget();
  88. /**
  89. * Hide dock widget.
  90. */
  91. void hideDockWidget();
  92. /**
  93. * Hides a dock area if all dock widgets in the area are closed.
  94. * This function updates the current selected tab and hides the parent
  95. * dock area if it is empty
  96. */
  97. void updateParentDockArea();
  98. /**
  99. * Setup the top tool bar
  100. */
  101. void setupToolBar();
  102. /**
  103. * Setup the main scroll area
  104. */
  105. void setupScrollArea();
  106. };
  107. // struct DockWidgetPrivate
  108. DockWidgetPrivate::DockWidgetPrivate(DockWidget *parent)
  109. : q(parent)
  110. {}
  111. void DockWidgetPrivate::showDockWidget()
  112. {
  113. if (!m_dockArea) {
  114. FloatingDockContainer *floatingWidget = new FloatingDockContainer(q);
  115. floatingWidget->resize(q->size());
  116. floatingWidget->show();
  117. } else {
  118. m_dockArea->setCurrentDockWidget(q);
  119. m_dockArea->toggleView(true);
  120. m_tabWidget->show();
  121. QSplitter *splitter = internal::findParent<QSplitter *>(m_dockArea);
  122. while (splitter && !splitter->isVisible()) {
  123. splitter->show();
  124. splitter = internal::findParent<QSplitter *>(splitter);
  125. }
  126. DockContainerWidget *container = m_dockArea->dockContainer();
  127. if (container->isFloating()) {
  128. FloatingDockContainer *floatingWidget
  129. = internal::findParent<FloatingDockContainer *>(container);
  130. floatingWidget->show();
  131. }
  132. }
  133. }
  134. void DockWidgetPrivate::hideDockWidget()
  135. {
  136. m_tabWidget->hide();
  137. updateParentDockArea();
  138. }
  139. void DockWidgetPrivate::updateParentDockArea()
  140. {
  141. if (!m_dockArea) {
  142. return;
  143. }
  144. auto nextDockWidget = m_dockArea->nextOpenDockWidget(q);
  145. if (nextDockWidget) {
  146. m_dockArea->setCurrentDockWidget(nextDockWidget);
  147. } else {
  148. m_dockArea->hideAreaWithNoVisibleContent();
  149. }
  150. }
  151. void DockWidgetPrivate::setupToolBar()
  152. {
  153. m_toolBar = new QToolBar(q);
  154. m_toolBar->setObjectName("dockWidgetToolBar");
  155. m_layout->insertWidget(0, m_toolBar);
  156. m_toolBar->setIconSize(QSize(16, 16));
  157. m_toolBar->toggleViewAction()->setEnabled(false);
  158. m_toolBar->toggleViewAction()->setVisible(false);
  159. QObject::connect(q, &DockWidget::topLevelChanged, q, &DockWidget::setToolbarFloatingStyle);
  160. }
  161. void DockWidgetPrivate::setupScrollArea()
  162. {
  163. m_scrollArea = new QScrollArea(q);
  164. m_scrollArea->setObjectName("dockWidgetScrollArea");
  165. m_scrollArea->setWidgetResizable(true);
  166. m_layout->addWidget(m_scrollArea);
  167. }
  168. DockWidget::DockWidget(const QString &uniqueId, QWidget *parent)
  169. : QFrame(parent)
  170. , d(new DockWidgetPrivate(this))
  171. {
  172. d->m_layout = new QBoxLayout(QBoxLayout::TopToBottom);
  173. d->m_layout->setContentsMargins(0, 0, 0, 0);
  174. d->m_layout->setSpacing(0);
  175. setLayout(d->m_layout);
  176. setWindowTitle(uniqueId); // temporarily use unique id as title
  177. setObjectName(uniqueId);
  178. d->m_tabWidget = componentsFactory()->createDockWidgetTab(this);
  179. d->m_toggleViewAction = new QAction(uniqueId, this);
  180. d->m_toggleViewAction->setCheckable(true);
  181. connect(d->m_toggleViewAction, &QAction::triggered, this, &DockWidget::toggleView);
  182. setToolbarFloatingStyle(false);
  183. }
  184. DockWidget::~DockWidget()
  185. {
  186. qCInfo(adsLog) << Q_FUNC_INFO;
  187. delete d;
  188. }
  189. void DockWidget::setToggleViewActionChecked(bool checked)
  190. {
  191. QAction *action = d->m_toggleViewAction;
  192. //action->blockSignals(true);
  193. action->setChecked(checked);
  194. //action->blockSignals(false);
  195. }
  196. void DockWidget::setWidget(QWidget *widget, eInsertMode insertMode)
  197. {
  198. QScrollArea *scrollAreaWidget = qobject_cast<QScrollArea *>(widget);
  199. if (scrollAreaWidget || ForceNoScrollArea == insertMode) {
  200. d->m_layout->addWidget(widget);
  201. if (scrollAreaWidget && scrollAreaWidget->viewport()) {
  202. scrollAreaWidget->viewport()->setProperty("dockWidgetContent", true);
  203. }
  204. } else {
  205. d->setupScrollArea();
  206. d->m_scrollArea->setWidget(widget);
  207. }
  208. d->m_widget = widget;
  209. d->m_widget->setProperty("dockWidgetContent", true);
  210. }
  211. QWidget *DockWidget::takeWidget()
  212. {
  213. // TODO Shouldn't m_widget being set to nullptr?!
  214. d->m_scrollArea->takeWidget();
  215. d->m_layout->removeWidget(d->m_widget);
  216. d->m_widget->setParent(nullptr);
  217. return d->m_widget;
  218. }
  219. QWidget *DockWidget::widget() const { return d->m_widget; }
  220. DockWidgetTab *DockWidget::tabWidget() const { return d->m_tabWidget; }
  221. void DockWidget::setFeatures(DockWidgetFeatures features)
  222. {
  223. if (d->m_features == features) {
  224. return;
  225. }
  226. d->m_features = features;
  227. emit featuresChanged(d->m_features);
  228. d->m_tabWidget->onDockWidgetFeaturesChanged();
  229. }
  230. void DockWidget::setFeature(DockWidgetFeature flag, bool on)
  231. {
  232. auto currentFeatures = features();
  233. internal::setFlag(currentFeatures, flag, on);
  234. setFeatures(currentFeatures);
  235. }
  236. DockWidget::DockWidgetFeatures DockWidget::features() const { return d->m_features; }
  237. DockManager *DockWidget::dockManager() const { return d->m_dockManager; }
  238. void DockWidget::setDockManager(DockManager *dockManager) { d->m_dockManager = dockManager; }
  239. DockContainerWidget *DockWidget::dockContainer() const
  240. {
  241. if (d->m_dockArea) {
  242. return d->m_dockArea->dockContainer();
  243. } else {
  244. return nullptr;
  245. }
  246. }
  247. DockAreaWidget *DockWidget::dockAreaWidget() const { return d->m_dockArea; }
  248. bool DockWidget::isFloating() const
  249. {
  250. if (!isInFloatingContainer()) {
  251. return false;
  252. }
  253. return dockContainer()->topLevelDockWidget() == this;
  254. }
  255. bool DockWidget::isInFloatingContainer() const
  256. {
  257. auto container = dockContainer();
  258. if (!container) {
  259. return false;
  260. }
  261. if (!container->isFloating()) {
  262. return false;
  263. }
  264. return true;
  265. }
  266. bool DockWidget::isClosed() const { return d->m_closed; }
  267. QAction *DockWidget::toggleViewAction() const { return d->m_toggleViewAction; }
  268. void DockWidget::setToggleViewActionMode(eToggleViewActionMode mode)
  269. {
  270. if (ActionModeToggle == mode) {
  271. d->m_toggleViewAction->setCheckable(true);
  272. d->m_toggleViewAction->setIcon(QIcon());
  273. } else {
  274. d->m_toggleViewAction->setCheckable(false);
  275. d->m_toggleViewAction->setIcon(d->m_tabWidget->icon());
  276. }
  277. }
  278. void DockWidget::toggleView(bool open)
  279. {
  280. // If the toggle view action mode is ActionModeShow, then Open is always
  281. // true if the sender is the toggle view action
  282. QAction *action = qobject_cast<QAction *>(sender());
  283. if (action == d->m_toggleViewAction && !d->m_toggleViewAction->isCheckable()) {
  284. open = true;
  285. }
  286. // If the dock widget state is different, then we really need to toggle
  287. // the state. If we are in the right state, then we simply make this
  288. // dock widget the current dock widget
  289. if (d->m_closed != !open) {
  290. toggleViewInternal(open);
  291. } else if (open && d->m_dockArea) {
  292. d->m_dockArea->setCurrentDockWidget(this);
  293. }
  294. }
  295. void DockWidget::toggleViewInternal(bool open)
  296. {
  297. DockContainerWidget *dockContainerWidget = dockContainer();
  298. DockWidget *topLevelDockWidgetBefore = dockContainerWidget
  299. ? dockContainerWidget->topLevelDockWidget()
  300. : nullptr;
  301. if (open) {
  302. d->showDockWidget();
  303. } else {
  304. d->hideDockWidget();
  305. }
  306. d->m_closed = !open;
  307. //d->m_toggleViewAction->blockSignals(true);
  308. d->m_toggleViewAction->setChecked(open);
  309. //d->m_toggleViewAction->blockSignals(false);
  310. if (d->m_dockArea) {
  311. d->m_dockArea->toggleDockWidgetView(this, open);
  312. }
  313. if (open && topLevelDockWidgetBefore) {
  314. DockWidget::emitTopLevelEventForWidget(topLevelDockWidgetBefore, false);
  315. }
  316. // Here we need to call the dockContainer() function again, because if
  317. // this dock widget was unassigned before the call to showDockWidget() then
  318. // it has a dock container now
  319. dockContainerWidget = dockContainer();
  320. DockWidget *topLevelDockWidgetAfter = dockContainerWidget
  321. ? dockContainerWidget->topLevelDockWidget()
  322. : nullptr;
  323. DockWidget::emitTopLevelEventForWidget(topLevelDockWidgetAfter, true);
  324. FloatingDockContainer *floatingContainer = dockContainerWidget->floatingWidget();
  325. if (floatingContainer) {
  326. floatingContainer->updateWindowTitle();
  327. }
  328. if (!open) {
  329. emit closed();
  330. }
  331. emit viewToggled(open);
  332. }
  333. void DockWidget::setDockArea(DockAreaWidget *dockArea)
  334. {
  335. d->m_dockArea = dockArea;
  336. d->m_toggleViewAction->setChecked(dockArea != nullptr && !this->isClosed());
  337. }
  338. void DockWidget::saveState(QXmlStreamWriter &stream) const
  339. {
  340. stream.writeStartElement("widget");
  341. stream.writeAttribute("name", objectName());
  342. stream.writeAttribute("closed", QVariant::fromValue(d->m_closed).toString());
  343. stream.writeEndElement();
  344. }
  345. void DockWidget::flagAsUnassigned()
  346. {
  347. d->m_closed = true;
  348. setParent(d->m_dockManager);
  349. setVisible(false);
  350. setDockArea(nullptr);
  351. tabWidget()->setParent(this);
  352. }
  353. bool DockWidget::event(QEvent *event)
  354. {
  355. switch (event->type()) {
  356. case QEvent::Hide:
  357. emit visibilityChanged(false);
  358. break;
  359. case QEvent::Show:
  360. emit visibilityChanged(geometry().right() >= 0 && geometry().bottom() >= 0);
  361. break;
  362. case QEvent::WindowTitleChange :
  363. {
  364. const auto title = windowTitle();
  365. if (d->m_tabWidget) {
  366. d->m_tabWidget->setText(title);
  367. }
  368. if (d->m_toggleViewAction) {
  369. d->m_toggleViewAction->setText(title);
  370. }
  371. if (d->m_dockArea) {
  372. d->m_dockArea->markTitleBarMenuOutdated(); // update tabs menu
  373. }
  374. emit titleChanged(title);
  375. }
  376. break;
  377. default:
  378. break;
  379. }
  380. return Super::event(event);
  381. }
  382. #ifndef QT_NO_TOOLTIP
  383. void DockWidget::setTabToolTip(const QString &text)
  384. {
  385. if (d->m_tabWidget) {
  386. d->m_tabWidget->setToolTip(text);
  387. }
  388. if (d->m_toggleViewAction) {
  389. d->m_toggleViewAction->setToolTip(text);
  390. }
  391. if (d->m_dockArea) {
  392. d->m_dockArea->markTitleBarMenuOutdated(); //update tabs menu
  393. }
  394. }
  395. #endif
  396. void DockWidget::setIcon(const QIcon &icon)
  397. {
  398. d->m_tabWidget->setIcon(icon);
  399. if (!d->m_toggleViewAction->isCheckable()) {
  400. d->m_toggleViewAction->setIcon(icon);
  401. }
  402. }
  403. QIcon DockWidget::icon() const { return d->m_tabWidget->icon(); }
  404. QToolBar *DockWidget::toolBar() const { return d->m_toolBar; }
  405. QToolBar *DockWidget::createDefaultToolBar()
  406. {
  407. if (!d->m_toolBar) {
  408. d->setupToolBar();
  409. }
  410. return d->m_toolBar;
  411. }
  412. void DockWidget::setToolBar(QToolBar *toolBar)
  413. {
  414. if (d->m_toolBar) {
  415. delete d->m_toolBar;
  416. }
  417. d->m_toolBar = toolBar;
  418. d->m_layout->insertWidget(0, d->m_toolBar);
  419. connect(this, &DockWidget::topLevelChanged, this, &DockWidget::setToolbarFloatingStyle);
  420. setToolbarFloatingStyle(isFloating());
  421. }
  422. void DockWidget::setToolBarStyle(Qt::ToolButtonStyle style, eState state)
  423. {
  424. if (StateFloating == state) {
  425. d->m_toolBarStyleFloating = style;
  426. } else {
  427. d->m_toolBarStyleDocked = style;
  428. }
  429. setToolbarFloatingStyle(isFloating());
  430. }
  431. Qt::ToolButtonStyle DockWidget::toolBarStyle(eState state) const
  432. {
  433. if (StateFloating == state) {
  434. return d->m_toolBarStyleFloating;
  435. } else {
  436. return d->m_toolBarStyleDocked;
  437. }
  438. }
  439. void DockWidget::setToolBarIconSize(const QSize &iconSize, eState state)
  440. {
  441. if (StateFloating == state) {
  442. d->m_toolBarIconSizeFloating = iconSize;
  443. } else {
  444. d->m_toolBarIconSizeDocked = iconSize;
  445. }
  446. setToolbarFloatingStyle(isFloating());
  447. }
  448. QSize DockWidget::toolBarIconSize(eState state) const
  449. {
  450. if (StateFloating == state) {
  451. return d->m_toolBarIconSizeFloating;
  452. } else {
  453. return d->m_toolBarIconSizeDocked;
  454. }
  455. }
  456. void DockWidget::setToolbarFloatingStyle(bool floating)
  457. {
  458. if (!d->m_toolBar) {
  459. return;
  460. }
  461. auto iconSize = floating ? d->m_toolBarIconSizeFloating : d->m_toolBarIconSizeDocked;
  462. if (iconSize != d->m_toolBar->iconSize()) {
  463. d->m_toolBar->setIconSize(iconSize);
  464. }
  465. auto buttonStyle = floating ? d->m_toolBarStyleFloating : d->m_toolBarStyleDocked;
  466. if (buttonStyle != d->m_toolBar->toolButtonStyle()) {
  467. d->m_toolBar->setToolButtonStyle(buttonStyle);
  468. }
  469. }
  470. void DockWidget::emitTopLevelEventForWidget(DockWidget *topLevelDockWidget, bool floating)
  471. {
  472. if (topLevelDockWidget) {
  473. topLevelDockWidget->dockAreaWidget()->updateTitleBarVisibility();
  474. topLevelDockWidget->emitTopLevelChanged(floating);
  475. }
  476. }
  477. void DockWidget::emitTopLevelChanged(bool floating)
  478. {
  479. if (floating != d->m_isFloatingTopLevel) {
  480. d->m_isFloatingTopLevel = floating;
  481. emit topLevelChanged(d->m_isFloatingTopLevel);
  482. }
  483. }
  484. void DockWidget::setClosedState(bool closed) { d->m_closed = closed; }
  485. QSize DockWidget::minimumSizeHint() const { return QSize(60, 40); }
  486. void DockWidget::setFloating()
  487. {
  488. if (isClosed()) {
  489. return;
  490. }
  491. d->m_tabWidget->detachDockWidget();
  492. }
  493. void DockWidget::deleteDockWidget()
  494. {
  495. dockManager()->removeDockWidget(this);
  496. deleteLater();
  497. d->m_closed = true;
  498. }
  499. void DockWidget::closeDockWidget()
  500. {
  501. closeDockWidgetInternal(true);
  502. }
  503. bool DockWidget::closeDockWidgetInternal(bool forceClose)
  504. {
  505. if (!forceClose) {
  506. emit closeRequested();
  507. }
  508. if (!forceClose && features().testFlag(DockWidget::CustomCloseHandling)) {
  509. return false;
  510. }
  511. if (features().testFlag(DockWidget::DockWidgetDeleteOnClose)) {
  512. // If the dock widget is floating, then we check if we also need to
  513. // delete the floating widget
  514. if (isFloating()) {
  515. FloatingDockContainer* floatingWidget = internal::findParent<
  516. FloatingDockContainer *>(this);
  517. if (floatingWidget->dockWidgets().count() == 1) {
  518. floatingWidget->deleteLater();
  519. } else {
  520. floatingWidget->hide();
  521. }
  522. }
  523. deleteDockWidget();
  524. } else {
  525. toggleView(false);
  526. }
  527. return true;
  528. }
  529. void DockWidget::setTitleBarActions(QList<QAction *> actions)
  530. {
  531. d->m_titleBarActions = actions;
  532. }
  533. QList<QAction *> DockWidget::titleBarActions() const
  534. {
  535. return d->m_titleBarActions;
  536. }
  537. } // namespace ADS