| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577 |
- /****************************************************************************
- **
- ** 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 "dockareatitlebar.h"
- #include "ads_globals.h"
- #include "dockareatabbar.h"
- #include "dockareawidget.h"
- #include "dockmanager.h"
- #include "dockoverlay.h"
- #include "dockwidget.h"
- #include "dockwidgettab.h"
- #include "floatingdockcontainer.h"
- #include "floatingdragpreview.h"
- #include "iconprovider.h"
- #include "dockcomponentsfactory.h"
- #include <QBoxLayout>
- #include <QLoggingCategory>
- #include <QMenu>
- #include <QMouseEvent>
- #include <QPushButton>
- #include <QScrollArea>
- #include <QStyle>
- #include <iostream>
- static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
- namespace ADS
- {
- /**
- * Private data class of DockAreaTitleBar class (pimpl)
- */
- struct DockAreaTitleBarPrivate
- {
- DockAreaTitleBar *q;
- QPointer<TitleBarButtonType> m_tabsMenuButton;
- QPointer<TitleBarButtonType> m_undockButton;
- QPointer<TitleBarButtonType> m_closeButton;
- QBoxLayout *m_layout;
- DockAreaWidget *m_dockArea;
- DockAreaTabBar *m_tabBar;
- bool m_menuOutdated = true;
- QMenu *m_tabsMenu;
- QList<TitleBarButtonType *> m_dockWidgetActionsButtons;
- QPoint m_dragStartMousePos;
- eDragState m_dragState = DraggingInactive;
- AbstractFloatingWidget *m_floatingWidget = nullptr;
- /**
- * Private data constructor
- */
- DockAreaTitleBarPrivate(DockAreaTitleBar *parent);
- /**
- * Creates the title bar close and menu buttons
- */
- void createButtons();
- /**
- * Creates the internal TabBar
- */
- void createTabBar();
- /**
- * Convenience function for DockManager access
- */
- DockManager *dockManager() const { return m_dockArea->dockManager(); }
- /**
- * Returns true if the given config flag is set
- */
- static bool testConfigFlag(DockManager::eConfigFlag flag)
- {
- return DockManager::configFlags().testFlag(flag);
- }
- /**
- * Test function for current drag state
- */
- bool isDraggingState(eDragState dragState) const { return this->m_dragState == dragState; }
- /**
- * Starts floating
- */
- void startFloating(const QPoint &offset);
- /**
- * Makes the dock area floating
- */
- AbstractFloatingWidget *makeAreaFloating(const QPoint &offset, eDragState dragState);
- }; // struct DockAreaTitleBarPrivate
- DockAreaTitleBarPrivate::DockAreaTitleBarPrivate(DockAreaTitleBar *parent)
- : q(parent)
- {}
- void DockAreaTitleBarPrivate::createButtons()
- {
- QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
- // Tabs menu button
- m_tabsMenuButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasTabsMenuButton));
- m_tabsMenuButton->setObjectName("tabsMenuButton");
- m_tabsMenuButton->setAutoRaise(true);
- m_tabsMenuButton->setPopupMode(QToolButton::InstantPopup);
- internal::setButtonIcon(m_tabsMenuButton,
- QStyle::SP_TitleBarUnshadeButton,
- ADS::DockAreaMenuIcon);
- QMenu *tabsMenu = new QMenu(m_tabsMenuButton);
- #ifndef QT_NO_TOOLTIP
- tabsMenu->setToolTipsVisible(true);
- #endif
- QObject::connect(tabsMenu, &QMenu::aboutToShow, q, &DockAreaTitleBar::onTabsMenuAboutToShow);
- m_tabsMenuButton->setMenu(tabsMenu);
- internal::setToolTip(m_tabsMenuButton, QObject::tr("List All Tabs"));
- m_tabsMenuButton->setSizePolicy(sizePolicy);
- m_layout->addWidget(m_tabsMenuButton, 0);
- QObject::connect(m_tabsMenuButton->menu(),
- &QMenu::triggered,
- q,
- &DockAreaTitleBar::onTabsMenuActionTriggered);
- // Undock button
- m_undockButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasUndockButton));
- m_undockButton->setObjectName("undockButton");
- m_undockButton->setAutoRaise(true);
- internal::setToolTip(m_undockButton, QObject::tr("Detach Group"));
- internal::setButtonIcon(m_undockButton,
- QStyle::SP_TitleBarNormalButton,
- ADS::DockAreaUndockIcon);
- m_undockButton->setSizePolicy(sizePolicy);
- m_layout->addWidget(m_undockButton, 0);
- QObject::connect(m_undockButton,
- &QToolButton::clicked,
- q,
- &DockAreaTitleBar::onUndockButtonClicked);
- // Close button
- m_closeButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasCloseButton));
- m_closeButton->setObjectName("closeButton");
- m_closeButton->setAutoRaise(true);
- internal::setButtonIcon(m_closeButton,
- QStyle::SP_TitleBarCloseButton,
- ADS::DockAreaCloseIcon);
- if (testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) {
- internal::setToolTip(m_closeButton, QObject::tr("Close Active Tab"));
- } else {
- internal::setToolTip(m_closeButton, QObject::tr("Close Group"));
- }
- m_closeButton->setSizePolicy(sizePolicy);
- m_closeButton->setIconSize(QSize(16, 16));
- m_layout->addWidget(m_closeButton, 0);
- QObject::connect(m_closeButton,
- &QToolButton::clicked,
- q,
- &DockAreaTitleBar::onCloseButtonClicked);
- }
- void DockAreaTitleBarPrivate::createTabBar()
- {
- m_tabBar = componentsFactory()->createDockAreaTabBar(m_dockArea);
- m_tabBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
- m_layout->addWidget(m_tabBar);
- QObject::connect(m_tabBar,
- &DockAreaTabBar::tabClosed,
- q,
- &DockAreaTitleBar::markTabsMenuOutdated);
- QObject::connect(m_tabBar,
- &DockAreaTabBar::tabOpened,
- q,
- &DockAreaTitleBar::markTabsMenuOutdated);
- QObject::connect(m_tabBar,
- &DockAreaTabBar::tabInserted,
- q,
- &DockAreaTitleBar::markTabsMenuOutdated);
- QObject::connect(m_tabBar,
- &DockAreaTabBar::removingTab,
- q,
- &DockAreaTitleBar::markTabsMenuOutdated);
- QObject::connect(m_tabBar,
- &DockAreaTabBar::tabMoved,
- q,
- &DockAreaTitleBar::markTabsMenuOutdated);
- QObject::connect(m_tabBar,
- &DockAreaTabBar::currentChanged,
- q,
- &DockAreaTitleBar::onCurrentTabChanged);
- QObject::connect(m_tabBar,
- &DockAreaTabBar::tabBarClicked,
- q,
- &DockAreaTitleBar::tabBarClicked);
- QObject::connect(m_tabBar,
- &DockAreaTabBar::elidedChanged,
- q,
- &DockAreaTitleBar::markTabsMenuOutdated);
- }
- AbstractFloatingWidget *DockAreaTitleBarPrivate::makeAreaFloating(const QPoint &offset,
- eDragState dragState)
- {
- QSize size = m_dockArea->size();
- m_dragState = dragState;
- bool opaqueUndocking = DockManager::configFlags().testFlag(DockManager::OpaqueUndocking)
- || (DraggingFloatingWidget != dragState);
- FloatingDockContainer *floatingDockContainer = nullptr;
- AbstractFloatingWidget *floatingWidget;
- if (opaqueUndocking) {
- floatingWidget = floatingDockContainer = new FloatingDockContainer(m_dockArea);
- } else {
- auto w = new FloatingDragPreview(m_dockArea);
- QObject::connect(w, &FloatingDragPreview::draggingCanceled, [=]() {
- m_dragState = DraggingInactive;
- });
- floatingWidget = w;
- }
- floatingWidget->startFloating(offset, size, dragState, nullptr);
- if (floatingDockContainer) {
- auto topLevelDockWidget = floatingDockContainer->topLevelDockWidget();
- if (topLevelDockWidget) {
- topLevelDockWidget->emitTopLevelChanged(true);
- }
- }
- return floatingWidget;
- }
- void DockAreaTitleBarPrivate::startFloating(const QPoint &offset)
- {
- m_floatingWidget = makeAreaFloating(offset, DraggingFloatingWidget);
- }
- TitleBarButton::TitleBarButton(bool visible, QWidget *parent)
- : TitleBarButtonType(parent),
- m_visible(visible),
- m_hideWhenDisabled(DockAreaTitleBarPrivate::testConfigFlag(DockManager::DockAreaHideDisabledButtons))
- {}
- void TitleBarButton::setVisible(bool visible)
- {
- // 'visible' can stay 'true' if and only if this button is configured to generaly visible:
- visible = visible && m_visible;
- // 'visible' can stay 'true' unless: this button is configured to be invisible when it
- // is disabled and it is currently disabled:
- if (visible && m_hideWhenDisabled) {
- visible = isEnabled();
- }
- Super::setVisible(visible);
- }
- bool TitleBarButton::event(QEvent *event)
- {
- if (QEvent::EnabledChange == event->type() && m_hideWhenDisabled) {
- // force setVisible() call
- // Calling setVisible() directly here doesn't work well when button is expected to be shown first time
- QMetaObject::invokeMethod(this, "setVisible", Qt::QueuedConnection, Q_ARG(bool, isEnabled()));
- }
- return Super::event(event);
- }
- SpacerWidget::SpacerWidget(QWidget *parent)
- : QWidget(parent)
- {
- setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
- setStyleSheet("border: none; background: none;");
- }
- DockAreaTitleBar::DockAreaTitleBar(DockAreaWidget *parent)
- : QFrame(parent)
- , d(new DockAreaTitleBarPrivate(this))
- {
- d->m_dockArea = parent;
- setObjectName("dockAreaTitleBar");
- d->m_layout = new QBoxLayout(QBoxLayout::LeftToRight);
- d->m_layout->setContentsMargins(0, 0, 0, 0);
- d->m_layout->setSpacing(0);
- setLayout(d->m_layout);
- setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
- d->createTabBar();
- d->m_layout->addWidget(new SpacerWidget(this));
- d->createButtons();
- }
- DockAreaTitleBar::~DockAreaTitleBar() {
- if (!d->m_closeButton.isNull())
- delete d->m_closeButton;
- if (!d->m_tabsMenuButton.isNull())
- delete d->m_tabsMenuButton;
- if (!d->m_undockButton.isNull())
- delete d->m_undockButton;
- delete d;
- }
- DockAreaTabBar *DockAreaTitleBar::tabBar() const { return d->m_tabBar; }
- void DockAreaTitleBar::markTabsMenuOutdated() {
- if (DockAreaTitleBarPrivate::testConfigFlag(DockManager::DockAreaDynamicTabsMenuButtonVisibility)) {
- bool hasElidedTabTitle = false;
- for (int i = 0; i < d->m_tabBar->count(); ++i) {
- if (!d->m_tabBar->isTabOpen(i))
- continue;
- DockWidgetTab* tab = d->m_tabBar->tab(i);
- if (tab->isTitleElided()) {
- hasElidedTabTitle = true;
- break;
- }
- }
- bool visible = (hasElidedTabTitle && (d->m_tabBar->count() > 1));
- QMetaObject::invokeMethod(d->m_tabsMenuButton, "setVisible", Qt::QueuedConnection, Q_ARG(bool, visible));
- }
- d->m_menuOutdated = true;
- }
- void DockAreaTitleBar::onTabsMenuAboutToShow()
- {
- if (!d->m_menuOutdated) {
- return;
- }
- QMenu *menu = d->m_tabsMenuButton->menu();
- menu->clear();
- for (int i = 0; i < d->m_tabBar->count(); ++i) {
- if (!d->m_tabBar->isTabOpen(i))
- continue;
- auto tab = d->m_tabBar->tab(i);
- QAction *action = menu->addAction(tab->icon(), tab->text());
- internal::setToolTip(action, tab->toolTip());
- action->setData(i);
- }
- d->m_menuOutdated = false;
- }
- void DockAreaTitleBar::onCloseButtonClicked()
- {
- qCInfo(adsLog) << Q_FUNC_INFO;
- if (d->testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) {
- d->m_tabBar->closeTab(d->m_tabBar->currentIndex());
- } else {
- d->m_dockArea->closeArea();
- }
- }
- void DockAreaTitleBar::onUndockButtonClicked()
- {
- if (d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable)) {
- d->makeAreaFloating(mapFromGlobal(QCursor::pos()), DraggingInactive);
- }
- }
- void DockAreaTitleBar::onTabsMenuActionTriggered(QAction *action)
- {
- int index = action->data().toInt();
- d->m_tabBar->setCurrentIndex(index);
- emit tabBarClicked(index);
- }
- void DockAreaTitleBar::updateDockWidgetActionsButtons()
- {
- DockWidget* dockWidget = d->m_tabBar->currentTab()->dockWidget();
- if (!d->m_dockWidgetActionsButtons.isEmpty()) {
- for (auto button : d->m_dockWidgetActionsButtons) {
- d->m_layout->removeWidget(button);
- delete button;
- }
- d->m_dockWidgetActionsButtons.clear();
- }
- auto actions = dockWidget->titleBarActions();
- if (actions.isEmpty())
- return;
- int insertIndex = indexOf(d->m_tabsMenuButton);
- for (auto action : actions) {
- auto button = new TitleBarButton(true, this);
- button->setDefaultAction(action);
- button->setAutoRaise(true);
- button->setPopupMode(QToolButton::InstantPopup);
- button->setObjectName(action->objectName());
- d->m_layout->insertWidget(insertIndex++, button, 0);
- d->m_dockWidgetActionsButtons.append(button);
- }
- }
- void DockAreaTitleBar::onCurrentTabChanged(int index)
- {
- if (index < 0)
- return;
- if (d->testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) {
- DockWidget *dockWidget = d->m_tabBar->tab(index)->dockWidget();
- d->m_closeButton->setEnabled(
- dockWidget->features().testFlag(DockWidget::DockWidgetClosable));
- }
- updateDockWidgetActionsButtons();
- }
- QAbstractButton *DockAreaTitleBar::button(eTitleBarButton which) const
- {
- switch (which) {
- case TitleBarButtonTabsMenu:
- return d->m_tabsMenuButton;
- case TitleBarButtonUndock:
- return d->m_undockButton;
- case TitleBarButtonClose:
- return d->m_closeButton;
- }
- return nullptr;
- }
- void DockAreaTitleBar::setVisible(bool visible)
- {
- Super::setVisible(visible);
- markTabsMenuOutdated();
- }
- void DockAreaTitleBar::mousePressEvent(QMouseEvent *event)
- {
- if (event->button() == Qt::LeftButton) {
- event->accept();
- d->m_dragStartMousePos = event->pos();
- d->m_dragState = DraggingMousePressed;
- return;
- }
- Super::mousePressEvent(event);
- }
- void DockAreaTitleBar::mouseReleaseEvent(QMouseEvent *event)
- {
- if (event->button() == Qt::LeftButton) {
- qCInfo(adsLog) << Q_FUNC_INFO;
- event->accept();
- auto CurrentDragState = d->m_dragState;
- d->m_dragStartMousePos = QPoint();
- d->m_dragState = DraggingInactive;
- if (DraggingFloatingWidget == CurrentDragState)
- d->m_floatingWidget->finishDragging();
- return;
- }
- Super::mouseReleaseEvent(event);
- }
- void DockAreaTitleBar::mouseMoveEvent(QMouseEvent *event)
- {
- Super::mouseMoveEvent(event);
- if (!(event->buttons() & Qt::LeftButton) || d->isDraggingState(DraggingInactive)) {
- d->m_dragState = DraggingInactive;
- return;
- }
- // move floating window
- if (d->isDraggingState(DraggingFloatingWidget)) {
- d->m_floatingWidget->moveFloating();
- return;
- }
- // If this is the last dock area in a dock container it does not make
- // sense to move it to a new floating widget and leave this one empty
- if (d->m_dockArea->dockContainer()->isFloating()
- && d->m_dockArea->dockContainer()->visibleDockAreaCount() == 1) {
- return;
- }
- // If one single dock widget in this area is not floatable then the whole
- // area is not floatable
- // If we do non opaque undocking, then we can create the floating drag
- // preview if the dock widget is movable
- auto features = d->m_dockArea->features();
- if (!features.testFlag(DockWidget::DockWidgetFloatable)
- && !(features.testFlag(DockWidget::DockWidgetMovable)
- && !DockManager::testConfigFlag(DockManager::OpaqueUndocking))) {
- return;
- }
- int dragDistance = (d->m_dragStartMousePos - event->pos()).manhattanLength();
- if (dragDistance >= DockManager::startDragDistance()) {
- qCInfo(adsLog) << "TabsScrollArea::startFloating";
- d->startFloating(d->m_dragStartMousePos);
- auto overlay = d->m_dockArea->dockManager()->containerOverlay();
- overlay->setAllowedAreas(OuterDockAreas);
- }
- return;
- }
- void DockAreaTitleBar::mouseDoubleClickEvent(QMouseEvent *event)
- {
- // If this is the last dock area in a dock container it does not make
- // sense to move it to a new floating widget and leave this one empty
- if (d->m_dockArea->dockContainer()->isFloating()
- && d->m_dockArea->dockContainer()->dockAreaCount() == 1)
- return;
- if (!d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable))
- return;
- d->makeAreaFloating(event->pos(), DraggingInactive);
- }
- void DockAreaTitleBar::contextMenuEvent(QContextMenuEvent *event)
- {
- event->accept();
- if (d->isDraggingState(DraggingFloatingWidget))
- return;
- QMenu menu(this);
- auto action = menu.addAction(tr("Detach Area"),
- this,
- &DockAreaTitleBar::onUndockButtonClicked);
- action->setEnabled(d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable));
- menu.addSeparator();
- action = menu.addAction(tr("Close Area"), this, &DockAreaTitleBar::onCloseButtonClicked);
- action->setEnabled(d->m_dockArea->features().testFlag(DockWidget::DockWidgetClosable));
- menu.addAction(tr("Close Other Areas"), d->m_dockArea, &DockAreaWidget::closeOtherAreas);
- menu.exec(event->globalPos());
- }
- void DockAreaTitleBar::insertWidget(int index, QWidget *widget)
- {
- d->m_layout->insertWidget(index, widget);
- }
- int DockAreaTitleBar::indexOf(QWidget *widget) const
- {
- return d->m_layout->indexOf(widget);
- }
- } // namespace ADS
|