Merge pull request #13721 from kadet1090/more-at-the-end

Gui: Move more button to the end WB TabBar
This commit is contained in:
Chris Hennes
2024-05-13 11:12:26 -05:00
committed by GitHub
6 changed files with 553 additions and 231 deletions

View File

@@ -627,20 +627,29 @@ WorkbenchGroup::WorkbenchGroup ( Command* pcCmd, QObject * parent )
this, &WorkbenchGroup::onWorkbenchActivated);
}
QAction* WorkbenchGroup::getOrCreateAction(const QString& wbName) {
if (!actionByWorkbenchName.contains(wbName)) {
actionByWorkbenchName[wbName] = new QAction;
}
return actionByWorkbenchName[wbName];
}
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<QToolBar*>(widget)->addWidget(wbSel);
static_cast<QToolBar*>(widget)->addWidget(workbenchSelectorWidget);
}
else if (widget->inherits("QMenu")) {
auto menu = qobject_cast<QMenu*>(widget);
@@ -656,13 +665,13 @@ void WorkbenchGroup::addTo(QWidget *widget)
void WorkbenchGroup::refreshWorkbenchList()
{
QStringList enabled_wbs_list = DlgSettingsWorkbenchesImp::getEnabledWorkbenches();
QStringList enabledWbNames = DlgSettingsWorkbenchesImp::getEnabledWorkbenches();
// Clear the actions.
for (QAction* action : actions()) {
groupAction()->removeAction(action);
delete action;
}
enabledWbsActions.clear();
disabledWbsActions.clear();
@@ -670,12 +679,16 @@ void WorkbenchGroup::refreshWorkbenchList()
// Create action list of enabled wb
int index = 0;
for (const auto& wbName : enabled_wbs_list) {
for (const auto& wbName : enabledWbNames) {
QString name = Application::Instance->workbenchMenuText(wbName);
QPixmap px = Application::Instance->workbenchIcon(wbName);
QString tip = Application::Instance->workbenchToolTip(wbName);
QAction* action = groupAction()->addAction(name);
QAction* action = getOrCreateAction(wbName);
groupAction()->addAction(action);
action->setText(name);
action->setCheckable(true);
action->setData(QVariant(index)); // set the index
action->setObjectName(wbName);
@@ -693,13 +706,17 @@ void WorkbenchGroup::refreshWorkbenchList()
}
// Also create action list of disabled wbs
QStringList disabled_wbs_list = DlgSettingsWorkbenchesImp::getDisabledWorkbenches();
for (const auto& wbName : disabled_wbs_list) {
QStringList disabledWbNames = DlgSettingsWorkbenchesImp::getDisabledWorkbenches();
for (const auto& wbName : disabledWbNames) {
QString name = Application::Instance->workbenchMenuText(wbName);
QPixmap px = Application::Instance->workbenchIcon(wbName);
QString tip = Application::Instance->workbenchToolTip(wbName);
QAction* action = groupAction()->addAction(name);
QAction* action = getOrCreateAction(wbName);
groupAction()->addAction(action);
action->setText(name);
action->setCheckable(true);
action->setData(QVariant(index)); // set the index
action->setObjectName(wbName);

View File

@@ -28,6 +28,7 @@
#include <QAction>
#include <QComboBox>
#include <QKeySequence>
#include <QMap>
#include <FCGlobal.h>
namespace Gui
@@ -188,13 +189,16 @@ class GuiExport WorkbenchGroup : public ActionGroup
{
Q_OBJECT
QAction* getOrCreateAction(const QString& wbName);
public:
/**
* Creates an action for the command \a pcCmd to load the workbench \a name
* when it gets activated.
*/
WorkbenchGroup (Command* pcCmd, QObject * parent);
void addTo (QWidget * widget) override;
WorkbenchGroup(Command* pcCmd, QObject* parent);
void addTo(QWidget * widget) override;
void refreshWorkbenchList();
void slotActivateWorkbench(const char*);
@@ -212,6 +216,8 @@ private:
QList<QAction*> enabledWbsActions;
QList<QAction*> disabledWbsActions;
QMap<QString, QAction*> actionByWorkbenchName;
Q_DISABLE_COPY(WorkbenchGroup)
};

View File

@@ -167,11 +167,13 @@ QList<ToolBarItem*> 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<class FuncT>
@@ -250,10 +271,12 @@ public:
{
for (int i = 0, c = _layout->count(); i < c; ++i) {
auto toolbar = qobject_cast<QToolBar*>(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<QWidget*>(QString::fromUtf8(name.c_str()));
if (widget) {
widget->setVisible(visible);
}
}
}
};
private:
QHBoxLayout *_layout;
QPointer<QTimer> _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<QToolBar*>(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<QWidget> 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();
}
}
@@ -750,9 +809,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)
@@ -776,7 +835,7 @@ bool ToolBarManager::addToolBarToArea(QObject *source, QMouseEvent *ev)
}
static QPointer<OverlayDragFrame> tbPlaceholder;
static QPointer<ToolBarArea> lastArea;
static QPointer<ToolBarAreaWidget> lastArea;
static int tbIndex = -1;
if (ev->type() == QEvent::MouseMove) {
if (tb->orientation() != Qt::Horizontal
@@ -798,11 +857,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) {
@@ -812,10 +871,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 {
@@ -833,11 +892,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;
}
@@ -886,40 +945,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);
}
}
@@ -928,13 +1007,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;
}
@@ -943,7 +1022,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);
@@ -979,18 +1058,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;
}
}
@@ -1042,7 +1122,7 @@ bool ToolBarManager::eventFilter(QObject *source, QEvent *ev)
case QEvent::Hide:
if (auto toolbar = qobject_cast<QToolBar*>(source)) {
auto parent = toolbar->parentWidget();
if (parent == menuBarLeftArea || parent == menuBarRightArea) {
if (parent == menuBarLeftAreaWidget || parent == menuBarRightAreaWidget) {
menuBarTimer.start(10);
}
}
@@ -1130,9 +1210,9 @@ QList<QToolBar*> 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<ToolBarManager*>(this));
}

