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.
This commit is contained in:
Kacper Donat
2024-05-06 23:34:09 +02:00
parent ec44573bdc
commit 4d1b3756f3
2 changed files with 187 additions and 92 deletions

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();
}
}
@@ -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<OverlayDragFrame> tbPlaceholder;
static QPointer<ToolBarArea> lastArea;
static QPointer<ToolBarAreaWidget> 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<QToolBar*>(source)) {
auto parent = toolbar->parentWidget();
if (parent == menuBarLeftArea || parent == menuBarRightArea) {
if (parent == menuBarLeftAreaWidget || parent == menuBarRightAreaWidget) {
menuBarTimer.start(10);
}
}
@@ -1131,9 +1211,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));
}