diff --git a/demo/MainWindow.cpp b/demo/MainWindow.cpp index 35d814d..c9016a4 100644 --- a/demo/MainWindow.cpp +++ b/demo/MainWindow.cpp @@ -196,6 +196,9 @@ static ads::CDockWidget* createFileSystemTreeDockWidget(QMenu* ViewMenu) .arg(FileSystemCount++)); DockWidget->setWidget(w); ViewMenu->addAction(DockWidget->toggleViewAction()); + // We disable focus to test focus highlighting if the dock widget content + // does not support focus + w->setFocusPolicy(Qt::NoFocus); return DockWidget; } @@ -571,6 +574,9 @@ CMainWindow::CMainWindow(QWidget *parent) : // dock widget. // CDockManager::setConfigFlag(CDockManager::HideSingleCentralWidgetTitleBar, true); + //CDockManager::setConfigFlag(CDockManager::AlwaysShowTabs, true); + CDockManager::setConfigFlag(CDockManager::FocusHighlighting, true); + // Now create the dock manager and its content d->DockManager = new CDockManager(this); @@ -656,13 +662,14 @@ void CMainWindow::onViewToggled(bool Open) //============================================================================ void CMainWindow::onViewVisibilityChanged(bool Visible) { + Q_UNUSED(Visible); auto DockWidget = qobject_cast(sender()); if (!DockWidget) { return; } - qDebug() << DockWidget->objectName() << " visibilityChanged(" << Visible << ")"; + //qDebug() << DockWidget->objectName() << " visibilityChanged(" << Visible << ")"; } diff --git a/examples/deleteonclose/main.cpp b/examples/deleteonclose/main.cpp index 0d60fc1..2f08ffe 100644 --- a/examples/deleteonclose/main.cpp +++ b/examples/deleteonclose/main.cpp @@ -10,6 +10,7 @@ int main(int argc, char *argv[]) QApplication a(argc, argv); QMainWindow w; + ads::CDockManager::setConfigFlag(ads::CDockManager::FocusHighlighting, true); auto dockManager = new ads::CDockManager(&w); QAction *action = new QAction("New Delete On Close", &w); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7893cce..b0bae44 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,6 +17,7 @@ set(ads_SRCS DockWidget.cpp DockWidgetTab.cpp DockingStateReader.cpp + DockFocusController.cpp ElidingLabel.cpp FloatingDockContainer.cpp FloatingDragPreview.cpp @@ -37,6 +38,7 @@ set(ads_INSTALL_INCLUDE DockWidget.h DockWidgetTab.h DockingStateReader.h + DockFocusController.h ElidingLabel.h FloatingDockContainer.h FloatingDragPreview.h diff --git a/src/DockAreaTitleBar.cpp b/src/DockAreaTitleBar.cpp index aec4cc9..61142e1 100644 --- a/src/DockAreaTitleBar.cpp +++ b/src/DockAreaTitleBar.cpp @@ -465,6 +465,11 @@ void CDockAreaTitleBar::mousePressEvent(QMouseEvent* ev) ev->accept(); d->DragStartMousePos = ev->pos(); d->DragState = DraggingMousePressed; + + if (CDockManager::configFlags().testFlag(CDockManager::FocusHighlighting)) + { + d->TabBar->currentTab()->setFocus(Qt::OtherFocusReason); + } return; } Super::mousePressEvent(ev); @@ -485,6 +490,7 @@ void CDockAreaTitleBar::mouseReleaseEvent(QMouseEvent* ev) { d->FloatingWidget->finishDragging(); } + return; } Super::mouseReleaseEvent(ev); diff --git a/src/DockAreaWidget.cpp b/src/DockAreaWidget.cpp index 3c3931c..ba01a9d 100644 --- a/src/DockAreaWidget.cpp +++ b/src/DockAreaWidget.cpp @@ -413,6 +413,7 @@ void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget, bool Activate) { d->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 @@ -428,7 +429,6 @@ void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget, { 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->ContentsLayout->count() > 1 && !dockManager()->isRestoringState()) diff --git a/src/DockContainerWidget.cpp b/src/DockContainerWidget.cpp index 1852b7d..b91ab53 100644 --- a/src/DockContainerWidget.cpp +++ b/src/DockContainerWidget.cpp @@ -1459,6 +1459,13 @@ void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWi // level widget anymore CDockWidget::emitTopLevelEventForWidget(SingleDockWidget, false); } + + window()->activateWindow(); + if (SingleDroppedDockWidget) + { + d->DockManager->notifyWidgetOrAreaRelocation(SingleDroppedDockWidget); + } + d->DockManager->notifyFloatingWidgetDrop(FloatingWidget); } @@ -1478,6 +1485,19 @@ void CDockContainerWidget::dropWidget(QWidget* Widget, DockWidgetArea DropArea, // If there was a top level widget before the drop, then it is not top // level widget anymore CDockWidget::emitTopLevelEventForWidget(SingleDockWidget, false); + CDockWidget* DockWidget = qobject_cast(Widget); + if (!DockWidget) + { + CDockAreaWidget* DockArea = qobject_cast(Widget); + auto OpenDockWidgets = DockArea->openedDockWidgets(); + if (OpenDockWidgets.count() == 1) + { + DockWidget = OpenDockWidgets[0]; + } + } + + window()->activateWindow(); + d->DockManager->notifyWidgetOrAreaRelocation(Widget); } diff --git a/src/DockContainerWidget.h b/src/DockContainerWidget.h index 83ef086..8f1b04c 100644 --- a/src/DockContainerWidget.h +++ b/src/DockContainerWidget.h @@ -193,7 +193,7 @@ public: bool isInFrontOf(CDockContainerWidget* Other) const; /** - * Returns the dock area at teh given global position or 0 if there is no + * Returns the dock area at the given global position or 0 if there is no * dock area at this position */ CDockAreaWidget* dockAreaAt(const QPoint& GlobalPos) const; diff --git a/src/DockFocusController.cpp b/src/DockFocusController.cpp new file mode 100644 index 0000000..43acc96 --- /dev/null +++ b/src/DockFocusController.cpp @@ -0,0 +1,322 @@ +//============================================================================ +/// \file DockFocusController.cpp +/// \author Uwe Kindler +/// \date 05.06.2020 +/// \brief Implementation of CDockFocusController class +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include "DockFocusController.h" + +#include +#include + +#include +#include + +#include "DockWidget.h" +#include "DockAreaWidget.h" +#include "DockWidgetTab.h" +#include "DockContainerWidget.h" +#include "FloatingDockContainer.h" +#include "DockManager.h" +#include "DockAreaTitleBar.h" + +#ifdef Q_OS_LINUX +#include "linux/FloatingWidgetTitleBar.h" +#endif + +namespace ads +{ +/** + * Private data class of CDockFocusController class (pimpl) + */ +struct DockFocusControllerPrivate +{ + CDockFocusController *_this; + QPointer FocusedDockWidget = nullptr; + QPointer FocusedArea = nullptr; +#ifdef Q_OS_LINUX + QPointer FloatingWidget = nullptr; +#endif + CDockManager* DockManager; + + /** + * Private data constructor + */ + DockFocusControllerPrivate(CDockFocusController *_public); + + /** + * This function updates the focus style of the given dock widget and + * the dock area that it belongs to + */ + void updateDockWidgetFocus(CDockWidget* DockWidget); +}; +// struct DockFocusControllerPrivate + + +//=========================================================================== +static void updateDockWidgetFocusStyle(CDockWidget* DockWidget, bool Focused) +{ + DockWidget->setProperty("focused", Focused); + DockWidget->tabWidget()->setProperty("focused", Focused); + DockWidget->tabWidget()->updateStyle(); + internal::repolishStyle(DockWidget); +} + + +//=========================================================================== +static void updateDockAreaFocusStyle(CDockAreaWidget* DockArea, bool Focused) +{ + DockArea->setProperty("focused", Focused); + internal::repolishStyle(DockArea); + internal::repolishStyle(DockArea->titleBar()); +} + + +//=========================================================================== +#ifdef Q_OS_LINUX +static void updateFloatingWidgetFocusStyle(CFloatingDockContainer* FloatingWidget, bool Focused) +{ + auto TitleBar = qobject_cast(FloatingWidget->titleBarWidget()); + if (!TitleBar) + { + return; + } + TitleBar->setProperty("focused", Focused); + TitleBar->updateStyle(); +} +#endif + + +//============================================================================ +DockFocusControllerPrivate::DockFocusControllerPrivate( + CDockFocusController *_public) : + _this(_public) +{ + +} + + +//============================================================================ +void DockFocusControllerPrivate::updateDockWidgetFocus(CDockWidget* DockWidget) +{ + CDockAreaWidget* NewFocusedDockArea = nullptr; + if (FocusedDockWidget) + { + updateDockWidgetFocusStyle(FocusedDockWidget, false); + } + + CDockWidget* old = FocusedDockWidget; + FocusedDockWidget = DockWidget; + updateDockWidgetFocusStyle(FocusedDockWidget, true); + NewFocusedDockArea = FocusedDockWidget->dockAreaWidget(); + if (NewFocusedDockArea && (FocusedArea != NewFocusedDockArea)) + { + if (FocusedArea) + { + QObject::disconnect(FocusedArea, SIGNAL(viewToggled(bool)), _this, SLOT(onFocusedDockAreaViewToggled(bool))); + updateDockAreaFocusStyle(FocusedArea, false); + } + + FocusedArea = NewFocusedDockArea; + updateDockAreaFocusStyle(FocusedArea, true); + QObject::connect(FocusedArea, SIGNAL(viewToggled(bool)), _this, SLOT(onFocusedDockAreaViewToggled(bool))); + } + + + auto NewFloatingWidget = 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 (FloatingWidget == NewFloatingWidget) + { + return; + } + + if (FloatingWidget) + { + updateFloatingWidgetFocusStyle(FloatingWidget, false); + } + FloatingWidget = NewFloatingWidget; + + if (FloatingWidget) + { + updateFloatingWidgetFocusStyle(FloatingWidget, true); + } +#endif + + if (old != DockWidget) + { + emit DockManager->focusedDockWidgetChanged(old, DockWidget); + } +} + + + +//============================================================================ +CDockFocusController::CDockFocusController(CDockManager* DockManager) : + Super(DockManager), + d(new DockFocusControllerPrivate(this)) +{ + d->DockManager = DockManager; + connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*, QWidget*)), + this, SLOT(onApplicationFocusChanged(QWidget*, QWidget*))); + connect(d->DockManager, SIGNAL(stateRestored()), SLOT(onStateRestored())); +} + +//============================================================================ +CDockFocusController::~CDockFocusController() +{ + delete d; +} + + +//=========================================================================== +void CDockFocusController::onApplicationFocusChanged(QWidget* focusedOld, QWidget* focusedNow) +{ + if (d->DockManager->isRestoringState()) + { + return; + } + + Q_UNUSED(focusedOld) + if (!focusedNow) + { + return; + } + + CDockWidget* 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 CDockFocusController::setDockWidgetFocused(CDockWidget* focusedNow) +{ + d->updateDockWidgetFocus(focusedNow); +} + + +//=========================================================================== +void CDockFocusController::onFocusedDockAreaViewToggled(bool Open) +{ + if (d->DockManager->isRestoringState()) + { + return; + } + + CDockAreaWidget* DockArea = qobject_cast(sender()); + if (!DockArea || Open) + { + return; + } + auto Container = DockArea->dockContainer(); + auto OpenedDockAreas = Container->openedDockAreas(); + if (OpenedDockAreas.isEmpty()) + { + return; + } + + CDockManager::setWidgetFocus(OpenedDockAreas[0]->currentDockWidget()->tabWidget()); +} + + +//=========================================================================== +void CDockFocusController::notifyWidgetOrAreaRelocation(QWidget* DroppedWidget) +{ + if (d->DockManager->isRestoringState()) + { + return; + } + + CDockWidget* DockWidget = qobject_cast(DroppedWidget); + if (DockWidget) + { + CDockManager::setWidgetFocus(DockWidget->tabWidget()); + return; + } + + CDockAreaWidget* DockArea = qobject_cast(DroppedWidget); + if (!DockArea) + { + return; + } + + DockWidget = DockArea->currentDockWidget(); + CDockManager::setWidgetFocus(DockWidget->tabWidget()); +} + + +//=========================================================================== +void CDockFocusController::notifyFloatingWidgetDrop(CFloatingDockContainer* FloatingWidget) +{ + if (!FloatingWidget || d->DockManager->isRestoringState()) + { + return; + } + + auto vDockWidget = FloatingWidget->property("FocusedDockWidget"); + if (!vDockWidget.isValid()) + { + return; + } + + auto DockWidget = vDockWidget.value(); + if (DockWidget) + { + DockWidget->dockAreaWidget()->setCurrentDockWidget(DockWidget); + CDockManager::setWidgetFocus(DockWidget->tabWidget()); + } +} + + +//========================================================================== +void CDockFocusController::onStateRestored() +{ + if (d->FocusedDockWidget) + { + updateDockWidgetFocusStyle(d->FocusedDockWidget, false); + } +} + +} // namespace ads + +//--------------------------------------------------------------------------- +// EOF DockFocusController.cpp diff --git a/src/DockFocusController.h b/src/DockFocusController.h new file mode 100644 index 0000000..d98d9d4 --- /dev/null +++ b/src/DockFocusController.h @@ -0,0 +1,89 @@ +#ifndef DockFocusControllerH +#define DockFocusControllerH +//============================================================================ +/// \file DockFocusController.h +/// \author Uwe Kindler +/// \date 05.06.2020 +/// \brief Declaration of CDockFocusController class +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include +#include "ads_globals.h" +#include "DockManager.h" + +namespace ads +{ +struct DockFocusControllerPrivate; +class CDockManager; +class CFloatingDockContainer; + +/** + * Manages focus styling of dock widgets and handling of focus changes + */ +class ADS_EXPORT CDockFocusController : public QObject +{ + Q_OBJECT +private: + DockFocusControllerPrivate* d; ///< private data (pimpl) + friend struct DockFocusControllerPrivate; + +private slots: + void onApplicationFocusChanged(QWidget *old, QWidget *now); + void onFocusedDockAreaViewToggled(bool Open); + void onStateRestored(); + +public: + using Super = QObject; + /** + * Default Constructor + */ + CDockFocusController(CDockManager* DockManager); + + /** + * Virtual Destructor + */ + virtual ~CDockFocusController(); + + /** + * Helper function to set focus depending on the configuration of the + * FocusStyling flag + */ + template + static void setWidgetFocus(QWidgetPtr widget) + { + if (!CDockManager::configFlags().testFlag(CDockManager::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(CFloatingDockContainer* FloatingWidget); + +public slots: + /** + * Request a focus change to the given dock widget + */ + void setDockWidgetFocused(CDockWidget* focusedNow); +}; // class DockFocusController +} + // namespace ads +//----------------------------------------------------------------------------- +#endif // DockFocusControllerH + diff --git a/src/DockManager.cpp b/src/DockManager.cpp index b750b30..84b97f2 100644 --- a/src/DockManager.cpp +++ b/src/DockManager.cpp @@ -53,6 +53,12 @@ #include "DockAreaWidget.h" #include "IconProvider.h" #include "DockingStateReader.h" +#include "DockAreaTitleBar.h" +#include "DockFocusController.h" + +#ifdef Q_OS_LINUX +#include "linux/FloatingWidgetTitleBar.h" +#endif /** @@ -101,6 +107,7 @@ struct DockManagerPrivate CDockManager::eViewMenuInsertionOrder MenuInsertionOrder = CDockManager::MenuAlphabeticallySorted; bool RestoringState = false; QVector UninitializedFloatingWidgets; + CDockFocusController* FocusController = nullptr; /** * Private data constructor @@ -459,6 +466,11 @@ CDockManager::CDockManager(QWidget *parent) : d->ContainerOverlay = new CDockOverlay(this, CDockOverlay::ModeContainerOverlay); d->Containers.append(this); d->loadStylesheet(); + + if (CDockManager::configFlags().testFlag(CDockManager::FocusHighlighting)) + { + d->FocusController = new CDockFocusController(this); + } } //============================================================================ @@ -593,12 +605,11 @@ bool CDockManager::restoreState(const QByteArray &state, int version) emit restoringState(); bool Result = d->restoreState(state, version); d->RestoringState = false; - emit stateRestored(); if (!IsHidden) { show(); } - + emit stateRestored(); return Result; } @@ -895,6 +906,36 @@ CIconProvider& CDockManager::iconProvider() } +//=========================================================================== +void CDockManager::notifyWidgetOrAreaRelocation(QWidget* DroppedWidget) +{ + if (d->FocusController) + { + d->FocusController->notifyWidgetOrAreaRelocation(DroppedWidget); + } +} + + +//=========================================================================== +void CDockManager::notifyFloatingWidgetDrop(CFloatingDockContainer* FloatingWidget) +{ + if (d->FocusController) + { + d->FocusController->notifyFloatingWidgetDrop(FloatingWidget); + } +} + + +//=========================================================================== +void CDockManager::setDockWidgetFocused(CDockWidget* DockWidget) +{ + if (d->FocusController) + { + d->FocusController->setDockWidgetFocused(DockWidget); + } +} + + } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/DockManager.h b/src/DockManager.h index a074249..6a9fb1f 100644 --- a/src/DockManager.h +++ b/src/DockManager.h @@ -84,6 +84,7 @@ private: friend struct FloatingDragPreviewPrivate; friend class CDockAreaTitleBar; + protected: /** * Registers the given floating widget in the internal list of @@ -118,6 +119,22 @@ protected: */ CDockOverlay* dockAreaOverlay() const; + + /** + * 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(CFloatingDockContainer* FloatingWidget); + + /** * Show the floating widgets that has been created floating */ @@ -161,7 +178,7 @@ public: FloatingContainerHasWidgetIcon = 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 DefaultDockAreaButtons = DockAreaHasCloseButton | DockAreaHasUndockButton @@ -410,12 +427,34 @@ public: */ static int startDragDistance(); + /** + * Helper function to set focus depending on the configuration of the + * FocusStyling flag + */ + template + static void setWidgetFocus(QWidgetPtr widget) + { + if (!CDockManager::configFlags().testFlag(CDockManager::FocusHighlighting)) + { + return; + } + + widget->setFocus(Qt::OtherFocusReason); + } + public slots: /** * Opens the perspective with the given name. */ void openPerspective(const QString& PerspectiveName); + /** + * Request a focus change to the given dock widget. + * This function only has an effect, if the flag CDockManager::FocusStyling + * is enabled + */ + void setDockWidgetFocused(CDockWidget* DockWidget); + signals: /** * This signal is emitted if the list of perspectives changed @@ -484,6 +523,13 @@ signals: * docking system but it is not deleted yet. */ void dockWidgetRemoved(CDockWidget* 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(CDockWidget* old, CDockWidget* now); }; // class DockManager } // namespace ads //----------------------------------------------------------------------------- diff --git a/src/DockWidget.cpp b/src/DockWidget.cpp index 0c5c2a2..3dba01b 100644 --- a/src/DockWidget.cpp +++ b/src/DockWidget.cpp @@ -235,6 +235,11 @@ CDockWidget::CDockWidget(const QString &title, QWidget *parent) : connect(d->ToggleViewAction, SIGNAL(triggered(bool)), this, SLOT(toggleView(bool))); setToolbarFloatingStyle(false); + + if (CDockManager::configFlags().testFlag(CDockManager::FocusHighlighting)) + { + setFocusPolicy(Qt::ClickFocus); + } } //============================================================================ diff --git a/src/DockWidgetTab.cpp b/src/DockWidgetTab.cpp index e60a717..e14fffe 100644 --- a/src/DockWidgetTab.cpp +++ b/src/DockWidgetTab.cpp @@ -160,6 +160,7 @@ struct DockWidgetTabPrivate GlobalDragStartMousePosition = GlobalPos; DragStartMousePosition = _this->mapFromGlobal(GlobalPos); } + }; // struct DockWidgetTabPrivate @@ -284,6 +285,10 @@ CDockWidgetTab::CDockWidgetTab(CDockWidget* DockWidget, QWidget *parent) : setAttribute(Qt::WA_NoMousePropagation, true); d->DockWidget = DockWidget; d->createLayout(); + if (CDockManager::configFlags().testFlag(CDockManager::FocusHighlighting)) + { + setFocusPolicy(Qt::ClickFocus); + } } //============================================================================ @@ -461,16 +466,33 @@ void CDockWidgetTab::setActiveTab(bool active) bool AllTabsHaveCloseButton = d->testConfigFlag(CDockManager::AllTabsHaveCloseButton); bool TabHasCloseButton = (ActiveTabHasCloseButton && active) | AllTabsHaveCloseButton; d->CloseButton->setVisible(DockWidgetClosable && TabHasCloseButton); - if (d->IsActiveTab == active) + + // Focus related stuff + if (CDockManager::configFlags().testFlag(CDockManager::FocusHighlighting) && !d->DockWidget->dockManager()->isRestoringState()) + { + bool UpdateFocusStyle = false; + if (active && !hasFocus()) + { + setFocus(Qt::OtherFocusReason); + UpdateFocusStyle = true; + } + + if (d->IsActiveTab == active) + { + if (UpdateFocusStyle) + { + updateStyle(); + } + return; + } + } + else if (d->IsActiveTab == active) { return; } d->IsActiveTab = active; - style()->unpolish(this); - style()->polish(this); - d->TitleLabel->style()->unpolish(d->TitleLabel); - d->TitleLabel->style()->polish(d->TitleLabel); + updateStyle(); update(); updateGeometry(); @@ -641,6 +663,13 @@ void CDockWidgetTab::setElideMode(Qt::TextElideMode mode) } +//============================================================================ +void CDockWidgetTab::updateStyle() +{ + internal::repolishStyle(this, internal::RepolishDirectChildren); +} + + } // namespace ads diff --git a/src/DockWidgetTab.h b/src/DockWidgetTab.h index 3be47a3..3ed7df8 100644 --- a/src/DockWidgetTab.h +++ b/src/DockWidgetTab.h @@ -39,6 +39,7 @@ namespace ads class CDockWidget; class CDockAreaWidget; struct DockWidgetTabPrivate; +class CDockManager; /** * A dock widget tab that shows a title and an icon. @@ -54,6 +55,7 @@ private: DockWidgetTabPrivate* d; ///< private data (pimpl) friend struct DockWidgetTabPrivate; friend class CDockWidget; + friend class CDockManager; void onDockWidgetFeaturesChanged(); private slots: @@ -152,6 +154,10 @@ public: */ void setElideMode(Qt::TextElideMode mode); + /** + * Update stylesheet style if a property changes + */ + void updateStyle(); public slots: virtual void setVisible(bool visible) override; diff --git a/src/FloatingDockContainer.cpp b/src/FloatingDockContainer.cpp index 217f024..e6595b9 100644 --- a/src/FloatingDockContainer.cpp +++ b/src/FloatingDockContainer.cpp @@ -637,6 +637,8 @@ CFloatingDockContainer::CFloatingDockContainer(CDockAreaWidget *DockArea) : { TopLevelDockWidget->emitTopLevelChanged(true); } + + d->DockManager->notifyWidgetOrAreaRelocation(DockArea); } //============================================================================ @@ -652,6 +654,8 @@ CFloatingDockContainer::CFloatingDockContainer(CDockWidget *DockWidget) : { TopLevelDockWidget->emitTopLevelChanged(true); } + + d->DockManager->notifyWidgetOrAreaRelocation(DockWidget); } //============================================================================ @@ -800,10 +804,17 @@ void CFloatingDockContainer::hideEvent(QHideEvent *event) d->Hiding = false; } + //============================================================================ void CFloatingDockContainer::showEvent(QShowEvent *event) { Super::showEvent(event); +#ifdef Q_OS_LINUX + if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting)) + { + this->window()->activateWindow(); + } +#endif } diff --git a/src/FloatingDockContainer.h b/src/FloatingDockContainer.h index 25e91cf..0099e2a 100644 --- a/src/FloatingDockContainer.h +++ b/src/FloatingDockContainer.h @@ -172,8 +172,7 @@ protected: /** * Call this function to update the window title */ - void updateWindowTitle(); - + void updateWindowTitle(); protected: // reimplements QWidget virtual void changeEvent(QEvent *event) override; diff --git a/src/ads.qrc b/src/ads.qrc index ecb2a6e..33157a2 100644 --- a/src/ads.qrc +++ b/src/ads.qrc @@ -4,5 +4,6 @@ images/close-button.svg images/close-button-disabled.svg stylesheets/default_linux.css + images/close-button-focused.svg diff --git a/src/ads_globals.cpp b/src/ads_globals.cpp index 4310dd2..b185e5e 100644 --- a/src/ads_globals.cpp +++ b/src/ads_globals.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "DockSplitter.h" #include "DockManager.h" @@ -118,6 +119,31 @@ void setButtonIcon(QAbstractButton* Button, QStyle::StandardPixmap StandarPixmap #endif } + +//============================================================================ +void repolishStyle(QWidget* w, eRepolishChildOptions Options) +{ + if (!w) + { + return; + } + w->style()->unpolish(w); + w->style()->polish(w); + + if (RepolishIgnoreChildren == Options) + { + return; + } + + QList Children = w->findChildren(QString(), + (RepolishDirectChildren == Options) ? Qt::FindDirectChildrenOnly: Qt::FindChildrenRecursively); + for (auto Widget : Children) + { + Widget->style()->unpolish(Widget); + Widget->style()->polish(Widget); + } +} + } // namespace internal } // namespace ads diff --git a/src/ads_globals.h b/src/ads_globals.h index ef6bf4a..3523be5 100644 --- a/src/ads_globals.h +++ b/src/ads_globals.h @@ -244,6 +244,21 @@ 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* w, eRepolishChildOptions Options = RepolishIgnoreChildren); + + } // namespace internal } // namespace ads diff --git a/src/images/close-button-disabled.svg b/src/images/close-button-disabled.svg index 7bce2a8..e213ca7 100644 --- a/src/images/close-button-disabled.svg +++ b/src/images/close-button-disabled.svg @@ -1,6 +1,4 @@ - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + inkscape:version="1.0 (4035a4fb49, 2020-05-01)" + version="1.1" + id="svg2" + viewBox="0 0 16 16" + height="16px" + width="16px"> + + + + + + + + + + + + + + + + + + + + + + + + + Jemis Mali + + + + + image/svg+xml + + + + + + diff --git a/src/images/close-button-focused.svg b/src/images/close-button-focused.svg new file mode 100644 index 0000000..b9a6fad --- /dev/null +++ b/src/images/close-button-focused.svg @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Jemis Mali + + + + + image/svg+xml + + + + + + diff --git a/src/images/close-button.svg b/src/images/close-button.svg index e6254df..c772029 100644 --- a/src/images/close-button.svg +++ b/src/images/close-button.svg @@ -1,6 +1,4 @@ - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + inkscape:version="1.0 (4035a4fb49, 2020-05-01)" + sodipodi:docname="close-button2.svg"> + + + + + + + + + + + + + + + + + + + + + + + + + Jemis Mali + + + + + image/svg+xml + + + + + + diff --git a/src/linux/FloatingWidgetTitleBar.cpp b/src/linux/FloatingWidgetTitleBar.cpp index babcaaa..7b81cac 100644 --- a/src/linux/FloatingWidgetTitleBar.cpp +++ b/src/linux/FloatingWidgetTitleBar.cpp @@ -115,7 +115,7 @@ void FloatingWidgetTitleBarPrivate::createLayout() //============================================================================ CFloatingWidgetTitleBar::CFloatingWidgetTitleBar(CFloatingDockContainer *parent) : - QWidget(parent), + QFrame(parent), d(new FloatingWidgetTitleBarPrivate(this)) { d->FloatingWidget = parent; @@ -172,16 +172,26 @@ void CFloatingWidgetTitleBar::mouseMoveEvent(QMouseEvent *ev) Super::mouseMoveEvent(ev); } + //============================================================================ void CFloatingWidgetTitleBar::enableCloseButton(bool Enable) { d->CloseButton->setEnabled(Enable); } + //============================================================================ void CFloatingWidgetTitleBar::setTitle(const QString &Text) { d->TitleLabel->setText(Text); } + +//============================================================================ +void CFloatingWidgetTitleBar::updateStyle() +{ + internal::repolishStyle(this); + internal::repolishStyle(d->TitleLabel); +} + } // namespace ads diff --git a/src/linux/FloatingWidgetTitleBar.h b/src/linux/FloatingWidgetTitleBar.h index d09ed38..13914db 100644 --- a/src/linux/FloatingWidgetTitleBar.h +++ b/src/linux/FloatingWidgetTitleBar.h @@ -29,7 +29,7 @@ //============================================================================ // INCLUDES //============================================================================ -#include +#include namespace ads { @@ -45,7 +45,7 @@ struct FloatingWidgetTitleBarPrivate; * for the docking system to work properly, we use our own titlebar here to * capture the required mouse events. */ -class CFloatingWidgetTitleBar : public QWidget +class CFloatingWidgetTitleBar : public QFrame { Q_OBJECT private: @@ -75,6 +75,11 @@ public: */ 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. diff --git a/src/src.pro b/src/src.pro index b54cbeb..fba3562 100644 --- a/src/src.pro +++ b/src/src.pro @@ -45,7 +45,8 @@ HEADERS += \ DockAreaTitleBar.h \ ElidingLabel.h \ IconProvider.h \ - DockComponentsFactory.h + DockComponentsFactory.h \ + DockFocusController.h SOURCES += \ @@ -64,7 +65,8 @@ SOURCES += \ DockAreaTitleBar.cpp \ ElidingLabel.cpp \ IconProvider.cpp \ - DockComponentsFactory.cpp + DockComponentsFactory.cpp \ + DockFocusController.cpp unix { diff --git a/src/stylesheets/default.css b/src/stylesheets/default.css index 5f7185d..23e23be 100644 --- a/src/stylesheets/default.css +++ b/src/stylesheets/default.css @@ -9,10 +9,6 @@ ads--CDockContainerWidget background: palette(dark); } -ads--CDockContainerWidget QSplitter::handle -{ - background: palette(dark); -} ads--CDockAreaWidget { @@ -82,13 +78,74 @@ QScrollArea#dockWidgetScrollArea #tabCloseButton:hover { - border: 1px solid rgba(0, 0, 0, 32); - background: rgba(0, 0, 0, 16); + /*border: 1px solid rgba(0, 0, 0, 32);*/ + background: rgba(0, 0, 0, 24); } #tabCloseButton:pressed { - background: rgba(0, 0, 0, 32); + background: rgba(0, 0, 0, 48); +} + +#tabCloseButton +{ + qproperty-icon: url(:/ads/images/close-button.svg); + qproperty-iconSize: 16px; } +ads--CDockSplitter::handle +{ + background-color: palette(dark); + /* uncomment the following line if you would like to change the size of + the splitter handles */ + /* height: 1px; */ +} + + +/* Focus related styling */ +ads--CDockWidgetTab[focused="true"] +{; + background: palette(highlight); + border-color: palette(highlight); +} + +ads--CDockWidgetTab[focused="true"] > #tabCloseButton +{ + qproperty-icon: url(:/ads/images/close-button-focused.svg) +} + + + +ads--CDockWidgetTab[focused="true"] > #tabCloseButton:hover +{ + background: rgba(255, 255, 255, 48); +} + + +ads--CDockWidgetTab[focused="true"] > #tabCloseButton:pressed +{ + background: rgba(255, 255, 255, 92); +} + + +ads--CDockWidgetTab[focused="true"] QLabel +{ + color: palette(light); +} + + +ads--CDockAreaTitleBar +{ + background: transparent; + border-bottom: 2px solid palette(light); + padding-bottom: 0px; +} + +ads--CDockAreaWidget[focused="true"] ads--CDockAreaTitleBar +{ + background: transparent; + border-bottom: 2px solid palette(highlight); + padding-bottom: 0px; +} + diff --git a/src/stylesheets/default_linux.css b/src/stylesheets/default_linux.css index 7a184be..0eb6717 100644 --- a/src/stylesheets/default_linux.css +++ b/src/stylesheets/default_linux.css @@ -84,13 +84,74 @@ QScrollArea#dockWidgetScrollArea #tabCloseButton:hover { - border: 1px solid rgba(0, 0, 0, 32); - background: rgba(0, 0, 0, 16); + /*border: 1px solid rgba(0, 0, 0, 32);*/ + background: rgba(0, 0, 0, 24); } #tabCloseButton:pressed { - background: rgba(0, 0, 0, 32); + background: rgba(0, 0, 0, 48); +} + +#tabCloseButton +{ + qproperty-icon: url(:/ads/images/close-button.svg); + qproperty-iconSize: 16px; +} + +/* Focus related styling */ +ads--CDockWidgetTab[focused="true"] +{ + background: palette(highlight); + border-color: palette(highlight); +} + +ads--CDockWidgetTab[focused="true"] > #tabCloseButton +{ + qproperty-icon: url(:/ads/images/close-button-focused.svg) } + +ads--CDockWidgetTab[focused="true"] > #tabCloseButton:hover +{ + background: rgba(255, 255, 255, 48); +} + + +ads--CDockWidgetTab[focused="true"] > #tabCloseButton:pressed +{ + background: rgba(255, 255, 255, 92); +} + +ads--CDockWidgetTab[focused="true"] QLabel +{ + color: palette(light); +} + + +ads--CDockAreaTitleBar +{ + background: transparent; + border-bottom: 2px solid palette(light); + padding-bottom: 0px; +} + +ads--CDockAreaWidget[focused="true"] ads--CDockAreaTitleBar +{ + background: transparent; + border-bottom: 2px solid palette(highlight); + padding-bottom: 0px; +} + + +ads--CFloatingDockContainer[isActiveWindow="true"] ads--CFloatingWidgetTitleBar +{ + background: palette(highlight); +} + + +ads--CFloatingDockContainer[isActiveWindow="true"] ads--CFloatingWidgetTitleBar > QLabel +{ + color: palette(light); +}