View File

@@ -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;

View File

@@ -31,16 +31,18 @@
# include <QScreen>
# include <QStatusBar>
# include <QToolBar>
# include <QLayout>
# include <QTimer>
#endif
#include "Base/Tools.h"
#include "Action.h"
#include "BitmapFactory.h"
#include "Command.h"
#include "PreferencePages/DlgSettingsWorkbenchesImp.h"
#include "DlgPreferencesImp.h"
#include "MainWindow.h"
#include "WorkbenchManager.h"
#include "WorkbenchSelector.h"
#include "ToolBarManager.h"
using namespace Gui;
@@ -77,17 +79,18 @@ void WorkbenchComboBox::refreshList(QList<QAction*> actionList)
{
clear();
ParameterGrp::handle hGrp;
hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Workbenches");
int itemStyleIndex = hGrp->GetInt("WorkbenchSelectorItem", 0);
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Workbenches");
auto itemStyle = static_cast<WorkbenchItemStyle>(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());
@@ -101,156 +104,307 @@ void WorkbenchComboBox::refreshList(QList<QAction*> actionList)
WorkbenchTabWidget::WorkbenchTabWidget(WorkbenchGroup* aGroup, QWidget* parent)
: QTabBar(parent)
: QWidget(parent)
, wbActionGroup(aGroup)
{
setToolTip(aGroup->toolTip());
setStatusTip(aGroup->action()->statusTip());
setWhatsThis(aGroup->action()->whatsThis());
QAction* moreAction = new QAction(this);
menu = new QMenu(this);
moreAction->setMenu(menu);
connect(moreAction, &QAction::triggered, [this]() {
menu->popup(QCursor::pos());
});
connect(menu, &QMenu::aboutToHide, this, [this]() {
// if the more tab did not triggered a disabled workbench, make sure we reselect the correct tab.
std::string activeWbName = WorkbenchManager::instance()->activeName();
for (int i = 0; i < count(); ++i) {
if (wbActionGroup->actions()[i]->objectName().toStdString() == activeWbName) {
setCurrentIndex(i + 1);
break;
}
}
});
if (parent->inherits("QToolBar")) {
// set the initial orientation. We cannot do updateLayoutAndTabOrientation(false);
// because on init the toolbar area is always TopToolBarArea.
ParameterGrp::handle hGrp;
hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Workbenches");
std::string orientation = hGrp->GetASCII("TabBarOrientation", "North");
this->setShape(orientation == "North" ? QTabBar::RoundedNorth :
orientation == "South" ? QTabBar::RoundedSouth :
orientation == "East" ? QTabBar::RoundedEast :
QTabBar::RoundedWest);
}
setDocumentMode(true);
setUsesScrollButtons(true);
setDrawBase(true);
setObjectName(QString::fromLatin1("WbTabBar"));
setIconSize(QSize(16, 16));
refreshList(aGroup->getEnabledWbActions());
connect(aGroup, &WorkbenchGroup::workbenchListRefreshed, this, &WorkbenchTabWidget::refreshList);
connect(aGroup->groupAction(), &QActionGroup::triggered, this, [this, aGroup](QAction* action) {
int index = aGroup->actions().indexOf(action) + 1;
if (index > this->count() - 1) {
index = 0;
}
setCurrentIndex(index);
});
connect(this, qOverload<int>(&QTabBar::tabBarClicked), aGroup, [aGroup, moreAction](int index) {
if(index == 0) {
moreAction->trigger();
}
else if (index <= aGroup->getEnabledWbActions().size()) {
aGroup->actions()[index - 1]->trigger();
}
});
tabBar = new QTabBar(this);
moreButton = new QToolButton(this);
layout = new QBoxLayout(QBoxLayout::LeftToRight, this);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(tabBar);
layout->addWidget(moreButton);
layout->setAlignment(moreButton, Qt::AlignCenter);
setLayout(layout);
moreButton->setIcon(Gui::BitmapFactory().iconFromTheme("list-add"));
moreButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
moreButton->setPopupMode(QToolButton::InstantPopup);
moreButton->setMenu(new QMenu(moreButton));
moreButton->setObjectName(QString::fromLatin1("WbTabBarMore"));
if (parent->inherits("QToolBar")) {
// Connect toolbar orientation changed
QToolBar* tb = qobject_cast<QToolBar*>(parent);
connect(tb, &QToolBar::topLevelChanged, this, &WorkbenchTabWidget::updateLayoutAndTabOrientation);
// when toolbar is created it is not yet placed in its designated area
// therefore we need to wait a bit and then update layout when it is ready
// this is prone to race conditions, but Qt does not supply any event that
// informs us about toolbar changing its placement.
//
// previous implementation saved that information to user settings and
// restored last layout but this creates issues when default workbench has
// different layout than last visited one
QTimer::singleShot(500, [this]() { updateLayout(); });
}
tabBar->setDocumentMode(true);
tabBar->setUsesScrollButtons(true);
tabBar->setDrawBase(true);
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);
if (auto toolBar = qobject_cast<QToolBar*>(parent)) {
connect(toolBar, &QToolBar::topLevelChanged, this, &WorkbenchTabWidget::updateLayout);
connect(toolBar, &QToolBar::orientationChanged, this, &WorkbenchTabWidget::updateLayout);
}
}
void WorkbenchTabWidget::refreshList(QList<QAction*> actionList)
inline Qt::LayoutDirection WorkbenchTabWidget::direction() const
{
// tabs->clear() (QTabBar has no clear)
for (int i = count() - 1; i >= 0; --i) {
removeTab(i);
}
ParameterGrp::handle hGrp;
hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Workbenches");
int itemStyleIndex = hGrp->GetInt("WorkbenchSelectorItem", 0);
QIcon icon = Gui::BitmapFactory().iconFromTheme("list-add");
if (itemStyleIndex == 2) {
addTab(QString::fromLatin1("+"));
}
else {
addTab(icon, QString::fromLatin1(""));
}
for (QAction* action : actionList) {
QIcon icon = action->icon();
if (icon.isNull() || itemStyleIndex == 2) {
addTab(action->text());
}
else if (itemStyleIndex == 1) {
addTab(icon, QString::fromLatin1(""));
}
else {
addTab(icon, action->text());
}
if (action->isChecked()) {
setCurrentIndex(count() - 1);
}
}
buildPrefMenu();
return _direction;
}
void WorkbenchTabWidget::updateLayoutAndTabOrientation(bool floating)
void WorkbenchTabWidget::setDirection(Qt::LayoutDirection direction)
{
auto parent = parentWidget();
if (!parent || !parent->inherits("QToolBar")) {
_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);
return;
}
ParameterGrp::handle hGrp = App::GetApplication()
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/Workbenches");
if (auto toolBar = qobject_cast<QToolBar*>(parentWidget())) {
setSizePolicy(toolBar->sizePolicy());
tabBar->setSizePolicy(toolBar->sizePolicy());
Qt::ToolBarArea area;
parent = parent->parentWidget();
if (toolBar->isFloating()) {
setToolBarArea(toolBar->orientation() == Qt::Horizontal ? Gui::ToolBarArea::TopToolBarArea : Gui::ToolBarArea::LeftToolBarArea);
return;
}
}
if (floating) {
area = Qt::TopToolBarArea;
auto toolBarArea = Gui::ToolBarManager::getInstance()->toolBarArea(parentWidget());
setToolBarArea(toolBarArea);
tabBar->setSelectionBehaviorOnRemove(
direction() == Qt::LeftToRight
? QTabBar::SelectLeftTab
: QTabBar::SelectRightTab
);
}
void WorkbenchTabWidget::handleWorkbenchSelection(QAction* selectedWorkbenchAction)
{
if (wbActionGroup->getDisabledWbActions().contains(selectedWorkbenchAction)) {
if (temporaryWorkbenchAction == selectedWorkbenchAction) {
return;
}
setTemporaryWorkbenchTab(selectedWorkbenchAction);
}
else if (parent && parent->parentWidget() == getMainWindow()->statusBar()) {
area = Qt::BottomToolBarArea;
updateLayout();
tabBar->setCurrentIndex(tabIndexForWorkbenchActivateAction(selectedWorkbenchAction));
}
void WorkbenchTabWidget::setTemporaryWorkbenchTab(QAction* workbenchActivateAction)
{
auto temporaryTabIndex = temporaryWorkbenchTabIndex();
if (temporaryWorkbenchAction) {
temporaryWorkbenchAction = nullptr;
tabBar->removeTab(temporaryTabIndex);
}
else if (parent && parent->parentWidget() == getMainWindow()->menuBar()) {
area = Qt::TopToolBarArea;
temporaryWorkbenchAction = workbenchActivateAction;
if (!workbenchActivateAction) {
return;
}
addWorkbenchTab(workbenchActivateAction, temporaryTabIndex);
adjustSize();
}
void WorkbenchTabWidget::handleTabChange(int selectedTabIndex)
{
// Prevents from rapid workbench changes on initialization as this can cause
// some serious race conditions.
if (isInitializing) {
return;
}
if (auto workbenchActivateAction = workbenchActivateActionByTabIndex(selectedTabIndex)) {
workbenchActivateAction->trigger();
}
if (selectedTabIndex != temporaryWorkbenchTabIndex()) {
setTemporaryWorkbenchTab(nullptr);
}
adjustSize();
}
void WorkbenchTabWidget::updateWorkbenchList()
{
if (isInitializing) {
return;
}
// 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
// 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) {
tabBar->removeTab(i);
}
for (QAction* action : wbActionGroup->getEnabledWbActions()) {
addWorkbenchTab(action);
}
if (temporaryWorkbenchAction != nullptr) {
setTemporaryWorkbenchTab(temporaryWorkbenchAction);
}
buildPrefMenu();
adjustSize();
}
int WorkbenchTabWidget::addWorkbenchTab(QAction* action, int tabIndex)
{
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Workbenches");
auto itemStyle = static_cast<WorkbenchItemStyle>(hGrp->GetInt("WorkbenchSelectorItem", 0));
// if tabIndex is negative we assume that tab must be placed at the end of tabBar (default behavior)
if (tabIndex < 0) {
tabIndex = tabBar->count();
}
// 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() || itemStyle == WorkbenchItemStyle::TextOnly) {
tabBar->insertTab(tabIndex, action->text());
}
else if (itemStyle == IconOnly) {
tabBar->insertTab(tabIndex, icon, {}); // empty string to ensure only icon is displayed
}
else {
QToolBar* tb = qobject_cast<QToolBar*>(parentWidget());
area = getMainWindow()->toolBarArea(tb);
tabBar->insertTab(tabIndex, icon, action->text());
}
if (area == Qt::LeftToolBarArea || area == Qt::RightToolBarArea) {
setShape(area == Qt::LeftToolBarArea ? QTabBar::RoundedWest : QTabBar::RoundedEast);
hGrp->SetASCII("TabBarOrientation", area == Qt::LeftToolBarArea ? "West" : "East");
tabBar->setTabToolTip(tabIndex, action->toolTip());
if (action->isChecked()) {
tabBar->setCurrentIndex(tabIndex);
}
else {
setShape(area == Qt::TopToolBarArea ? QTabBar::RoundedNorth : QTabBar::RoundedSouth);
hGrp->SetASCII("TabBarOrientation", area == Qt::TopToolBarArea ? "North" : "South");
return tabIndex;
}
void WorkbenchTabWidget::setToolBarArea(Gui::ToolBarArea area)
{
switch (area) {
case Gui::ToolBarArea::LeftToolBarArea:
case Gui::ToolBarArea::RightToolBarArea: {
setDirection(Qt::LeftToRight);
layout->setDirection(direction() == Qt::LeftToRight ? QBoxLayout::TopToBottom : QBoxLayout::BottomToTop);
tabBar->setShape(area == Gui::ToolBarArea::LeftToolBarArea ? QTabBar::RoundedWest : QTabBar::RoundedEast);
break;
}
case Gui::ToolBarArea::TopToolBarArea:
case Gui::ToolBarArea::BottomToolBarArea:
case Gui::ToolBarArea::LeftMenuToolBarArea:
case Gui::ToolBarArea::RightMenuToolBarArea:
case Gui::ToolBarArea::StatusBarToolBarArea: {
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);
break;
}
default:
// no-op
break;
}
adjustSize();
}
void WorkbenchTabWidget::buildPrefMenu()
{
auto menu = moreButton->menu();
menu->clear();
// Add disabled workbenches, sorted alphabetically.
menu->addActions(wbActionGroup->getDisabledWbActions());
for (auto action : wbActionGroup->getDisabledWbActions()) {
if (action->text() == QString::fromLatin1("<none>")) {
continue;
}
menu->addAction(action);
}
menu->addSeparator();
@@ -262,4 +416,11 @@ void WorkbenchTabWidget::buildPrefMenu()
});
}
void WorkbenchTabWidget::adjustSize()
{
QWidget::adjustSize();
parentWidget()->adjustSize();
}
#include "moc_WorkbenchSelector.cpp"

