From f2378636e2a6a7ec0f8124fbe5d0770bce5bec0f Mon Sep 17 00:00:00 2001 From: Federico Fuga Date: Fri, 8 Nov 2024 10:38:51 +0100 Subject: [PATCH] Add ability to customize the DockArea Title Bar and DockWidget Tab context menu --- demo/MainWindow.cpp | 17 ++++++++--------- demo/MyDockAreaTitleBar.h | 34 ++++++++++++++++++++++++++++++++++ src/DockAreaTitleBar.cpp | 36 +++++++++++++++++++++++------------- src/DockAreaTitleBar.h | 14 ++++++++++++++ src/DockWidgetTab.cpp | 27 +++++++++++++++++---------- src/DockWidgetTab.h | 14 ++++++++++++++ 6 files changed, 110 insertions(+), 32 deletions(-) create mode 100644 demo/MyDockAreaTitleBar.h diff --git a/demo/MainWindow.cpp b/demo/MainWindow.cpp index cde5492b..55bb6ef3 100644 --- a/demo/MainWindow.cpp +++ b/demo/MainWindow.cpp @@ -75,18 +75,17 @@ #endif #endif -#include "DockManager.h" -#include "DockWidget.h" -#include "DockAreaWidget.h" -#include "DockAreaTitleBar.h" #include "DockAreaTabBar.h" -#include "FloatingDockContainer.h" +#include "DockAreaTitleBar.h" +#include "DockAreaWidget.h" #include "DockComponentsFactory.h" -#include "StatusDialog.h" +#include "DockManager.h" #include "DockSplitter.h" +#include "DockWidget.h" +#include "FloatingDockContainer.h" #include "ImageViewer.h" - - +#include "MyDockAreaTitleBar.h" +#include "StatusDialog.h" /** * Returns a random number from 0 to highest - 1 @@ -147,7 +146,7 @@ class CCustomComponentsFactory : public ads::CDockComponentsFactory using Super = ads::CDockComponentsFactory; ads::CDockAreaTitleBar* createDockAreaTitleBar(ads::CDockAreaWidget* DockArea) const override { - auto TitleBar = new ads::CDockAreaTitleBar(DockArea); + auto TitleBar = new MyDockAreaTitleBar(DockArea); auto CustomButton = new QToolButton(DockArea); CustomButton->setToolTip(QObject::tr("Help")); CustomButton->setIcon(svgIcon(":/adsdemo/images/help_outline.svg")); diff --git a/demo/MyDockAreaTitleBar.h b/demo/MyDockAreaTitleBar.h new file mode 100644 index 00000000..074cc795 --- /dev/null +++ b/demo/MyDockAreaTitleBar.h @@ -0,0 +1,34 @@ +// +// Created by fuga on 08 nov 2024. +// + +#ifndef QTADS_MYDOCKAREATITLEBAR_H +#define QTADS_MYDOCKAREATITLEBAR_H + +#include + +class MyDockAreaTitleBar : public ads::CDockAreaTitleBar { +public: + explicit MyDockAreaTitleBar(ads::CDockAreaWidget* parent) + : CDockAreaTitleBar(parent) + {} + + QMenu* buildContextMenu(QMenu*) override + { + auto menu = ads::CDockAreaTitleBar::buildContextMenu(nullptr); + menu->addSeparator(); + auto action = menu->addAction(tr("Format HardDrive")); + + connect(action, &QAction::triggered, this, [this](){ + QMessageBox msgBox; + msgBox.setText("No, just kidding"); + msgBox.setStandardButtons(QMessageBox::Abort); + msgBox.setDefaultButton(QMessageBox::Abort); + msgBox.exec(); + }); + + return menu; + } +}; + +#endif // QTADS_MYDOCKAREATITLEBAR_H diff --git a/src/DockAreaTitleBar.cpp b/src/DockAreaTitleBar.cpp index 32a1d1a5..8c19cfdc 100644 --- a/src/DockAreaTitleBar.cpp +++ b/src/DockAreaTitleBar.cpp @@ -765,24 +765,35 @@ void CDockAreaTitleBar::contextMenuEvent(QContextMenuEvent* ev) return; } - const bool isAutoHide = d->DockArea->isAutoHide(); + auto Menu = buildContextMenu(nullptr); + Menu->exec(ev->globalPos()); + delete Menu; +} + +QMenu* CDockAreaTitleBar::buildContextMenu(QMenu *Menu) +{ + const bool isAutoHide = d->DockArea->isAutoHide(); const bool isTopLevelArea = d->DockArea->isTopLevelArea(); QAction* Action; - QMenu Menu(this); - if (!isTopLevelArea) + if (Menu == nullptr) + { + Menu = new QMenu(this); + } + + if (!isTopLevelArea) { - Action = Menu.addAction(isAutoHide ? tr("Detach") : tr("Detach Group"), + Action = Menu->addAction(isAutoHide ? tr("Detach") : tr("Detach Group"), this, SLOT(onUndockButtonClicked())); Action->setEnabled(d->DockArea->features().testFlag(CDockWidget::DockWidgetFloatable)); if (CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled)) { - Action = Menu.addAction(isAutoHide ? tr("Unpin (Dock)") : tr("Pin Group"), this, SLOT(onAutoHideDockAreaActionClicked())); + Action = Menu->addAction(isAutoHide ? tr("Unpin (Dock)") : tr("Pin Group"), this, SLOT(onAutoHideDockAreaActionClicked())); auto AreaIsPinnable = d->DockArea->features().testFlag(CDockWidget::DockWidgetPinnable); Action->setEnabled(AreaIsPinnable); if (!isAutoHide) { - auto menu = Menu.addMenu(tr("Pin Group To...")); + auto menu = Menu->addMenu(tr("Pin Group To...")); menu->setEnabled(AreaIsPinnable); d->createAutoHideToAction(tr("Top"), SideBarTop, menu); d->createAutoHideToAction(tr("Left"), SideBarLeft, menu); @@ -790,28 +801,27 @@ void CDockAreaTitleBar::contextMenuEvent(QContextMenuEvent* ev) d->createAutoHideToAction(tr("Bottom"), SideBarBottom, menu); } } - Menu.addSeparator(); + Menu->addSeparator(); } if (isAutoHide) { - Action = Menu.addAction(tr("Minimize"), this, SLOT(minimizeAutoHideContainer())); - Action = Menu.addAction(tr("Close"), this, SLOT(onAutoHideCloseActionTriggered())); + Action = Menu->addAction(tr("Minimize"), this, SLOT(minimizeAutoHideContainer())); + Action = Menu->addAction(tr("Close"), this, SLOT(onAutoHideCloseActionTriggered())); } else { - Action = Menu.addAction(isAutoHide ? tr("Close") : tr("Close Group"), this, SLOT(onCloseButtonClicked())); + Action = Menu->addAction(isAutoHide ? tr("Close") : tr("Close Group"), this, SLOT(onCloseButtonClicked())); } Action->setEnabled(d->DockArea->features().testFlag(CDockWidget::DockWidgetClosable)); if (!isAutoHide && !isTopLevelArea) { - Action = Menu.addAction(tr("Close Other Groups"), d->DockArea, SLOT(closeOtherAreas())); + Action = Menu->addAction(tr("Close Other Groups"), d->DockArea, SLOT(closeOtherAreas())); } - Menu.exec(ev->globalPos()); + return Menu; } - //============================================================================ void CDockAreaTitleBar::insertWidget(int index, QWidget *widget) { diff --git a/src/DockAreaTitleBar.h b/src/DockAreaTitleBar.h index 6f07eea8..56820c4b 100644 --- a/src/DockAreaTitleBar.h +++ b/src/DockAreaTitleBar.h @@ -249,6 +249,20 @@ public Q_SLOTS: */ bool isAutoHide() const; + /** + * Fills the provided menu with standard entries. If a nullptr is passed, a + * new menu is created and filled with standard entries. + * This function is called from the actual version of contextMenuEvent, but + * can be called from any code. Caller is responsible of deleting the created + * object. + * + * @param menu The QMenu to fill with standard entries. If nullptr, a new + * QMenu will be created. + * @return The filled QMenu, either the provided one or a newly created one if + * nullptr was passed. + */ + virtual QMenu *buildContextMenu(QMenu *); + Q_SIGNALS: /** * This signal is emitted if a tab in the tab bar is clicked by the user diff --git a/src/DockWidgetTab.cpp b/src/DockWidgetTab.cpp index 654c000a..1380288b 100644 --- a/src/DockWidgetTab.cpp +++ b/src/DockWidgetTab.cpp @@ -529,26 +529,34 @@ void CDockWidgetTab::contextMenuEvent(QContextMenuEvent* ev) return; } + auto Menu = buildContextMenu(nullptr); d->saveDragStartMousePosition(ev->globalPos()); + Menu->exec(ev->globalPos()); +} +QMenu* CDockWidgetTab::buildContextMenu(QMenu *Menu) +{ + if (Menu == nullptr) { + Menu = new QMenu(this); + } + const bool isFloatable = d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable); const bool isNotOnlyTabInContainer = !d->DockArea->dockContainer()->hasTopLevelDockWidget(); const bool isTopLevelArea = d->DockArea->isTopLevelArea(); const bool isDetachable = isFloatable && isNotOnlyTabInContainer; QAction* Action; - QMenu Menu(this); if (!isTopLevelArea) { - Action = Menu.addAction(tr("Detach"), this, SLOT(detachDockWidget())); + Action = Menu->addAction(tr("Detach"), this, SLOT(detachDockWidget())); Action->setEnabled(isDetachable); if (CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled)) { - Action = Menu.addAction(tr("Pin"), this, SLOT(autoHideDockWidget())); + Action = Menu->addAction(tr("Pin"), this, SLOT(autoHideDockWidget())); auto IsPinnable = d->DockWidget->features().testFlag(CDockWidget::DockWidgetPinnable); Action->setEnabled(IsPinnable); - auto menu = Menu.addMenu(tr("Pin To...")); + auto menu = Menu->addMenu(tr("Pin To...")); menu->setEnabled(IsPinnable); d->createAutoHideToAction(tr("Top"), SideBarTop, menu); d->createAutoHideToAction(tr("Left"), SideBarLeft, menu); @@ -557,17 +565,16 @@ void CDockWidgetTab::contextMenuEvent(QContextMenuEvent* ev) } } - Menu.addSeparator(); - Action = Menu.addAction(tr("Close"), this, SIGNAL(closeRequested())); + Menu->addSeparator(); + Action = Menu->addAction(tr("Close"), this, SIGNAL(closeRequested())); Action->setEnabled(isClosable()); if (d->DockArea->openDockWidgetsCount() > 1) { - Action = Menu.addAction(tr("Close Others"), this, SIGNAL(closeOtherTabsRequested())); + Action = Menu->addAction(tr("Close Others"), this, SIGNAL(closeOtherTabsRequested())); } - Menu.exec(ev->globalPos()); -} - + return Menu; +} //============================================================================ bool CDockWidgetTab::isActiveTab() const { diff --git a/src/DockWidgetTab.h b/src/DockWidgetTab.h index 9db14950..02781aff 100644 --- a/src/DockWidgetTab.h +++ b/src/DockWidgetTab.h @@ -186,6 +186,20 @@ private Q_SLOTS: */ eDragState dragState() const; + /** + * Fills the provided menu with standard entries. If a nullptr is passed, a + * new menu is created and filled with standard entries. + * This function is called from the actual version of contextMenuEvent, but + * can be called from any code. Caller is responsible of deleting the created + * object. + * + * @param menu The QMenu to fill with standard entries. If nullptr, a new + * QMenu will be created. + * @return The filled QMenu, either the provided one or a newly created one if + * nullptr was passed. + */ + virtual QMenu *buildContextMenu(QMenu *); + public Q_SLOTS: virtual void setVisible(bool visible) override;