From 82f6b68307dd8f73fa5c4c15e08611769d61b76e Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 5 May 2024 13:42:37 +0200 Subject: [PATCH] Gui: Use RTL layout when Wb Tab Bar is placed in right corner Right corner is placed to the right edge of screen, so its natural growth occours on the left side. Basically it is Right to Left order and so in that case the "end" is actually on left and so TabBar should grow in that direction. Unfortunately it is not possible to simply use RTL Qt feature to handle that case as it would result in reverse order of workbenches (people will still read it in LTR order) and icons on the right which is not wanted. That's custom support is introduced. --- src/Gui/Action.cpp | 9 +- src/Gui/WorkbenchSelector.cpp | 162 +++++++++++++++++++++++++--------- src/Gui/WorkbenchSelector.h | 25 +++++- 3 files changed, 148 insertions(+), 48 deletions(-) diff --git a/src/Gui/Action.cpp b/src/Gui/Action.cpp index eee53308af..3806703c8f 100644 --- a/src/Gui/Action.cpp +++ b/src/Gui/Action.cpp @@ -632,15 +632,16 @@ void WorkbenchGroup::addTo(QWidget *widget) if (widget->inherits("QToolBar")) { ParameterGrp::handle hGrp; hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Workbenches"); - QWidget* wbSel; + + QWidget* workbenchSelectorWidget; if (hGrp->GetInt("WorkbenchSelectorType", 0) == 0) { - wbSel = new WorkbenchComboBox(this, widget); + workbenchSelectorWidget = new WorkbenchComboBox(this, widget); } else { - wbSel = new WorkbenchTabWidget(this, widget); + workbenchSelectorWidget = new WorkbenchTabWidget(this, widget); } - static_cast(widget)->addWidget(wbSel); + static_cast(widget)->addWidget(workbenchSelectorWidget); } else if (widget->inherits("QMenu")) { auto menu = qobject_cast(widget); diff --git a/src/Gui/WorkbenchSelector.cpp b/src/Gui/WorkbenchSelector.cpp index dfec90806b..dc8ca097a4 100644 --- a/src/Gui/WorkbenchSelector.cpp +++ b/src/Gui/WorkbenchSelector.cpp @@ -80,15 +80,17 @@ void WorkbenchComboBox::refreshList(QList actionList) ParameterGrp::handle hGrp; hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Workbenches"); - int itemStyleIndex = hGrp->GetInt("WorkbenchSelectorItem", 0); + + auto itemStyle = static_cast(hGrp->GetInt("WorkbenchSelectorItem", 0)); for (QAction* action : actionList) { QIcon icon = action->icon(); - if (icon.isNull() || itemStyleIndex == 2) { + + if (icon.isNull() || itemStyle == WorkbenchItemStyle::TextOnly) { addItem(action->text()); } - else if (itemStyleIndex == 1) { - addItem(icon, QString::fromLatin1("")); + else if (itemStyle == WorkbenchItemStyle::IconOnly) { + addItem(icon, {}); // empty string to ensure that only icon is displayed } else { addItem(icon, action->text()); @@ -149,7 +151,7 @@ WorkbenchTabWidget::WorkbenchTabWidget(WorkbenchGroup* aGroup, QWidget* parent) tabBar->setIconSize(QSize(16, 16)); updateWorkbenchList(); - + connect(aGroup, &WorkbenchGroup::workbenchListRefreshed, this, &WorkbenchTabWidget::updateWorkbenchList); connect(aGroup->groupAction(), &QActionGroup::triggered, this, &WorkbenchTabWidget::handleWorkbenchSelection); connect(tabBar, &QTabBar::currentChanged, this, &WorkbenchTabWidget::handleTabChange); @@ -160,7 +162,54 @@ WorkbenchTabWidget::WorkbenchTabWidget(WorkbenchGroup* aGroup, QWidget* parent) } } -void WorkbenchTabWidget::updateLayout() +inline Qt::LayoutDirection WorkbenchTabWidget::direction() const +{ + return _direction; +} + +void WorkbenchTabWidget::setDirection(Qt::LayoutDirection direction) +{ + _direction = direction; + + Q_EMIT directionChanged(direction); +} + +inline int WorkbenchTabWidget::temporaryWorkbenchTabIndex() const +{ + if (direction() == Qt::RightToLeft) { + return 0; + } + + int nextTabIndex = tabBar->count(); + + return temporaryWorkbenchAction ? nextTabIndex - 1 : nextTabIndex; +} + +QAction* WorkbenchTabWidget::workbenchActivateActionByTabIndex(int tabIndex) const +{ + if (temporaryWorkbenchAction && tabIndex == temporaryWorkbenchTabIndex()) { + return temporaryWorkbenchAction; + } + + auto it = tabIndexToAction.find(tabIndex); + + if (it != tabIndexToAction.end()) { + return it->second; + } + + return nullptr; +} + +int WorkbenchTabWidget::tabIndexForWorkbenchActivateAction(QAction* workbenchActivateAction) const +{ + if (workbenchActivateAction == temporaryWorkbenchAction) { + return temporaryWorkbenchTabIndex(); + } + + return actionToTabIndex.at(workbenchActivateAction); +} + +void WorkbenchTabWidget::updateLayout() { if (!parentWidget()) { setToolBarArea(Gui::ToolBarArea::TopToolBarArea); @@ -180,30 +229,47 @@ void WorkbenchTabWidget::updateLayout() auto toolBarArea = Gui::ToolBarManager::getInstance()->toolBarArea(parentWidget()); setToolBarArea(toolBarArea); + + tabBar->setSelectionBehaviorOnRemove( + direction() == Qt::LeftToRight + ? QTabBar::SelectLeftTab + : QTabBar::SelectRightTab + ); } -void WorkbenchTabWidget::handleWorkbenchSelection(QAction* selectedWorkbenchAction) +void WorkbenchTabWidget::handleWorkbenchSelection(QAction* selectedWorkbenchAction) { if (wbActionGroup->getDisabledWbActions().contains(selectedWorkbenchAction)) { - if (additionalWorkbenchAction == selectedWorkbenchAction) { + if (temporaryWorkbenchAction == selectedWorkbenchAction) { return; } - if (additionalWorkbenchAction) { - tabBar->removeTab(tabBar->count() - 1); - } - - additionalWorkbenchAction = selectedWorkbenchAction; - - addWorkbenchTab(selectedWorkbenchAction); - tabBar->setCurrentIndex(tabBar->count() - 1); - - return; + setTemporaryWorkbenchTab(selectedWorkbenchAction); } updateLayout(); - tabBar->setCurrentIndex(actionToTabIndex[selectedWorkbenchAction]); + tabBar->setCurrentIndex(tabIndexForWorkbenchActivateAction(selectedWorkbenchAction)); +} + +void WorkbenchTabWidget::setTemporaryWorkbenchTab(QAction* workbenchActivateAction) +{ + auto temporaryTabIndex = temporaryWorkbenchTabIndex(); + + if (temporaryWorkbenchAction) { + temporaryWorkbenchAction = nullptr; + tabBar->removeTab(temporaryTabIndex); + } + + temporaryWorkbenchAction = workbenchActivateAction; + + if (!workbenchActivateAction) { + return; + } + + addWorkbenchTab(workbenchActivateAction, temporaryTabIndex); + + adjustSize(); } void WorkbenchTabWidget::handleTabChange(int selectedTabIndex) @@ -214,13 +280,12 @@ void WorkbenchTabWidget::handleTabChange(int selectedTabIndex) return; } - if (tabIndexToAction.find(selectedTabIndex) != tabIndexToAction.end()) { - tabIndexToAction[selectedTabIndex]->trigger(); + if (auto workbenchActivateAction = workbenchActivateActionByTabIndex(selectedTabIndex)) { + workbenchActivateAction->trigger(); } - if (selectedTabIndex != tabBar->count() - 1 && additionalWorkbenchAction) { - tabBar->removeTab(tabBar->count() - 1); - additionalWorkbenchAction = nullptr; + if (selectedTabIndex != temporaryWorkbenchTabIndex()) { + setTemporaryWorkbenchTab(nullptr); } adjustSize(); @@ -229,11 +294,12 @@ void WorkbenchTabWidget::handleTabChange(int selectedTabIndex) void WorkbenchTabWidget::updateWorkbenchList() { // As clearing and adding tabs can cause changing current tab in QTabBar. - // This in turn will cause workbench to change, so we need to prevent + // This in turn will cause workbench to change, so we need to prevent // processing of such events until the QTabBar is fully prepared. Base::StateLocker lock(isInitializing); actionToTabIndex.clear(); + tabIndexToAction.clear(); // tabs->clear() (QTabBar has no clear) for (int i = tabBar->count() - 1; i >= 0; --i) { @@ -244,34 +310,39 @@ void WorkbenchTabWidget::updateWorkbenchList() addWorkbenchTab(action); } - if (additionalWorkbenchAction != nullptr) { - addWorkbenchTab(additionalWorkbenchAction); + if (temporaryWorkbenchAction != nullptr) { + setTemporaryWorkbenchTab(temporaryWorkbenchAction); } buildPrefMenu(); adjustSize(); } -void WorkbenchTabWidget::addWorkbenchTab(QAction* action) +int WorkbenchTabWidget::addWorkbenchTab(QAction* action, int tabIndex) { ParameterGrp::handle hGrp; hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Workbenches"); - int itemStyleIndex = hGrp->GetInt("WorkbenchSelectorItem", 0); + auto itemStyle = static_cast(hGrp->GetInt("WorkbenchSelectorItem", 0)); - auto tabIndex = tabBar->count(); + // if tabIndex is negative we assume that tab must be placed at the end of tabBar (default behavior) + if (tabIndex < 0) { + tabIndex = tabBar->count(); + } - actionToTabIndex[action] = tabIndex; - tabIndexToAction[tabIndex] = action; + // for the maps we consider order in which tabs have been added + // that's why here we use tabBar->count() instead of tabIndex + actionToTabIndex[action] = tabBar->count(); + tabIndexToAction[tabBar->count()] = action; QIcon icon = action->icon(); - if (icon.isNull() || itemStyleIndex == 2) { - tabBar->addTab(action->text()); + if (icon.isNull() || itemStyle == WorkbenchItemStyle::TextOnly) { + tabBar->insertTab(tabIndex, action->text()); } - else if (itemStyleIndex == 1) { - tabBar->addTab(icon, QString::fromLatin1("")); + else if (itemStyle == IconOnly) { + tabBar->insertTab(tabIndex, icon, {}); // empty string to ensure only icon is displayed } else { - tabBar->addTab(icon, action->text()); + tabBar->insertTab(tabIndex, icon, action->text()); } tabBar->setTabToolTip(tabIndex, action->toolTip()); @@ -279,6 +350,8 @@ void WorkbenchTabWidget::addWorkbenchTab(QAction* action) if (action->isChecked()) { tabBar->setCurrentIndex(tabIndex); } + + return tabIndex; } void WorkbenchTabWidget::setToolBarArea(Gui::ToolBarArea area) @@ -286,9 +359,10 @@ void WorkbenchTabWidget::setToolBarArea(Gui::ToolBarArea area) ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Workbenches"); switch (area) { - case Gui::ToolBarArea::LeftToolBarArea: + case Gui::ToolBarArea::LeftToolBarArea: case Gui::ToolBarArea::RightToolBarArea: { - layout->setDirection(QBoxLayout::TopToBottom); + setDirection(Qt::LeftToRight); + layout->setDirection(direction() == Qt::LeftToRight ? QBoxLayout::TopToBottom : QBoxLayout::BottomToTop); tabBar->setShape(area == Gui::ToolBarArea::LeftToolBarArea ? QTabBar::RoundedWest : QTabBar::RoundedEast); hGrp->SetASCII("TabBarOrientation", area == Gui::ToolBarArea::LeftToolBarArea ? "West" : "East"); break; @@ -299,13 +373,17 @@ void WorkbenchTabWidget::setToolBarArea(Gui::ToolBarArea area) case Gui::ToolBarArea::LeftMenuToolBarArea: case Gui::ToolBarArea::RightMenuToolBarArea: case Gui::ToolBarArea::StatusBarToolBarArea: { - layout->setDirection(QBoxLayout::LeftToRight); - - bool isTop = + bool isTop = area == Gui::ToolBarArea::TopToolBarArea || area == Gui::ToolBarArea::LeftMenuToolBarArea || area == Gui::ToolBarArea::RightMenuToolBarArea; + bool isRightAligned = + area == Gui::ToolBarArea::RightMenuToolBarArea || + area == Gui::ToolBarArea::StatusBarToolBarArea; + + setDirection(isRightAligned ? Qt::RightToLeft : Qt::LeftToRight); + layout->setDirection(direction() == Qt::LeftToRight ? QBoxLayout::LeftToRight : QBoxLayout::RightToLeft); tabBar->setShape(isTop ? QTabBar::RoundedNorth : QTabBar::RoundedSouth); hGrp->SetASCII("TabBarOrientation", isTop ? "North" : "South"); break; diff --git a/src/Gui/WorkbenchSelector.h b/src/Gui/WorkbenchSelector.h index aae32512e4..b60724fd9b 100644 --- a/src/Gui/WorkbenchSelector.h +++ b/src/Gui/WorkbenchSelector.h @@ -37,6 +37,12 @@ namespace Gui { class WorkbenchGroup; +enum WorkbenchItemStyle { + IconAndText = 0, + IconOnly = 1, + TextOnly = 2 +}; + class GuiExport WorkbenchComboBox : public QComboBox { Q_OBJECT @@ -56,8 +62,15 @@ private: class GuiExport WorkbenchTabWidget : public QWidget { Q_OBJECT + Q_PROPERTY(Qt::LayoutDirection direction READ direction WRITE setDirection NOTIFY directionChanged) - void addWorkbenchTab(QAction* workbenchActivateAction); + int addWorkbenchTab(QAction* workbenchActivateAction, int index = -1); + + void setTemporaryWorkbenchTab(QAction* workbenchActivateAction); + int temporaryWorkbenchTabIndex() const; + + QAction* workbenchActivateActionByTabIndex(int tabIndex) const; + int tabIndexForWorkbenchActivateAction(QAction* workbenchActivateAction) const; public: explicit WorkbenchTabWidget(WorkbenchGroup* aGroup, QWidget* parent = nullptr); @@ -65,6 +78,8 @@ public: void setToolBarArea(Gui::ToolBarArea area); void buildPrefMenu(); + Qt::LayoutDirection direction() const; + void setDirection(Qt::LayoutDirection direction); void adjustSize(); @@ -75,6 +90,9 @@ public Q_SLOTS: void updateLayout(); void updateWorkbenchList(); +Q_SIGNALS: + void directionChanged(const Qt::LayoutDirection&); + private: bool isInitializing = false; @@ -82,8 +100,11 @@ private: QToolButton* moreButton; QTabBar* tabBar; QBoxLayout* layout; + + Qt::LayoutDirection _direction = Qt::LeftToRight; + // this action is used for workbenches that are typically disabled - QAction* additionalWorkbenchAction = nullptr; + QAction* temporaryWorkbenchAction = nullptr; std::map actionToTabIndex; std::map tabIndexToAction;