diff --git a/src/Gui/MainWindow.cpp b/src/Gui/MainWindow.cpp index 1f454358abb4..d8a05b22577c 100644 --- a/src/Gui/MainWindow.cpp +++ b/src/Gui/MainWindow.cpp @@ -801,14 +801,6 @@ QMenu* MainWindow::createPopupMenu () populateDockWindowMenu(menu); menu->addSeparator(); populateToolBarMenu(menu); - QMenu *undockMenu = new QMenu(menu); - ToolBarManager::getInstance()->populateUndockMenu(undockMenu); - if (undockMenu->actions().isEmpty()) { - delete undockMenu; - } - else { - menu->addMenu(undockMenu); - } menu->addSeparator(); Workbench* wb = WorkbenchManager::instance()->active(); if (wb) { diff --git a/src/Gui/ToolBarManager.cpp b/src/Gui/ToolBarManager.cpp index a69d283a7b5a..e3a6ffb01e58 100644 --- a/src/Gui/ToolBarManager.cpp +++ b/src/Gui/ToolBarManager.cpp @@ -29,10 +29,12 @@ # include # include # include +# include # include # include # include # include +# include #endif #include @@ -188,6 +190,7 @@ class ToolBarArea : public QWidget { if (_layout->indexOf(w) < 0) { _layout->addWidget(w); + ToolBarManager::getInstance()->setToolBarMovable(qobject_cast(w)); adjustParent(); QString name = w->objectName(); if (!name.isEmpty()) { @@ -207,6 +210,7 @@ class ToolBarArea : public QWidget _layout->removeWidget(w); } _layout->insertWidget(idx, w); + ToolBarManager::getInstance()->setToolBarMovable(qobject_cast(w)); adjustParent(); saveState(); } @@ -221,6 +225,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(); + } + } QString name = w->objectName(); if (!name.isEmpty()) { Base::ConnectionBlocker block(_conn); @@ -294,10 +304,137 @@ 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); +} + +QAction *ToolBarGrip::action() const +{ + return _action; +} + +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); + getMainWindow()->addToolBar(toolbar); + toolbar->setWindowFlags(Qt::Tool + | Qt::FramelessWindowHint + | Qt::X11BypassWindowManagerHint); + toolbar->adjustSize(); + pos = toolbar->mapFromGlobal(pos); + toolbar->move(pos.x()-10, pos.y()-10); + toolbar->setVisible(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::_instance = nullptr; // NOLINT ToolBarManager* ToolBarManager::getInstance() @@ -756,6 +893,18 @@ void ToolBarManager::restoreState() const menuBarLeftArea->restoreState(mbLeftToolBars); } +ToolBarArea *ToolBarManager::getToolBarArea(QToolBar *toolbar) const +{ + auto parent = toolbar->parentWidget(); + if (parent == statusBarArea) + return statusBarArea; + if (parent == menuBarLeftArea) + return menuBarLeftArea; + if (parent == menuBarRightArea) + return menuBarRightArea; + return nullptr; +} + bool ToolBarManager::addToolBarToArea(QObject *source, QMouseEvent *ev) { auto statusBar = getMainWindow()->statusBar(); @@ -887,47 +1036,9 @@ bool ToolBarManager::addToolBarToArea(QObject *source, 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(); - auto undockAction = new QAction(menu); - undockAction->setText(action->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); - Q_EMIT toolbar->topLevelChanged(true); - }); - }; - if (area) { - area->foreachToolBar(addMenuUndockItem); - } - else { - statusBarArea->foreachToolBar(addMenuUndockItem); - menuBarLeftArea->foreachToolBar(addMenuUndockItem); - menuBarRightArea->foreachToolBar(addMenuUndockItem); - } -} - bool ToolBarManager::showContextMenu(QObject *source) { QMenu menu; - QMenu menuUndock; QLayout* layout = nullptr; ToolBarArea* area = nullptr; if (getMainWindow()->statusBar() == source) { @@ -957,12 +1068,7 @@ bool ToolBarManager::showContextMenu(QObject *source) } area->foreachToolBar(addMenuVisibleItem); - populateUndockMenu(&menuUndock, area); - if (!menuUndock.actions().empty()) { - menu.addSeparator(); - menu.addMenu(&menuUndock); - } menu.exec(QCursor::pos()); return true; } @@ -1097,9 +1203,47 @@ void Gui::ToolBarManager::setMovable(bool moveable) const { for (auto& tb : toolBars()) { tb->setMovable(moveable); + setToolBarMovable(tb); } } +void ToolBarManager::setToolBarMovable(QToolBar *toolbar) const +{ + if (!toolbar) + return; + + bool movable = toolbar->isMovable(); + + if (auto area = getToolBarArea(toolbar)) { + if (auto grip = toolbar->findChild()) { + if (!movable) { + toolbar->removeAction(grip->action()); + grip->deleteLater(); + } + } + else { + new ToolBarGrip(toolbar); + } + area->adjustParent(); + } + QString name = QStringLiteral("_fc_toolbar_sep_"); + auto sep = toolbar->findChild(name); + if (sep) { + if (movable) { + toolbar->removeAction(sep); + sep->deleteLater(); + } + } + else if (!movable) { + auto actions = toolbar->actions(); + if (actions.size()) { + sep = toolbar->insertSeparator(actions[0]); + sep->setObjectName(name); + } + } +} + + QToolBar* ToolBarManager::findToolBar(const QList& toolbars, const QString& item) const { for (QToolBar* it : toolbars) { diff --git a/src/Gui/ToolBarManager.h b/src/Gui/ToolBarManager.h index c2e695bdf360..57ab8548d660 100644 --- a/src/Gui/ToolBarManager.h +++ b/src/Gui/ToolBarManager.h @@ -28,7 +28,9 @@ #include #include +#include #include +#include #include #include @@ -86,6 +88,25 @@ class GuiExport ToolBarItem QList _items; }; +class ToolBarGrip: public QWidget +{ + Q_OBJECT +public: + ToolBarGrip(QToolBar *); + + QAction *action() const; + +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. @@ -123,7 +144,8 @@ class GuiExport ToolBarManager : public QObject int toolBarIconSize(QWidget *widget=nullptr) const; void setupToolBarIconSize(); - void populateUndockMenu(QMenu *menu, ToolBarArea *area = nullptr); + ToolBarArea *getToolBarArea(QToolBar *) const; + void setToolBarMovable(QToolBar *) const; protected: void setup(ToolBarItem*, QToolBar*) const;