From 4d1b3756f327c7699087d4d268eebb8d7e2619ac Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Mon, 6 May 2024 23:34:09 +0200 Subject: [PATCH] Gui: Add our ToolBarArea enumeration This refactors implementation of toolbars in menu / status bar a bit. It introduces enum with all possible areas like it is in Qt that can be later used to decide what to do based on toolbar placement. --- src/Gui/ToolBarManager.cpp | 252 ++++++++++++++++++++++++------------- src/Gui/ToolBarManager.h | 27 +++- 2 files changed, 187 insertions(+), 92 deletions(-) diff --git a/src/Gui/ToolBarManager.cpp b/src/Gui/ToolBarManager.cpp index a69d283a7b..c3de923293 100644 --- a/src/Gui/ToolBarManager.cpp +++ b/src/Gui/ToolBarManager.cpp @@ -167,11 +167,13 @@ QList ToolBarItem::getItems() const namespace Gui { -class ToolBarArea : public QWidget +class ToolBarAreaWidget : public QWidget { using inherited = QWidget; + public: - ToolBarArea(QWidget *parent, + ToolBarAreaWidget(QWidget *parent, + ToolBarArea area, const ParameterGrp::handle& hParam, boost::signals2::scoped_connection &conn, QTimer *timer = nullptr) @@ -179,34 +181,45 @@ public: , _sizingTimer(timer) , _hParam(hParam) , _conn(conn) + , _area(area) { _layout = new QHBoxLayout(this); _layout->setContentsMargins(QMargins()); } - void addWidget(QWidget *w) + void addWidget(QWidget *widget) { - if (_layout->indexOf(w) < 0) { - _layout->addWidget(w); - adjustParent(); - QString name = w->objectName(); - if (!name.isEmpty()) { - Base::ConnectionBlocker block(_conn); - _hParam->SetInt(w->objectName().toUtf8().constData(), _layout->count()-1); - } + if (_layout->indexOf(widget) > 0) { + return; + } + + _layout->addWidget(widget); + adjustParent(); + + QString name = widget->objectName(); + + if (!name.isEmpty()) { + Base::ConnectionBlocker block(_conn); + _hParam->SetInt(widget->objectName().toUtf8().constData(), _layout->count() - 1); } } - void insertWidget(int idx, QWidget *w) + void insertWidget(int index, QWidget *widget) { - int index = _layout->indexOf(w); - if (index == idx) { + int currentIndex = _layout->indexOf(widget); + + // we are inserting widget at the same place, this is no-op + if (currentIndex == index) { return; } - if (index > 0) { - _layout->removeWidget(w); + + // widget already exists in the area, we need to first remove it and then recreate + if (currentIndex > 0) { + _layout->removeWidget(widget); } - _layout->insertWidget(idx, w); + + _layout->insertWidget(index, widget); + adjustParent(); saveState(); } @@ -218,20 +231,23 @@ public: } } - void removeWidget(QWidget *w) + void removeWidget(QWidget *widget) { - _layout->removeWidget(w); - QString name = w->objectName(); + _layout->removeWidget(widget); + + QString name = widget->objectName(); if (!name.isEmpty()) { Base::ConnectionBlocker block(_conn); _hParam->RemoveInt(name.toUtf8().constData()); } + adjustParent(); } QWidget *widgetAt(int index) const { auto item = _layout->itemAt(index); + return item ? item->widget() : nullptr; } @@ -240,9 +256,14 @@ public: return _layout->count(); } - int indexOf(QWidget *w) const + int indexOf(QWidget *widget) const { - return _layout->indexOf(w); + return _layout->indexOf(widget); + } + + ToolBarArea area() const + { + return _area; } template @@ -250,10 +271,12 @@ public: { for (int i = 0, c = _layout->count(); i < c; ++i) { auto toolbar = qobject_cast(widgetAt(i)); + if (!toolbar || toolbar->objectName().isEmpty() || toolbar->objectName().startsWith(QStringLiteral("*"))) { continue; } + func(toolbar, i, this); } } @@ -261,10 +284,12 @@ public: void saveState() { Base::ConnectionBlocker block(_conn); + for (auto &v : _hParam->GetIntMap()) { _hParam->RemoveInt(v.first.c_str()); } - foreachToolBar([this](QToolBar *toolbar, int idx, ToolBarArea*) { + + foreachToolBar([this](QToolBar *toolbar, int idx, ToolBarAreaWidget*) { _hParam->SetInt(toolbar->objectName().toUtf8().constData(), idx); }); } @@ -281,17 +306,19 @@ public: for (const auto &[name, visible] : _hParam->GetBoolMap()) { auto widget = findChild(QString::fromUtf8(name.c_str())); + if (widget) { widget->setVisible(visible); } } - } + }; private: QHBoxLayout *_layout; QPointer _sizingTimer; ParameterGrp::handle _hParam; boost::signals2::scoped_connection &_conn; + ToolBarArea _area; }; } // namespace Gui @@ -343,10 +370,10 @@ void ToolBarManager::setupStatusBar() { if (auto sb = getMainWindow()->statusBar()) { sb->installEventFilter(this); - statusBarArea = new ToolBarArea(sb, hStatusBar, connParam); - statusBarArea->setObjectName(QStringLiteral("StatusBarArea")); - sb->insertPermanentWidget(2, statusBarArea); - statusBarArea->show(); + statusBarAreaWidget = new ToolBarAreaWidget(sb, ToolBarArea::StatusBarToolBarArea, hStatusBar, connParam); + statusBarAreaWidget->setObjectName(QStringLiteral("StatusBarArea")); + sb->insertPermanentWidget(2, statusBarAreaWidget); + statusBarAreaWidget->show(); } } @@ -354,14 +381,14 @@ void ToolBarManager::setupMenuBar() { if (auto mb = getMainWindow()->menuBar()) { mb->installEventFilter(this); - menuBarLeftArea = new ToolBarArea(mb, hMenuBarLeft, connParam, &menuBarTimer); - menuBarLeftArea->setObjectName(QStringLiteral("MenuBarLeftArea")); - mb->setCornerWidget(menuBarLeftArea, Qt::TopLeftCorner); - menuBarLeftArea->show(); - menuBarRightArea = new ToolBarArea(mb, hMenuBarRight, connParam, &menuBarTimer); - menuBarRightArea->setObjectName(QStringLiteral("MenuBarRightArea")); - mb->setCornerWidget(menuBarRightArea, Qt::TopRightCorner); - menuBarRightArea->show(); + menuBarLeftAreaWidget = new ToolBarAreaWidget(mb, ToolBarArea::LeftMenuToolBarArea, hMenuBarLeft, connParam, &menuBarTimer); + menuBarLeftAreaWidget->setObjectName(QStringLiteral("MenuBarLeftArea")); + mb->setCornerWidget(menuBarLeftAreaWidget, Qt::TopLeftCorner); + menuBarLeftAreaWidget->show(); + menuBarRightAreaWidget = new ToolBarAreaWidget(mb, ToolBarArea::RightMenuToolBarArea, hMenuBarRight, connParam, &menuBarTimer); + menuBarRightAreaWidget->setObjectName(QStringLiteral("MenuBarRightArea")); + mb->setCornerWidget(menuBarRightAreaWidget, Qt::TopRightCorner); + menuBarRightAreaWidget->show(); } } @@ -440,6 +467,38 @@ void ToolBarManager::setupMenuBarTimer() }); } +ToolBarArea ToolBarManager::toolBarArea(QWidget *widget) const +{ + if (auto toolBar = qobject_cast(widget)) { + if (toolBar->isFloating()) { + return ToolBarArea::NoToolBarArea; + } + + auto qtToolBarArea = getMainWindow()->toolBarArea(toolBar); + switch (qtToolBarArea) { + case Qt::LeftToolBarArea: + return ToolBarArea::LeftToolBarArea; + case Qt::RightToolBarArea: + return ToolBarArea::RightToolBarArea; + case Qt::TopToolBarArea: + return ToolBarArea::TopToolBarArea; + case Qt::BottomToolBarArea: + return ToolBarArea::BottomToolBarArea; + default: + // no-op + break; + } + } + + for (auto &areaWidget : { statusBarAreaWidget, menuBarLeftAreaWidget, menuBarRightAreaWidget }) { + if (areaWidget->indexOf(widget) >= 0) { + return areaWidget->area(); + } + } + + return ToolBarArea::NoToolBarArea; +} + namespace { QPointer createActionWidget() { @@ -470,7 +529,7 @@ int ToolBarManager::toolBarIconSize(QWidget *widget) const { int s = _toolBarIconSize; if (widget) { - if (widget->parentWidget() == statusBarArea) { + if (widget->parentWidget() == statusBarAreaWidget) { if (_statusBarIconSize > 0) { s = _statusBarIconSize; } @@ -478,8 +537,8 @@ int ToolBarManager::toolBarIconSize(QWidget *widget) const s *= 0.6; } } - else if (widget->parentWidget() == menuBarLeftArea - || widget->parentWidget() == menuBarRightArea) { + else if (widget->parentWidget() == menuBarLeftAreaWidget + || widget->parentWidget() == menuBarRightAreaWidget) { if (_menuBarIconSize > 0) { s = _menuBarIconSize; } @@ -508,11 +567,11 @@ void ToolBarManager::setToolBarIconSize(QToolBar *toolbar) { int s = toolBarIconSize(toolbar); toolbar->setIconSize(QSize(s, s)); - if (toolbar->parentWidget() == menuBarLeftArea) { - menuBarLeftArea->adjustParent(); + if (toolbar->parentWidget() == menuBarLeftAreaWidget) { + menuBarLeftAreaWidget->adjustParent(); } - else if (toolbar->parentWidget() == menuBarRightArea) { - menuBarRightArea->adjustParent(); + else if (toolbar->parentWidget() == menuBarRightAreaWidget) { + menuBarRightAreaWidget->adjustParent(); } } @@ -751,9 +810,9 @@ void ToolBarManager::restoreState() const setMovable(!areToolBarsLocked()); - statusBarArea->restoreState(sbToolBars); - menuBarRightArea->restoreState(mbRightToolBars); - menuBarLeftArea->restoreState(mbLeftToolBars); + statusBarAreaWidget->restoreState(sbToolBars); + menuBarRightAreaWidget->restoreState(mbRightToolBars); + menuBarLeftAreaWidget->restoreState(mbLeftToolBars); } bool ToolBarManager::addToolBarToArea(QObject *source, QMouseEvent *ev) @@ -777,7 +836,7 @@ bool ToolBarManager::addToolBarToArea(QObject *source, QMouseEvent *ev) } static QPointer tbPlaceholder; - static QPointer lastArea; + static QPointer lastArea; static int tbIndex = -1; if (ev->type() == QEvent::MouseMove) { if (tb->orientation() != Qt::Horizontal @@ -799,11 +858,11 @@ bool ToolBarManager::addToolBarToArea(QObject *source, QMouseEvent *ev) } QPoint pos = QCursor::pos(); - ToolBarArea *area = nullptr; + ToolBarAreaWidget *area = nullptr; if (statusBar) { QRect rect(statusBar->mapToGlobal(QPoint(0,0)), statusBar->size()); if (rect.contains(pos)) { - area = statusBarArea; + area = statusBarAreaWidget; } } if (!area) { @@ -813,10 +872,10 @@ bool ToolBarManager::addToolBarToArea(QObject *source, QMouseEvent *ev) QRect rect(menuBar->mapToGlobal(QPoint(0,0)), menuBar->size()); if (rect.contains(pos)) { if (pos.x() - rect.left() < menuBar->width()/2) { - area = menuBarLeftArea; + area = menuBarLeftAreaWidget; } else { - area = menuBarRightArea; + area = menuBarRightAreaWidget; } } else { @@ -834,11 +893,11 @@ bool ToolBarManager::addToolBarToArea(QObject *source, QMouseEvent *ev) int idx = 0; for (int c = area->count(); idx < c ;++idx) { - auto w = area->widgetAt(idx); - if (!w || w->isHidden()) { + auto widget = area->widgetAt(idx); + if (!widget || widget->isHidden()) { continue; } - int p = w->mapToGlobal(w->rect().center()).x(); + int p = widget->mapToGlobal(widget->rect().center()).x(); if (pos.x() < p) { break; } @@ -887,40 +946,60 @@ bool ToolBarManager::addToolBarToArea(QObject *source, QMouseEvent *ev) return false; } -void ToolBarManager::populateUndockMenu(QMenu *menu, ToolBarArea *area) +void ToolBarManager::populateUndockMenu(QMenu *menu, ToolBarAreaWidget *area) { menu->setTitle(tr("Undock toolbars")); - auto tooltip = QObject::tr("Undock from status bar"); - auto addMenuUndockItem = [&](QToolBar *toolbar, int, ToolBarArea *area) { - auto *action = toolbar->toggleViewAction(); + auto tooltip = QObject::tr("Undock from toolbar area"); + + auto addMenuUndockItem = [&](QToolBar *toolbar, int, ToolBarAreaWidget *area) { + auto toggleViewAction = toolbar->toggleViewAction(); auto undockAction = new QAction(menu); - undockAction->setText(action->text()); + + undockAction->setText(toggleViewAction->text()); undockAction->setToolTip(tooltip); + menu->addAction(undockAction); QObject::connect(undockAction, &QAction::triggered, [area, toolbar]() { if (toolbar->parentWidget() == getMainWindow()) { return; } + auto pos = toolbar->mapToGlobal(QPoint(0, 0)); - QSignalBlocker blocker(toolbar); - area->removeWidget(toolbar); - getMainWindow()->addToolBar(toolbar); - toolbar->setWindowFlags(Qt::Tool - | Qt::FramelessWindowHint - | Qt::X11BypassWindowManagerHint); - toolbar->move(pos.x(), pos.y()-toolbar->height()-10); - toolbar->adjustSize(); - toolbar->setVisible(true); + auto yOffset = toolbar->height(); + + // if widget is on the bottom move it up instead + if (area->area() == Gui::ToolBarArea::StatusBarToolBarArea) { + yOffset *= -1; + } + + { + // Block signals caused by manually floating the widget + QSignalBlocker blocker(toolbar); + + area->removeWidget(toolbar); + getMainWindow()->addToolBar(toolbar); + + // this will make toolbar floating, there is no better way to do that. + toolbar->setWindowFlags(Qt::Tool + | Qt::FramelessWindowHint + | Qt::X11BypassWindowManagerHint); + toolbar->move(pos.x(), pos.y() + yOffset); + toolbar->adjustSize(); + toolbar->setVisible(true); + } + + // but don't block actual information about widget being floated Q_EMIT toolbar->topLevelChanged(true); }); }; - if (area) { + + if (area) { area->foreachToolBar(addMenuUndockItem); } else { - statusBarArea->foreachToolBar(addMenuUndockItem); - menuBarLeftArea->foreachToolBar(addMenuUndockItem); - menuBarRightArea->foreachToolBar(addMenuUndockItem); + statusBarAreaWidget->foreachToolBar(addMenuUndockItem); + menuBarLeftAreaWidget->foreachToolBar(addMenuUndockItem); + menuBarRightAreaWidget->foreachToolBar(addMenuUndockItem); } } @@ -929,13 +1008,13 @@ bool ToolBarManager::showContextMenu(QObject *source) QMenu menu; QMenu menuUndock; QLayout* layout = nullptr; - ToolBarArea* area = nullptr; + ToolBarAreaWidget* area = nullptr; if (getMainWindow()->statusBar() == source) { - area = statusBarArea; + area = statusBarAreaWidget; layout = findLayoutOfObject(source, area); } else if (getMainWindow()->menuBar() == source) { - area = findToolBarArea(); + area = findToolBarAreaWidget(); if (!area) { return false; } @@ -944,7 +1023,7 @@ bool ToolBarManager::showContextMenu(QObject *source) return false; } - auto addMenuVisibleItem = [&](QToolBar *toolbar, int, ToolBarArea *) { + auto addMenuVisibleItem = [&](QToolBar *toolbar, int, ToolBarAreaWidget *) { auto action = toolbar->toggleViewAction(); if ((action->isVisible() || toolbar->isVisible()) && action->text().size()) { action->setVisible(true); @@ -980,18 +1059,19 @@ QLayout* ToolBarManager::findLayoutOfObject(QObject* source, QWidget* area) cons return layout; } -ToolBarArea* ToolBarManager::findToolBarArea() const +ToolBarAreaWidget* ToolBarManager::findToolBarAreaWidget() const { - ToolBarArea* area = nullptr; + ToolBarAreaWidget* area = nullptr; + QPoint pos = QCursor::pos(); - QRect rect(menuBarLeftArea->mapToGlobal(QPoint(0,0)), menuBarLeftArea->size()); + QRect rect(menuBarLeftAreaWidget->mapToGlobal(QPoint(0,0)), menuBarLeftAreaWidget->size()); if (rect.contains(pos)) { - area = menuBarLeftArea; + area = menuBarLeftAreaWidget; } else { - rect = QRect(menuBarRightArea->mapToGlobal(QPoint(0,0)), menuBarRightArea->size()); + rect = QRect(menuBarRightAreaWidget->mapToGlobal(QPoint(0,0)), menuBarRightAreaWidget->size()); if (rect.contains(pos)) { - area = menuBarRightArea; + area = menuBarRightAreaWidget; } } @@ -1043,7 +1123,7 @@ bool ToolBarManager::eventFilter(QObject *source, QEvent *ev) case QEvent::Hide: if (auto toolbar = qobject_cast(source)) { auto parent = toolbar->parentWidget(); - if (parent == menuBarLeftArea || parent == menuBarRightArea) { + if (parent == menuBarLeftAreaWidget || parent == menuBarRightAreaWidget) { menuBarTimer.start(10); } } @@ -1131,9 +1211,9 @@ QList ToolBarManager::toolBars() const auto parent = it->parentWidget(); if (parent == mw || parent == mw->statusBar() - || parent == statusBarArea - || parent == menuBarLeftArea - || parent == menuBarRightArea) { + || parent == statusBarAreaWidget + || parent == menuBarLeftAreaWidget + || parent == menuBarRightAreaWidget) { tb.push_back(it); it->installEventFilter(const_cast(this)); } diff --git a/src/Gui/ToolBarManager.h b/src/Gui/ToolBarManager.h index c2e695bdf3..f5ee13cc6f 100644 --- a/src/Gui/ToolBarManager.h +++ b/src/Gui/ToolBarManager.h @@ -41,7 +41,20 @@ class QToolBar; namespace Gui { -class ToolBarArea; +// Qt treats area as Flag so in theory toolbar could be in multiple areas at once. +// We don't do that here so simple enum should suffice. +enum GuiExport ToolBarArea { + NoToolBarArea, + LeftToolBarArea, + RightToolBarArea, + TopToolBarArea, + BottomToolBarArea, + LeftMenuToolBarArea, + RightMenuToolBarArea, + StatusBarToolBarArea, +}; + +class ToolBarAreaWidget; class GuiExport ToolBarItem { @@ -123,7 +136,9 @@ public: int toolBarIconSize(QWidget *widget=nullptr) const; void setupToolBarIconSize(); - void populateUndockMenu(QMenu *menu, ToolBarArea *area = nullptr); + void populateUndockMenu(QMenu *menu, ToolBarAreaWidget *area = nullptr); + + ToolBarArea toolBarArea(QWidget* toolBar) const; protected: void setup(ToolBarItem*, QToolBar*) const; @@ -158,7 +173,7 @@ private: void setupMenuBarTimer(); void addToMenu(QLayout* layout, QWidget* area, QMenu* menu); QLayout* findLayoutOfObject(QObject* source, QWidget* area) const; - ToolBarArea* findToolBarArea() const; + ToolBarAreaWidget* findToolBarAreaWidget() const; private: QStringList toolbarNames; @@ -169,9 +184,9 @@ private: QTimer sizeTimer; QTimer resizeTimer; boost::signals2::scoped_connection connParam; - ToolBarArea *statusBarArea = nullptr; - ToolBarArea *menuBarLeftArea = nullptr; - ToolBarArea *menuBarRightArea = nullptr; + ToolBarAreaWidget *statusBarAreaWidget = nullptr; + ToolBarAreaWidget *menuBarLeftAreaWidget = nullptr; + ToolBarAreaWidget *menuBarRightAreaWidget = nullptr; ParameterGrp::handle hGeneral; ParameterGrp::handle hPref; ParameterGrp::handle hStatusBar;