diff --git a/src/libs/advanceddockingsystem/CMakeLists.txt b/src/libs/advanceddockingsystem/CMakeLists.txt index 70be4cbe102..e060cfaa636 100644 --- a/src/libs/advanceddockingsystem/CMakeLists.txt +++ b/src/libs/advanceddockingsystem/CMakeLists.txt @@ -7,6 +7,7 @@ add_qtc_library(AdvancedDockingSystem dockareawidget.cpp dockareawidget.h dockcomponentsfactory.cpp dockcomponentsfactory.h dockcontainerwidget.cpp dockcontainerwidget.h + dockfocuscontroller.cpp dockfocuscontroller.h dockingstatereader.cpp dockingstatereader.h dockmanager.cpp dockmanager.h dockoverlay.cpp dockoverlay.h @@ -21,7 +22,6 @@ add_qtc_library(AdvancedDockingSystem workspacemodel.cpp workspacemodel.h workspaceview.cpp workspaceview.h workspacedialog.ui - resources.qrc ) extend_qtc_library(AdvancedDockingSystem diff --git a/src/libs/advanceddockingsystem/ads_globals.cpp b/src/libs/advanceddockingsystem/ads_globals.cpp index 36df8ed869c..a93139bd7c9 100644 --- a/src/libs/advanceddockingsystem/ads_globals.cpp +++ b/src/libs/advanceddockingsystem/ads_globals.cpp @@ -41,6 +41,7 @@ #include #include +#include #include namespace ADS { @@ -93,7 +94,7 @@ void hideEmptyParentSplitters(DockSplitter *splitter) } } -void setButtonIcon(QAbstractButton* button, +void setButtonIcon(QAbstractButton *button, QStyle::StandardPixmap standarPixmap, ADS::eIcon customIconId) { @@ -116,5 +117,25 @@ void setButtonIcon(QAbstractButton* button, } } +void repolishStyle(QWidget *widget, eRepolishChildOptions options) +{ + if (!widget) + return; + + widget->style()->unpolish(widget); + widget->style()->polish(widget); + + if (RepolishIgnoreChildren == options) + return; + + QList children = widget->findChildren(QString(), + (RepolishDirectChildren == options) ? Qt::FindDirectChildrenOnly : Qt::FindChildrenRecursively); + for (auto w : children) + { + w->style()->unpolish(w); + w->style()->polish(w); + } +} + } // namespace internal } // namespace ADS diff --git a/src/libs/advanceddockingsystem/ads_globals.h b/src/libs/advanceddockingsystem/ads_globals.h index eede7a171ae..1790fdf69d9 100644 --- a/src/libs/advanceddockingsystem/ads_globals.h +++ b/src/libs/advanceddockingsystem/ads_globals.h @@ -72,8 +72,6 @@ QT_END_NAMESPACE namespace ADS { -enum eStateFileVersion { InitialVerison = 0, Version1 = 1, CurrentVersion = Version1 }; - class DockSplitter; enum DockWidgetArea { @@ -228,5 +226,18 @@ void setToolTip(QObjectPtr obj, const QString &tip) void setButtonIcon(QAbstractButton *button, QStyle::StandardPixmap standarPixmap, ADS::eIcon CustomIconId); +enum eRepolishChildOptions +{ + RepolishIgnoreChildren, + RepolishDirectChildren, + RepolishChildrenRecursively +}; + +/** + * Calls unpolish() / polish for the style of the given widget to update + * stylesheet if a property changes + */ +void repolishStyle(QWidget *widget, eRepolishChildOptions options = RepolishIgnoreChildren); + } // namespace internal } // namespace ADS diff --git a/src/libs/advanceddockingsystem/advanceddockingsystem-lib.pri b/src/libs/advanceddockingsystem/advanceddockingsystem-lib.pri index 57526219853..a058d30cac6 100644 --- a/src/libs/advanceddockingsystem/advanceddockingsystem-lib.pri +++ b/src/libs/advanceddockingsystem/advanceddockingsystem-lib.pri @@ -5,9 +5,6 @@ shared { } ## Input -RESOURCES += \ - resources.qrc - HEADERS += \ ads_globals.h \ dockareatabbar.h \ @@ -15,6 +12,7 @@ HEADERS += \ dockareawidget.h \ dockcomponentsfactory.h \ dockcontainerwidget.h \ + dockfocuscontroller.h \ dockingstatereader.h \ dockmanager.h \ dockoverlay.h \ @@ -36,6 +34,7 @@ SOURCES += \ dockareawidget.cpp \ dockcomponentsfactory.cpp \ dockcontainerwidget.cpp \ + dockfocuscontroller.cpp \ dockingstatereader.cpp \ dockmanager.cpp \ dockoverlay.cpp \ diff --git a/src/libs/advanceddockingsystem/advanceddockingsystem.qbs b/src/libs/advanceddockingsystem/advanceddockingsystem.qbs index e2877556f0e..3759b9f0d62 100644 --- a/src/libs/advanceddockingsystem/advanceddockingsystem.qbs +++ b/src/libs/advanceddockingsystem/advanceddockingsystem.qbs @@ -19,6 +19,7 @@ QtcLibrary { "dockareawidget.cpp", "dockareawidget.h", "dockcomponentsfactory.cpp", "dockcomponentsfactory.h", "dockcontainerwidget.cpp", "dockcontainerwidget.h", + "dockfocuscontroller.cpp", "dockfocuscontroller.h", "dockingstatereader.cpp", "dockingstatereader.h", "dockmanager.cpp", "dockmanager.h", "dockoverlay.cpp", "dockoverlay.h", @@ -32,8 +33,7 @@ QtcLibrary { "workspacedialog.cpp", "workspacedialog.h", "workspacemodel.cpp", "workspacemodel.h", "workspaceview.cpp", "workspaceview.h", - "workspacedialog.ui", - "resources.qrc" + "workspacedialog.ui" ] } diff --git a/src/libs/advanceddockingsystem/dockareatabbar.cpp b/src/libs/advanceddockingsystem/dockareatabbar.cpp index 02d2ae19cde..3208f0c7560 100644 --- a/src/libs/advanceddockingsystem/dockareatabbar.cpp +++ b/src/libs/advanceddockingsystem/dockareatabbar.cpp @@ -82,12 +82,12 @@ namespace ADS /** * Convenience function to access first tab */ - DockWidgetTab *firstTab() const {return q->tab(0);} + DockWidgetTab *firstTab() const { return q->tab(0); } /** * Convenience function to access last tab */ - DockWidgetTab *lastTab() const {return q->tab(q->count() - 1);} + DockWidgetTab *lastTab() const { return q->tab(q->count() - 1); } }; // class DockAreaTabBarPrivate DockAreaTabBarPrivate::DockAreaTabBarPrivate(DockAreaTabBar *parent) @@ -140,11 +140,10 @@ namespace ADS { event->accept(); const int direction = event->angleDelta().y(); - if (direction < 0) { + if (direction < 0) horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 20); - } else { + else horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 20); - } } void DockAreaTabBar::setCurrentIndex(int index) @@ -189,9 +188,11 @@ namespace ADS &DockAreaTabBar::elidedChanged); dockWidgetTab->installEventFilter(this); emit tabInserted(index); - if (index <= d->m_currentIndex || d->m_currentIndex == -1) { + if (index <= d->m_currentIndex) setCurrentIndex(d->m_currentIndex + 1); - } + else if (d->m_currentIndex == -1) + setCurrentIndex(index); + updateGeometry(); } @@ -235,11 +236,11 @@ namespace ADS dockWidgetTab->disconnect(this); dockWidgetTab->removeEventFilter(this); qCInfo(adsLog) << "NewCurrentIndex " << newCurrentIndex; - if (newCurrentIndex != d->m_currentIndex) { + if (newCurrentIndex != d->m_currentIndex) setCurrentIndex(newCurrentIndex); - } else { + else d->updateTabs(); - } + updateGeometry(); } @@ -247,12 +248,11 @@ namespace ADS DockWidgetTab *DockAreaTabBar::currentTab() const { - if (d->m_currentIndex < 0) { + if (d->m_currentIndex < 0) return nullptr; - } else { + else return qobject_cast( d->m_tabsLayout->itemAt(d->m_currentIndex)->widget()); - } } void DockAreaTabBar::onTabClicked() @@ -292,9 +292,8 @@ namespace ADS // If the the dock widget blocks closing, i.e. if the flag // CustomCloseHandling is set, and the dock widget is still open, // then we do not need to correct the index - if (currentTab->dockWidget()->isClosed()) { + if (currentTab->dockWidget()->isClosed()) i -= offset; - } } } } diff --git a/src/libs/advanceddockingsystem/dockareatitlebar.cpp b/src/libs/advanceddockingsystem/dockareatitlebar.cpp index 79ba7ae70d5..4430f936723 100644 --- a/src/libs/advanceddockingsystem/dockareatitlebar.cpp +++ b/src/libs/advanceddockingsystem/dockareatitlebar.cpp @@ -103,10 +103,11 @@ namespace ADS /** * Returns true if the given config flag is set + * Convenience function to ease config flag testing */ static bool testConfigFlag(DockManager::eConfigFlag flag) { - return DockManager::configFlags().testFlag(flag); + return DockManager::testConfigFlag(flag); } /** @@ -159,7 +160,7 @@ namespace ADS // Undock button m_undockButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasUndockButton)); - m_undockButton->setObjectName("undockButton"); + m_undockButton->setObjectName("detachGroupButton"); m_undockButton->setAutoRaise(true); internal::setToolTip(m_undockButton, QObject::tr("Detach Group")); internal::setButtonIcon(m_undockButton, @@ -175,16 +176,16 @@ namespace ADS // Close button m_closeButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasCloseButton)); - m_closeButton->setObjectName("closeButton"); + m_closeButton->setObjectName("dockAreaCloseButton"); m_closeButton->setAutoRaise(true); internal::setButtonIcon(m_closeButton, QStyle::SP_TitleBarCloseButton, ADS::DockAreaCloseIcon); - if (testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) { + if (testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) internal::setToolTip(m_closeButton, QObject::tr("Close Active Tab")); - } else { + else internal::setToolTip(m_closeButton, QObject::tr("Close Group")); - } + m_closeButton->setSizePolicy(sizePolicy); m_closeButton->setIconSize(iconSize); m_layout->addWidget(m_closeButton, 0); @@ -238,7 +239,7 @@ namespace ADS { QSize size = m_dockArea->size(); m_dragState = dragState; - bool opaqueUndocking = DockManager::configFlags().testFlag(DockManager::OpaqueUndocking) + bool opaqueUndocking = DockManager::testConfigFlag(DockManager::OpaqueUndocking) || (DraggingFloatingWidget != dragState); FloatingDockContainer *floatingDockContainer = nullptr; AbstractFloatingWidget *floatingWidget; @@ -269,9 +270,9 @@ namespace ADS } TitleBarButton::TitleBarButton(bool visible, QWidget *parent) - : TitleBarButtonType(parent), - m_visible(visible), - m_hideWhenDisabled(DockAreaTitleBarPrivate::testConfigFlag(DockManager::DockAreaHideDisabledButtons)) + : TitleBarButtonType(parent) + , m_visible(visible) + , m_hideWhenDisabled(DockAreaTitleBarPrivate::testConfigFlag(DockManager::DockAreaHideDisabledButtons)) {} void TitleBarButton::setVisible(bool visible) @@ -281,9 +282,8 @@ namespace ADS // '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) { + if (visible && m_hideWhenDisabled) visible = isEnabled(); - } Super::setVisible(visible); } @@ -360,9 +360,8 @@ namespace ADS void DockAreaTitleBar::onTabsMenuAboutToShow() { - if (!d->m_menuOutdated) { + if (!d->m_menuOutdated) return; - } QMenu *menu = d->m_tabsMenuButton->menu(); menu->clear(); @@ -382,18 +381,16 @@ namespace ADS void DockAreaTitleBar::onCloseButtonClicked() { qCInfo(adsLog) << Q_FUNC_INFO; - if (d->testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) { + if (d->testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) d->m_tabBar->closeTab(d->m_tabBar->currentIndex()); - } else { + else d->m_dockArea->closeArea(); - } } void DockAreaTitleBar::onUndockButtonClicked() { - if (d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable)) { + if (d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable)) d->makeAreaFloating(mapFromGlobal(QCursor::pos()), DraggingInactive); - } } void DockAreaTitleBar::onTabsMenuActionTriggered(QAction *action) @@ -470,6 +467,9 @@ namespace ADS event->accept(); d->m_dragStartMousePos = event->pos(); d->m_dragState = DraggingMousePressed; + if (DockManager::testConfigFlag(DockManager::FocusHighlighting)) + d->m_tabBar->currentTab()->setFocus(Qt::OtherFocusReason); + return; } Super::mousePressEvent(event); @@ -505,7 +505,7 @@ namespace ADS return; } - // If this is the last dock area in a dock container it does not make + // If this is the last dock area in a floating 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) { @@ -525,7 +525,7 @@ namespace ADS int dragDistance = (d->m_dragStartMousePos - event->pos()).manhattanLength(); if (dragDistance >= DockManager::startDragDistance()) { - qCInfo(adsLog) << "TabsScrollArea::startFloating"; + qCInfo(adsLog) << "DockAreaTitlBar::startFloating"; d->startFloating(d->m_dragStartMousePos); auto overlay = d->m_dockArea->dockManager()->containerOverlay(); overlay->setAllowedAreas(OuterDockAreas); diff --git a/src/libs/advanceddockingsystem/dockareawidget.cpp b/src/libs/advanceddockingsystem/dockareawidget.cpp index 6e7f9d5c993..3f367a637c3 100644 --- a/src/libs/advanceddockingsystem/dockareawidget.cpp +++ b/src/libs/advanceddockingsystem/dockareawidget.cpp @@ -103,16 +103,15 @@ namespace ADS void insertWidget(int index, QWidget *widget) { widget->setParent(nullptr); - if (index < 0) { + if (index < 0) index = m_widgets.count(); - } + m_widgets.insert(index, widget); if (m_currentIndex < 0) { setCurrentIndex(index); } else { - if (index <= m_currentIndex) { + if (index <= m_currentIndex) ++m_currentIndex; - } } } @@ -123,11 +122,13 @@ namespace ADS { if (currentWidget() == widget) { auto layoutItem = m_parentLayout->takeAt(1); - if (layoutItem) { + if (layoutItem) layoutItem->widget()->setParent(nullptr); - } + m_currentWidget = nullptr; m_currentIndex = -1; + } else if (indexOf(widget) < m_currentIndex) { + --m_currentIndex; } m_widgets.removeOne(widget); } @@ -144,9 +145,8 @@ namespace ADS { QWidget *prev = currentWidget(); QWidget *next = widget(index); - if (!next || (next == prev && !m_currentWidget)) { + if (!next || (next == prev && !m_currentWidget)) return; - } bool reenableUpdates = false; QWidget *parent = m_parentLayout->parentWidget(); @@ -156,22 +156,18 @@ namespace ADS parent->setUpdatesEnabled(false); } - // TODO - auto layoutItem = m_parentLayout->takeAt(1); - if (layoutItem) { + if (auto layoutItem = m_parentLayout->takeAt(1)) layoutItem->widget()->setParent(nullptr); - } m_parentLayout->addWidget(next); - if (prev) { + if (prev) prev->hide(); - } + m_currentIndex = index; m_currentWidget = next; - if (reenableUpdates) { + if (reenableUpdates) parent->setUpdatesEnabled(true); - } } /** @@ -262,7 +258,7 @@ namespace ADS DockAreaTabBar *tabBar() const { return m_titleBar->tabBar(); } /** - * Udpates the enable state of the close and detach button + * Updates the enable state of the close and detach button */ void updateTitleBarButtonStates(); @@ -330,9 +326,8 @@ namespace ADS d->createTitleBar(); d->m_contentsLayout = new DockAreaLayout(d->m_layout); - if (d->m_dockManager) { + if (d->m_dockManager) emit d->m_dockManager->dockAreaCreated(this); - } } DockAreaWidget::~DockAreaWidget() @@ -357,6 +352,7 @@ namespace ADS void DockAreaWidget::insertDockWidget(int index, DockWidget *dockWidget, bool activate) { d->m_contentsLayout->insertWidget(index, dockWidget); + dockWidget->setDockArea(this); dockWidget->tabWidget()->setDockAreaWidget(this); auto tabWidget = dockWidget->tabWidget(); // Inserting the tab will change the current index which in turn will @@ -370,10 +366,14 @@ namespace ADS dockWidget->minimumSizeHint().height())); d->m_minSizeHint.setWidth(qMax(d->m_minSizeHint.width(), dockWidget->minimumSizeHint().width())); - if (activate) { + if (activate) setCurrentIndex(index); - } - dockWidget->setDockArea(this); + + // If this dock area is hidden, then we need to make it visible again + // by calling dockWidget->toggleViewInternal(true); + if (!this->isVisible() && d->m_contentsLayout->count() > 1 && !dockManager()->isRestoringState()) + dockWidget->toggleViewInternal(true); + d->updateTitleBarButtonStates(); } @@ -389,7 +389,7 @@ namespace ADS DockContainerWidget *dockContainerWidget = dockContainer(); if (nextOpen) { setCurrentDockWidget(nextOpen); - } else if (d->m_contentsLayout->isEmpty() && dockContainerWidget->dockAreaCount() > 1) { + } else if (d->m_contentsLayout->isEmpty() && dockContainerWidget->dockAreaCount() >= 1) { qCInfo(adsLog) << "Dock Area empty"; dockContainerWidget->removeDockArea(this); this->deleteLater(); @@ -404,9 +404,8 @@ namespace ADS updateTitleBarVisibility(); d->updateMinimumSizeHint(); auto topLevelDockWidget = dockContainerWidget->topLevelDockWidget(); - if (topLevelDockWidget) { + if (topLevelDockWidget) topLevelDockWidget->emitTopLevelChanged(true); - } #if (ADS_DEBUG_LEVEL > 0) dockContainerWidget->dumpLayout(); @@ -423,17 +422,19 @@ namespace ADS //Hide empty floating widget DockContainerWidget *container = this->dockContainer(); - if (!container->isFloating()) { + if (!container->isFloating() + && DockManager::testConfigFlag(DockManager::HideSingleCentralWidgetTitleBar)) return; - } updateTitleBarVisibility(); auto topLevelWidget = container->topLevelDockWidget(); auto floatingWidget = container->floatingWidget(); if (topLevelWidget) { - floatingWidget->updateWindowTitle(); + if (floatingWidget) + floatingWidget->updateWindowTitle(); + DockWidget::emitTopLevelEventForWidget(topLevelWidget, true); - } else if (container->openedDockAreas().isEmpty()) { + } else if (container->openedDockAreas().isEmpty() && floatingWidget) { floatingWidget->hide(); } } @@ -442,28 +443,25 @@ namespace ADS { qCInfo(adsLog) << Q_FUNC_INFO << "index" << index; auto *currentDockWidget = dockWidget(index); - if (currentDockWidget->features().testFlag(DockWidget::DockWidgetDeleteOnClose)) { + if (currentDockWidget->features().testFlag(DockWidget::DockWidgetDeleteOnClose)) currentDockWidget->closeDockWidgetInternal(); - } else { + else currentDockWidget->toggleView(false); - } } DockWidget *DockAreaWidget::currentDockWidget() const { int currentIdx = currentIndex(); - if (currentIdx < 0) { + if (currentIdx < 0) return nullptr; - } return dockWidget(currentIdx); } void DockAreaWidget::setCurrentDockWidget(DockWidget *dockWidget) { - if (dockManager()->isRestoringState()) { + if (dockManager()->isRestoringState()) return; - } internalSetCurrentDockWidget(dockWidget); } @@ -471,9 +469,8 @@ namespace ADS void DockAreaWidget::internalSetCurrentDockWidget(DockWidget *dockWidget) { int index = indexOf(dockWidget); - if (index < 0) { + if (index < 0) return; - } setCurrentIndex(index); } @@ -488,9 +485,8 @@ namespace ADS auto cw = d->m_contentsLayout->currentWidget(); auto nw = d->m_contentsLayout->widget(index); - if (cw == nw && !nw->isHidden()) { + if (cw == nw && !nw->isHidden()) return; - } emit currentChanging(index); currentTabBar->setCurrentIndex(index); @@ -513,9 +509,9 @@ namespace ADS QList DockAreaWidget::dockWidgets() const { QList dockWidgetList; - for (int i = 0; i < d->m_contentsLayout->count(); ++i) { + for (int i = 0; i < d->m_contentsLayout->count(); ++i) dockWidgetList.append(dockWidget(i)); - } + return dockWidgetList; } @@ -523,9 +519,8 @@ namespace ADS { int count = 0; for (int i = 0; i < d->m_contentsLayout->count(); ++i) { - if (!dockWidget(i)->isClosed()) { + if (!dockWidget(i)->isClosed()) ++count; - } } return count; } @@ -535,9 +530,8 @@ namespace ADS QList dockWidgetList; for (int i = 0; i < d->m_contentsLayout->count(); ++i) { DockWidget *currentDockWidget = dockWidget(i); - if (!currentDockWidget->isClosed()) { + if (!currentDockWidget->isClosed()) dockWidgetList.append(dockWidget(i)); - } } return dockWidgetList; } @@ -545,12 +539,11 @@ namespace ADS int DockAreaWidget::indexOfFirstOpenDockWidget() const { for (int i = 0; i < d->m_contentsLayout->count(); ++i) { - if (!dockWidget(i)->isClosed()) { + if (!dockWidget(i)->isClosed()) return i; - } } - return -1; + return - 1; } int DockAreaWidget::dockWidgetsCount() const { return d->m_contentsLayout->count(); } @@ -585,24 +578,23 @@ namespace ADS void DockAreaWidget::updateTitleBarVisibility() { DockContainerWidget *container = dockContainer(); - if (!container) { + if (!container) return; - } - if (DockManager::configFlags().testFlag(DockManager::AlwaysShowTabs)) { + if (DockManager::testConfigFlag(DockManager::AlwaysShowTabs)) return; - } if (d->m_titleBar) { - d->m_titleBar->setVisible(!container->isFloating() || !container->hasTopLevelDockWidget()); + bool hidden = container->hasTopLevelDockWidget() && (container->isFloating() + || DockManager::testConfigFlag(DockManager::HideSingleCentralWidgetTitleBar)); + d->m_titleBar->setVisible(!hidden); } } void DockAreaWidget::markTitleBarMenuOutdated() { - if (d->m_titleBar) { + if (d->m_titleBar) d->m_titleBar->markTabsMenuOutdated(); - } } void DockAreaWidget::saveState(QXmlStreamWriter &stream) const @@ -614,9 +606,9 @@ namespace ADS 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) { + for (int i = 0; i < d->m_contentsLayout->count(); ++i) dockWidget(i)->saveState(stream); - } + stream.writeEndElement(); } @@ -643,15 +635,15 @@ namespace ADS { if (BitwiseAnd == mode) { DockWidget::DockWidgetFeatures features(DockWidget::AllDockWidgetFeatures); - for (const auto dockWidget : dockWidgets()) { + for (const auto dockWidget : dockWidgets()) features &= dockWidget->features(); - } + return features; } else { DockWidget::DockWidgetFeatures features(DockWidget::NoDockWidgetFeatures); - for (const auto dockWidget : dockWidgets()) { + for (const auto dockWidget : dockWidgets()) features |= dockWidget->features(); - } + return features; } } @@ -666,9 +658,8 @@ namespace ADS void DockAreaWidget::setVisible(bool visible) { Super::setVisible(visible); - if (d->m_updateTitleBarButtons) { + if (d->m_updateTitleBarButtons) d->updateTitleBarButtonStates(); - } } void DockAreaWidget::setAllowedAreas(DockWidgetAreas areas) @@ -695,9 +686,8 @@ namespace ADS && openDockWidgets[0]->features().testFlag(DockWidget::DockWidgetDeleteOnClose)) { openDockWidgets[0]->closeDockWidgetInternal(); } else { - for (auto dockWidget : openedDockWidgets()) { + for (auto dockWidget : openedDockWidgets()) dockWidget->toggleView(false); - } } } diff --git a/src/libs/advanceddockingsystem/dockcontainerwidget.cpp b/src/libs/advanceddockingsystem/dockcontainerwidget.cpp index b308879a9f6..1b0af0dcf88 100644 --- a/src/libs/advanceddockingsystem/dockcontainerwidget.cpp +++ b/src/libs/advanceddockingsystem/dockcontainerwidget.cpp @@ -95,11 +95,10 @@ namespace ADS */ static void insertWidgetIntoSplitter(QSplitter *splitter, QWidget *widget, bool append) { - if (append) { + if (append) splitter->addWidget(widget); - } else { + else splitter->insertWidget(0, widget); - } } /** @@ -128,14 +127,14 @@ namespace ADS * Adds dock widget to container and returns the dock area that contains * the inserted dock widget */ - DockAreaWidget *dockWidgetIntoContainer(DockWidgetArea area, DockWidget *dockWidget); + DockAreaWidget *addDockWidgetToContainer(DockWidgetArea area, DockWidget *dockWidget); /** * Adds dock widget to a existing DockWidgetArea */ - DockAreaWidget *dockWidgetIntoDockArea(DockWidgetArea area, - DockWidget *dockWidget, - DockAreaWidget *targetDockArea); + DockAreaWidget *addDockWidgetToDockArea(DockWidgetArea area, + DockWidget *dockWidget, + DockAreaWidget *targetDockArea); /** * Add dock area to this container @@ -233,14 +232,12 @@ namespace ADS */ void initVisibleDockAreaCount() { - if (m_visibleDockAreaCount > -1) { + if (m_visibleDockAreaCount > -1) return; - } m_visibleDockAreaCount = 0; - for (auto dockArea : m_dockAreas) { + for (auto dockArea : m_dockAreas) m_visibleDockAreaCount += dockArea->isHidden() ? 0 : 1; - } } /** @@ -248,7 +245,7 @@ namespace ADS */ int visibleDockAreaCount() { - // Lazy initialization - we initialize the VisibleDockAreaCount variable + // Lazy initialization - we initialize the m_visibleDockAreaCount variable // on first use initVisibleDockAreaCount(); return m_visibleDockAreaCount; @@ -278,12 +275,29 @@ namespace ADS DockSplitter *createSplitter(Qt::Orientation orientation, QWidget *parent = nullptr) { auto *splitter = new DockSplitter(orientation, parent); - splitter->setOpaqueResize( - DockManager::configFlags().testFlag(DockManager::OpaqueSplitterResize)); + splitter->setOpaqueResize(DockManager::testConfigFlag(DockManager::OpaqueSplitterResize)); splitter->setChildrenCollapsible(false); return splitter; } + /** + * Ensures equal distribution of the sizes of a splitter if an dock widget + * is inserted from code + */ + void adjustSplitterSizesOnInsertion(QSplitter *splitter, qreal lastRatio = 1.0) + { + const int areaSize = (splitter->orientation() == Qt::Horizontal) ? splitter->width() + : splitter->height(); + auto splitterSizes = splitter->sizes(); + + const qreal totalRatio = splitterSizes.size() - 1.0 + lastRatio; + for (int i = 0; i < splitterSizes.size() - 1; ++i) + splitterSizes[i] = areaSize / totalRatio; + + splitterSizes.back() = areaSize * lastRatio / totalRatio; + splitter->setSizes(splitterSizes); + } + void onDockAreaViewToggled(bool visible) { DockAreaWidget *dockArea = qobject_cast(q->sender()); @@ -309,9 +323,8 @@ namespace ADS auto dropOverlay = m_dockManager->dockAreaOverlay(); dropOverlay->setAllowedAreas(dockArea->allowedAreas()); dropArea = dropOverlay->showOverlay(dockArea); - if (containerDropArea != InvalidDockWidgetArea && containerDropArea != dropArea) { + if (containerDropArea != InvalidDockWidgetArea && containerDropArea != dropArea) dropArea = InvalidDockWidgetArea; - } if (dropArea != InvalidDockWidgetArea) { qCInfo(adsLog) << "Dock Area Drop Content: " << dropArea; @@ -323,9 +336,8 @@ namespace ADS if (InvalidDockWidgetArea == dropArea) { dropArea = containerDropArea; qCInfo(adsLog) << "Container Drop Content: " << dropArea; - if (dropArea != InvalidDockWidgetArea) { + if (dropArea != InvalidDockWidgetArea) return DropModeIntoContainer; - } } return DropModeInvalid; @@ -373,11 +385,9 @@ namespace ADS if (floatingSplitter->count() == 1) { insertWidgetIntoSplitter(splitter, floatingSplitter->widget(0), insertParam.append()); } else if (floatingSplitter->orientation() == insertParam.orientation()) { - while (floatingSplitter->count()) { - insertWidgetIntoSplitter(splitter, - floatingSplitter->widget(0), - insertParam.append()); - } + int insertIndex = insertParam.append() ? splitter->count() : 0; + while (floatingSplitter->count()) + splitter->insertWidget(insertIndex++, floatingSplitter->widget(0)); } else { insertWidgetIntoSplitter(splitter, floatingSplitter, insertParam.append()); } @@ -388,9 +398,9 @@ namespace ADS // If we dropped the floating widget into the main dock container that does // not contain any dock widgets, then splitter is invisible and we need to // show it to display the docked widgets - if (!splitter->isVisible()) { + if (!splitter->isVisible()) splitter->show(); - } + q->dumpLayout(); } @@ -405,9 +415,8 @@ namespace ADS // If the floating widget contains only one single dock are, then the // current dock widget of the dock area will also be the future current // dock widget in the drop area. - if (topLevelDockArea) { + if (topLevelDockArea) newCurrentIndex = topLevelDockArea->currentIndex(); - } for (int i = 0; i < newDockWidgets.count(); ++i) { DockWidget *dockWidget = newDockWidgets[i]; @@ -415,9 +424,8 @@ namespace ADS // If the floating widget contains multiple visible dock areas, then we // simply pick the first visible open dock widget and make it // the current one. - if (newCurrentIndex < 0 && !dockWidget->isClosed()) { + if (newCurrentIndex < 0 && !dockWidget->isClosed()) newCurrentIndex = i; - } } targetArea->setCurrentIndex(newCurrentIndex); targetArea->updateTitleBarVisibility(); @@ -464,9 +472,8 @@ namespace ADS } else { adjustSplitterSizes = (floatingSplitter->count() == 1); int insertIndex = areaIndex + insertParam.insertOffset(); - while (floatingSplitter->count()) { + while (floatingSplitter->count()) targetAreaSplitter->insertWidget(insertIndex++, floatingSplitter->widget(0)); - } } if (adjustSplitterSizes) { @@ -516,9 +523,12 @@ namespace ADS if (droppedDockWidget) { DockAreaWidget *oldDockArea = droppedDockWidget->dockAreaWidget(); - if (oldDockArea) { + if (oldDockArea == targetArea) + return; + + if (oldDockArea) oldDockArea->removeDockWidget(droppedDockWidget); - } + targetArea->insertDockWidget(0, droppedDockWidget, true); } else { QList newDockWidgets = droppedArea->dockWidgets(); @@ -553,9 +563,9 @@ namespace ADS if (droppedDockWidget) { newDockArea = new DockAreaWidget(m_dockManager, q); DockAreaWidget *oldDockArea = droppedDockWidget->dockAreaWidget(); - if (oldDockArea) { + if (oldDockArea) oldDockArea->removeDockWidget(droppedDockWidget); - } + newDockArea->addDockWidget(droppedDockWidget); } else { droppedDockArea->dockContainer()->removeDockArea(droppedDockArea); @@ -564,25 +574,25 @@ namespace ADS auto insertParam = internal::dockAreaInsertParameters(area); QSplitter *targetAreaSplitter = internal::findParent(targetArea); - int areaIndex = targetAreaSplitter->indexOf(targetArea); + const int areaIndex = targetAreaSplitter->indexOf(targetArea); auto sizes = targetAreaSplitter->sizes(); if (targetAreaSplitter->orientation() == insertParam.orientation()) { - int targetAreaSize = (insertParam.orientation() == Qt::Horizontal) - ? targetArea->width() - : targetArea->height(); + const int targetAreaSize = (insertParam.orientation() == Qt::Horizontal) + ? targetArea->width() + : targetArea->height(); targetAreaSplitter->insertWidget(areaIndex + insertParam.insertOffset(), newDockArea); - int size = (targetAreaSize - targetAreaSplitter->handleWidth()) / 2; + const int size = (targetAreaSize - targetAreaSplitter->handleWidth()) / 2; sizes[areaIndex] = size; sizes.insert(areaIndex, size); } else { auto sizes = targetAreaSplitter->sizes(); - int targetAreaSize = (insertParam.orientation() == Qt::Horizontal) - ? targetArea->width() - : targetArea->height(); + const int targetAreaSize = (insertParam.orientation() == Qt::Horizontal) + ? targetArea->width() + : targetArea->height(); QSplitter *newSplitter = createSplitter(insertParam.orientation()); newSplitter->addWidget(targetArea); insertWidgetIntoSplitter(newSplitter, newDockArea, insertParam.append()); - int size = targetAreaSize / 2; + const int size = targetAreaSize / 2; newSplitter->setSizes({size, size}); targetAreaSplitter->insertWidget(areaIndex, newSplitter); } @@ -600,9 +610,9 @@ namespace ADS if (droppedDockWidget) { newDockArea = new DockAreaWidget(m_dockManager, q); DockAreaWidget *oldDockArea = droppedDockWidget->dockAreaWidget(); - if (oldDockArea) { + if (oldDockArea) oldDockArea->removeDockWidget(droppedDockWidget); - } + newDockArea->addDockWidget(droppedDockWidget); } else { // We check, if we insert the dropped widget into the same place that @@ -627,8 +637,8 @@ namespace ADS void DockContainerWidgetPrivate::addDockAreasToList(const QList newDockAreas) { - int countBefore = m_dockAreas.count(); - int newAreaCount = newDockAreas.count(); + const int countBefore = m_dockAreas.count(); + const int newAreaCount = newDockAreas.count(); appendDockAreas(newDockAreas); // If the user dropped a floating widget that contains only one single // visible dock area, then its title bar button TitleBarButtonUndock is @@ -640,13 +650,11 @@ namespace ADS // We need to ensure, that the dock area title bar is visible. The title bar // is invisible, if the dock are is a single dock area in a floating widget. - if (1 == countBefore) { + if (1 == countBefore) m_dockAreas.at(0)->updateTitleBarVisibility(); - } - if (1 == newAreaCount) { + if (1 == newAreaCount) m_dockAreas.last()->updateTitleBarVisibility(); - } emitDockAreasAdded(); } @@ -674,23 +682,21 @@ namespace ADS stream.writeAttribute("count", QString::number(splitter->count())); qCInfo(adsLog) << "NodeSplitter orient: " << splitter->orientation() << " WidgetCont: " << splitter->count(); - for (int i = 0; i < splitter->count(); ++i) { + for (int i = 0; i < splitter->count(); ++i) saveChildNodesState(stream, splitter->widget(i)); - } stream.writeStartElement("sizes"); QStringList sizes; - for (auto size : splitter->sizes()) { + for (auto size : splitter->sizes()) sizes.append(QString::number(size)); - } + stream.writeCharacters(sizes.join(" ")); - stream.writeEndElement(); - stream.writeEndElement(); + stream.writeEndElement(); // sizes + stream.writeEndElement(); // splitter } else { DockAreaWidget *dockArea = qobject_cast(widget); - if (dockArea) { + if (dockArea) dockArea->saveState(stream); - } } } @@ -701,22 +707,22 @@ namespace ADS QVariant orientationVar = QVariant(stateReader.attributes().value("orientation").toString()); // Check if the orientation string is convertable - if (!orientationVar.canConvert()) { + if (!orientationVar.canConvert()) return false; - } + Qt::Orientation orientation = orientationVar.value(); bool ok; int widgetCount = stateReader.attributes().value("count").toInt(&ok); - if (!ok) { + if (!ok) return false; - } + qCInfo(adsLog) << "Restore NodeSplitter Orientation: " << orientation << " WidgetCount: " << widgetCount; QSplitter *splitter = nullptr; - if (!testing) { + if (!testing) splitter = createSplitter(orientation); - } + bool visible = false; QList sizes; while (stateReader.readNextStartElement()) { @@ -739,13 +745,11 @@ namespace ADS stateReader.skipCurrentElement(); } - if (!result) { + if (!result) return false; - } - if (testing || !childNode) { + if (testing || !childNode) continue; - } qCInfo(adsLog) << "ChildNode isVisible " << childNode->isVisible() << " isVisibleTo " << childNode->isVisibleTo(splitter); @@ -753,9 +757,8 @@ namespace ADS visible |= childNode->isVisibleTo(splitter); } - if (sizes.count() != widgetCount) { + if (sizes.count() != widgetCount) return false; - } if (!testing) { if (!splitter->count()) { @@ -782,22 +785,20 @@ namespace ADS #ifdef ADS_DEBUG_PRINT bool ok; int tabs = stateReader.attributes().value("tabs").toInt(&ok); - if (!ok) { + if (!ok) return false; - } + qCInfo(adsLog) << "Restore NodeDockArea Tabs: " << tabs << " Current: " << currentDockWidget; #endif DockAreaWidget *dockArea = nullptr; - if (!testing) { + if (!testing) dockArea = new DockAreaWidget(m_dockManager, q); - } while (stateReader.readNextStartElement()) { - if (stateReader.name() != "widget") { + if (stateReader.name() != "widget") continue; - } auto objectName = stateReader.attributes().value("name"); if (objectName.isEmpty()) { @@ -806,16 +807,15 @@ namespace ADS } QVariant closedVar = QVariant(stateReader.attributes().value("closed").toString()); - if (!closedVar.canConvert()) { + if (!closedVar.canConvert()) return false; - } + bool closed = closedVar.value(); stateReader.skipCurrentElement(); DockWidget *dockWidget = m_dockManager->findDockWidget(objectName.toString()); - if (!dockWidget || testing) { + if (!dockWidget || testing) continue; - } qCInfo(adsLog) << "Dock Widget found - parent " << dockWidget->parent(); // We hide the DockArea here to prevent the short display (the flashing) @@ -828,9 +828,8 @@ namespace ADS dockWidget->setProperty(internal::dirtyProperty, false); } - if (testing) { + if (testing) return true; - } if (!dockArea->dockWidgetsCount()) { delete dockArea; @@ -865,8 +864,8 @@ namespace ADS return result; } - DockAreaWidget *DockContainerWidgetPrivate::dockWidgetIntoContainer(DockWidgetArea area, - DockWidget *dockWidget) + DockAreaWidget *DockContainerWidgetPrivate::addDockWidgetToContainer(DockWidgetArea area, + DockWidget *dockWidget) { DockAreaWidget *newDockArea = new DockAreaWidget(m_dockManager, q); newDockArea->addDockWidget(dockWidget); @@ -880,13 +879,15 @@ namespace ADS { auto insertParam = internal::dockAreaInsertParameters(area); // As long as we have only one dock area in the splitter we can adjust its orientation - if (m_dockAreas.count() <= 1) { + if (m_dockAreas.count() <= 1) m_rootSplitter->setOrientation(insertParam.orientation()); - } QSplitter *splitter = m_rootSplitter; if (splitter->orientation() == insertParam.orientation()) { insertWidgetIntoSplitter(splitter, newDockArea, insertParam.append()); + if (splitter->isHidden()) + splitter->show(); + } else { QSplitter *newSplitter = createSplitter(insertParam.orientation()); if (insertParam.append()) { @@ -924,14 +925,13 @@ namespace ADS << (splitter->isHidden() ? " " : "v") << " " << QString::number(splitter->count()).toStdString() << std::endl; #endif - for (int i = 0; i < splitter->count(); ++i) { + for (int i = 0; i < splitter->count(); ++i) dumpRecursive(level + 1, splitter->widget(i)); - } } else { DockAreaWidget *dockArea = qobject_cast(widget); - if (!dockArea) { + if (!dockArea) return; - } + #ifdef ADS_DEBUG_PRINT qDebug("%sDockArea", buf.data()); std::cout << buf.data() << (dockArea->isHidden() ? " " : "v") @@ -953,10 +953,9 @@ namespace ADS #endif } - DockAreaWidget *DockContainerWidgetPrivate::dockWidgetIntoDockArea(DockWidgetArea area, - DockWidget *dockWidget, - DockAreaWidget - *targetDockArea) + DockAreaWidget *DockContainerWidgetPrivate::addDockWidgetToDockArea(DockWidgetArea area, + DockWidget *dockWidget, + DockAreaWidget *targetDockArea) { if (CenterDockWidgetArea == area) { targetDockArea->addDockWidget(dockWidget); @@ -973,12 +972,20 @@ namespace ADS if (targetAreaSplitter->orientation() == insertParam.orientation()) { qCInfo(adsLog) << "TargetAreaSplitter->orientation() == InsertParam.orientation()"; targetAreaSplitter->insertWidget(index + insertParam.insertOffset(), newDockArea); + // do nothing, if flag is not enabled + if (DockManager::testConfigFlag(DockManager::EqualSplitOnInsertion)) + adjustSplitterSizesOnInsertion(targetAreaSplitter); } else { qCInfo(adsLog) << "TargetAreaSplitter->orientation() != InsertParam.orientation()"; + auto targetAreaSizes = targetAreaSplitter->sizes(); QSplitter *newSplitter = createSplitter(insertParam.orientation()); newSplitter->addWidget(targetDockArea); insertWidgetIntoSplitter(newSplitter, newDockArea, insertParam.append()); targetAreaSplitter->insertWidget(index, newSplitter); + if (DockManager::testConfigFlag(DockManager::EqualSplitOnInsertion)) { + targetAreaSplitter->setSizes(targetAreaSizes); + adjustSplitterSizesOnInsertion(newSplitter); + } } appendDockAreas({newDockArea}); @@ -1011,9 +1018,9 @@ namespace ADS DockContainerWidget::~DockContainerWidget() { - if (d->m_dockManager) { + if (d->m_dockManager) d->m_dockManager->removeDockContainer(this); - } + delete d; } @@ -1022,24 +1029,21 @@ namespace ADS DockAreaWidget *dockAreaWidget) { DockAreaWidget *oldDockArea = dockWidget->dockAreaWidget(); - if (oldDockArea) { + if (oldDockArea) oldDockArea->removeDockWidget(dockWidget); - } dockWidget->setDockManager(d->m_dockManager); - if (dockAreaWidget) { - return d->dockWidgetIntoDockArea(area, dockWidget, dockAreaWidget); - } else { - return d->dockWidgetIntoContainer(area, dockWidget); - } + if (dockAreaWidget) + return d->addDockWidgetToDockArea(area, dockWidget, dockAreaWidget); + else + return d->addDockWidgetToContainer(area, dockWidget); } void DockContainerWidget::removeDockWidget(DockWidget * dockWidget) { DockAreaWidget *area = dockWidget->dockAreaWidget(); - if (area) { + if (area) area->removeDockWidget(dockWidget); - } } unsigned int DockContainerWidget::zOrderIndex() const { return d->m_zOrderIndex; } @@ -1052,11 +1056,10 @@ namespace ADS bool DockContainerWidget::event(QEvent *event) { bool result = QWidget::event(event); - if (event->type() == QEvent::WindowActivate) { + if (event->type() == QEvent::WindowActivate) d->m_zOrderIndex = ++zOrderCounter; - } else if (event->type() == QEvent::Show && !d->m_zOrderIndex) { + else if (event->type() == QEvent::Show && !d->m_zOrderIndex) d->m_zOrderIndex = ++zOrderCounter; - } return result; } @@ -1064,9 +1067,8 @@ namespace ADS void DockContainerWidget::addDockArea(DockAreaWidget *dockAreaWidget, DockWidgetArea area) { DockContainerWidget *container = dockAreaWidget->dockContainer(); - if (container && container != this) { + if (container && container != this) container->removeDockArea(dockAreaWidget); - } d->addDockArea(dockAreaWidget, area); } @@ -1085,9 +1087,8 @@ namespace ADS // Remove this area from cached areas const auto &cache = d->m_lastAddedAreaCache; - if (auto p = std::find(cache, cache + sizeof(cache) / sizeof(cache[0]), area)) { + if (auto p = std::find(cache, cache + sizeof(cache) / sizeof(cache[0]), area)) d->m_lastAddedAreaCache[std::distance(cache, p)] = nullptr; - } // If splitter has more than 1 widgets, we are finished and can leave if (splitter->count() > 1) { @@ -1149,9 +1150,8 @@ namespace ADS { for (auto dockArea : d->m_dockAreas) { if (dockArea->isVisible() - && dockArea->rect().contains(dockArea->mapFromGlobal(globalPosition))) { + && dockArea->rect().contains(dockArea->mapFromGlobal(globalPosition))) return dockArea; - } } return nullptr; @@ -1169,9 +1169,8 @@ namespace ADS int DockContainerWidget::visibleDockAreaCount() const { int result = 0; - for (auto dockArea : d->m_dockAreas) { + for (auto dockArea : d->m_dockAreas) result += dockArea->isHidden() ? 0 : 1; - } return result; @@ -1195,9 +1194,8 @@ namespace ADS auto dropOverlay = d->m_dockManager->dockAreaOverlay(); dropOverlay->setAllowedAreas(dockArea->allowedAreas()); dropArea = dropOverlay->showOverlay(dockArea); - if (containerDropArea != InvalidDockWidgetArea && containerDropArea != dropArea) { + if (containerDropArea != InvalidDockWidgetArea && containerDropArea != dropArea) dropArea = InvalidDockWidgetArea; - } if (dropArea != InvalidDockWidgetArea) { qCInfo(adsLog) << "Dock Area Drop Content: " << dropArea; @@ -1227,6 +1225,11 @@ namespace ADS // level widget anymore DockWidget::emitTopLevelEventForWidget(singleDockWidget, false); } + window()->activateWindow(); + if (singleDroppedDockWidget) + d->m_dockManager->notifyWidgetOrAreaRelocation(singleDroppedDockWidget); + + d->m_dockManager->notifyFloatingWidgetDrop(floatingWidget); } void DockContainerWidget::dropWidget(QWidget *widget, DockWidgetArea dropArea, DockAreaWidget *targetAreaWidget) @@ -1240,15 +1243,25 @@ namespace ADS // If there was a top level widget before the drop, then it is not top // level widget anymore DockWidget::emitTopLevelEventForWidget(singleDockWidget, false); + DockWidget *dockWidget = qobject_cast(widget); + if (!dockWidget) + { + DockAreaWidget *dockArea = qobject_cast(widget); + auto openDockWidgets = dockArea->openedDockWidgets(); + if (openDockWidgets.count() == 1) + dockWidget = openDockWidgets[0]; + } + + window()->activateWindow(); + d->m_dockManager->notifyWidgetOrAreaRelocation(widget); } QList DockContainerWidget::openedDockAreas() const { QList result; for (auto dockArea : d->m_dockAreas) { - if (!dockArea->isHidden()) { + if (!dockArea->isHidden()) result.append(dockArea); - } } return result; @@ -1272,9 +1285,9 @@ namespace ADS bool DockContainerWidget::restoreState(DockingStateReader &stateReader, bool testing) { QVariant floatingVar = QVariant(stateReader.attributes().value("floating").toString()); - if (!floatingVar.canConvert()) { + if (!floatingVar.canConvert()) return false; - } + bool isFloating = floatingVar.value(); qCInfo(adsLog) << "Restore DockContainerWidget Floating" << isFloating; @@ -1289,18 +1302,16 @@ namespace ADS if (isFloating) { qCInfo(adsLog) << "Restore floating widget"; - if (!stateReader.readNextStartElement() || stateReader.name() != "geometry") { + if (!stateReader.readNextStartElement() || stateReader.name() != "geometry") return false; - } QByteArray geometryString = stateReader .readElementText( DockingStateReader::ErrorOnUnexpectedElement) .toLocal8Bit(); QByteArray geometry = QByteArray::fromBase64(geometryString); - if (geometry.isEmpty()) { + if (geometry.isEmpty()) return false; - } if (!testing) { FloatingDockContainer *floatingDockContainer = floatingWidget(); @@ -1308,19 +1319,16 @@ namespace ADS } } - if (!d->restoreChildNodes(stateReader, newRootSplitter, testing)) { + if (!d->restoreChildNodes(stateReader, newRootSplitter, testing)) return false; - } - if (testing) { + if (testing) return true; - } // If the root splitter is empty, rostoreChildNodes returns a 0 pointer // and we need to create a new empty root splitter - if (!newRootSplitter) { + if (!newRootSplitter) newRootSplitter = d->createSplitter(Qt::Horizontal); - } d->m_layout->replaceWidget(d->m_rootSplitter, newRootSplitter); QSplitter *oldRoot = d->m_rootSplitter; @@ -1334,9 +1342,9 @@ namespace ADS void DockContainerWidget::createRootSplitter() { - if (d->m_rootSplitter) { + if (d->m_rootSplitter) return; - } + d->m_rootSplitter = d->createSplitter(Qt::Horizontal); d->m_layout->addWidget(d->m_rootSplitter); } @@ -1359,14 +1367,9 @@ namespace ADS bool DockContainerWidget::hasTopLevelDockWidget() const { - if (!isFloating()) { - return false; - } - auto dockAreas = openedDockAreas(); - if (dockAreas.count() != 1) { + if (dockAreas.count() != 1) return false; - } return dockAreas[0]->openDockWidgetsCount() == 1; } @@ -1374,28 +1377,21 @@ namespace ADS DockWidget *DockContainerWidget::topLevelDockWidget() const { auto dockArea = topLevelDockArea(); - if (!dockArea) { + if (!dockArea) return nullptr; - } auto dockWidgets = dockArea->openedDockWidgets(); - if (dockWidgets.count() != 1) { + if (dockWidgets.count() != 1) return nullptr; - } return dockWidgets[0]; } DockAreaWidget *DockContainerWidget::topLevelDockArea() const { - if (!isFloating()) { - return nullptr; - } - auto dockAreas = openedDockAreas(); - if (dockAreas.count() != 1) { + if (dockAreas.count() != 1) return nullptr; - } return dockAreas[0]; } @@ -1403,9 +1399,8 @@ namespace ADS QList DockContainerWidget::dockWidgets() const { QList result; - for (const auto dockArea : d->m_dockAreas) { + for (const auto dockArea : d->m_dockAreas) result.append(dockArea->dockWidgets()); - } return result; } @@ -1413,9 +1408,8 @@ namespace ADS DockWidget::DockWidgetFeatures DockContainerWidget::features() const { DockWidget::DockWidgetFeatures features(DockWidget::AllDockWidgetFeatures); - for (const auto dockArea : d->m_dockAreas) { + for (const auto dockArea : d->m_dockAreas) features &= dockArea->features(); - } return features; } @@ -1428,18 +1422,15 @@ namespace ADS void DockContainerWidget::closeOtherAreas(DockAreaWidget *keepOpenArea) { for (const auto dockArea : d->m_dockAreas) { - if (dockArea == keepOpenArea) { + if (dockArea == keepOpenArea) continue; - } - if (!dockArea->features(BitwiseAnd).testFlag(DockWidget::DockWidgetClosable)) { + if (!dockArea->features(BitwiseAnd).testFlag(DockWidget::DockWidgetClosable)) continue; - } // We do not close areas with widgets with custom close handling - if (dockArea->features(BitwiseOr).testFlag(DockWidget::CustomCloseHandling)) { + if (dockArea->features(BitwiseOr).testFlag(DockWidget::CustomCloseHandling)) continue; - } dockArea->closeArea(); } diff --git a/src/libs/advanceddockingsystem/dockcontainerwidget.h b/src/libs/advanceddockingsystem/dockcontainerwidget.h index 266156c5b71..d50ab6c3070 100644 --- a/src/libs/advanceddockingsystem/dockcontainerwidget.h +++ b/src/libs/advanceddockingsystem/dockcontainerwidget.h @@ -59,10 +59,10 @@ class FloatingDragPreviewPrivate; /** * Container that manages a number of dock areas with single dock widgets - * or tabyfied dock widgets in each area. + * or tabified dock widgets in each area. * Each window that support docking has a DockContainerWidget. That means - * the main application window and all floating windows contain - * a DockContainerWidget. + * the main application window and all floating windows contain a + * DockContainerWidget instance. */ class ADS_EXPORT DockContainerWidget : public QFrame { @@ -86,13 +86,11 @@ protected: */ bool event(QEvent *event) override; -public: // TODO temporary /** * Access function for the internal root splitter */ QSplitter *rootSplitter() const; -protected: /** * Helper function for creation of the root splitter */ diff --git a/src/libs/advanceddockingsystem/dockfocuscontroller.cpp b/src/libs/advanceddockingsystem/dockfocuscontroller.cpp new file mode 100644 index 00000000000..0895fe81c32 --- /dev/null +++ b/src/libs/advanceddockingsystem/dockfocuscontroller.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** 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 "dockfocuscontroller.h" + +#include "dockareawidget.h" +#include "dockareatitlebar.h" +#include "dockcontainerwidget.h" +#include "dockmanager.h" +#include "dockwidget.h" +#include "dockwidgettab.h" +#include "floatingdockcontainer.h" + +#ifdef Q_OS_LINUX +#include "linux/floatingwidgettitlebar.h" +#endif + +#include +#include + +#include +#include + +namespace ADS +{ + /** + * Private data class of CDockFocusController class (pimpl) + */ + class DockFocusControllerPrivate + { + public: + DockFocusController *q; + QPointer m_focusedDockWidget = nullptr; + QPointer m_focusedArea = nullptr; + #ifdef Q_OS_LINUX + QPointer m_floatingWidget = nullptr; + #endif + DockManager *m_dockManager; + + /** + * Private data constructor + */ + DockFocusControllerPrivate(DockFocusController *parent); + + /** + * This function updates the focus style of the given dock widget and + * the dock area that it belongs to + */ + void updateDockWidgetFocus(DockWidget *dockWidget); + }; // class DockFocusControllerPrivate + + static void updateDockWidgetFocusStyle(DockWidget *dockWidget, bool focused) + { + dockWidget->setProperty("focused", focused); + dockWidget->tabWidget()->setProperty("focused", focused); + dockWidget->tabWidget()->updateStyle(); + internal::repolishStyle(dockWidget); + } + + static void updateDockAreaFocusStyle(DockAreaWidget *dockArea, bool focused) + { + dockArea->setProperty("focused", focused); + internal::repolishStyle(dockArea); + internal::repolishStyle(dockArea->titleBar()); + } + +#ifdef Q_OS_LINUX + static void updateFloatingWidgetFocusStyle(FloatingDockContainer *floatingWidget, bool focused) + { + auto titleBar = qobject_cast(floatingWidget->titleBarWidget()); + if (!titleBar) + return; + + titleBar->setProperty("focused", focused); + titleBar->updateStyle(); + } +#endif + + DockFocusControllerPrivate::DockFocusControllerPrivate(DockFocusController *parent) + : q(parent) + {} + + void DockFocusControllerPrivate::updateDockWidgetFocus(DockWidget *dockWidget) + { + DockAreaWidget *newFocusedDockArea = nullptr; + if (m_focusedDockWidget) + updateDockWidgetFocusStyle(m_focusedDockWidget, false); + + DockWidget *old = m_focusedDockWidget; + m_focusedDockWidget = dockWidget; + updateDockWidgetFocusStyle(m_focusedDockWidget, true); + newFocusedDockArea = m_focusedDockWidget->dockAreaWidget(); + if (newFocusedDockArea && (m_focusedArea != newFocusedDockArea)) + { + if (m_focusedArea) + { + QObject::disconnect(m_focusedArea, &DockAreaWidget::viewToggled, + q, &DockFocusController::onFocusedDockAreaViewToggled); + updateDockAreaFocusStyle(m_focusedArea, false); + } + + m_focusedArea = newFocusedDockArea; + updateDockAreaFocusStyle(m_focusedArea, true); + QObject::connect(m_focusedArea, &DockAreaWidget::viewToggled, + q, &DockFocusController::onFocusedDockAreaViewToggled); + } + + auto newFloatingWidget = m_focusedDockWidget->dockContainer()->floatingWidget(); + if (newFloatingWidget) + newFloatingWidget->setProperty("FocusedDockWidget", QVariant::fromValue(dockWidget)); + + #ifdef Q_OS_LINUX + // This code is required for styling the floating widget titlebar for linux + // depending on the current focus state + if (m_floatingWidget == newFloatingWidget) + return; + + if (m_floatingWidget) + updateFloatingWidgetFocusStyle(m_floatingWidget, false); + + m_floatingWidget = newFloatingWidget; + + if (m_floatingWidget) + updateFloatingWidgetFocusStyle(m_floatingWidget, true); + #endif + + if (old != dockWidget) + emit m_dockManager->focusedDockWidgetChanged(old, dockWidget); + } + + DockFocusController::DockFocusController(DockManager *dockManager) + : QObject(dockManager) + , d(new DockFocusControllerPrivate(this)) + { + d->m_dockManager = dockManager; + connect(qApp, &QApplication::focusChanged, + this, &DockFocusController::onApplicationFocusChanged); + connect(d->m_dockManager, &DockManager::stateRestored, + this, &DockFocusController::onStateRestored); + } + + DockFocusController::~DockFocusController() + { + delete d; + } + + void DockFocusController::onApplicationFocusChanged(QWidget *focusedOld, QWidget *focusedNow) + { + if (d->m_dockManager->isRestoringState()) + return; + + if (!focusedNow) + return; + + DockWidget *dockWidget = nullptr; + auto dockWidgetTab = qobject_cast(focusedNow); + if (dockWidgetTab) + dockWidget = dockWidgetTab->dockWidget(); + + if (!dockWidget) + dockWidget = qobject_cast(focusedNow); + + if (!dockWidget) + dockWidget = internal::findParent(focusedNow); + + #ifdef Q_OS_LINUX + if (!dockWidget) + return; + #else + if (!dockWidget || dockWidget->tabWidget()->isHidden()) + return; + #endif + + d->updateDockWidgetFocus(dockWidget); + } + + void DockFocusController::setDockWidgetFocused(DockWidget *focusedNow) + { + d->updateDockWidgetFocus(focusedNow); + } + + void DockFocusController::onFocusedDockAreaViewToggled(bool open) + { + if (d->m_dockManager->isRestoringState()) + return; + + DockAreaWidget* dockArea = qobject_cast(sender()); + if (!dockArea || open) + return; + + auto container = dockArea->dockContainer(); + auto openedDockAreas = container->openedDockAreas(); + if (openedDockAreas.isEmpty()) + return; + + DockManager::setWidgetFocus(openedDockAreas[0]->currentDockWidget()->tabWidget()); + } + + void DockFocusController::notifyWidgetOrAreaRelocation(QWidget *droppedWidget) + { + if (d->m_dockManager->isRestoringState()) + return; + + DockWidget *dockWidget = qobject_cast(droppedWidget); + if (dockWidget) + { + DockManager::setWidgetFocus(dockWidget->tabWidget()); + return; + } + + DockAreaWidget* dockArea = qobject_cast(droppedWidget); + if (!dockArea) + return; + + dockWidget = dockArea->currentDockWidget(); + DockManager::setWidgetFocus(dockWidget->tabWidget()); + } + + void DockFocusController::notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget) + { + if (!floatingWidget || d->m_dockManager->isRestoringState()) + return; + + auto vDockWidget = floatingWidget->property("FocusedDockWidget"); + if (!vDockWidget.isValid()) + return; + + auto dockWidget = vDockWidget.value(); + if (dockWidget) { + dockWidget->dockAreaWidget()->setCurrentDockWidget(dockWidget); + DockManager::setWidgetFocus(dockWidget->tabWidget()); + } + } + + void DockFocusController::onStateRestored() + { + if (d->m_focusedDockWidget) + updateDockWidgetFocusStyle(d->m_focusedDockWidget, false); + } + +} // namespace ADS diff --git a/src/libs/advanceddockingsystem/dockfocuscontroller.h b/src/libs/advanceddockingsystem/dockfocuscontroller.h new file mode 100644 index 00000000000..dfe6a841859 --- /dev/null +++ b/src/libs/advanceddockingsystem/dockfocuscontroller.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#pragma once + +#include "ads_globals.h" +#include "dockmanager.h" + +#include + +namespace ADS { + +class DockFocusControllerPrivate; +class DockManager; +class FloatingDockContainer; + +/** + * Manages focus styling of dock widgets and handling of focus changes + */ +class ADS_EXPORT DockFocusController : public QObject +{ + Q_OBJECT + +private: + DockFocusControllerPrivate* d; ///< private data (pimpl) + friend class DockFocusControllerPrivate; + +private: + void onApplicationFocusChanged(QWidget *old, QWidget *now); + void onFocusedDockAreaViewToggled(bool open); + void onStateRestored(); + +public: + /** + * Default Constructor + */ + DockFocusController(DockManager *dockManager); + + /** + * Virtual Destructor + */ + ~DockFocusController() override; + + /** + * Helper function to set focus depending on the configuration of the + * FocusStyling flag + */ + template + static void setWidgetFocus(QWidgetPtr widget) + { + if (!DockManager::testConfigFlag(DockManager::FocusHighlighting)) + return; + + widget->setFocus(Qt::OtherFocusReason); + } + + /** + * A container needs to call this function if a widget has been dropped + * into it + */ + void notifyWidgetOrAreaRelocation(QWidget *relocatedWidget); + + /** + * This function is called, if a floating widget has been dropped into + * an new position. + * When this function is called, all dock widgets of the FloatingWidget + * are already inserted into its new position + */ + void notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget); + + /** + * Request a focus change to the given dock widget + */ + void setDockWidgetFocused(DockWidget *focusedNow); +}; // class DockFocusController + +} // namespace ADS diff --git a/src/libs/advanceddockingsystem/dockmanager.cpp b/src/libs/advanceddockingsystem/dockmanager.cpp index 2e0d7c0dd61..7958b9efb63 100644 --- a/src/libs/advanceddockingsystem/dockmanager.cpp +++ b/src/libs/advanceddockingsystem/dockmanager.cpp @@ -36,7 +36,9 @@ #include "dockmanager.h" #include "ads_globals.h" +#include "dockareatitlebar.h" #include "dockareawidget.h" +#include "dockfocuscontroller.h" #include "dockingstatereader.h" #include "dockoverlay.h" #include "dockwidget.h" @@ -72,6 +74,15 @@ static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWar namespace ADS { + /** + * Internal file version in case the structure changes internally + */ + enum eStateFileVersion { + InitialVersion = 0, //!< InitialVersion + Version1 = 1, //!< Version1 + CurrentVersion = Version1 //!< CurrentVersion + }; + static DockManager::ConfigFlags g_staticConfigFlags = DockManager::DefaultNonOpaqueConfig; /** @@ -88,6 +99,7 @@ namespace ADS QMap m_dockWidgetsMap; bool m_restoringState = false; QVector m_uninitializedFloatingWidgets; + DockFocusController *m_focusController = nullptr; QString m_workspaceName; bool m_workspaceListDirty = true; @@ -159,11 +171,10 @@ namespace ADS } else { qCInfo(adsLog) << "d->m_containers[i]->restoreState "; auto container = m_containers[index]; - if (container->isFloating()) { + if (container->isFloating()) result = container->floatingWidget()->restoreState(stream, testing); - } else { + else result = container->restoreState(stream, testing); - } } return result; @@ -190,6 +201,17 @@ namespace ADS return false; stateReader.setFileVersion(v); + + qCInfo(adsLog) << stateReader.attributes().value("userVersion"); + // Older files do not support UserVersion but we still want to load them so + // we first test if the attribute exists + if (!stateReader.attributes().value("userVersion").isEmpty()) + { + v = stateReader.attributes().value("userVersion").toInt(&ok); + if (!ok || v != version) + return false; + } + bool result = true; #ifdef ADS_DEBUG_PRINT int dockContainers = stateReader.attributes().value("containers").toInt(); @@ -248,9 +270,8 @@ namespace ADS DockAreaWidget *dockArea = dockContainer->dockArea(i); QString dockWidgetName = dockArea->property("currentDockWidget").toString(); DockWidget *dockWidget = nullptr; - if (!dockWidgetName.isEmpty()) { + if (!dockWidgetName.isEmpty()) dockWidget = q->findDockWidget(dockWidgetName); - } if (!dockWidget || dockWidget->isClosed()) { int index = dockArea->indexOfFirstOpenDockWidget(); @@ -276,9 +297,8 @@ namespace ADS } else { for (int i = 0; i < dockContainer->dockAreaCount(); ++i) { auto dockArea = dockContainer->dockArea(i); - for (auto dockWidget : dockArea->dockWidgets()) { + for (auto dockWidget : dockArea->dockWidgets()) dockWidget->emitTopLevelChanged(false); - } } } } @@ -326,6 +346,9 @@ namespace ADS d->m_dockAreaOverlay = new DockOverlay(this, DockOverlay::ModeDockAreaOverlay); d->m_containerOverlay = new DockOverlay(this, DockOverlay::ModeContainerOverlay); d->m_containers.append(this); + + if (DockManager::configFlags().testFlag(DockManager::FocusHighlighting)) + d->m_focusController = new DockFocusController(this); } DockManager::~DockManager() @@ -398,13 +421,12 @@ namespace ADS DockAreaWidget *DockManager::addDockWidgetTab(DockWidgetArea area, DockWidget *dockWidget) { DockAreaWidget *areaWidget = lastAddedDockAreaWidget(area); - if (areaWidget) { + if (areaWidget) return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, areaWidget); - } else if (!openedDockAreas().isEmpty()) { + else if (!openedDockAreas().isEmpty()) return addDockWidget(area, dockWidget, openedDockAreas().last()); - } else { + else return addDockWidget(area, dockWidget, nullptr); - } } DockAreaWidget *DockManager::addDockWidgetTabToArea(DockWidget *dockWidget, @@ -423,11 +445,11 @@ namespace ADS dockWidget->setDockManager(this); FloatingDockContainer *floatingWidget = new FloatingDockContainer(dockWidget); floatingWidget->resize(dockWidget->size()); - if (isVisible()) { + if (isVisible()) floatingWidget->show(); - } else { + else d->m_uninitializedFloatingWidgets.append(floatingWidget); - } + return floatingWidget; } @@ -450,9 +472,8 @@ namespace ADS void DockManager::removeDockContainer(DockContainerWidget *dockContainer) { - if (this != dockContainer) { + if (this != dockContainer) d->m_containers.removeAll(dockContainer); - } } DockOverlay *DockManager::containerOverlay() const { return d->m_containerOverlay; } @@ -479,7 +500,8 @@ namespace ADS stream.setAutoFormatting(configFlags.testFlag(XmlAutoFormattingEnabled)); stream.writeStartDocument(); stream.writeStartElement("QtAdvancedDockingSystem"); - stream.writeAttribute("version", QString::number(version)); + stream.writeAttribute("version", QString::number(CurrentVersion)); + stream.writeAttribute("userVersion", QString::number(version)); stream.writeAttribute("containers", QString::number(d->m_containers.count())); for (auto container : d->m_containers) container->saveState(stream); @@ -512,10 +534,10 @@ namespace ADS emit restoringState(); bool result = d->restoreState(state, version); d->m_restoringState = false; - emit stateRestored(); if (!isHidden) show(); + emit stateRestored(); return result; } @@ -574,15 +596,14 @@ namespace ADS emit aboutToSaveWorkspace(); bool result = write(activeWorkspace(), saveState(), parentWidget()); - if (result) { + if (result) d->m_workspaceDateTimes.insert(activeWorkspace(), QDateTime::currentDateTime()); - } else { + else QMessageBox::warning(parentWidget(), tr("Cannot Save Workspace"), tr("Could not save workspace to file %1") .arg(workspaceNameToFilePath(d->m_workspaceName) .toUserOutput())); - } return result; } @@ -638,9 +659,8 @@ namespace ADS = workspacePresetsDir.entryInfoList(QStringList() << QLatin1Char('*') + m_fileExt, QDir::NoFilter, QDir::Time); - for (const QFileInfo &fileInfo : workspacePresetsFiles) { + for (const QFileInfo &fileInfo : workspacePresetsFiles) d->m_workspacePresets.insert(fileNameToWorkspaceName(fileInfo.completeBaseName())); - } } return d->m_workspacePresets; } @@ -819,8 +839,10 @@ namespace ADS { if (!cloneWorkspace(original, newName)) return false; + if (original == activeWorkspace()) openWorkspace(newName); + return deleteWorkspace(original); } @@ -1002,10 +1024,10 @@ namespace ADS QFile file(filePath); if (file.exists()) { - if (!file.copy(workspaceDir.filePath(fileName))) { + if (!file.copy(workspaceDir.filePath(fileName))) qCInfo(adsLog) << QString("Could not copy '%1' to '%2' error: %3").arg( filePath, workspaceDir.filePath(fileName), file.errorString()); - } + d->m_workspaceListDirty = true; } } @@ -1020,4 +1042,22 @@ namespace ADS d->m_settings->setValue(Constants::STARTUP_WORKSPACE_SETTINGS_KEY, activeWorkspace()); } + void DockManager::notifyWidgetOrAreaRelocation(QWidget *droppedWidget) + { + if (d->m_focusController) + d->m_focusController->notifyWidgetOrAreaRelocation(droppedWidget); + } + + void DockManager::notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget) + { + if (d->m_focusController) + d->m_focusController->notifyFloatingWidgetDrop(floatingWidget); + } + + void DockManager::setDockWidgetFocused(DockWidget *dockWidget) + { + if (d->m_focusController) + d->m_focusController->setDockWidgetFocused(dockWidget); + } + } // namespace ADS diff --git a/src/libs/advanceddockingsystem/dockmanager.h b/src/libs/advanceddockingsystem/dockmanager.h index 4cda0948a79..43ba751e48b 100644 --- a/src/libs/advanceddockingsystem/dockmanager.h +++ b/src/libs/advanceddockingsystem/dockmanager.h @@ -142,6 +142,32 @@ protected: */ DockOverlay *dockAreaOverlay() const; + /** + * A container needs to call this function if a widget has been dropped + * into it + */ + void notifyWidgetOrAreaRelocation(QWidget *droppedWidget); + + /** + * This function is called, if a floating widget has been dropped into + * an new position. + * When this function is called, all dock widgets of the FloatingWidget + * are already inserted into its new position + */ + void notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget); + + /** + * This function is called, if the given DockWidget has been relocated from + * the old container ContainerOld to the new container DockWidget->dockContainer() + */ + void notifyDockWidgetRelocation(DockWidget *dockWidget, DockContainerWidget *containerOld); + + /** + * This function is called, if the given DockAreahas been relocated from + * the old container ContainerOld to the new container DockArea->dockContainer() + */ + void notifyDockAreaRelocation(DockAreaWidget *dockArea, DockContainerWidget *containerOld); + /** * Show the floating widgets that has been created floating */ @@ -190,11 +216,18 @@ public: DockAreaHideDisabledButtons = 0x10000, //!< If the flag is set disabled dock area buttons will not appear on the tollbar at all (enabling them will bring them back) DockAreaDynamicTabsMenuButtonVisibility - = 0x20000, //!< If the flag is set dock area will disable a tabs menu button when there is only one tab in the area + = 0x20000, //!< If the flag is set, the tabs menu button will be shown only when it is required - that means, if the tabs are elided. If the tabs are not elided, it is hidden FloatingContainerHasWidgetTitle - = 0x40000, + = 0x40000, //!< If set, the Floating Widget window title reflects the title of the current dock widget otherwise it displays application name as window title FloatingContainerHasWidgetIcon - = 0x80000, + = 0x80000, //!< If set, the Floating Widget icon reflects the icon of the current dock widget otherwise it displays application icon + HideSingleCentralWidgetTitleBar + = 0x100000, //!< If there is only one single visible dock widget in the main dock container (the dock manager) and if this flag is set, then the titlebar of this dock widget will be hidden + //!< this only makes sense for non draggable and non floatable widgets and enables the creation of some kind of "central" widget + FocusHighlighting + = 0x200000, //!< enables styling of focused dock widget tabs or floating widget titlebar + EqualSplitOnInsertion + = 0x400000, ///!< if enabled, the space is equally distributed to all widgets in a splitter DefaultDockAreaButtons = DockAreaHasCloseButton | DockAreaHasUndockButton @@ -267,6 +300,19 @@ public: */ static int startDragDistance(); + /** + * Helper function to set focus depending on the configuration of the + * FocusStyling flag + */ + template + static void setWidgetFocus(QWidgetPtr widget) + { + if (!DockManager::testConfigFlag(DockManager::FocusHighlighting)) + return; + + widget->setFocus(Qt::OtherFocusReason); + } + /** * Set the QtCreator settings. */ @@ -358,8 +404,12 @@ public: * If auto formatting is enabled, the output is intended and line wrapped. * The XmlMode XmlAutoFormattingDisabled is better if you would like to have * a more compact XML output - i.e. for storage in ini files. + * The version number is stored as part of the data. + * To restore the saved state, pass the return value and version number + * to restoreState(). + * \see restoreState() */ - QByteArray saveState(int version = Version1) const; + QByteArray saveState(int version = 0) const; /** * Restores the state of this dockmanagers dockwidgets. @@ -367,8 +417,9 @@ public: * not match, the dockmanager's state is left unchanged, and this function * returns false; otherwise, the state is restored, and this function * returns true. + * \see saveState() */ - bool restoreState(const QByteArray &state, int version = Version1); + bool restoreState(const QByteArray &state, int version = 0); /** * This function returns true between the restoringState() and @@ -376,6 +427,13 @@ public: */ bool isRestoringState() const; + /** + * Request a focus change to the given dock widget. + * This function only has an effect, if the flag CDockManager::FocusStyling + * is enabled + */ + void setDockWidgetFocused(DockWidget *dockWidget); + signals: /** * This signal is emitted if the list of workspaces changed. @@ -444,6 +502,13 @@ signals: */ void dockWidgetRemoved(DockWidget *dockWidget); + /** + * This signal is emitted if the focused dock widget changed. + * Both old and now can be nullptr. + * The focused dock widget is the one that is highlighted in the GUI + */ + void focusedDockWidgetChanged(DockWidget *old, DockWidget *now); + public: void showWorkspaceMananger(); diff --git a/src/libs/advanceddockingsystem/dockoverlay.cpp b/src/libs/advanceddockingsystem/dockoverlay.cpp index 64558e0d483..b32c8bf3f2c 100644 --- a/src/libs/advanceddockingsystem/dockoverlay.cpp +++ b/src/libs/advanceddockingsystem/dockoverlay.cpp @@ -36,6 +36,7 @@ #include "dockoverlay.h" #include "dockareawidget.h" +#include "dockareatitlebar.h" #include @@ -151,12 +152,10 @@ namespace ADS { */ qreal dropIndicatiorWidth(QLabel *label) const { -#ifdef Q_OS_LINUX - Q_UNUSED(label) - return 40; -#else - return static_cast(label->fontMetrics().height()) * 3.f; -#endif + if (Utils::HostOsInfo::isLinuxHost()) + return 40; + else + return static_cast(label->fontMetrics().height()) * 3.f; } QWidget *createDropIndicatorWidget(DockWidgetArea dockWidgetArea, DockOverlay::eMode mode) @@ -190,10 +189,10 @@ namespace ADS { DockWidgetArea dockWidgetArea, DockOverlay::eMode mode) { - QColor borderColor = iconColor(DockOverlayCross::FrameColor); - QColor backgroundColor = iconColor(DockOverlayCross::WindowBackgroundColor); - double devicePixelRatio = q->window()->devicePixelRatioF(); - QSizeF pixmapSize = size * devicePixelRatio; + const QColor borderColor = iconColor(DockOverlayCross::FrameColor); + const QColor backgroundColor = iconColor(DockOverlayCross::WindowBackgroundColor); + const double devicePixelRatio = q->window()->devicePixelRatioF(); + const QSizeF pixmapSize = size * devicePixelRatio; QPixmap pixmap(pixmapSize.toSize()); pixmap.fill(QColor(0, 0, 0, 0)); @@ -259,7 +258,7 @@ namespace ADS { break; } - QSizeF baseSize = baseRect.size(); + const QSizeF baseSize = baseRect.size(); if (DockOverlay::ModeContainerOverlay == mode && dockWidgetArea != CenterDockWidgetArea) { baseRect = areaRect; } @@ -296,7 +295,7 @@ namespace ADS { // draw window title bar painter.setBrush(borderColor); - QRectF frameRect(baseRect.topLeft(), QSizeF(baseRect.width(), baseSize.height() / 10)); + const QRectF frameRect(baseRect.topLeft(), QSizeF(baseRect.width(), baseSize.height() / 10)); painter.drawRect(frameRect); painter.restore(); @@ -370,6 +369,7 @@ namespace ADS { { if (areas == d->m_allowedAreas) return; + d->m_allowedAreas = areas; d->m_cross->reset(); } @@ -382,19 +382,17 @@ namespace ADS { DockWidgetArea DockOverlay::dropAreaUnderCursor() const { DockWidgetArea result = d->m_cross->cursorLocation(); - if (result != InvalidDockWidgetArea) { + if (result != InvalidDockWidgetArea) return result; - } DockAreaWidget *dockArea = qobject_cast(d->m_targetWidget.data()); - if (!dockArea) { + if (!dockArea) return result; - } if (dockArea->allowedAreas().testFlag(CenterDockWidgetArea) - && dockArea->titleBarGeometry().contains(dockArea->mapFromGlobal(QCursor::pos()))) { + && !dockArea->titleBar()->isHidden() + && dockArea->titleBarGeometry().contains(dockArea->mapFromGlobal(QCursor::pos()))) return CenterDockWidgetArea; - } return result; } @@ -518,9 +516,9 @@ namespace ADS { bool DockOverlay::event(QEvent *event) { bool result = Super::event(event); - if (event->type() == QEvent::Polish) { + if (event->type() == QEvent::Polish) d->m_cross->setupOverlayCross(d->m_mode); - } + return result; } @@ -622,13 +620,12 @@ namespace ADS { void DockOverlayCross::updateOverlayIcons() { - if (windowHandle()->devicePixelRatio() == d->m_lastDevicePixelRatio) { // TODO + if (windowHandle()->devicePixelRatio() == d->m_lastDevicePixelRatio) // TODO return; - } - for (auto Widget : d->m_dropIndicatorWidgets) { - d->updateDropIndicatorIcon(Widget); - } + for (auto widget : d->m_dropIndicatorWidgets) + d->updateDropIndicatorIcon(widget); + d->m_lastDevicePixelRatio = devicePixelRatioF(); } @@ -661,7 +658,7 @@ namespace ADS { for (constIt = areas.begin(); constIt != areas.end(); ++constIt) { const DockWidgetArea area = constIt.key(); QWidget *widget = constIt.value(); - QPoint position = d->areaGridPosition(area); + const QPoint position = d->areaGridPosition(area); d->m_gridLayout->addWidget(widget, position.x(), position.y(), @@ -717,9 +714,9 @@ namespace ADS { void DockOverlayCross::showEvent(QShowEvent *) { - if (d->m_updateRequired) { + if (d->m_updateRequired) setupOverlayCross(d->m_mode); - } + this->updatePosition(); } @@ -744,12 +741,11 @@ namespace ADS { // Update visibility of area widgets based on allowedAreas. for (auto area : allAreas) { - QPoint position = d->areaGridPosition(area); + const QPoint position = d->areaGridPosition(area); QLayoutItem *item = d->m_gridLayout->itemAtPosition(position.x(), position.y()); QWidget *widget = nullptr; - if (item && (widget = item->widget()) != nullptr) { + if (item && (widget = item->widget()) != nullptr) widget->setVisible(allowedAreas.testFlag(area)); - } } } @@ -766,9 +762,9 @@ namespace ADS { for (const auto &colorListEntry : colorList) { auto componentColor = colorListEntry.split('=', QString::SkipEmptyParts); int component = colorCompenentStringMap.value(componentColor[0], -1); - if (component < 0) { + if (component < 0) continue; - } + d->m_iconColors[component] = QColor(componentColor[1]); } diff --git a/src/libs/advanceddockingsystem/dockwidget.cpp b/src/libs/advanceddockingsystem/dockwidget.cpp index c703b0a6650..1c6947f7b5f 100644 --- a/src/libs/advanceddockingsystem/dockwidget.cpp +++ b/src/libs/advanceddockingsystem/dockwidget.cpp @@ -55,6 +55,7 @@ #include #include #include +#include static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg) @@ -127,6 +128,7 @@ namespace ADS if (!m_dockArea) { FloatingDockContainer *floatingWidget = new FloatingDockContainer(q); floatingWidget->resize(q->size()); + m_tabWidget->show(); floatingWidget->show(); } else { m_dockArea->setCurrentDockWidget(q); @@ -204,6 +206,9 @@ namespace ADS d->m_toggleViewAction->setCheckable(true); connect(d->m_toggleViewAction, &QAction::triggered, this, &DockWidget::toggleView); setToolbarFloatingStyle(false); + + if (DockManager::testConfigFlag(DockManager::FocusHighlighting)) + setFocusPolicy(Qt::ClickFocus); } DockWidget::~DockWidget() @@ -222,7 +227,10 @@ namespace ADS void DockWidget::setWidget(QWidget *widget, eInsertMode insertMode) { - QScrollArea *scrollAreaWidget = qobject_cast(widget); + if (d->m_widget) + takeWidget(); + + auto scrollAreaWidget = qobject_cast(widget); if (scrollAreaWidget || ForceNoScrollArea == insertMode) { d->m_layout->addWidget(widget); if (scrollAreaWidget && scrollAreaWidget->viewport()) @@ -238,11 +246,23 @@ namespace ADS 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 *w = nullptr; + if (d->m_scrollArea) { + d->m_layout->removeWidget(d->m_scrollArea); + w = d->m_scrollArea->takeWidget(); + delete d->m_scrollArea; + d->m_scrollArea = nullptr; + d->m_widget = nullptr; + } else if (d->m_widget) { + d->m_layout->removeWidget(d->m_widget); + w = d->m_widget; + d->m_widget = nullptr; + } + + if (w) + w->setParent(nullptr); + + return w; } QWidget *DockWidget::widget() const { return d->m_widget; } @@ -251,9 +271,9 @@ namespace ADS void DockWidget::setFeatures(DockWidgetFeatures features) { - if (d->m_features == features) { + if (d->m_features == features) return; - } + d->m_features = features; emit featuresChanged(d->m_features); d->m_tabWidget->onDockWidgetFeaturesChanged(); @@ -274,11 +294,10 @@ namespace ADS DockContainerWidget *DockWidget::dockContainer() const { - if (d->m_dockArea) { + if (d->m_dockArea) return d->m_dockArea->dockContainer(); - } else { + else return nullptr; - } } DockAreaWidget *DockWidget::dockAreaWidget() const { return d->m_dockArea; } @@ -347,11 +366,11 @@ namespace ADS ? beforeDockContainerWidget->topLevelDockWidget() : nullptr; - if (open) { + if (open) d->showDockWidget(); - } else { + else d->hideDockWidget(); - } + d->m_closed = !open; //d->m_toggleViewAction->blockSignals(true); d->m_toggleViewAction->setChecked(open); @@ -449,7 +468,7 @@ namespace ADS d->m_toggleViewAction->setToolTip(text); if (d->m_dockArea) - d->m_dockArea->markTitleBarMenuOutdated(); //update tabs menu + d->m_dockArea->markTitleBarMenuOutdated(); // update tabs menu } #endif @@ -617,4 +636,58 @@ namespace ADS return d->m_titleBarActions; } + void DockWidget::showFullScreen() + { + if (isFloating()) + dockContainer()->floatingWidget()->showFullScreen(); + else + Super::showFullScreen(); + } + + void DockWidget::showNormal() + { + if (isFloating()) + dockContainer()->floatingWidget()->showNormal(); + else + Super::showNormal(); + } + + bool DockWidget::isFullScreen() const + { + if (isFloating()) + return dockContainer()->floatingWidget()->isFullScreen(); + else + return Super::isFullScreen(); + } + + void DockWidget::setAsCurrentTab() + { + if (d->m_dockArea && !isClosed()) + d->m_dockArea->setCurrentDockWidget(this); + } + + bool DockWidget::isTabbed() const + { + return d->m_dockArea && (d->m_dockArea->openDockWidgetsCount() > 1); + } + + bool DockWidget::isCurrentTab() const + { + return d->m_dockArea && (d->m_dockArea->currentDockWidget() == this); + } + + void DockWidget::raise() + { + if (isClosed()) + return; + + setAsCurrentTab(); + if (isInFloatingContainer()) + { + auto floatingWindow = window(); + floatingWindow->raise(); + floatingWindow->activateWindow(); + } + } + } // namespace ADS diff --git a/src/libs/advanceddockingsystem/dockwidget.h b/src/libs/advanceddockingsystem/dockwidget.h index 72c8659fcd3..ee9626d7da9 100644 --- a/src/libs/advanceddockingsystem/dockwidget.h +++ b/src/libs/advanceddockingsystem/dockwidget.h @@ -152,8 +152,8 @@ public: using Super = QFrame; enum DockWidgetFeature { - DockWidgetClosable = 0x01, - DockWidgetMovable = 0x02,///< this feature is not properly implemented yet and is ignored + DockWidgetClosable = 0x01,///< dock widget has a close button + DockWidgetMovable = 0x02,///< dock widget is movable and can be moved to a new position in the current dock container DockWidgetFloatable = 0x04, DockWidgetDeleteOnClose = 0x08, ///< deletes the dock widget when it is closed CustomCloseHandling = 0x10, @@ -435,6 +435,26 @@ public: void setTabToolTip(const QString &text); #endif + /** + * Returns true if the dock widget is floating and if the floating dock + * container is full screen + */ + bool isFullScreen() const; + + /** + * Returns true if this dock widget is in a dock area, that contains at + * least 2 opened dock widgets + */ + bool isTabbed() const; + + /** + * Returns true if this dock widget is the current one in the dock + * area widget that contains it. + * If the dock widget is the only opened dock widget in a dock area, + * the true is returned + */ + bool isCurrentTab() const; + public: // reimplements QFrame /** * Emits titleChanged signal if title change event occurs @@ -447,6 +467,23 @@ public: // reimplements QFrame */ void toggleView(bool open = true); + /** + * Makes this dock widget the current tab in its dock area. + * The function only has an effect, if the dock widget is open. A call + * to this function will not toggle the view, so if it is closed, + * nothing will happen + */ + void setAsCurrentTab(); + + /** + * Brings the dock widget to the front + * This means: + * - If the dock widget is tabbed with other dock widgets but its tab is not current, it's made current. + * - If the dock widget is floating, QWindow::raise() is called. + * This only applies if the dock widget is already open. If closed, does nothing. + */ + void raise(); + /** * This function will make a docked widget floating */ @@ -463,6 +500,26 @@ public: // reimplements QFrame */ void closeDockWidget(); + /** + * Shows the widget in full-screen mode. + * Normally this function only affects windows. To make the interface + * compatible to QDockWidget, this function also maximizes a floating + * dock widget. + * + * \note Full-screen mode works fine under Windows, but has certain + * problems (doe not work) under X (Linux). These problems are due to + * limitations of the ICCCM protocol that specifies the communication + * between X11 clients and the window manager. ICCCM simply does not + * understand the concept of non-decorated full-screen windows. + */ + void showFullScreen(); + + /** + * This function complements showFullScreen() to restore the widget + * after it has been in full screen mode. + */ + void showNormal(); + signals: /** * This signal is emitted if the dock widget is opened or closed diff --git a/src/libs/advanceddockingsystem/dockwidgettab.cpp b/src/libs/advanceddockingsystem/dockwidgettab.cpp index b46d1720509..af0ce4baef5 100644 --- a/src/libs/advanceddockingsystem/dockwidgettab.cpp +++ b/src/libs/advanceddockingsystem/dockwidgettab.cpp @@ -116,7 +116,7 @@ namespace ADS */ bool testConfigFlag(DockManager::eConfigFlag flag) const { - return DockManager::configFlags().testFlag(flag); + return DockManager::testConfigFlag(flag); } /** @@ -232,18 +232,20 @@ namespace ADS qCInfo(adsLog) << "startFloating"; m_dragState = draggingState; - QSize size = m_dockArea->size(); AbstractFloatingWidget *floatingWidget = nullptr; - bool opaqueUndocking = DockManager::configFlags().testFlag(DockManager::OpaqueUndocking) + bool opaqueUndocking = DockManager::testConfigFlag(DockManager::OpaqueUndocking) || (DraggingFloatingWidget != draggingState); // If section widget has multiple tabs, we take only one tab // If it has only one single tab, we can move the complete // dock area into floating widget + QSize size; if (m_dockArea->dockWidgetsCount() > 1) { floatingWidget = createFloatingWidget(m_dockWidget, opaqueUndocking); + size = m_dockWidget->size(); } else { floatingWidget = createFloatingWidget(m_dockArea, opaqueUndocking); + size = m_dockArea->size(); } if (DraggingFloatingWidget == draggingState) { @@ -265,6 +267,8 @@ namespace ADS setAttribute(Qt::WA_NoMousePropagation, true); d->m_dockWidget = dockWidget; d->createLayout(); + if (DockManager::testConfigFlag(DockManager::FocusHighlighting)) + setFocusPolicy(Qt::ClickFocus); } DockWidgetTab::~DockWidgetTab() @@ -358,9 +362,9 @@ namespace ADS // If we undock, we need to restore the initial position of this // tab because it looks strange if it remains on its dragged position if (d->isDraggingState(DraggingTab) - && !DockManager::configFlags().testFlag(DockManager::OpaqueUndocking)) { + && !DockManager::testConfigFlag(DockManager::OpaqueUndocking)) parentWidget()->layout()->update(); - } + d->startFloating(); } return; @@ -370,9 +374,9 @@ namespace ADS { // If we start dragging the tab, we save its initial position to // restore it later - if (DraggingTab != d->m_dragState) { + if (DraggingTab != d->m_dragState) d->m_tabDragStartPosition = this->pos(); - } + d->m_dragState = DraggingTab; return; } @@ -383,9 +387,8 @@ namespace ADS void DockWidgetTab::contextMenuEvent(QContextMenuEvent *event) { event->accept(); - if (d->isDraggingState(DraggingFloatingWidget)) { + if (d->isDraggingState(DraggingFloatingWidget)) return; - } d->saveDragStartMousePosition(event->globalPos()); QMenu menu(this); @@ -413,16 +416,27 @@ namespace ADS bool allTabsHaveCloseButton = d->testConfigFlag(DockManager::AllTabsHaveCloseButton); bool tabHasCloseButton = (activeTabHasCloseButton && active) | allTabsHaveCloseButton; d->m_closeButton->setVisible(dockWidgetClosable && tabHasCloseButton); - if (d->m_isActiveTab == active) { + + // Focus related stuff + if (DockManager::testConfigFlag(DockManager::FocusHighlighting) + && !d->m_dockWidget->dockManager()->isRestoringState()) { + bool updateFocusStyle = false; + if (active && !hasFocus()) { + setFocus(Qt::OtherFocusReason); + updateFocusStyle = true; + } + + if (d->m_isActiveTab == active) { + if (updateFocusStyle) + updateStyle(); + return; + } + } else if (d->m_isActiveTab == active) { return; } d->m_isActiveTab = active; - - style()->unpolish(this); - style()->polish(this); - d->m_titleLabel->style()->unpolish(d->m_titleLabel); - d->m_titleLabel->style()->polish(d->m_titleLabel); + updateStyle(); update(); updateGeometry(); @@ -438,9 +452,8 @@ namespace ADS void DockWidgetTab::setIcon(const QIcon &icon) { QBoxLayout *boxLayout = qobject_cast(layout()); - if (!d->m_iconLabel && icon.isNull()) { + if (!d->m_iconLabel && icon.isNull()) return; - } if (!d->m_iconLabel) { d->m_iconLabel = new QLabel(); @@ -499,9 +512,9 @@ namespace ADS void DockWidgetTab::detachDockWidget() { - if (!d->m_dockWidget->features().testFlag(DockWidget::DockWidgetFloatable)) { + if (!d->m_dockWidget->features().testFlag(DockWidget::DockWidgetFloatable)) return; - } + d->saveDragStartMousePosition(QCursor::pos()); d->startFloating(DraggingInactive); } @@ -527,4 +540,14 @@ namespace ADS d->m_closeButton->setSizePolicy(sizePolicy); } + void DockWidgetTab::setElideMode(Qt::TextElideMode mode) + { + d->m_titleLabel->setElideMode(mode); + } + + void DockWidgetTab::updateStyle() + { + internal::repolishStyle(this, internal::RepolishDirectChildren); + } + } // namespace ADS diff --git a/src/libs/advanceddockingsystem/dockwidgettab.h b/src/libs/advanceddockingsystem/dockwidgettab.h index 8526b825962..15dd13582f1 100644 --- a/src/libs/advanceddockingsystem/dockwidgettab.h +++ b/src/libs/advanceddockingsystem/dockwidgettab.h @@ -59,6 +59,7 @@ private: DockWidgetTabPrivate *d; ///< private data (pimpl) friend class DockWidgetTabPrivate; friend class DockWidget; + friend class DockManager; void onDockWidgetFeaturesChanged(); void detachDockWidget(); @@ -150,6 +151,16 @@ public: */ bool event(QEvent *event) override; + /** + * Sets the text elide mode + */ + void setElideMode(Qt::TextElideMode mode); + + /** + * Update stylesheet style if a property changes + */ + void updateStyle(); + void setVisible(bool visible) override; signals: diff --git a/src/libs/advanceddockingsystem/elidinglabel.cpp b/src/libs/advanceddockingsystem/elidinglabel.cpp index dfd812bae2a..4753ae7dd43 100644 --- a/src/libs/advanceddockingsystem/elidinglabel.cpp +++ b/src/libs/advanceddockingsystem/elidinglabel.cpp @@ -115,9 +115,8 @@ namespace ADS { void ElidingLabel::mouseReleaseEvent(QMouseEvent *event) { Super::mouseReleaseEvent(event); - if (event->button() != Qt::LeftButton) { + if (event->button() != Qt::LeftButton) return; - } emit clicked(); } @@ -131,17 +130,17 @@ namespace ADS { void ElidingLabel::resizeEvent(QResizeEvent *event) { - if (!d->isModeElideNone()) { + if (!d->isModeElideNone()) d->elideText(event->size().width()); - } + Super::resizeEvent(event); } QSize ElidingLabel::minimumSizeHint() const { - if (pixmap() != nullptr || d->isModeElideNone()) { + if (pixmap() != nullptr || d->isModeElideNone()) return QLabel::minimumSizeHint(); - } + const QFontMetrics &fm = fontMetrics(); #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) QSize size(fm.horizontalAdvance(d->m_text.left(2) + "…"), fm.height()); @@ -153,9 +152,9 @@ namespace ADS { QSize ElidingLabel::sizeHint() const { - if (pixmap() != nullptr || d->isModeElideNone()) { + if (pixmap() != nullptr || d->isModeElideNone()) return QLabel::sizeHint(); - } + const QFontMetrics &fm = fontMetrics(); #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) QSize size(fm.horizontalAdvance(d->m_text), QLabel::sizeHint().height()); @@ -167,10 +166,10 @@ namespace ADS { void ElidingLabel::setText(const QString &text) { + d->m_text = text; if (d->isModeElideNone()) { Super::setText(text); } else { - d->m_text = text; internal::setToolTip(this, text); d->elideText(this->size().width()); } diff --git a/src/libs/advanceddockingsystem/floatingdockcontainer.cpp b/src/libs/advanceddockingsystem/floatingdockcontainer.cpp index 18c70834dd7..46e6f8478f3 100644 --- a/src/libs/advanceddockingsystem/floatingdockcontainer.cpp +++ b/src/libs/advanceddockingsystem/floatingdockcontainer.cpp @@ -41,6 +41,12 @@ #include "dockoverlay.h" #include "dockwidget.h" #include "linux/floatingwidgettitlebar.h" +#ifdef Q_OS_WIN +#include +#ifdef _MSC_VER +#pragma comment(lib, "User32.lib") +#endif +#endif #include #include @@ -56,6 +62,303 @@ static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWar namespace ADS { +#ifdef Q_OS_WIN +#if 0 // set to 1 if you need this function for debugging +/** + * Just for debugging to convert windows message identifiers to strings + */ +static const char* windowsMessageString(int messageId) +{ + switch (messageId) + { + case 0: return "WM_NULL"; + case 1: return "WM_CREATE"; + case 2: return "WM_DESTROY"; + case 3: return "WM_MOVE"; + case 5: return "WM_SIZE"; + case 6: return "WM_ACTIVATE"; + case 7: return "WM_SETFOCUS"; + case 8: return "WM_KILLFOCUS"; + case 10: return "WM_ENABLE"; + case 11: return "WM_SETREDRAW"; + case 12: return "WM_SETTEXT"; + case 13: return "WM_GETTEXT"; + case 14: return "WM_GETTEXTLENGTH"; + case 15: return "WM_PAINT"; + case 16: return "WM_CLOSE"; + case 17: return "WM_QUERYENDSESSION"; + case 18: return "WM_QUIT"; + case 19: return "WM_QUERYOPEN"; + case 20: return "WM_ERASEBKGND"; + case 21: return "WM_SYSCOLORCHANGE"; + case 22: return "WM_ENDSESSION"; + case 24: return "WM_SHOWWINDOW"; + case 25: return "WM_CTLCOLOR"; + case 26: return "WM_WININICHANGE"; + case 27: return "WM_DEVMODECHANGE"; + case 28: return "WM_ACTIVATEAPP"; + case 29: return "WM_FONTCHANGE"; + case 30: return "WM_TIMECHANGE"; + case 31: return "WM_CANCELMODE"; + case 32: return "WM_SETCURSOR"; + case 33: return "WM_MOUSEACTIVATE"; + case 34: return "WM_CHILDACTIVATE"; + case 35: return "WM_QUEUESYNC"; + case 36: return "WM_GETMINMAXINFO"; + case 38: return "WM_PAINTICON"; + case 39: return "WM_ICONERASEBKGND"; + case 40: return "WM_NEXTDLGCTL"; + case 42: return "WM_SPOOLERSTATUS"; + case 43: return "WM_DRAWITEM"; + case 44: return "WM_MEASUREITEM"; + case 45: return "WM_DELETEITEM"; + case 46: return "WM_VKEYTOITEM"; + case 47: return "WM_CHARTOITEM"; + case 48: return "WM_SETFONT"; + case 49: return "WM_GETFONT"; + case 50: return "WM_SETHOTKEY"; + case 51: return "WM_GETHOTKEY"; + case 55: return "WM_QUERYDRAGICON"; + case 57: return "WM_COMPAREITEM"; + case 61: return "WM_GETOBJECT"; + case 65: return "WM_COMPACTING"; + case 68: return "WM_COMMNOTIFY"; + case 70: return "WM_WINDOWPOSCHANGING"; + case 71: return "WM_WINDOWPOSCHANGED"; + case 72: return "WM_POWER"; + case 73: return "WM_COPYGLOBALDATA"; + case 74: return "WM_COPYDATA"; + case 75: return "WM_CANCELJOURNAL"; + case 78: return "WM_NOTIFY"; + case 80: return "WM_INPUTLANGCHANGEREQUEST"; + case 81: return "WM_INPUTLANGCHANGE"; + case 82: return "WM_TCARD"; + case 83: return "WM_HELP"; + case 84: return "WM_USERCHANGED"; + case 85: return "WM_NOTIFYFORMAT"; + case 123: return "WM_CONTEXTMENU"; + case 124: return "WM_STYLECHANGING"; + case 125: return "WM_STYLECHANGED"; + case 126: return "WM_DISPLAYCHANGE"; + case 127: return "WM_GETICON"; + case 128: return "WM_SETICON"; + case 129: return "WM_NCCREATE"; + case 130: return "WM_NCDESTROY"; + case 131: return "WM_NCCALCSIZE"; + case 132: return "WM_NCHITTEST"; + case 133: return "WM_NCPAINT"; + case 134: return "WM_NCACTIVATE"; + case 135: return "WM_GETDLGCODE"; + case 136: return "WM_SYNCPAINT"; + case 160: return "WM_NCMOUSEMOVE"; + case 161: return "WM_NCLBUTTONDOWN"; + case 162: return "WM_NCLBUTTONUP"; + case 163: return "WM_NCLBUTTONDBLCLK"; + case 164: return "WM_NCRBUTTONDOWN"; + case 165: return "WM_NCRBUTTONUP"; + case 166: return "WM_NCRBUTTONDBLCLK"; + case 167: return "WM_NCMBUTTONDOWN"; + case 168: return "WM_NCMBUTTONUP"; + case 169: return "WM_NCMBUTTONDBLCLK"; + case 171: return "WM_NCXBUTTONDOWN"; + case 172: return "WM_NCXBUTTONUP"; + case 173: return "WM_NCXBUTTONDBLCLK"; + case 176: return "EM_GETSEL"; + case 177: return "EM_SETSEL"; + case 178: return "EM_GETRECT"; + case 179: return "EM_SETRECT"; + case 180: return "EM_SETRECTNP"; + case 181: return "EM_SCROLL"; + case 182: return "EM_LINESCROLL"; + case 183: return "EM_SCROLLCARET"; + case 185: return "EM_GETMODIFY"; + case 187: return "EM_SETMODIFY"; + case 188: return "EM_GETLINECOUNT"; + case 189: return "EM_LINEINDEX"; + case 190: return "EM_SETHANDLE"; + case 191: return "EM_GETHANDLE"; + case 192: return "EM_GETTHUMB"; + case 193: return "EM_LINELENGTH"; + case 194: return "EM_REPLACESEL"; + case 195: return "EM_SETFONT"; + case 196: return "EM_GETLINE"; + case 197: return "EM_LIMITTEXT / EM_SETLIMITTEXT"; + case 198: return "EM_CANUNDO"; + case 199: return "EM_UNDO"; + case 200: return "EM_FMTLINES"; + case 201: return "EM_LINEFROMCHAR"; + case 202: return "EM_SETWORDBREAK"; + case 203: return "EM_SETTABSTOPS"; + case 204: return "EM_SETPASSWORDCHAR"; + case 205: return "EM_EMPTYUNDOBUFFER"; + case 206: return "EM_GETFIRSTVISIBLELINE"; + case 207: return "EM_SETREADONLY"; + case 209: return "EM_SETWORDBREAKPROC / EM_GETWORDBREAKPROC"; + case 210: return "EM_GETPASSWORDCHAR"; + case 211: return "EM_SETMARGINS"; + case 212: return "EM_GETMARGINS"; + case 213: return "EM_GETLIMITTEXT"; + case 214: return "EM_POSFROMCHAR"; + case 215: return "EM_CHARFROMPOS"; + case 216: return "EM_SETIMESTATUS"; + case 217: return "EM_GETIMESTATUS"; + case 224: return "SBM_SETPOS"; + case 225: return "SBM_GETPOS"; + case 226: return "SBM_SETRANGE"; + case 227: return "SBM_GETRANGE"; + case 228: return "SBM_ENABLE_ARROWS"; + case 230: return "SBM_SETRANGEREDRAW"; + case 233: return "SBM_SETSCROLLINFO"; + case 234: return "SBM_GETSCROLLINFO"; + case 235: return "SBM_GETSCROLLBARINFO"; + case 240: return "BM_GETCHECK"; + case 241: return "BM_SETCHECK"; + case 242: return "BM_GETSTATE"; + case 243: return "BM_SETSTATE"; + case 244: return "BM_SETSTYLE"; + case 245: return "BM_CLICK"; + case 246: return "BM_GETIMAGE"; + case 247: return "BM_SETIMAGE"; + case 248: return "BM_SETDONTCLICK"; + case 255: return "WM_INPUT"; + case 256: return "WM_KEYDOWN"; + case 257: return "WM_KEYUP"; + case 258: return "WM_CHAR"; + case 259: return "WM_DEADCHAR"; + case 260: return "WM_SYSKEYDOWN"; + case 261: return "WM_SYSKEYUP"; + case 262: return "WM_SYSCHAR"; + case 263: return "WM_SYSDEADCHAR"; + case 265: return "WM_UNICHAR / WM_WNT_CONVERTREQUESTEX"; + case 266: return "WM_CONVERTREQUEST"; + case 267: return "WM_CONVERTRESULT"; + case 268: return "WM_INTERIM"; + case 269: return "WM_IME_STARTCOMPOSITION"; + case 270: return "WM_IME_ENDCOMPOSITION"; + case 272: return "WM_INITDIALOG"; + case 273: return "WM_COMMAND"; + case 274: return "WM_SYSCOMMAND"; + case 275: return "WM_TIMER"; + case 276: return "WM_HSCROLL"; + case 277: return "WM_VSCROLL"; + case 278: return "WM_INITMENU"; + case 279: return "WM_INITMENUPOPUP"; + case 280: return "WM_SYSTIMER"; + case 287: return "WM_MENUSELECT"; + case 288: return "WM_MENUCHAR"; + case 289: return "WM_ENTERIDLE"; + case 290: return "WM_MENURBUTTONUP"; + case 291: return "WM_MENUDRAG"; + case 292: return "WM_MENUGETOBJECT"; + case 293: return "WM_UNINITMENUPOPUP"; + case 294: return "WM_MENUCOMMAND"; + case 295: return "WM_CHANGEUISTATE"; + case 296: return "WM_UPDATEUISTATE"; + case 297: return "WM_QUERYUISTATE"; + case 306: return "WM_CTLCOLORMSGBOX"; + case 307: return "WM_CTLCOLOREDIT"; + case 308: return "WM_CTLCOLORLISTBOX"; + case 309: return "WM_CTLCOLORBTN"; + case 310: return "WM_CTLCOLORDLG"; + case 311: return "WM_CTLCOLORSCROLLBAR"; + case 312: return "WM_CTLCOLORSTATIC"; + case 512: return "WM_MOUSEMOVE"; + case 513: return "WM_LBUTTONDOWN"; + case 514: return "WM_LBUTTONUP"; + case 515: return "WM_LBUTTONDBLCLK"; + case 516: return "WM_RBUTTONDOWN"; + case 517: return "WM_RBUTTONUP"; + case 518: return "WM_RBUTTONDBLCLK"; + case 519: return "WM_MBUTTONDOWN"; + case 520: return "WM_MBUTTONUP"; + case 521: return "WM_MBUTTONDBLCLK"; + case 522: return "WM_MOUSEWHEEL"; + case 523: return "WM_XBUTTONDOWN"; + case 524: return "WM_XBUTTONUP"; + case 525: return "WM_XBUTTONDBLCLK"; + case 528: return "WM_PARENTNOTIFY"; + case 529: return "WM_ENTERMENULOOP"; + case 530: return "WM_EXITMENULOOP"; + case 531: return "WM_NEXTMENU"; + case 532: return "WM_SIZING"; + case 533: return "WM_CAPTURECHANGED"; + case 534: return "WM_MOVING"; + case 536: return "WM_POWERBROADCAST"; + case 537: return "WM_DEVICECHANGE"; + case 544: return "WM_MDICREATE"; + case 545: return "WM_MDIDESTROY"; + case 546: return "WM_MDIACTIVATE"; + case 547: return "WM_MDIRESTORE"; + case 548: return "WM_MDINEXT"; + case 549: return "WM_MDIMAXIMIZE"; + case 550: return "WM_MDITILE"; + case 551: return "WM_MDICASCADE"; + case 552: return "WM_MDIICONARRANGE"; + case 553: return "WM_MDIGETACTIVE"; + case 560: return "WM_MDISETMENU"; + case 561: return "WM_ENTERSIZEMOVE"; + case 562: return "WM_EXITSIZEMOVE"; + case 563: return "WM_DROPFILES"; + case 564: return "WM_MDIREFRESHMENU"; + case 640: return "WM_IME_REPORT"; + case 641: return "WM_IME_SETCONTEXT"; + case 642: return "WM_IME_NOTIFY"; + case 643: return "WM_IME_CONTROL"; + case 644: return "WM_IME_COMPOSITIONFULL"; + case 645: return "WM_IME_SELECT"; + case 646: return "WM_IME_CHAR"; + case 648: return "WM_IME_REQUEST"; + case 656: return "WM_IME_KEYDOWN"; + case 657: return "WM_IME_KEYUP"; + case 672: return "WM_NCMOUSEHOVER"; + case 673: return "WM_MOUSEHOVER"; + case 674: return "WM_NCMOUSELEAVE"; + case 675: return "WM_MOUSELEAVE"; + case 768: return "WM_CUT"; + case 769: return "WM_COPY"; + case 770: return "WM_PASTE"; + case 771: return "WM_CLEAR"; + case 772: return "WM_UNDO"; + case 773: return "WM_RENDERFORMAT"; + case 774: return "WM_RENDERALLFORMATS"; + case 775: return "WM_DESTROYCLIPBOARD"; + case 776: return "WM_DRAWCLIPBOARD"; + case 777: return "WM_PAINTCLIPBOARD"; + case 778: return "WM_VSCROLLCLIPBOARD"; + case 779: return "WM_SIZECLIPBOARD"; + case 780: return "WM_ASKCBFORMATNAME"; + case 781: return "WM_CHANGECBCHAIN"; + case 782: return "WM_HSCROLLCLIPBOARD"; + case 783: return "WM_QUERYNEWPALETTE"; + case 784: return "WM_PALETTEISCHANGING"; + case 785: return "WM_PALETTECHANGED"; + case 786: return "WM_HOTKEY"; + case 791: return "WM_PRINT"; + case 792: return "WM_PRINTCLIENT"; + case 793: return "WM_APPCOMMAND"; + case 856: return "WM_HANDHELDFIRST"; + case 863: return "WM_HANDHELDLAST"; + case 864: return "WM_AFXFIRST"; + case 895: return "WM_AFXLAST"; + case 896: return "WM_PENWINFIRST"; + case 897: return "WM_RCRESULT"; + case 898: return "WM_HOOKRCRESULT"; + case 899: return "WM_GLOBALRCCHANGE / WM_PENMISCINFO"; + case 900: return "WM_SKB"; + case 901: return "WM_HEDITCTL / WM_PENCTL"; + case 902: return "WM_PENMISC"; + case 903: return "WM_CTLINIT"; + case 904: return "WM_PENEVENT"; + case 911: return "WM_PENWINLAST"; + default: + return "unknown WM_ message"; + } + + return "unknown WM_ message"; +} +#endif +#endif + AbstractFloatingWidget::~AbstractFloatingWidget() = default; static unsigned int zOrderCounter = 0; @@ -74,8 +377,9 @@ namespace ADS DockContainerWidget *m_dropContainer = nullptr; DockAreaWidget *m_singleDockArea = nullptr; QPoint m_dragStartPos; - QWidget *m_mouseEventHandler = nullptr; - FloatingWidgetTitleBar *m_titleBar = nullptr; + bool m_hiding = false; + QWidget *m_mouseEventHandler = nullptr; // linux only + FloatingWidgetTitleBar *m_titleBar = nullptr; // linux only /** * Private data constructor @@ -90,7 +394,7 @@ namespace ADS */ static bool testConfigFlag(DockManager::eConfigFlag flag) { - return DockManager::configFlags().testFlag(flag); + return DockManager::testConfigFlag(flag); } /** @@ -146,11 +450,11 @@ namespace ADS if (m_dockManager->dockAreaOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea || m_dockManager->containerOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea) { - // Resize the floating widget to the size of the highlighted drop area rectangle DockOverlay *overlay = m_dockManager->containerOverlay(); if (!overlay->dropOverlayRect().isValid()) overlay = m_dockManager->dockAreaOverlay(); + // Resize the floating widget to the size of the highlighted drop area rectangle QRect rect = overlay->dropOverlayRect(); int frameWidth = (q->frameSize().width() - q->rect().width()) / 2; int titleBarHeight = q->frameSize().height() - q->rect().height() - frameWidth; @@ -246,7 +550,7 @@ namespace ADS this, &FloatingDockContainer::onDockAreasAddedOrRemoved); -#ifdef Q_OS_LINUX + #ifdef Q_OS_LINUX d->m_titleBar = new FloatingWidgetTitleBar(this); setWindowFlags(windowFlags() | Qt::Tool); QDockWidget::setWidget(d->m_dockContainer); @@ -257,14 +561,14 @@ namespace ADS &FloatingWidgetTitleBar::closeRequested, this, &FloatingDockContainer::close); -#else + #else setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint); QBoxLayout *boxLayout = new QBoxLayout(QBoxLayout::TopToBottom); boxLayout->setContentsMargins(0, 0, 0, 0); boxLayout->setSpacing(0); setLayout(boxLayout); boxLayout->addWidget(d->m_dockContainer); -#endif + #endif dockManager->registerFloatingWidget(this); } @@ -272,24 +576,26 @@ namespace ADS : FloatingDockContainer(dockArea->dockManager()) { d->m_dockContainer->addDockArea(dockArea); - if (Utils::HostOsInfo::isLinuxHost()) - d->m_titleBar->enableCloseButton(isClosable()); - - auto dw = topLevelDockWidget(); - if (dw) + #ifdef Q_OS_LINUX + d->m_titleBar->enableCloseButton(isClosable()); + #endif + if (auto dw = topLevelDockWidget()) dw->emitTopLevelChanged(true); + + d->m_dockManager->notifyWidgetOrAreaRelocation(dockArea); } FloatingDockContainer::FloatingDockContainer(DockWidget *dockWidget) : FloatingDockContainer(dockWidget->dockManager()) { d->m_dockContainer->addDockWidget(CenterDockWidgetArea, dockWidget); - if (Utils::HostOsInfo::isLinuxHost()) - d->m_titleBar->enableCloseButton(isClosable()); - - auto dw = topLevelDockWidget(); - if (dw) + #ifdef Q_OS_LINUX + d->m_titleBar->enableCloseButton(isClosable()); + #endif + if (auto dw = topLevelDockWidget()) dw->emitTopLevelChanged(true); + + d->m_dockManager->notifyWidgetOrAreaRelocation(dockWidget); } FloatingDockContainer::~FloatingDockContainer() @@ -313,33 +619,56 @@ namespace ADS } } - void FloatingDockContainer::moveEvent(QMoveEvent *event) +#ifdef Q_OS_WIN +bool FloatingDockContainer::nativeEvent(const QByteArray &eventType, void *message, long *result) +{ + QWidget::nativeEvent(eventType, message, result); + MSG *msg = static_cast(message); + switch (msg->message) { - QWidget::moveEvent(event); - switch (d->m_draggingState) { - case DraggingMousePressed: - // TODO Is checking for windows only sufficient or has macOS also problems? - if (Utils::HostOsInfo::isWindowsHost()) - QApplication::instance()->installEventFilter(this); - - d->setState(DraggingFloatingWidget); - d->updateDropOverlays(QCursor::pos()); - break; - - case DraggingFloatingWidget: - d->updateDropOverlays(QCursor::pos()); - if (Utils::HostOsInfo::isMacHost()) { - // In macOS when hiding the DockAreaOverlay the application would set - // the main window as the active window for some reason. This fixes - // that by resetting the active window to the floating widget after - // updating the overlays. - QApplication::setActiveWindow(this); - } - break; - default: - break; + case WM_MOVING: + { + if (d->isState(DraggingFloatingWidget)) + d->updateDropOverlays(QCursor::pos()); } + break; + + case WM_NCLBUTTONDOWN: + if (msg->wParam == HTCAPTION && d->isState(DraggingInactive)) + { + qCInfo(adsLog) << Q_FUNC_INFO << "WM_NCLBUTTONDOWN" << eventType; + d->m_dragStartPos = pos(); + d->setState(DraggingMousePressed); + } + break; + + case WM_NCLBUTTONDBLCLK: + d->setState(DraggingInactive); + break; + + case WM_ENTERSIZEMOVE: + if (d->isState(DraggingMousePressed)) + { + qCInfo(adsLog) << Q_FUNC_INFO << "WM_ENTERSIZEMOVE" << eventType; + d->setState(DraggingFloatingWidget); + d->updateDropOverlays(QCursor::pos()); + } + break; + + case WM_EXITSIZEMOVE: + if (d->isState(DraggingFloatingWidget)) + { + qCInfo(adsLog) << Q_FUNC_INFO << "WM_EXITSIZEMOVE" << eventType; + if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) + d->handleEscapeKey(); + else + d->titleMouseReleaseEvent(); + } + break; } + return false; +} +#endif void FloatingDockContainer::closeEvent(QCloseEvent *event) { @@ -368,112 +697,21 @@ namespace ADS if (d->m_dockManager->isRestoringState()) return; + d->m_hiding = true; for (auto dockArea : d->m_dockContainer->openedDockAreas()) { for (auto dockWidget : dockArea->openedDockWidgets()) dockWidget->toggleView(false); } + d->m_hiding = false; } - void FloatingDockContainer::showEvent(QShowEvent *event) { Super::showEvent(event); } - - bool FloatingDockContainer::event(QEvent *event) + void FloatingDockContainer::showEvent(QShowEvent *event) { - switch (d->m_draggingState) { - case DraggingInactive: { - // Normally we would check here, if the left mouse button is pressed. - // But from QT version 5.12.2 on the mouse events from - // QEvent::NonClientAreaMouseButtonPress return the wrong mouse button - // The event always returns Qt::RightButton even if the left button is clicked. - // It is really great to work around the whole NonClientMouseArea bugs - - -#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 2)) - if (event->type() == QEvent::NonClientAreaMouseButtonPress - /*&& QGuiApplication::mouseButtons().testFlag(Qt::LeftButton)*/) -#else - if (event->type() == QEvent::NonClientAreaMouseButtonPress - && QGuiApplication::mouseButtons().testFlag(Qt::LeftButton)) -#endif - { - qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonPress" - << event->type(); - d->m_dragStartPos = pos(); - d->setState(DraggingMousePressed); - } - } break; - - case DraggingMousePressed: - switch (event->type()) { - case QEvent::NonClientAreaMouseButtonDblClick: - qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonDblClick"; - d->setState(DraggingInactive); - break; - - case QEvent::Resize: - // If the first event after the mouse press is a resize event, then - // the user resizes the window instead of dragging it around. - // But there is one exception. If the window is maximized, - // then dragging the window via title bar will cause the widget to - // leave the maximized state. This in turn will trigger a resize event. - // To know, if the resize event was triggered by user via moving a - // corner of the window frame or if it was caused by a windows state - // change, we check, if we are not in maximized state. - if (!isMaximized()) { - d->setState(DraggingInactive); - } - break; - - default: - break; - } - break; - - case DraggingFloatingWidget: - if (event->type() == QEvent::NonClientAreaMouseButtonRelease) { - qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonRelease"; - d->titleMouseReleaseEvent(); - } - break; - - default: - break; - } - -#if (ADS_DEBUG_LEVEL > 0) - qDebug() << "FloatingDockContainer::event " << event->type(); -#endif - return QWidget::event(event); - } - - bool FloatingDockContainer::eventFilter(QObject *watched, QEvent *event) - { - Q_UNUSED(watched); - // I have not found a way to detect non client area key press events to - // handle escape key presses. On Windows, if the escape key is pressed while - // dragging around a widget, the widget position is reset to its start position - // which in turn generates a QEvent::NonClientAreaMouseButtonRelease event - // if the mouse is outside of the widget after the move to its initial position - // or a QEvent::MouseButtonRelease event, if the mouse is inside of the widget - // after the position has been reset. - // So we can install an event filter on the application to get these events - // here to properly cancel dragging and hide the overlays. - // If we are in DraggingFloatingWidget state, it means the widget - // has been dragged already but if the position is the same like - // the start position, then this is an indication that the escape - // key has been pressed. - if (event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::NonClientAreaMouseButtonRelease) - { - qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::MouseButtonRelease or QEvent::NonClientAreaMouseButtonRelease" - << "d->m_draggingState " << d->m_draggingState; - QApplication::instance()->removeEventFilter(this); - if (d->m_dragStartPos == pos()) - { - d->handleEscapeKey(); - return true; - } - return false; - } - return false; + Super::showEvent(event); + #ifdef Q_OS_LINUX + if (DockManager::testConfigFlag(DockManager::FocusHighlighting)) + window()->activateWindow(); + #endif } void FloatingDockContainer::startFloating(const QPoint &dragStartMousePos, @@ -481,29 +719,52 @@ namespace ADS eDragState dragState, QWidget *mouseEventHandler) { + #ifndef Q_OS_LINUX + Q_UNUSED(mouseEventHandler) + #endif resize(size); d->setState(dragState); d->m_dragStartMousePosition = dragStartMousePos; - if (Utils::HostOsInfo::isLinuxHost()) { - if (DraggingFloatingWidget == dragState) { - setAttribute(Qt::WA_X11NetWmWindowTypeDock, true); - d->m_mouseEventHandler = mouseEventHandler; - if (d->m_mouseEventHandler) { - d->m_mouseEventHandler->grabMouse(); - } - } + #ifdef Q_OS_LINUX + if (DraggingFloatingWidget == dragState) { + setAttribute(Qt::WA_X11NetWmWindowTypeDock, true); + d->m_mouseEventHandler = mouseEventHandler; + if (d->m_mouseEventHandler) + d->m_mouseEventHandler->grabMouse(); } + #endif moveFloating(); show(); } void FloatingDockContainer::moveFloating() { - int borderSize = (frameSize().width() - size().width()) / 2; + const int borderSize = (frameSize().width() - size().width()) / 2; const QPoint moveToPos = QCursor::pos() - d->m_dragStartMousePosition - QPoint(borderSize, 0); move(moveToPos); + + switch (d->m_draggingState) + { + case DraggingMousePressed: + d->setState(DraggingFloatingWidget); + d->updateDropOverlays(QCursor::pos()); + break; + + case DraggingFloatingWidget: + d->updateDropOverlays(QCursor::pos()); + // On macOS when hiding the DockAreaOverlay the application would set + // the main window as the active window for some reason. This fixes + // that by resetting the active window to the floating widget after + // updating the overlays. + if (Utils::HostOsInfo::isMacHost()) + QApplication::setActiveWindow(this); + + break; + default: + break; + } } bool FloatingDockContainer::isClosable() const @@ -538,10 +799,14 @@ namespace ADS void FloatingDockContainer::updateWindowTitle() { - auto topLevelDockArea = d->m_dockContainer->topLevelDockArea(); - if (topLevelDockArea) { - DockWidget *currentWidget = topLevelDockArea->currentDockWidget(); - d->reflectCurrentWidget(currentWidget); + // If this floating container will be hidden, then updating the window + // title is not required anymore + if (d->m_hiding) + return; + + if (auto topLevelDockArea = d->m_dockContainer->topLevelDockArea()) { + if (DockWidget *currentWidget = topLevelDockArea->currentDockWidget()) + d->reflectCurrentWidget(currentWidget); } else { d->setWindowTitle(QApplication::applicationDisplayName()); setWindowIcon(QApplication::windowIcon()); @@ -557,9 +822,8 @@ namespace ADS bool FloatingDockContainer::restoreState(DockingStateReader &stream, bool testing) { - if (!d->m_dockContainer->restoreState(stream, testing)) { + if (!d->m_dockContainer->restoreState(stream, testing)) return false; - } onDockAreasAddedOrRemoved(); return true; @@ -584,16 +848,113 @@ namespace ADS { qCInfo(adsLog) << Q_FUNC_INFO; - if (Utils::HostOsInfo::isLinuxHost()) { - setAttribute(Qt::WA_X11NetWmWindowTypeDock, false); - setWindowOpacity(1); - activateWindow(); - if (d->m_mouseEventHandler) { - d->m_mouseEventHandler->releaseMouse(); - d->m_mouseEventHandler = nullptr; - } + #ifdef Q_OS_LINUX + setAttribute(Qt::WA_X11NetWmWindowTypeDock, false); + setWindowOpacity(1); + activateWindow(); + if (d->m_mouseEventHandler) { + d->m_mouseEventHandler->releaseMouse(); + d->m_mouseEventHandler = nullptr; } + #endif d->titleMouseReleaseEvent(); } +#ifdef Q_OS_MACOS +bool FloatingDockContainer::event(QEvent *event) +{ + switch (d->m_draggingState) + { + case DraggingInactive: + { + // Normally we would check here, if the left mouse button is pressed. + // But from QT version 5.12.2 on the mouse events from + // QEvent::NonClientAreaMouseButtonPress return the wrong mouse button + // The event always returns Qt::RightButton even if the left button + // is clicked. + // It is really great to work around the whole NonClientMouseArea + // bugs +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 2)) + if (event->type() == QEvent::NonClientAreaMouseButtonPress + /*&& QGuiApplication::mouseButtons().testFlag(Qt::LeftButton)*/) +#else + if (event->type() == QEvent::NonClientAreaMouseButtonPress + && QGuiApplication::mouseButtons().testFlag(Qt::LeftButton)) +#endif + { + qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonPress" + << event->type(); + d->m_dragStartPos = pos(); + d->setState(DraggingMousePressed); + } + } + break; + + case DraggingMousePressed: + switch (event->type()) + { + case QEvent::NonClientAreaMouseButtonDblClick: + qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonDblClick"; + d->setState(DraggingInactive); + break; + + case QEvent::Resize: + // If the first event after the mouse press is a resize event, then + // the user resizes the window instead of dragging it around. + // But there is one exception. If the window is maximized, + // then dragging the window via title bar will cause the widget to + // leave the maximized state. This in turn will trigger a resize event. + // To know, if the resize event was triggered by user via moving a + // corner of the window frame or if it was caused by a windows state + // change, we check, if we are not in maximized state. + if (!isMaximized()) + d->setState(DraggingInactive); + break; + + default: + break; + } + break; + + case DraggingFloatingWidget: + if (event->type() == QEvent::NonClientAreaMouseButtonRelease) + { + qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonRelease"; + d->titleMouseReleaseEvent(); + } + break; + + default: + break; + } + +#if (ADS_DEBUG_LEVEL > 0) + qDebug() << Q_FUNC_INFO << event->type(); +#endif + return QWidget::event(event); +} + +void FloatingDockContainer::moveEvent(QMoveEvent *event) +{ + QWidget::moveEvent(event); + switch (d->m_draggingState) + { + case DraggingMousePressed: + d->setState(DraggingFloatingWidget); + d->updateDropOverlays(QCursor::pos()); + break; + + case DraggingFloatingWidget: + d->updateDropOverlays(QCursor::pos()); + // On macOS when hiding the DockAreaOverlay the application would set + // the main window as the active window for some reason. This fixes + // that by resetting the active window to the floating widget after + // updating the overlays. + QApplication::setActiveWindow(this); + break; + default: + break; + } +} +#endif } // namespace ADS diff --git a/src/libs/advanceddockingsystem/floatingdockcontainer.h b/src/libs/advanceddockingsystem/floatingdockcontainer.h index 458c1540d73..5e45e335a4b 100644 --- a/src/libs/advanceddockingsystem/floatingdockcontainer.h +++ b/src/libs/advanceddockingsystem/floatingdockcontainer.h @@ -183,12 +183,21 @@ protected: protected: // reimplements QWidget void changeEvent(QEvent *event) override; - void moveEvent(QMoveEvent *event) override; - bool event(QEvent *event) override; void closeEvent(QCloseEvent *event) override; void hideEvent(QHideEvent *event) override; void showEvent(QShowEvent *event) override; - bool eventFilter(QObject *watched, QEvent *event) override; + +#ifdef Q_OS_MACOS + virtual bool event(QEvent *event) override; + virtual void moveEvent(QMoveEvent *event) override; +#endif + +#ifdef Q_OS_WIN + /** + * Native event filter for handling WM_MOVING messages on Windows + */ + bool nativeEvent(const QByteArray &eventType, void *message, long *result) override; +#endif public: using Super = QWidget; diff --git a/src/libs/advanceddockingsystem/floatingdragpreview.cpp b/src/libs/advanceddockingsystem/floatingdragpreview.cpp index 539ef4a11c7..ece71843417 100644 --- a/src/libs/advanceddockingsystem/floatingdragpreview.cpp +++ b/src/libs/advanceddockingsystem/floatingdragpreview.cpp @@ -62,7 +62,6 @@ namespace ADS FloatingDragPreview *q; QWidget *m_content = nullptr; DockAreaWidget *m_contentSourceArea = nullptr; - DockContainerWidget *m_contenSourceContainer = nullptr; QPoint m_dragStartMousePosition; DockManager *m_dockManager = nullptr; DockContainerWidget *m_dropContainer = nullptr; @@ -93,6 +92,12 @@ namespace ADS m_dockManager->dockAreaOverlay()->hideOverlay(); q->close(); } + + /** + * Creates the real floating widget in case the mouse is released outside + * outside of any drop area + */ + void createFloatingWidget(); }; // class FloatingDragPreviewPrivate void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &globalPosition) @@ -106,7 +111,7 @@ namespace ADS if (!containerWidget->isVisible()) continue; - QPoint mappedPosition = containerWidget->mapFromGlobal(globalPosition); + const QPoint mappedPosition = containerWidget->mapFromGlobal(globalPosition); if (containerWidget->rect().contains(mappedPosition)) { if (!topContainer || containerWidget->isInFrontOf(topContainer)) topContainer = containerWidget; @@ -122,18 +127,16 @@ namespace ADS if (!topContainer) { containerOverlay->hideOverlay(); dockAreaOverlay->hideOverlay(); - if (DockManager::configFlags().testFlag(DockManager::DragPreviewIsDynamic)) + if (DockManager::testConfigFlag(DockManager::DragPreviewIsDynamic)) setHidden(false); return; } - int visibleDockAreas = topContainer->visibleDockAreaCount(); + const int visibleDockAreas = topContainer->visibleDockAreaCount(); containerOverlay->setAllowedAreas(visibleDockAreas > 1 ? OuterDockAreas : AllDockAreas); - DockWidgetArea containerArea = containerOverlay->showOverlay(topContainer); - containerOverlay->enableDropPreview(containerArea != InvalidDockWidgetArea); auto dockArea = topContainer->dockAreaAt(globalPosition); - if (dockArea && dockArea->isVisible() && visibleDockAreas > 0 + if (dockArea && dockArea->isVisible() && visibleDockAreas >= 0 && dockArea != m_contentSourceArea) { dockAreaOverlay->enableDropPreview(true); dockAreaOverlay->setAllowedAreas((visibleDockAreas == 1) ? NoDockWidgetArea @@ -143,25 +146,28 @@ namespace ADS // A CenterDockWidgetArea for the dockAreaOverlay() indicates that the mouse is in the // title bar. If the ContainerArea is valid then we ignore the dock area of the // dockAreaOverlay() and disable the drop preview - if ((area == CenterDockWidgetArea) && (containerArea != InvalidDockWidgetArea)) { + if ((area == CenterDockWidgetArea) && (containerDropArea != InvalidDockWidgetArea)) { dockAreaOverlay->enableDropPreview(false); containerOverlay->enableDropPreview(true); } else { containerOverlay->enableDropPreview(InvalidDockWidgetArea == area); } + containerOverlay->showOverlay(topContainer); } else { dockAreaOverlay->hideOverlay(); // If there is only one single visible dock area in a container, then // it does not make sense to show a dock overlay because the dock area // would be removed and inserted at the same position if (visibleDockAreas <= 1) - containerOverlay->hide(); + containerOverlay->hideOverlay(); + else + containerOverlay->showOverlay(topContainer); if (dockArea == m_contentSourceArea && InvalidDockWidgetArea == containerDropArea) m_dropContainer = nullptr; } - if (DockManager::configFlags().testFlag(DockManager::DragPreviewIsDynamic)) { + if (DockManager::testConfigFlag(DockManager::DragPreviewIsDynamic)) { setHidden(dockDropArea != InvalidDockWidgetArea || containerDropArea != InvalidDockWidgetArea); } @@ -171,13 +177,38 @@ namespace ADS : q(parent) {} + void FloatingDragPreviewPrivate::createFloatingWidget() + { + DockWidget *dockWidget = qobject_cast(m_content); + DockAreaWidget *dockArea = qobject_cast(m_content); + + FloatingDockContainer *floatingWidget = nullptr; + + if (dockWidget && dockWidget->features().testFlag(DockWidget::DockWidgetFloatable)) + floatingWidget = new FloatingDockContainer(dockWidget); + else if (dockArea && dockArea->features().testFlag(DockWidget::DockWidgetFloatable)) + floatingWidget = new FloatingDockContainer(dockArea); + + if (floatingWidget) { + floatingWidget->setGeometry(q->geometry()); + floatingWidget->show(); + if (!DockManager::testConfigFlag(DockManager::DragPreviewHasWindowFrame)) { + QApplication::processEvents(); + int frameHeight = floatingWidget->frameGeometry().height() - floatingWidget->geometry().height(); + QRect fixedGeometry = q->geometry(); + fixedGeometry.adjust(0, frameHeight, 0, 0); + floatingWidget->setGeometry(fixedGeometry); + } + } + } + FloatingDragPreview::FloatingDragPreview(QWidget *content, QWidget *parent) : QWidget(parent) , d(new FloatingDragPreviewPrivate(this)) { d->m_content = content; setAttribute(Qt::WA_DeleteOnClose); - if (DockManager::configFlags().testFlag(DockManager::DragPreviewHasWindowFrame)) { + if (DockManager::testConfigFlag(DockManager::DragPreviewHasWindowFrame)) { setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint); } else { setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); @@ -195,7 +226,7 @@ namespace ADS // Create a static image of the widget that should get undocked // This is like some kind preview image like it is uses in drag and drop operations - if (DockManager::configFlags().testFlag(DockManager::DragPreviewShowsContentPixmap)) { + if (DockManager::testConfigFlag(DockManager::DragPreviewShowsContentPixmap)) { d->m_contentPreviewPixmap = QPixmap(content->size()); content->render(&d->m_contentPreviewPixmap); } @@ -213,10 +244,9 @@ namespace ADS content->dockManager()) // TODO static_cast? { d->m_dockManager = content->dockManager(); - if (content->dockAreaWidget()->openDockWidgetsCount() == 1) { + if (content->dockAreaWidget()->openDockWidgetsCount() == 1) d->m_contentSourceArea = content->dockAreaWidget(); - d->m_contenSourceContainer = content->dockContainer(); - } + setWindowTitle(content->windowTitle()); } @@ -226,7 +256,6 @@ namespace ADS { d->m_dockManager = content->dockManager(); d->m_contentSourceArea = content; - d->m_contenSourceContainer = content->dockContainer(); setWindowTitle(content->currentDockWidget()->windowTitle()); } @@ -234,10 +263,11 @@ namespace ADS void FloatingDragPreview::moveFloating() { - int borderSize = (frameSize().width() - size().width()) / 2; + const int borderSize = (frameSize().width() - size().width()) / 2; const QPoint moveToPos = QCursor::pos() - d->m_dragStartMousePosition - QPoint(borderSize, 0); move(moveToPos); + d->updateDropOverlays(QCursor::pos()); } void FloatingDragPreview::startFloating(const QPoint &dragStartMousePos, @@ -253,46 +283,24 @@ namespace ADS show(); } - void FloatingDragPreview::moveEvent(QMoveEvent *event) - { - QWidget::moveEvent(event); - d->updateDropOverlays(QCursor::pos()); - } - void FloatingDragPreview::finishDragging() { qCInfo(adsLog) << Q_FUNC_INFO; auto dockDropArea = d->m_dockManager->dockAreaOverlay()->visibleDropAreaUnderCursor(); auto containerDropArea = d->m_dockManager->containerOverlay()->visibleDropAreaUnderCursor(); - if (d->m_dropContainer && (dockDropArea != InvalidDockWidgetArea)) { - d->m_dropContainer->dropWidget(d->m_content, - dockDropArea, - d->m_dropContainer->dockAreaAt(QCursor::pos())); - } else if (d->m_dropContainer && (containerDropArea != InvalidDockWidgetArea)) { - d->m_dropContainer->dropWidget(d->m_content, containerDropArea, nullptr); + if (!d->m_dropContainer) { + d->createFloatingWidget(); + } else if (dockDropArea != InvalidDockWidgetArea) { + d->m_dropContainer->dropWidget(d->m_content, dockDropArea, d->m_dropContainer->dockAreaAt(QCursor::pos())); + } else if (containerDropArea != InvalidDockWidgetArea) { + // If there is only one single dock area, and we drop into the center + // then we tabify the dropped widget into the only visible dock area + if (d->m_dropContainer->visibleDockAreaCount() <= 1 && CenterDockWidgetArea == containerDropArea) + d->m_dropContainer->dropWidget(d->m_content, containerDropArea, d->m_dropContainer->dockAreaAt(QCursor::pos())); + else + d->m_dropContainer->dropWidget(d->m_content, containerDropArea, nullptr); } else { - DockWidget *dockWidget = qobject_cast(d->m_content); - FloatingDockContainer *floatingWidget = nullptr; - - if (dockWidget && dockWidget->features().testFlag(DockWidget::DockWidgetFloatable)) { - floatingWidget = new FloatingDockContainer(dockWidget); - } else { - DockAreaWidget *dockArea = qobject_cast(d->m_content); - if (dockArea->features().testFlag(DockWidget::DockWidgetFloatable)) - floatingWidget = new FloatingDockContainer(dockArea); - } - - if (floatingWidget) { - floatingWidget->setGeometry(this->geometry()); - floatingWidget->show(); - if (!DockManager::configFlags().testFlag(DockManager::DragPreviewHasWindowFrame)) { - QApplication::processEvents(); - int frameHeight = floatingWidget->frameGeometry().height() - floatingWidget->geometry().height(); - QRect fixedGeometry = this->geometry(); - fixedGeometry.adjust(0, frameHeight, 0, 0); - floatingWidget->setGeometry(fixedGeometry); - } - } + d->createFloatingWidget(); } this->close(); @@ -307,11 +315,11 @@ namespace ADS return; QPainter painter(this); - if (DockManager::configFlags().testFlag(DockManager::DragPreviewShowsContentPixmap)) + if (DockManager::testConfigFlag(DockManager::DragPreviewShowsContentPixmap)) painter.drawPixmap(QPoint(0, 0), d->m_contentPreviewPixmap); // If we do not have a window frame then we paint a QRubberBand like frameless window - if (!DockManager::configFlags().testFlag(DockManager::DragPreviewHasWindowFrame)) { + if (!DockManager::testConfigFlag(DockManager::DragPreviewHasWindowFrame)) { QColor color = palette().color(QPalette::Active, QPalette::Highlight); QPen pen = painter.pen(); pen.setColor(color.darker(120)); diff --git a/src/libs/advanceddockingsystem/floatingdragpreview.h b/src/libs/advanceddockingsystem/floatingdragpreview.h index 2b0b36d0149..88e590c5c2a 100644 --- a/src/libs/advanceddockingsystem/floatingdragpreview.h +++ b/src/libs/advanceddockingsystem/floatingdragpreview.h @@ -64,11 +64,6 @@ private: void onApplicationStateChanged(Qt::ApplicationState state); protected: - /** - * Updates the drop overlays - */ - void moveEvent(QMoveEvent *event) override; - /** * Cares about painting the */ diff --git a/src/libs/advanceddockingsystem/images/close-button-disabled.svg b/src/libs/advanceddockingsystem/images/close-button-disabled.svg deleted file mode 100644 index 9c60c74ad82..00000000000 --- a/src/libs/advanceddockingsystem/images/close-button-disabled.svg +++ /dev/null @@ -1,122 +0,0 @@ - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/libs/advanceddockingsystem/images/close-button.svg b/src/libs/advanceddockingsystem/images/close-button.svg deleted file mode 100644 index 679bffcf60a..00000000000 --- a/src/libs/advanceddockingsystem/images/close-button.svg +++ /dev/null @@ -1,119 +0,0 @@ - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp b/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp index 7d92952d700..7393ae458fb 100644 --- a/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp +++ b/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp @@ -41,7 +41,7 @@ namespace ADS { using TabLabelType = ElidingLabel; -using CloseButtonType = QPushButton; +using CloseButtonType = QToolButton; /** * @brief Private data class of public interface CFloatingWidgetTitleBar @@ -76,7 +76,7 @@ void FloatingWidgetTitleBarPrivate::createLayout() m_closeButton = new CloseButtonType(); m_closeButton->setObjectName("floatingTitleCloseButton"); - m_closeButton->setFlat(true); + m_closeButton->setAutoRaise(true); internal::setButtonIcon(m_closeButton, QStyle::SP_TitleBarCloseButton, ADS::FloatingWidgetCloseIcon); @@ -106,7 +106,7 @@ void FloatingWidgetTitleBarPrivate::createLayout() } FloatingWidgetTitleBar::FloatingWidgetTitleBar(FloatingDockContainer *parent) - : QWidget(parent) + : QFrame(parent) , d(new FloatingWidgetTitleBarPrivate(this)) { d->m_floatingWidget = parent; @@ -131,9 +131,9 @@ void FloatingWidgetTitleBar::mousePressEvent(QMouseEvent *event) void FloatingWidgetTitleBar::mouseReleaseEvent(QMouseEvent *event) { d->m_dragState = DraggingInactive; - if (d->m_floatingWidget) { + if (d->m_floatingWidget) d->m_floatingWidget->finishDragging(); - } + Super::mouseReleaseEvent(event); } @@ -164,4 +164,9 @@ void FloatingWidgetTitleBar::setTitle(const QString &text) d->m_titleLabel->setText(text); } +void FloatingWidgetTitleBar::updateStyle() +{ + internal::repolishStyle(this, internal::RepolishDirectChildren); +} + } // namespace ADS diff --git a/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.h b/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.h index 1b42a788f69..f56405ffa8e 100644 --- a/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.h +++ b/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.h @@ -25,7 +25,7 @@ #pragma once -#include +#include namespace ADS { @@ -34,12 +34,12 @@ class FloatingWidgetTitleBarPrivate; /** * Titlebar for floating widgets to capture non client are mouse events. - * Linux does not support NonClieantArea mouse events like + * Linux does not support NonClientArea mouse events like * QEvent::NonClientAreaMouseButtonPress. Because these events are required * for the docking system to work properly, we use our own titlebar here to * capture the required mouse events. */ -class FloatingWidgetTitleBar : public QWidget +class FloatingWidgetTitleBar : public QFrame { Q_OBJECT private: @@ -55,24 +55,29 @@ public: explicit FloatingWidgetTitleBar(FloatingDockContainer *parent = nullptr); /** - * Virtual Destructor - */ + * Virtual Destructor + */ ~FloatingWidgetTitleBar() override; /** - * Enables / disables the window close button. - */ + * Enables / disables the window close button. + */ void enableCloseButton(bool enable); /** - * Sets the window title, that means, the text of the internal tile label. - */ + * Sets the window title, that means, the text of the internal tile label. + */ void setTitle(const QString &text); + /** + * Update stylesheet style if a property changes + */ + void updateStyle(); + signals: /** - * This signal is emitted, if the close button is clicked. - */ + * This signal is emitted, if the close button is clicked. + */ void closeRequested(); }; diff --git a/src/libs/advanceddockingsystem/resources.qrc b/src/libs/advanceddockingsystem/resources.qrc deleted file mode 100644 index facf3347516..00000000000 --- a/src/libs/advanceddockingsystem/resources.qrc +++ /dev/null @@ -1,6 +0,0 @@ - - - images/close-button.svg - images/close-button-disabled.svg - - diff --git a/src/plugins/qmldesigner/components/resources/dockwidgets.css b/src/plugins/qmldesigner/components/resources/dockwidgets.css index b878e5c6ebd..0aac817fbc9 100644 --- a/src/plugins/qmldesigner/components/resources/dockwidgets.css +++ b/src/plugins/qmldesigner/components/resources/dockwidgets.css @@ -59,9 +59,15 @@ ADS--DockWidget border-width: 0; } -ADS--DockAreaTitleBar{ background-color: creatorTheme.BackgroundColorDark; } +ADS--DockAreaTitleBar +{ + background-color: creatorTheme.BackgroundColorDark; +} -QWidget#tabsContainerWidget { background-color: creatorTheme.BackgroundColorDark; } +QWidget#tabsContainerWidget +{ + background-color: creatorTheme.BackgroundColorDark; +} ADS--TitleBarButton { @@ -126,3 +132,33 @@ QScrollBar::sub-page { height: 0px; width: 0px; } + +/* Focus related styling */ +ADS--DockWidgetTab[focused="true"] { + background: creatorTheme.DSinteraction; + border-color: creatorTheme.DSinteraction; +} + +ADS--DockWidgetTab[focused="true"] > #tabCloseButton:hover { + background: rgba(255, 255, 255, 48); +} + +ADS--DockWidgetTab[focused="true"] > #tabCloseButton:pressed { + background: rgba(255, 255, 255, 92); +} + +ADS--DockWidgetTab[focused="true"] QLabel { + color: palette(creatorTheme.QmlDesigner_TabDark); +} + +ADS--DockAreaTitleBar { + background: transparent; + border-bottom: 2px solid creatorTheme.QmlDesigner_TabLight; + padding-bottom: 0px; +} + +ADS--DockAreaWidget[focused="true"] ADS--DockAreaTitleBar { + background: transparent; + border-bottom: 2px solid creatorTheme.DSinteraction; + padding-bottom: 0px; +} diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index c1b63b557f4..c6b4af7054c 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -228,8 +228,9 @@ void DesignModeWidget::setup() auto settings = Core::ICore::settings(QSettings::UserScope); + ADS::DockManager::setConfigFlags(ADS::DockManager::DefaultNonOpaqueConfig); + ADS::DockManager::setConfigFlag(ADS::DockManager::FocusHighlighting, true); m_dockManager = new ADS::DockManager(this); - m_dockManager->setConfigFlags(ADS::DockManager::DefaultNonOpaqueConfig); m_dockManager->setSettings(settings); m_dockManager->setWorkspacePresetsPath(Core::ICore::resourcePath() + QLatin1String("/qmldesigner/workspacePresets/"));