diff --git a/src/Gui/MainWindow.cpp b/src/Gui/MainWindow.cpp index 28578da7d02b..ebad5beda527 100644 --- a/src/Gui/MainWindow.cpp +++ b/src/Gui/MainWindow.cpp @@ -768,7 +768,6 @@ void populateMenu(QMenu *menu, MenuType type, bool popup, QString tooltip, ltooltip; QMenu *hiddenMenu = nullptr; QMenu *lockMenu = nullptr; - QMenu *undockMenu = nullptr; menu->setToolTipsVisible(true); if (ViewParams::getEnableMenuBarCheckBox()) @@ -790,8 +789,6 @@ void populateMenu(QMenu *menu, MenuType type, bool popup, !ToolBarManager::getInstance()->isDefaultMovable(), &checkbox); QObject::connect(lockAction, &QAction::toggled, cb); QObject::connect(checkbox, &QCheckBox::toggled, cb); - undockMenu = new QMenu(menu); - ToolBarManager::getInstance()->populateUndockMenu(undockMenu); } else { lockAction = lockMenu->addAction(QObject::tr("Default")); QObject::connect(lockAction, &QAction::toggled, cb); @@ -945,8 +942,6 @@ void populateMenu(QMenu *menu, MenuType type, bool popup, } } - if (undockMenu && !undockMenu->actions().isEmpty()) - menu->addMenu(undockMenu); if (hiddenMenu) menu->addMenu(hiddenMenu); if (lockMenu) diff --git a/src/Gui/ToolBarManager.cpp b/src/Gui/ToolBarManager.cpp index 27ebe05b91f6..4f9689a00c35 100644 --- a/src/Gui/ToolBarManager.cpp +++ b/src/Gui/ToolBarManager.cpp @@ -32,6 +32,7 @@ # include # include # include +# include # include # include #endif @@ -223,6 +224,7 @@ class ToolBarArea : public QWidget { if (_layout->indexOf(w) < 0) { _layout->addWidget(w); + ToolBarManager::getInstance()->setToolBarMovable(qobject_cast(w)); adjustParentSize(); QString name = w->objectName(); if (!name.isEmpty()) { @@ -240,6 +242,7 @@ class ToolBarArea : public QWidget if (index > 0) _layout->removeWidget(w); _layout->insertWidget(idx, w); + ToolBarManager::getInstance()->setToolBarMovable(qobject_cast(w)); adjustParentSize(); saveState(); } @@ -254,6 +257,12 @@ class ToolBarArea : public QWidget void removeWidget(QWidget *w) { _layout->removeWidget(w); + if (auto tb = qobject_cast(w)) { + if (auto grip = tb->findChild()) { + tb->removeAction(grip->action()); + grip->deleteLater(); + } + } adjustParentSize(); QString name = w->objectName(); if (!name.isEmpty()) { @@ -325,8 +334,133 @@ class ToolBarArea : public QWidget boost::signals2::scoped_connection &_conn; }; +class ToolBar: public QToolBar +{ +public: + void initStyleOption(QStyleOptionToolBar *option) const + { + QToolBar::initStyleOption(option); + } +}; + } // namespace Gui + +////////////////////////////////////////////////////////////////////// + +ToolBarGrip::ToolBarGrip(QToolBar * parent) + :QWidget(parent) +{ + QStyle *style = parent->style(); + QStyleOptionToolBar opt; + static_cast(parent)->initStyleOption(&opt); + opt.features = QStyleOptionToolBar::Movable; + int width = style->subElementRect(QStyle::SE_ToolBarHandle, &opt, parent).width(); + this->setFixedWidth(width+4); + + setCursor(Qt::OpenHandCursor); + setMouseTracking(true); + auto actions = parent->actions(); + _action = parent->insertWidget(actions.isEmpty()?nullptr:actions[0], this); + setVisible(true); +} + +void ToolBarGrip::paintEvent(QPaintEvent*) +{ + QPainter painter(this); + if (auto toolbar = qobject_cast(parentWidget())) { + QStyle *style = toolbar->style(); + QStyleOptionToolBar opt; + static_cast(toolbar)->initStyleOption(&opt); + opt.features = QStyleOptionToolBar::Movable; + // opt.rect = style->subElementRect(QStyle::SE_ToolBarHandle, &opt, toolbar); + opt.rect = this->rect(); + style->drawPrimitive(QStyle::PE_IndicatorToolBarHandle, &opt, &painter, toolbar); + } + else { + painter.setPen(Qt::transparent); + painter.setOpacity(0.5); + painter.setBrush(QBrush(Qt::black, Qt::Dense6Pattern)); + QRect rect(this->rect()); + painter.drawRect(rect); + } +} + +void ToolBarGrip::mouseMoveEvent(QMouseEvent *me) +{ + auto toolbar = qobject_cast(parentWidget()); + if (!toolbar) { + return; + } + auto area = ToolBarManager::getInstance()->getToolBarArea(toolbar); + if (!area) + return; + + QPoint pos = me->globalPos(); + QRect rect(toolbar->mapToGlobal(QPoint(0,0)), toolbar->size()); + if (rect.contains(pos)) + return; + + // If mouse is out of the toolbar, remove the toolbar from area and float the + // toolbar + { + QSignalBlocker blocker(toolbar); + area->removeWidget(toolbar); + ToolBarManager::getInstance()->addToolBarToMainWindow(toolbar); + toolbar->setWindowFlags(Qt::Tool + | Qt::FramelessWindowHint + | Qt::X11BypassWindowManagerHint); + toolbar->adjustSize(); + pos = toolbar->mapFromGlobal(pos); + toolbar->move(pos.x()-10, pos.y()-10); + ToolBarManager::getInstance()->setToolBarVisible(toolbar, true); + } + toolbar->topLevelChanged(true); + + // After removing from area, this grip will be deleted. In order to + // continue toolbar dragging (because the mouse button is still pressed), + // we fake mouse events and send to toolbar. For some reason, + // send/postEvent() does not work, only timer works. + QPointer tb(toolbar); + QTimer::singleShot(0, [tb] { + auto modifiers = QApplication::queryKeyboardModifiers(); + auto buttons = QApplication::mouseButtons(); + if (buttons != Qt::LeftButton + || QWidget::mouseGrabber() + || modifiers != Qt::NoModifier + || !tb) { + return; + } + QPoint pos(10, 10); + QPoint globalPos(tb->mapToGlobal(pos)); + QMouseEvent mouseEvent( + QEvent::MouseButtonPress, + pos, globalPos, Qt::LeftButton, buttons, modifiers); + QApplication::sendEvent(tb, &mouseEvent); + + // Mose follow the mouse press event with mouse move with some offset + // in order to activate toolbar dragging. + QPoint offset(30, 30); + QMouseEvent mouseMoveEvent( + QEvent::MouseMove, + pos+offset, globalPos+offset, + Qt::LeftButton, buttons, modifiers); + QApplication::sendEvent(tb, &mouseMoveEvent); + }); +} + +void ToolBarGrip::mousePressEvent(QMouseEvent *) +{ + setCursor(Qt::ClosedHandCursor); +} + +void ToolBarGrip::mouseReleaseEvent(QMouseEvent *) +{ + setCursor(Qt::OpenHandCursor); +} + +////////////////////////////////////////////////////////////////////// + ToolBarManager::ToolBarManager() { hGeneral = App::GetApplication().GetUserParameter().GetGroup( @@ -628,6 +762,7 @@ void ToolBarManager::onTimer() auto curArea = isGlobal ? gArea : area; QByteArray name = v.first.toUtf8(); int idx = hStatusBar->GetInt(name, -1); + tb->setMovable(hMovable->GetBool(name, movable)); if (idx >= 0) { sbToolBars[idx] = tb; @@ -647,15 +782,13 @@ void ToolBarManager::onTimer() } if (tb->parentWidget() != getMainWindow()) { - Base::StateLocker guard(adding); - getMainWindow()->addToolBar(tb); + addToolBarToMainWindow(tb); } if (defArea != curArea && mw->toolBarArea(tb) == defArea) lines.emplace(ToolBarKey(tb),tb); if (tb->toggleViewAction()->isVisible()) setToolBarVisible(tb, hPref->GetBool(name, tb->isVisible())); - tb->setMovable(hMovable->GetBool(name, movable)); } bool first = true; @@ -833,8 +966,7 @@ void ToolBarManager::setup(ToolBarItem* toolBarItems) if (toolbar_added) { auto area = getMainWindow()->toolBarArea(toolbar); if (!isToolBarAllowed(toolbar, area)) { - Base::StateLocker guard(adding); - getMainWindow()->addToolBar(toolbar); + addToolBarToMainWindow(toolbar); } int &top_width = widths[area]; @@ -967,8 +1099,7 @@ void ToolBarManager::restoreState() continue; } if (toolbar->parentWidget() != getMainWindow()) { - Base::StateLocker guard(adding); - getMainWindow()->addToolBar(toolbar); + addToolBarToMainWindow(toolbar); } } @@ -978,6 +1109,24 @@ void ToolBarManager::restoreState() restored = true; } +void ToolBarManager::addToolBarToMainWindow(QToolBar *toolbar) +{ + Base::StateLocker guard(adding); + getMainWindow()->addToolBar(toolbar); +} + +ToolBarArea *ToolBarManager::getToolBarArea(QToolBar *toolbar) +{ + auto parent = toolbar->parentWidget(); + if (parent == statusBarArea) + return statusBarArea; + if (parent == menuBarLeftArea) + return menuBarLeftArea; + if (parent == menuBarRightArea) + return menuBarRightArea; + return nullptr; +} + void ToolBarManager::retranslate() { for (auto &v : toolBars()) { @@ -1092,22 +1241,43 @@ void ToolBarManager::onToggleStatusBarWidget(QWidget *w, bool visible) hStatusBar->SetBool(w->objectName().toUtf8().constData(), w->isVisible()); } -void ToolBarManager::onMovableChanged(bool movable) +void ToolBarManager::onMovableChanged(bool) +{ + setToolBarMovable(qobject_cast(sender())); +} + +void ToolBarManager::setToolBarMovable(QToolBar *toolbar) { - auto toolbar = qobject_cast(sender()); if (!toolbar) return; + + bool movable = toolbar->isMovable() && isDefaultMovable(); + if (restored && !toolbar->objectName().isEmpty()) { Base::ConnectionBlocker block(connParam); hMovable->SetBool(toolbar->objectName().toUtf8(), movable); } + if (auto area = getToolBarArea(toolbar)) { + if (auto grip = toolbar->findChild()) { + if (!movable) { + toolbar->removeAction(grip->action()); + grip->deleteLater(); + } + } + else { + new ToolBarGrip(toolbar); + } + area->adjustParentSize(); + } QString name = QStringLiteral("_fc_toolbar_sep_"); auto sep = toolbar->findChild(name); if (sep) { - toolbar->removeAction(sep); - sep->deleteLater(); + if (movable) { + toolbar->removeAction(sep); + sep->deleteLater(); + } } - if (!movable) { + else if (!movable) { auto actions = toolbar->actions(); if (actions.size()) { sep = toolbar->insertSeparator(actions[0]); @@ -1248,57 +1418,9 @@ bool ToolBarManager::addToolBarToArea(QObject *o, QMouseEvent *ev) return false; } -void ToolBarManager::populateUndockMenu(QMenu *menu, ToolBarArea *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(); - QCheckBox *checkbox; - auto wa = Action::addCheckBox(menu, action->text(), - tooltip, action->icon(), true, &checkbox); - QObject::connect(wa, &QAction::toggled, [checkbox](bool checked){ - QSignalBlocker block(checkbox); - checkbox->setChecked(checked); - }); - QObject::connect(wa, &QWidgetAction::toggled, [area, toolbar, this](bool checked) { - if (checked) { - QSignalBlocker blocker(toolbar); - area->addWidget(toolbar); - setToolBarVisible(toolbar, true); - } else if (toolbar->parentWidget() == getMainWindow()) - return; - else { - auto pos = toolbar->mapToGlobal(QPoint(0, 0)); - QSignalBlocker blocker(toolbar); - area->removeWidget(toolbar); - { - Base::StateLocker guard(adding); - getMainWindow()->addToolBar(toolbar); - } - toolbar->setWindowFlags(Qt::Tool - | Qt::FramelessWindowHint - | Qt::X11BypassWindowManagerHint); - toolbar->move(pos.x(), pos.y()-toolbar->height()-10); - toolbar->adjustSize(); - setToolBarVisible(toolbar, true); - } - toolbar->topLevelChanged(!checked); - }); - }; - if (area) - area->foreachToolBar(addMenuUndockItem); - else { - statusBarArea->foreachToolBar(addMenuUndockItem); - menuBarLeftArea->foreachToolBar(addMenuUndockItem); - menuBarRightArea->foreachToolBar(addMenuUndockItem); - } -} - bool ToolBarManager::showContextMenu(QObject *source) { QMenu menu; - QMenu menuUndock; QHBoxLayout *layout = nullptr; ToolBarArea *area; if (getMainWindow()->statusBar() == source) { @@ -1367,12 +1489,6 @@ bool ToolBarManager::showContextMenu(QObject *source) } area->foreachToolBar(addMenuVisibleItem); - populateUndockMenu(&menuUndock, area); - - if (menuUndock.actions().size()) { - menu.addSeparator(); - menu.addMenu(&menuUndock); - } menu.exec(QCursor::pos()); return true; } diff --git a/src/Gui/ToolBarManager.h b/src/Gui/ToolBarManager.h index 6e57ece768f1..a258bc540159 100644 --- a/src/Gui/ToolBarManager.h +++ b/src/Gui/ToolBarManager.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -84,6 +85,29 @@ class GuiExport ToolBarItem QList _items; }; + +class ToolBarGrip: public QWidget +{ + Q_OBJECT +public: + ToolBarGrip(QToolBar *); + + QAction *action() + { + return _action; + } + +protected: + void paintEvent(QPaintEvent*); + void mouseMoveEvent(QMouseEvent *); + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + +private: + QPointer _action; +}; + + /** * The ToolBarManager class is responsible for the creation of toolbars and appending them * to the main window. @@ -121,7 +145,10 @@ class GuiExport ToolBarManager: public QObject int toolBarIconSize(QWidget *widget=nullptr) const; void setupToolBarIconSize(); - void populateUndockMenu(QMenu *menu, ToolBarArea *area = nullptr); + + void addToolBarToMainWindow(QToolBar *); + ToolBarArea *getToolBarArea(QToolBar *); + void setToolBarMovable(QToolBar *); protected Q_SLOTS: void onToggleToolBar(bool);