| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625 |
- /****************************************************************************
- **
- ** Copyright (C) 2020 Uwe Kindler
- ** Contact: https://www.qt.io/licensing/
- **
- ** This file is part of Qt Creator.
- **
- ** Commercial License Usage
- ** Licensees holding valid commercial Qt licenses may use this file in
- ** accordance with the commercial license agreement provided with the
- ** Software or, alternatively, in accordance with the terms contained in
- ** a written agreement between you and The Qt Company. For licensing terms
- ** and conditions see https://www.qt.io/terms-conditions. For further
- ** information use the contact form at https://www.qt.io/contact-us.
- **
- ** GNU Lesser General Public License Usage
- ** Alternatively, this file may be used under the terms of the GNU Lesser
- ** General Public License version 2.1 or (at your option) any later version.
- ** The licenses are as published by the Free Software Foundation
- ** and appearing in the file LICENSE.LGPLv21 included in the packaging
- ** of this file. Please review the following information to ensure
- ** the GNU Lesser General Public License version 2.1 requirements
- ** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
- **
- ** GNU General Public License Usage
- ** Alternatively, this file may be used under the terms of the GNU
- ** General Public License version 3 or (at your option) any later version
- ** approved by the KDE Free Qt Foundation. The licenses are as published by
- ** the Free Software Foundation and appearing in the file LICENSE.GPL3
- ** included in the packaging of this file. Please review the following
- ** information to ensure the GNU General Public License requirements will
- ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
- **
- ****************************************************************************/
- #include "dockwidget.h"
- #include "ads_globals.h"
- #include "dockareawidget.h"
- #include "dockcomponentsfactory.h"
- #include "dockcontainerwidget.h"
- #include "dockmanager.h"
- #include "docksplitter.h"
- #include "dockwidgettab.h"
- #include "floatingdockcontainer.h"
- #include <QAction>
- #include <QBoxLayout>
- #include <QEvent>
- #include <QLoggingCategory>
- #include <QPointer>
- #include <QScrollArea>
- #include <QSplitter>
- #include <QStack>
- #include <QTextStream>
- #include <QToolBar>
- #include <QXmlStreamWriter>
- static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
- namespace ADS
- {
- /**
- * Private data class of DockWidget class (pimpl)
- */
- struct DockWidgetPrivate
- {
- DockWidget *q = nullptr;
- QBoxLayout *m_layout = nullptr;
- QWidget *m_widget = nullptr;
- DockWidgetTab *m_tabWidget = nullptr;
- DockWidget::DockWidgetFeatures m_features = DockWidget::DefaultDockWidgetFeatures;
- DockManager *m_dockManager = nullptr;
- DockAreaWidget *m_dockArea = nullptr;
- QAction *m_toggleViewAction = nullptr;
- bool m_closed = false;
- QScrollArea *m_scrollArea = nullptr;
- QToolBar *m_toolBar = nullptr;
- Qt::ToolButtonStyle m_toolBarStyleDocked = Qt::ToolButtonIconOnly;
- Qt::ToolButtonStyle m_toolBarStyleFloating = Qt::ToolButtonTextUnderIcon;
- QSize m_toolBarIconSizeDocked = QSize(16, 16);
- QSize m_toolBarIconSizeFloating = QSize(24, 24);
- bool m_isFloatingTopLevel = false;
- QList<QAction *> m_titleBarActions;
- /**
- * Private data constructor
- */
- DockWidgetPrivate(DockWidget *parent);
- /**
- * Show dock widget
- */
- void showDockWidget();
- /**
- * Hide dock widget.
- */
- void hideDockWidget();
- /**
- * Hides a dock area if all dock widgets in the area are closed.
- * This function updates the current selected tab and hides the parent
- * dock area if it is empty
- */
- void updateParentDockArea();
- /**
- * Setup the top tool bar
- */
- void setupToolBar();
- /**
- * Setup the main scroll area
- */
- void setupScrollArea();
- };
- // struct DockWidgetPrivate
- DockWidgetPrivate::DockWidgetPrivate(DockWidget *parent)
- : q(parent)
- {}
- void DockWidgetPrivate::showDockWidget()
- {
- if (!m_dockArea) {
- FloatingDockContainer *floatingWidget = new FloatingDockContainer(q);
- floatingWidget->resize(q->size());
- floatingWidget->show();
- } else {
- m_dockArea->setCurrentDockWidget(q);
- m_dockArea->toggleView(true);
- m_tabWidget->show();
- QSplitter *splitter = internal::findParent<QSplitter *>(m_dockArea);
- while (splitter && !splitter->isVisible()) {
- splitter->show();
- splitter = internal::findParent<QSplitter *>(splitter);
- }
- DockContainerWidget *container = m_dockArea->dockContainer();
- if (container->isFloating()) {
- FloatingDockContainer *floatingWidget
- = internal::findParent<FloatingDockContainer *>(container);
- floatingWidget->show();
- }
- }
- }
- void DockWidgetPrivate::hideDockWidget()
- {
- m_tabWidget->hide();
- updateParentDockArea();
- }
- void DockWidgetPrivate::updateParentDockArea()
- {
- if (!m_dockArea) {
- return;
- }
- auto nextDockWidget = m_dockArea->nextOpenDockWidget(q);
- if (nextDockWidget) {
- m_dockArea->setCurrentDockWidget(nextDockWidget);
- } else {
- m_dockArea->hideAreaWithNoVisibleContent();
- }
- }
- void DockWidgetPrivate::setupToolBar()
- {
- m_toolBar = new QToolBar(q);
- m_toolBar->setObjectName("dockWidgetToolBar");
- m_layout->insertWidget(0, m_toolBar);
- m_toolBar->setIconSize(QSize(16, 16));
- m_toolBar->toggleViewAction()->setEnabled(false);
- m_toolBar->toggleViewAction()->setVisible(false);
- QObject::connect(q, &DockWidget::topLevelChanged, q, &DockWidget::setToolbarFloatingStyle);
- }
- void DockWidgetPrivate::setupScrollArea()
- {
- m_scrollArea = new QScrollArea(q);
- m_scrollArea->setObjectName("dockWidgetScrollArea");
- m_scrollArea->setWidgetResizable(true);
- m_layout->addWidget(m_scrollArea);
- }
- DockWidget::DockWidget(const QString &uniqueId, QWidget *parent)
- : QFrame(parent)
- , d(new DockWidgetPrivate(this))
- {
- d->m_layout = new QBoxLayout(QBoxLayout::TopToBottom);
- d->m_layout->setContentsMargins(0, 0, 0, 0);
- d->m_layout->setSpacing(0);
- setLayout(d->m_layout);
- setWindowTitle(uniqueId); // temporarily use unique id as title
- setObjectName(uniqueId);
- d->m_tabWidget = componentsFactory()->createDockWidgetTab(this);
- d->m_toggleViewAction = new QAction(uniqueId, this);
- d->m_toggleViewAction->setCheckable(true);
- connect(d->m_toggleViewAction, &QAction::triggered, this, &DockWidget::toggleView);
- setToolbarFloatingStyle(false);
- }
- DockWidget::~DockWidget()
- {
- qCInfo(adsLog) << Q_FUNC_INFO;
- delete d;
- }
- void DockWidget::setToggleViewActionChecked(bool checked)
- {
- QAction *action = d->m_toggleViewAction;
- //action->blockSignals(true);
- action->setChecked(checked);
- //action->blockSignals(false);
- }
- void DockWidget::setWidget(QWidget *widget, eInsertMode insertMode)
- {
- QScrollArea *scrollAreaWidget = qobject_cast<QScrollArea *>(widget);
- if (scrollAreaWidget || ForceNoScrollArea == insertMode) {
- d->m_layout->addWidget(widget);
- if (scrollAreaWidget && scrollAreaWidget->viewport()) {
- scrollAreaWidget->viewport()->setProperty("dockWidgetContent", true);
- }
- } else {
- d->setupScrollArea();
- d->m_scrollArea->setWidget(widget);
- }
- d->m_widget = widget;
- d->m_widget->setProperty("dockWidgetContent", true);
- }
- QWidget *DockWidget::takeWidget()
- {
- // TODO Shouldn't m_widget being set to nullptr?!
- d->m_scrollArea->takeWidget();
- d->m_layout->removeWidget(d->m_widget);
- d->m_widget->setParent(nullptr);
- return d->m_widget;
- }
- QWidget *DockWidget::widget() const { return d->m_widget; }
- DockWidgetTab *DockWidget::tabWidget() const { return d->m_tabWidget; }
- void DockWidget::setFeatures(DockWidgetFeatures features)
- {
- if (d->m_features == features) {
- return;
- }
- d->m_features = features;
- emit featuresChanged(d->m_features);
- d->m_tabWidget->onDockWidgetFeaturesChanged();
- }
- void DockWidget::setFeature(DockWidgetFeature flag, bool on)
- {
- auto currentFeatures = features();
- internal::setFlag(currentFeatures, flag, on);
- setFeatures(currentFeatures);
- }
- DockWidget::DockWidgetFeatures DockWidget::features() const { return d->m_features; }
- DockManager *DockWidget::dockManager() const { return d->m_dockManager; }
- void DockWidget::setDockManager(DockManager *dockManager) { d->m_dockManager = dockManager; }
- DockContainerWidget *DockWidget::dockContainer() const
- {
- if (d->m_dockArea) {
- return d->m_dockArea->dockContainer();
- } else {
- return nullptr;
- }
- }
- DockAreaWidget *DockWidget::dockAreaWidget() const { return d->m_dockArea; }
- bool DockWidget::isFloating() const
- {
- if (!isInFloatingContainer()) {
- return false;
- }
- return dockContainer()->topLevelDockWidget() == this;
- }
- bool DockWidget::isInFloatingContainer() const
- {
- auto container = dockContainer();
- if (!container) {
- return false;
- }
- if (!container->isFloating()) {
- return false;
- }
- return true;
- }
- bool DockWidget::isClosed() const { return d->m_closed; }
- QAction *DockWidget::toggleViewAction() const { return d->m_toggleViewAction; }
- void DockWidget::setToggleViewActionMode(eToggleViewActionMode mode)
- {
- if (ActionModeToggle == mode) {
- d->m_toggleViewAction->setCheckable(true);
- d->m_toggleViewAction->setIcon(QIcon());
- } else {
- d->m_toggleViewAction->setCheckable(false);
- d->m_toggleViewAction->setIcon(d->m_tabWidget->icon());
- }
- }
- void DockWidget::toggleView(bool open)
- {
- // If the toggle view action mode is ActionModeShow, then Open is always
- // true if the sender is the toggle view action
- QAction *action = qobject_cast<QAction *>(sender());
- if (action == d->m_toggleViewAction && !d->m_toggleViewAction->isCheckable()) {
- open = true;
- }
- // If the dock widget state is different, then we really need to toggle
- // the state. If we are in the right state, then we simply make this
- // dock widget the current dock widget
- if (d->m_closed != !open) {
- toggleViewInternal(open);
- } else if (open && d->m_dockArea) {
- d->m_dockArea->setCurrentDockWidget(this);
- }
- }
- void DockWidget::toggleViewInternal(bool open)
- {
- DockContainerWidget *dockContainerWidget = dockContainer();
- DockWidget *topLevelDockWidgetBefore = dockContainerWidget
- ? dockContainerWidget->topLevelDockWidget()
- : nullptr;
- if (open) {
- d->showDockWidget();
- } else {
- d->hideDockWidget();
- }
- d->m_closed = !open;
- //d->m_toggleViewAction->blockSignals(true);
- d->m_toggleViewAction->setChecked(open);
- //d->m_toggleViewAction->blockSignals(false);
- if (d->m_dockArea) {
- d->m_dockArea->toggleDockWidgetView(this, open);
- }
- if (open && topLevelDockWidgetBefore) {
- DockWidget::emitTopLevelEventForWidget(topLevelDockWidgetBefore, false);
- }
- // Here we need to call the dockContainer() function again, because if
- // this dock widget was unassigned before the call to showDockWidget() then
- // it has a dock container now
- dockContainerWidget = dockContainer();
- DockWidget *topLevelDockWidgetAfter = dockContainerWidget
- ? dockContainerWidget->topLevelDockWidget()
- : nullptr;
- DockWidget::emitTopLevelEventForWidget(topLevelDockWidgetAfter, true);
- FloatingDockContainer *floatingContainer = dockContainerWidget->floatingWidget();
- if (floatingContainer) {
- floatingContainer->updateWindowTitle();
- }
- if (!open) {
- emit closed();
- }
- emit viewToggled(open);
- }
- void DockWidget::setDockArea(DockAreaWidget *dockArea)
- {
- d->m_dockArea = dockArea;
- d->m_toggleViewAction->setChecked(dockArea != nullptr && !this->isClosed());
- }
- void DockWidget::saveState(QXmlStreamWriter &stream) const
- {
- stream.writeStartElement("widget");
- stream.writeAttribute("name", objectName());
- stream.writeAttribute("closed", QVariant::fromValue(d->m_closed).toString());
- stream.writeEndElement();
- }
- void DockWidget::flagAsUnassigned()
- {
- d->m_closed = true;
- setParent(d->m_dockManager);
- setVisible(false);
- setDockArea(nullptr);
- tabWidget()->setParent(this);
- }
- bool DockWidget::event(QEvent *event)
- {
- switch (event->type()) {
- case QEvent::Hide:
- emit visibilityChanged(false);
- break;
- case QEvent::Show:
- emit visibilityChanged(geometry().right() >= 0 && geometry().bottom() >= 0);
- break;
- case QEvent::WindowTitleChange :
- {
- const auto title = windowTitle();
- if (d->m_tabWidget) {
- d->m_tabWidget->setText(title);
- }
- if (d->m_toggleViewAction) {
- d->m_toggleViewAction->setText(title);
- }
- if (d->m_dockArea) {
- d->m_dockArea->markTitleBarMenuOutdated(); // update tabs menu
- }
- emit titleChanged(title);
- }
- break;
- default:
- break;
- }
- return Super::event(event);
- }
- #ifndef QT_NO_TOOLTIP
- void DockWidget::setTabToolTip(const QString &text)
- {
- if (d->m_tabWidget) {
- d->m_tabWidget->setToolTip(text);
- }
- if (d->m_toggleViewAction) {
- d->m_toggleViewAction->setToolTip(text);
- }
- if (d->m_dockArea) {
- d->m_dockArea->markTitleBarMenuOutdated(); //update tabs menu
- }
- }
- #endif
- void DockWidget::setIcon(const QIcon &icon)
- {
- d->m_tabWidget->setIcon(icon);
- if (!d->m_toggleViewAction->isCheckable()) {
- d->m_toggleViewAction->setIcon(icon);
- }
- }
- QIcon DockWidget::icon() const { return d->m_tabWidget->icon(); }
- QToolBar *DockWidget::toolBar() const { return d->m_toolBar; }
- QToolBar *DockWidget::createDefaultToolBar()
- {
- if (!d->m_toolBar) {
- d->setupToolBar();
- }
- return d->m_toolBar;
- }
- void DockWidget::setToolBar(QToolBar *toolBar)
- {
- if (d->m_toolBar) {
- delete d->m_toolBar;
- }
- d->m_toolBar = toolBar;
- d->m_layout->insertWidget(0, d->m_toolBar);
- connect(this, &DockWidget::topLevelChanged, this, &DockWidget::setToolbarFloatingStyle);
- setToolbarFloatingStyle(isFloating());
- }
- void DockWidget::setToolBarStyle(Qt::ToolButtonStyle style, eState state)
- {
- if (StateFloating == state) {
- d->m_toolBarStyleFloating = style;
- } else {
- d->m_toolBarStyleDocked = style;
- }
- setToolbarFloatingStyle(isFloating());
- }
- Qt::ToolButtonStyle DockWidget::toolBarStyle(eState state) const
- {
- if (StateFloating == state) {
- return d->m_toolBarStyleFloating;
- } else {
- return d->m_toolBarStyleDocked;
- }
- }
- void DockWidget::setToolBarIconSize(const QSize &iconSize, eState state)
- {
- if (StateFloating == state) {
- d->m_toolBarIconSizeFloating = iconSize;
- } else {
- d->m_toolBarIconSizeDocked = iconSize;
- }
- setToolbarFloatingStyle(isFloating());
- }
- QSize DockWidget::toolBarIconSize(eState state) const
- {
- if (StateFloating == state) {
- return d->m_toolBarIconSizeFloating;
- } else {
- return d->m_toolBarIconSizeDocked;
- }
- }
- void DockWidget::setToolbarFloatingStyle(bool floating)
- {
- if (!d->m_toolBar) {
- return;
- }
- auto iconSize = floating ? d->m_toolBarIconSizeFloating : d->m_toolBarIconSizeDocked;
- if (iconSize != d->m_toolBar->iconSize()) {
- d->m_toolBar->setIconSize(iconSize);
- }
- auto buttonStyle = floating ? d->m_toolBarStyleFloating : d->m_toolBarStyleDocked;
- if (buttonStyle != d->m_toolBar->toolButtonStyle()) {
- d->m_toolBar->setToolButtonStyle(buttonStyle);
- }
- }
- void DockWidget::emitTopLevelEventForWidget(DockWidget *topLevelDockWidget, bool floating)
- {
- if (topLevelDockWidget) {
- topLevelDockWidget->dockAreaWidget()->updateTitleBarVisibility();
- topLevelDockWidget->emitTopLevelChanged(floating);
- }
- }
- void DockWidget::emitTopLevelChanged(bool floating)
- {
- if (floating != d->m_isFloatingTopLevel) {
- d->m_isFloatingTopLevel = floating;
- emit topLevelChanged(d->m_isFloatingTopLevel);
- }
- }
- void DockWidget::setClosedState(bool closed) { d->m_closed = closed; }
- QSize DockWidget::minimumSizeHint() const { return QSize(60, 40); }
- void DockWidget::setFloating()
- {
- if (isClosed()) {
- return;
- }
- d->m_tabWidget->detachDockWidget();
- }
- void DockWidget::deleteDockWidget()
- {
- dockManager()->removeDockWidget(this);
- deleteLater();
- d->m_closed = true;
- }
- void DockWidget::closeDockWidget()
- {
- closeDockWidgetInternal(true);
- }
- bool DockWidget::closeDockWidgetInternal(bool forceClose)
- {
- if (!forceClose) {
- emit closeRequested();
- }
- if (!forceClose && features().testFlag(DockWidget::CustomCloseHandling)) {
- return false;
- }
- if (features().testFlag(DockWidget::DockWidgetDeleteOnClose)) {
- // If the dock widget is floating, then we check if we also need to
- // delete the floating widget
- if (isFloating()) {
- FloatingDockContainer* floatingWidget = internal::findParent<
- FloatingDockContainer *>(this);
- if (floatingWidget->dockWidgets().count() == 1) {
- floatingWidget->deleteLater();
- } else {
- floatingWidget->hide();
- }
- }
- deleteDockWidget();
- } else {
- toggleView(false);
- }
- return true;
- }
- void DockWidget::setTitleBarActions(QList<QAction *> actions)
- {
- d->m_titleBarActions = actions;
- }
- QList<QAction *> DockWidget::titleBarActions() const
- {
- return d->m_titleBarActions;
- }
- } // namespace ADS
|