View File

@@ -27,12 +27,22 @@
#include <QComboBox>
#include <QTabBar>
#include <QMenu>
#include <QToolButton>
#include <QLayout>
#include <FCGlobal.h>
#include <Gui/ToolBarManager.h>
#include <map>
namespace Gui
{
class WorkbenchGroup;
enum WorkbenchItemStyle {
IconAndText = 0,
IconOnly = 1,
TextOnly = 2
};
class GuiExport WorkbenchComboBox : public QComboBox
{
Q_OBJECT
@@ -49,22 +59,55 @@ private:
};
class GuiExport WorkbenchTabWidget : public QTabBar
class GuiExport WorkbenchTabWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY(Qt::LayoutDirection direction READ direction WRITE setDirection NOTIFY directionChanged)
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);
void updateLayoutAndTabOrientation(bool);
void setToolBarArea(Gui::ToolBarArea area);
void buildPrefMenu();
Qt::LayoutDirection direction() const;
void setDirection(Qt::LayoutDirection direction);
void adjustSize();
public Q_SLOTS:
void refreshList(QList<QAction*>);
void handleWorkbenchSelection(QAction* selectedWorkbenchAction);
void handleTabChange(int selectedTabIndex);
void updateLayout();
void updateWorkbenchList();
Q_SIGNALS:
void directionChanged(const Qt::LayoutDirection&);
private:
bool isInitializing = false;
WorkbenchGroup* wbActionGroup;
QMenu* menu;
QToolButton* moreButton;
QTabBar* tabBar;
QBoxLayout* layout;
Qt::LayoutDirection _direction = Qt::LeftToRight;
// this action is used for workbenches that are typically disabled
QAction* temporaryWorkbenchAction = nullptr;
std::map<QAction*, int> actionToTabIndex;
std::map<int, QAction*> tabIndexToAction;
};