| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686 |
- /****************************************************************************
- **
- ** 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 "dockareawidget.h"
- #include "dockareatabbar.h"
- #include "dockareatitlebar.h"
- #include "dockcomponentsfactory.h"
- #include "dockcontainerwidget.h"
- #include "dockmanager.h"
- #include "dockoverlay.h"
- #include "docksplitter.h"
- #include "dockwidget.h"
- #include "dockwidgettab.h"
- #include "floatingdockcontainer.h"
- #include <QList>
- #include <QLoggingCategory>
- #include <QMenu>
- #include <QPushButton>
- #include <QScrollArea>
- #include <QScrollBar>
- #include <QSplitter>
- #include <QStackedLayout>
- #include <QStyle>
- #include <QVector>
- #include <QWheelEvent>
- #include <QXmlStreamWriter>
- #include <iostream>
- static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
- namespace ADS
- {
- static const char *const INDEX_PROPERTY = "index";
- static const char *const ACTION_PROPERTY = "action";
- /**
- * Internal dock area layout mimics stack layout but only inserts the current
- * widget into the internal QLayout object.
- * \warning Only the current widget has a parent. All other widgets
- * do not have a parent. That means, a widget that is in this layout may
- * return nullptr for its parent() function if it is not the current widget.
- */
- class DockAreaLayout
- {
- private:
- QBoxLayout *m_parentLayout;
- QList<QWidget *> m_widgets;
- int m_currentIndex = -1;
- QWidget *m_currentWidget = nullptr;
- public:
- /**
- * Creates an instance with the given parent layout
- */
- DockAreaLayout(QBoxLayout *parentLayout)
- : m_parentLayout(parentLayout)
- {}
- /**
- * Returns the number of widgets in this layout
- */
- int count() const { return m_widgets.count(); }
- /**
- * Inserts the widget at the given index position into the internal widget
- * list
- */
- void insertWidget(int index, QWidget *widget)
- {
- widget->setParent(nullptr);
- if (index < 0) {
- index = m_widgets.count();
- }
- m_widgets.insert(index, widget);
- if (m_currentIndex < 0) {
- setCurrentIndex(index);
- } else {
- if (index <= m_currentIndex) {
- ++m_currentIndex;
- }
- }
- }
- /**
- * Removes the given widget from the layout
- */
- void removeWidget(QWidget *widget)
- {
- if (currentWidget() == widget) {
- auto layoutItem = m_parentLayout->takeAt(1);
- if (layoutItem) {
- layoutItem->widget()->setParent(nullptr);
- }
- m_currentWidget = nullptr;
- m_currentIndex = -1;
- }
- m_widgets.removeOne(widget);
- }
- /**
- * Returns the current selected widget
- */
- QWidget *currentWidget() const { return m_currentWidget; }
- /**
- * Activates the widget with the give index.
- */
- void setCurrentIndex(int index)
- {
- QWidget *prev = currentWidget();
- QWidget *next = widget(index);
- if (!next || (next == prev && !m_currentWidget)) {
- return;
- }
- bool reenableUpdates = false;
- QWidget *parent = m_parentLayout->parentWidget();
- if (parent && parent->updatesEnabled()) {
- reenableUpdates = true;
- parent->setUpdatesEnabled(false);
- }
- // TODO
- auto layoutItem = m_parentLayout->takeAt(1);
- if (layoutItem) {
- layoutItem->widget()->setParent(nullptr);
- }
- m_parentLayout->addWidget(next);
- if (prev) {
- prev->hide();
- }
- m_currentIndex = index;
- m_currentWidget = next;
- if (reenableUpdates) {
- parent->setUpdatesEnabled(true);
- }
- }
- /**
- * Returns the index of the current active widget
- */
- int currentIndex() const { return m_currentIndex; }
- /**
- * Returns true if there are no widgets in the layout
- */
- bool isEmpty() const { return m_widgets.empty(); }
- /**
- * Returns the index of the given widget
- */
- int indexOf(QWidget *widget) const { return m_widgets.indexOf(widget); }
- /**
- * Returns the widget for the given index
- */
- QWidget *widget(int index) const
- {
- return (index < m_widgets.size()) ? m_widgets.at(index) : nullptr;
- }
- /**
- * Returns the geometry of the current active widget
- */
- QRect geometry() const { return m_widgets.empty() ? QRect() : currentWidget()->geometry(); }
- };
- /**
- * Private data class of DockAreaWidget class (pimpl)
- */
- struct DockAreaWidgetPrivate
- {
- DockAreaWidget *q = nullptr;
- QBoxLayout *m_layout = nullptr;
- DockAreaLayout *m_contentsLayout = nullptr;
- DockAreaTitleBar *m_titleBar = nullptr;
- DockManager *m_dockManager = nullptr;
- bool m_updateTitleBarButtons = false;
- DockWidgetAreas m_allowedAreas = AllDockAreas;
- /**
- * Private data constructor
- */
- DockAreaWidgetPrivate(DockAreaWidget *parent);
- /**
- * Creates the layout for top area with tabs and close button
- */
- void createTitleBar();
- /**
- * Returns the dock widget with the given index
- */
- DockWidget *dockWidgetAt(int index)
- {
- return qobject_cast<DockWidget *>(m_contentsLayout->widget(index));
- }
- /**
- * Convenience function to ease title widget access by index
- */
- DockWidgetTab *tabWidgetAt(int index) { return dockWidgetAt(index)->tabWidget(); }
- /**
- * Returns the tab action of the given dock widget
- */
- QAction *dockWidgetTabAction(DockWidget *dockWidget) const
- {
- return qvariant_cast<QAction *>(dockWidget->property(ACTION_PROPERTY));
- }
- /**
- * Returns the index of the given dock widget
- */
- int dockWidgetIndex(DockWidget *dockWidget) const
- {
- return dockWidget->property(INDEX_PROPERTY).toInt();
- }
- /**
- * Convenience function for tabbar access
- */
- DockAreaTabBar *tabBar() const { return m_titleBar->tabBar(); }
- /**
- * Udpates the enable state of the close and detach button
- */
- void updateTitleBarButtonStates();
- };
- // struct DockAreaWidgetPrivate
- DockAreaWidgetPrivate::DockAreaWidgetPrivate(DockAreaWidget *parent)
- : q(parent)
- {}
- void DockAreaWidgetPrivate::createTitleBar()
- {
- m_titleBar = componentsFactory()->createDockAreaTitleBar(q);
- m_layout->addWidget(m_titleBar);
- QObject::connect(tabBar(),
- &DockAreaTabBar::tabCloseRequested,
- q,
- &DockAreaWidget::onTabCloseRequested);
- QObject::connect(m_titleBar,
- &DockAreaTitleBar::tabBarClicked,
- q,
- &DockAreaWidget::setCurrentIndex);
- QObject::connect(tabBar(), &DockAreaTabBar::tabMoved, q, &DockAreaWidget::reorderDockWidget);
- }
- void DockAreaWidgetPrivate::updateTitleBarButtonStates()
- {
- if (q->isHidden()) {
- m_updateTitleBarButtons = true;
- return;
- }
- m_titleBar->button(TitleBarButtonClose)
- ->setEnabled(q->features().testFlag(DockWidget::DockWidgetClosable));
- m_titleBar->button(TitleBarButtonUndock)
- ->setEnabled(q->features().testFlag(DockWidget::DockWidgetFloatable));
- m_titleBar->updateDockWidgetActionsButtons();
- m_updateTitleBarButtons = false;
- }
- DockAreaWidget::DockAreaWidget(DockManager *dockManager, DockContainerWidget *parent)
- : QFrame(parent)
- , d(new DockAreaWidgetPrivate(this))
- {
- d->m_dockManager = dockManager;
- d->m_layout = new QBoxLayout(QBoxLayout::TopToBottom);
- d->m_layout->setContentsMargins(0, 0, 0, 0);
- d->m_layout->setSpacing(0);
- setLayout(d->m_layout);
- d->createTitleBar();
- d->m_contentsLayout = new DockAreaLayout(d->m_layout);
- if (d->m_dockManager) {
- emit d->m_dockManager->dockAreaCreated(this);
- }
- }
- DockAreaWidget::~DockAreaWidget()
- {
- qCInfo(adsLog) << Q_FUNC_INFO;
- delete d->m_contentsLayout;
- delete d;
- }
- DockManager *DockAreaWidget::dockManager() const { return d->m_dockManager; }
- DockContainerWidget *DockAreaWidget::dockContainer() const
- {
- return internal::findParent<DockContainerWidget *>(this);
- }
- void DockAreaWidget::addDockWidget(DockWidget *dockWidget)
- {
- insertDockWidget(d->m_contentsLayout->count(), dockWidget);
- }
- void DockAreaWidget::insertDockWidget(int index, DockWidget *dockWidget, bool activate)
- {
- d->m_contentsLayout->insertWidget(index, dockWidget);
- dockWidget->tabWidget()->setDockAreaWidget(this);
- auto tabWidget = dockWidget->tabWidget();
- // Inserting the tab will change the current index which in turn will
- // make the tab widget visible in the slot
- d->tabBar()->blockSignals(true);
- d->tabBar()->insertTab(index, tabWidget);
- d->tabBar()->blockSignals(false);
- tabWidget->setVisible(!dockWidget->isClosed());
- dockWidget->setProperty(INDEX_PROPERTY, index);
- if (activate) {
- setCurrentIndex(index);
- }
- dockWidget->setDockArea(this);
- d->updateTitleBarButtonStates();
- }
- void DockAreaWidget::removeDockWidget(DockWidget *dockWidget)
- {
- qCInfo(adsLog) << Q_FUNC_INFO;
- auto nextOpen = nextOpenDockWidget(dockWidget);
- d->m_contentsLayout->removeWidget(dockWidget);
- auto tabWidget = dockWidget->tabWidget();
- tabWidget->hide();
- d->tabBar()->removeTab(tabWidget);
- DockContainerWidget *dockContainerWidget = dockContainer();
- if (nextOpen) {
- setCurrentDockWidget(nextOpen);
- } else if (d->m_contentsLayout->isEmpty() && dockContainerWidget->dockAreaCount() > 1) {
- qCInfo(adsLog) << "Dock Area empty";
- dockContainerWidget->removeDockArea(this);
- this->deleteLater();
- } else {
- // if contents layout is not empty but there are no more open dock
- // widgets, then we need to hide the dock area because it does not
- // contain any visible content
- hideAreaWithNoVisibleContent();
- }
- d->updateTitleBarButtonStates();
- updateTitleBarVisibility();
- auto topLevelDockWidget = dockContainerWidget->topLevelDockWidget();
- if (topLevelDockWidget) {
- topLevelDockWidget->emitTopLevelChanged(true);
- }
- #if (ADS_DEBUG_LEVEL > 0)
- dockContainerWidget->dumpLayout();
- #endif
- }
- void DockAreaWidget::hideAreaWithNoVisibleContent()
- {
- this->toggleView(false);
- // Hide empty parent splitters
- auto splitter = internal::findParent<DockSplitter *>(this);
- internal::hideEmptyParentSplitters(splitter);
- //Hide empty floating widget
- DockContainerWidget *container = this->dockContainer();
- if (!container->isFloating()) {
- return;
- }
- updateTitleBarVisibility();
- auto topLevelWidget = container->topLevelDockWidget();
- auto floatingWidget = container->floatingWidget();
- if (topLevelWidget) {
- floatingWidget->updateWindowTitle();
- DockWidget::emitTopLevelEventForWidget(topLevelWidget, true);
- } else if (container->openedDockAreas().isEmpty()) {
- floatingWidget->hide();
- }
- }
- void DockAreaWidget::onTabCloseRequested(int index)
- {
- qCInfo(adsLog) << Q_FUNC_INFO << "index" << index;
- auto *currentDockWidget = dockWidget(index);
- if (currentDockWidget->features().testFlag(DockWidget::DockWidgetDeleteOnClose)) {
- currentDockWidget->closeDockWidgetInternal();
- } else {
- currentDockWidget->toggleView(false);
- }
- }
- DockWidget *DockAreaWidget::currentDockWidget() const
- {
- int currentIdx = currentIndex();
- if (currentIdx < 0) {
- return nullptr;
- }
- return dockWidget(currentIdx);
- }
- void DockAreaWidget::setCurrentDockWidget(DockWidget *dockWidget)
- {
- if (dockManager()->isRestoringState()) {
- return;
- }
- internalSetCurrentDockWidget(dockWidget);
- }
- void DockAreaWidget::internalSetCurrentDockWidget(DockWidget *dockWidget)
- {
- int index = indexOf(dockWidget);
- if (index < 0) {
- return;
- }
- setCurrentIndex(index);
- }
- void DockAreaWidget::setCurrentIndex(int index)
- {
- auto currentTabBar = d->tabBar();
- if (index < 0 || index > (currentTabBar->count() - 1)) {
- qWarning() << Q_FUNC_INFO << "Invalid index" << index;
- return;
- }
- auto cw = d->m_contentsLayout->currentWidget();
- auto nw = d->m_contentsLayout->widget(index);
- if (cw == nw && !nw->isHidden()) {
- return;
- }
- emit currentChanging(index);
- currentTabBar->setCurrentIndex(index);
- d->m_contentsLayout->setCurrentIndex(index);
- d->m_contentsLayout->currentWidget()->show();
- emit currentChanged(index);
- }
- int DockAreaWidget::currentIndex() const { return d->m_contentsLayout->currentIndex(); }
- QRect DockAreaWidget::titleBarGeometry() const { return d->m_titleBar->geometry(); }
- QRect DockAreaWidget::contentAreaGeometry() const { return d->m_contentsLayout->geometry(); }
- int DockAreaWidget::indexOf(DockWidget *dockWidget)
- {
- return d->m_contentsLayout->indexOf(dockWidget);
- }
- QList<DockWidget *> DockAreaWidget::dockWidgets() const
- {
- QList<DockWidget *> dockWidgetList;
- for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
- dockWidgetList.append(dockWidget(i));
- }
- return dockWidgetList;
- }
- int DockAreaWidget::openDockWidgetsCount() const
- {
- int count = 0;
- for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
- if (!dockWidget(i)->isClosed()) {
- ++count;
- }
- }
- return count;
- }
- QList<DockWidget *> DockAreaWidget::openedDockWidgets() const
- {
- QList<DockWidget *> dockWidgetList;
- for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
- DockWidget *currentDockWidget = dockWidget(i);
- if (!currentDockWidget->isClosed()) {
- dockWidgetList.append(dockWidget(i));
- }
- }
- return dockWidgetList;
- }
- int DockAreaWidget::indexOfFirstOpenDockWidget() const
- {
- for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
- if (!dockWidget(i)->isClosed()) {
- return i;
- }
- }
- return -1;
- }
- int DockAreaWidget::dockWidgetsCount() const { return d->m_contentsLayout->count(); }
- DockWidget *DockAreaWidget::dockWidget(int index) const
- {
- return qobject_cast<DockWidget *>(d->m_contentsLayout->widget(index));
- }
- void DockAreaWidget::reorderDockWidget(int fromIndex, int toIndex)
- {
- qCInfo(adsLog) << Q_FUNC_INFO;
- if (fromIndex >= d->m_contentsLayout->count() || fromIndex < 0
- || toIndex >= d->m_contentsLayout->count() || toIndex < 0 || fromIndex == toIndex) {
- qCInfo(adsLog) << "Invalid index for tab movement" << fromIndex << toIndex;
- return;
- }
- auto widget = d->m_contentsLayout->widget(fromIndex);
- d->m_contentsLayout->removeWidget(widget);
- d->m_contentsLayout->insertWidget(toIndex, widget);
- setCurrentIndex(toIndex);
- }
- void DockAreaWidget::toggleDockWidgetView(DockWidget *dockWidget, bool open)
- {
- Q_UNUSED(dockWidget)
- Q_UNUSED(open)
- updateTitleBarVisibility();
- }
- void DockAreaWidget::updateTitleBarVisibility()
- {
- DockContainerWidget *container = dockContainer();
- if (!container) {
- return;
- }
- if (DockManager::configFlags().testFlag(DockManager::AlwaysShowTabs)) {
- return;
- }
- if (d->m_titleBar) {
- d->m_titleBar->setVisible(!container->isFloating() || !container->hasTopLevelDockWidget());
- }
- }
- void DockAreaWidget::markTitleBarMenuOutdated()
- {
- if (d->m_titleBar) {
- d->m_titleBar->markTabsMenuOutdated();
- }
- }
- void DockAreaWidget::saveState(QXmlStreamWriter &stream) const
- {
- stream.writeStartElement("area");
- stream.writeAttribute("tabs", QString::number(d->m_contentsLayout->count()));
- auto localDockWidget = currentDockWidget();
- QString name = localDockWidget ? localDockWidget->objectName() : "";
- stream.writeAttribute("current", name);
- qCInfo(adsLog) << Q_FUNC_INFO << "TabCount: " << d->m_contentsLayout->count()
- << " Current: " << name;
- for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
- dockWidget(i)->saveState(stream);
- }
- stream.writeEndElement();
- }
- DockWidget *DockAreaWidget::nextOpenDockWidget(DockWidget *dockWidget) const
- {
- auto openDockWidgets = openedDockWidgets();
- if (openDockWidgets.count() > 1
- || (openDockWidgets.count() == 1 && openDockWidgets[0] != dockWidget)) {
- DockWidget *nextDockWidget;
- if (openDockWidgets.last() == dockWidget) {
- nextDockWidget = openDockWidgets[openDockWidgets.count() - 2];
- } else {
- int nextIndex = openDockWidgets.indexOf(dockWidget) + 1;
- nextDockWidget = openDockWidgets[nextIndex];
- }
- return nextDockWidget;
- } else {
- return nullptr;
- }
- }
- DockWidget::DockWidgetFeatures DockAreaWidget::features(eBitwiseOperator mode) const
- {
- if (BitwiseAnd == mode) {
- DockWidget::DockWidgetFeatures features(DockWidget::AllDockWidgetFeatures);
- for (const auto dockWidget : dockWidgets()) {
- features &= dockWidget->features();
- }
- return features;
- } else {
- DockWidget::DockWidgetFeatures features(DockWidget::NoDockWidgetFeatures);
- for (const auto dockWidget : dockWidgets()) {
- features |= dockWidget->features();
- }
- return features;
- }
- }
- void DockAreaWidget::toggleView(bool open)
- {
- setVisible(open);
- emit viewToggled(open);
- }
- void DockAreaWidget::setVisible(bool visible)
- {
- Super::setVisible(visible);
- if (d->m_updateTitleBarButtons) {
- d->updateTitleBarButtonStates();
- }
- }
- void DockAreaWidget::setAllowedAreas(DockWidgetAreas areas)
- {
- d->m_allowedAreas = areas;
- }
- DockWidgetAreas DockAreaWidget::allowedAreas() const
- {
- return d->m_allowedAreas;
- }
- QAbstractButton *DockAreaWidget::titleBarButton(eTitleBarButton which) const
- {
- return d->m_titleBar->button(which);
- }
- void DockAreaWidget::closeArea()
- {
- // If there is only one single dock widget and this widget has the
- // DeleteOnClose feature, then we delete the dock widget now
- auto openDockWidgets = openedDockWidgets();
- if (openDockWidgets.count() == 1
- && openDockWidgets[0]->features().testFlag(DockWidget::DockWidgetDeleteOnClose)) {
- openDockWidgets[0]->closeDockWidgetInternal();
- } else {
- for (auto dockWidget : openedDockWidgets()) {
- dockWidget->toggleView(false);
- }
- }
- }
- void DockAreaWidget::closeOtherAreas() { dockContainer()->closeOtherAreas(this); }
- DockAreaTitleBar *DockAreaWidget::titleBar() const { return d->m_titleBar; }
- } // namespace ADS
|