diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index f58c18072e..0e62bc16d1 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -2082,6 +2082,13 @@ void Application::runApplication() << QString::fromUtf8((App::Application::getResourceDir() + "Gui/Stylesheets/").c_str()) << QLatin1String(":/stylesheets"); QDir::setSearchPaths(QString::fromLatin1("qss"), qssPaths); + // setup the search paths for Qt overlay style sheets + QStringList qssOverlayPaths; + qssOverlayPaths << QString::fromUtf8((App::Application::getUserAppDataDir() + + "Gui/Stylesheets/overlay").c_str()) + << QString::fromUtf8((App::Application::getResourceDir() + + "Gui/Stylesheets/overlay").c_str()); + QDir::setSearchPaths(QStringLiteral("overlay"), qssOverlayPaths); // set search paths for images QStringList imagePaths; diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index 811bb10ab9..7d62108de2 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -1066,10 +1066,12 @@ SOURCE_GROUP("Widget" FILES ${Widget_SRCS}) SET(Params_CPP_SRCS TreeParams.cpp + OverlayParams.cpp ) SET(Params_HPP_SRCS TreeParams.h + OverlayParams.h ) SET(Params_SRCS @@ -1103,6 +1105,8 @@ SOURCE_GROUP("View" FILES ${View_SRCS}) # The workbench sources SET(Workbench_CPP_SRCS DockWindowManager.cpp + OverlayManager.cpp + OverlayWidgets.cpp MenuManager.cpp PythonWorkbenchPyImp.cpp ToolBarManager.cpp @@ -1116,6 +1120,8 @@ SET(Workbench_CPP_SRCS SET(Workbench_SRCS ${Workbench_CPP_SRCS} DockWindowManager.h + OverlayManager.h + OverlayWidgets.h MenuManager.h ToolBarManager.h ToolBoxManager.h diff --git a/src/Gui/ComboView.h b/src/Gui/ComboView.h index 0aac485b05..b63e2cf856 100644 --- a/src/Gui/ComboView.h +++ b/src/Gui/ComboView.h @@ -23,6 +23,7 @@ #ifndef GUI_DOCKWND_COMBOVIEW_H #define GUI_DOCKWND_COMBOVIEW_H +#include #include "DockWindow.h" @@ -70,6 +71,8 @@ public: */ ComboView(Gui::Document* pcDocument, QWidget *parent=nullptr); + void setShowModel(bool); + /** * A destructor. * A more elaborate description of the destructor. diff --git a/src/Gui/CommandView.cpp b/src/Gui/CommandView.cpp index bf648a4ce8..d691a433b7 100644 --- a/src/Gui/CommandView.cpp +++ b/src/Gui/CommandView.cpp @@ -67,6 +67,8 @@ #include "Macro.h" #include "MainWindow.h" #include "NavigationStyle.h" +#include "OverlayParams.h" +#include "OverlayManager.h" #include "SceneInspector.h" #include "Selection.h" #include "SelectionObject.h" @@ -3715,6 +3717,279 @@ Action * StdCmdSelBoundingBox::createAction() return pcAction; } +//=========================================================================== +// Std_DockOverlayAll +//=========================================================================== + +DEF_STD_CMD(StdCmdDockOverlayAll) + +StdCmdDockOverlayAll::StdCmdDockOverlayAll() + :Command("Std_DockOverlayAll") +{ + sGroup = "View"; + sMenuText = QT_TR_NOOP("Toggle overlay for all"); + sToolTipText = QT_TR_NOOP("Toggle overlay mode for all docked windows"); + sWhatsThis = "Std_DockOverlayAll"; + sStatusTip = sToolTipText; + sAccel = "F4"; + eType = 0; +} + +void StdCmdDockOverlayAll::activated(int iMsg) +{ + Q_UNUSED(iMsg); + OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleAll); +} + +//=========================================================================== +// Std_DockOverlayTransparentAll +//=========================================================================== + +DEF_STD_CMD(StdCmdDockOverlayTransparentAll) + +StdCmdDockOverlayTransparentAll::StdCmdDockOverlayTransparentAll() + :Command("Std_DockOverlayTransparentAll") +{ + sGroup = "View"; + sMenuText = QT_TR_NOOP("Toggle transparent for all"); + sToolTipText = QT_TR_NOOP("Toggle transparent for all overlay docked window.\n" + "This makes the docked widget stay transparent at all times."); + sWhatsThis = "Std_DockOverlayTransparentAll"; + sStatusTip = sToolTipText; + sAccel = "SHIFT+F4"; + eType = 0; +} + +void StdCmdDockOverlayTransparentAll::activated(int iMsg) +{ + Q_UNUSED(iMsg); + OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleTransparentAll); +} + +//=========================================================================== +// Std_DockOverlayToggle +//=========================================================================== + +DEF_STD_CMD(StdCmdDockOverlayToggle) + +StdCmdDockOverlayToggle::StdCmdDockOverlayToggle() + :Command("Std_DockOverlayToggle") +{ + sGroup = "View"; + sMenuText = QT_TR_NOOP("Toggle overlay"); + sToolTipText = QT_TR_NOOP("Toggle overlay mode of the docked window under cursor"); + sWhatsThis = "Std_DockOverlayToggle"; + sStatusTip = sToolTipText; + sAccel = "F3"; + eType = 0; +} + +void StdCmdDockOverlayToggle::activated(int iMsg) +{ + Q_UNUSED(iMsg); + OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleActive); +} + +//=========================================================================== +// Std_DockOverlayToggleTransparent +//=========================================================================== + +DEF_STD_CMD(StdCmdDockOverlayToggleTransparent) + +StdCmdDockOverlayToggleTransparent::StdCmdDockOverlayToggleTransparent() + :Command("Std_DockOverlayToggleTransparent") +{ + sGroup = "Standard-View"; + sMenuText = QT_TR_NOOP("Toggle transparent"); + sToolTipText = QT_TR_NOOP("Toggle transparent mode for the docked widget under cursor.\n" + "This makes the docked widget stay transparent at all times."); + sWhatsThis = "Std_DockOverlayToggleTransparent"; + sStatusTip = sToolTipText; + sAccel = "SHIFT+F3"; + eType = 0; +} + +void StdCmdDockOverlayToggleTransparent::activated(int iMsg) +{ + Q_UNUSED(iMsg); + OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleTransparent); +} + +//=========================================================================== +// Std_DockOverlayToggleLeft +//=========================================================================== + +DEF_STD_CMD(StdCmdDockOverlayToggleLeft) + +StdCmdDockOverlayToggleLeft::StdCmdDockOverlayToggleLeft() + :Command("Std_DockOverlayToggleLeft") +{ + sGroup = "Standard-View"; + sMenuText = QT_TR_NOOP("Toggle left"); + sToolTipText = QT_TR_NOOP("Show/hide left overlay panel"); + sWhatsThis = "Std_DockOverlayToggleLeft"; + sStatusTip = sToolTipText; + sAccel = "SHIFT+Left"; + sPixmap = "qss:overlay/close.svg"; + eType = 0; +} + +void StdCmdDockOverlayToggleLeft::activated(int iMsg) +{ + Q_UNUSED(iMsg); + OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleLeft); +} + +//=========================================================================== +// Std_DockOverlayToggleRight +//=========================================================================== + +DEF_STD_CMD(StdCmdDockOverlayToggleRight) + +StdCmdDockOverlayToggleRight::StdCmdDockOverlayToggleRight() + :Command("Std_DockOverlayToggleRight") +{ + sGroup = "Standard-View"; + sMenuText = QT_TR_NOOP("Toggle right"); + sToolTipText = QT_TR_NOOP("Show/hide right overlay panel"); + sWhatsThis = "Std_DockOverlayToggleRight"; + sStatusTip = sToolTipText; + sAccel = "SHIFT+Right"; + sPixmap = "qss:overlay/close.svg"; + eType = 0; +} + +void StdCmdDockOverlayToggleRight::activated(int iMsg) +{ + Q_UNUSED(iMsg); + OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleRight); +} + +//=========================================================================== +// Std_DockOverlayToggleTop +//=========================================================================== + +DEF_STD_CMD(StdCmdDockOverlayToggleTop) + +StdCmdDockOverlayToggleTop::StdCmdDockOverlayToggleTop() + :Command("Std_DockOverlayToggleTop") +{ + sGroup = "Standard-View"; + sMenuText = QT_TR_NOOP("Toggle top"); + sToolTipText = QT_TR_NOOP("Show/hide top overlay panel"); + sWhatsThis = "Std_DockOverlayToggleTop"; + sStatusTip = sToolTipText; + sAccel = "SHIFT+Up"; + sPixmap = "qss:overlay/close.svg"; + eType = 0; +} + +void StdCmdDockOverlayToggleTop::activated(int iMsg) +{ + Q_UNUSED(iMsg); + OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleTop); +} + +//=========================================================================== +// Std_DockOverlayToggleBottom +//=========================================================================== + +DEF_STD_CMD(StdCmdDockOverlayToggleBottom) + +StdCmdDockOverlayToggleBottom::StdCmdDockOverlayToggleBottom() + :Command("Std_DockOverlayToggleBottom") +{ + sGroup = "Standard-View"; + sMenuText = QT_TR_NOOP("Toggle bottom"); + sToolTipText = QT_TR_NOOP("Show/hide bottom overlay panel"); + sWhatsThis = "Std_DockOverlayToggleBottom"; + sStatusTip = sToolTipText; + sAccel = "SHIFT+Down"; + sPixmap = "qss:overlay/close.svg"; + eType = 0; +} + +void StdCmdDockOverlayToggleBottom::activated(int iMsg) +{ + Q_UNUSED(iMsg); + OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleBottom); +} + +//=========================================================================== +// Std_DockOverlayMouseTransparent +//=========================================================================== + +DEF_STD_CMD_AC(StdCmdDockOverlayMouseTransparent) + +StdCmdDockOverlayMouseTransparent::StdCmdDockOverlayMouseTransparent() + :Command("Std_DockOverlayMouseTransparent") +{ + sGroup = "View"; + sMenuText = QT_TR_NOOP("Bypass mouse events in dock overlay"); + sToolTipText = QT_TR_NOOP("Bypass all mouse events in dock overlay"); + sWhatsThis = "Std_DockOverlayMouseTransparent"; + sStatusTip = sToolTipText; + sAccel = "T, T"; + eType = NoTransaction; +} + +void StdCmdDockOverlayMouseTransparent::activated(int iMsg) +{ + (void)iMsg; + bool checked = !OverlayManager::instance()->isMouseTransparent(); + OverlayManager::instance()->setMouseTransparent(checked); + if(_pcAction) + _pcAction->setChecked(checked,true); +} + +Action * StdCmdDockOverlayMouseTransparent::createAction(void) { + Action *pcAction = Command::createAction(); + pcAction->setCheckable(true); + pcAction->setIcon(QIcon()); + _pcAction = pcAction; + isActive(); + return pcAction; +} + +bool StdCmdDockOverlayMouseTransparent::isActive() { + bool checked = OverlayManager::instance()->isMouseTransparent(); + if(_pcAction && _pcAction->isChecked()!=checked) + _pcAction->setChecked(checked,true); + return true; +} + +// ============================================================================ + +class StdCmdDockOverlay : public GroupCommand +{ +public: + StdCmdDockOverlay() + :GroupCommand("Std_DockOverlay") + { + sGroup = "View"; + sMenuText = QT_TR_NOOP("Dock window overlay"); + sToolTipText = QT_TR_NOOP("Setting docked window overlay mode"); + sWhatsThis = "Std_DockOverlay"; + sStatusTip = sToolTipText; + eType = 0; + bCanLog = false; + + addCommand(new StdCmdDockOverlayAll()); + addCommand(new StdCmdDockOverlayTransparentAll()); + addCommand(); + addCommand(new StdCmdDockOverlayToggle()); + addCommand(new StdCmdDockOverlayToggleTransparent()); + addCommand(); + addCommand(new StdCmdDockOverlayMouseTransparent()); + addCommand(); + addCommand(new StdCmdDockOverlayToggleLeft()); + addCommand(new StdCmdDockOverlayToggleRight()); + addCommand(new StdCmdDockOverlayToggleTop()); + addCommand(new StdCmdDockOverlayToggleBottom()); + }; + virtual const char* className() const {return "StdCmdDockOverlay";} +}; + //=========================================================================== // Std_StoreWorkingView //=========================================================================== @@ -3859,6 +4134,7 @@ void CreateViewStdCommands() rcCmdMgr.addCommand(new CmdViewMeasureToggleAll()); rcCmdMgr.addCommand(new StdCmdSelBoundingBox()); rcCmdMgr.addCommand(new StdCmdTreeViewActions()); + rcCmdMgr.addCommand(new StdCmdDockOverlay()); auto hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); if(hGrp->GetASCII("GestureRollFwdCommand").empty()) diff --git a/src/Gui/DockWindowManager.cpp b/src/Gui/DockWindowManager.cpp index cf1b6a1f5c..bf94522cb1 100644 --- a/src/Gui/DockWindowManager.cpp +++ b/src/Gui/DockWindowManager.cpp @@ -25,15 +25,23 @@ #ifndef _PreComp_ # include # include +# include # include # include +# include # include +# include #endif +#include + #include +#include +#include #include "DockWindowManager.h" #include "MainWindow.h" +#include "OverlayManager.h" using namespace Gui; @@ -45,7 +53,7 @@ DockWindowItems::~DockWindowItems() = default; void DockWindowItems::addDockWidget(const char* name, Qt::DockWidgetArea pos, bool visibility, bool tabbed) { DockWindowItem item; - item.name = QString::fromLatin1(name); + item.name = QString::fromUtf8(name); item.pos = pos; item.visibility = visibility; item.tabbed = tabbed; @@ -55,7 +63,7 @@ void DockWindowItems::addDockWidget(const char* name, Qt::DockWidgetArea pos, bo void DockWindowItems::setDockingArea(const char* name, Qt::DockWidgetArea pos) { for (QList::iterator it = _items.begin(); it != _items.end(); ++it) { - if (it->name == QLatin1String(name)) { + if (it->name == QString::fromUtf8(name)) { it->pos = pos; break; } @@ -65,7 +73,7 @@ void DockWindowItems::setDockingArea(const char* name, Qt::DockWidgetArea pos) void DockWindowItems::setVisibility(const char* name, bool v) { for (QList::iterator it = _items.begin(); it != _items.end(); ++it) { - if (it->name == QLatin1String(name)) { + if (it->name == QString::fromUtf8(name)) { it->visibility = v; break; } @@ -87,11 +95,66 @@ const QList& DockWindowItems::dockWidgets() const // ----------------------------------------------------------- namespace Gui { + +class DockWidgetEventFilter: public QObject { +public: + bool eventFilter(QObject *o, QEvent *e) { + if (!o->isWidgetType() || e->type() != QEvent::MouseMove) + return false; + auto widget = qobject_cast(o); + if (!widget || !widget->isFloating()) { + if (overridden) { + overridden = false; + QApplication::restoreOverrideCursor(); + } + return false; + } + if (static_cast(e)->buttons() != Qt::NoButton) + return false; + auto pos = QCursor::pos(); + QPoint topLeft = widget->mapToGlobal(QPoint(cursorMargin, cursorMargin)); + int h = widget->frameGeometry().height(); + int w = widget->frameGeometry().width(); + QPoint bottomRight = widget->mapToGlobal(QPoint(w-cursorMargin, h-cursorMargin)); + bool left = QRect(topLeft - QPoint(cursorMargin,cursorMargin), QSize(cursorMargin, h)).contains(pos); + bool right = QRect(bottomRight.x(), topLeft.y(), cursorMargin, h).contains(pos); + bool bottom = QRect(topLeft.x()-cursorMargin, bottomRight.y(), w, cursorMargin).contains(pos); + auto cursor = Qt::ArrowCursor; + if (left && bottom) + cursor = Qt::SizeBDiagCursor; + else if (right && bottom) + cursor = Qt::SizeFDiagCursor; + else if (bottom) + cursor = Qt::SizeVerCursor; + else if (left || right) + cursor = Qt::SizeHorCursor; + else if (overridden) { + overridden = false; + QApplication::restoreOverrideCursor(); + return false; + } + if (overridden) + QApplication::changeOverrideCursor(cursor); + else { + overridden = true; + QApplication::setOverrideCursor(cursor); + } + return false; + } + + bool overridden = false; + int cursorMargin = 5; +}; + struct DockWindowManagerP { QList _dockedWindows; QMap > _dockWindows; DockWindowItems _dockWindowItems; + ParameterGrp::handle _hPref; + boost::signals2::scoped_connection _connParam; + QTimer _timer; + DockWidgetEventFilter _dockWidgetEventFilter; }; } // namespace Gui @@ -113,6 +176,39 @@ void DockWindowManager::destruct() DockWindowManager::DockWindowManager() { d = new DockWindowManagerP; + qApp->installEventFilter(&d->_dockWidgetEventFilter); + d->_hPref = App::GetApplication().GetUserParameter().GetGroup( + "BaseApp/MainWindow/DockWindows"); + d->_dockWidgetEventFilter.cursorMargin = d->_hPref->GetInt("CursorMargin", 5); + d->_connParam = d->_hPref->Manager()->signalParamChanged.connect( + [this](ParameterGrp *Param, ParameterGrp::ParamType Type, const char *name, const char *) { + if(Param == d->_hPref) { + switch(Type) { + case ParameterGrp::ParamType::FCBool: + // For batch process UI setting changes, e.g. loading new preferences + d->_timer.start(100); + break; + case ParameterGrp::ParamType::FCInt: + if (name && boost::equals(name, "CursorMargin")) + d->_dockWidgetEventFilter.cursorMargin = d->_hPref->GetInt("CursorMargin", 5); + break; + default: + break; + } + } + }); + + d->_timer.setSingleShot(true); + + connect(&d->_timer, &QTimer::timeout, [this](){ + for(auto w : this->getDockWindows()) { + if (auto dw = qobject_cast(w)) { + QSignalBlocker blocker(dw); + QByteArray dockName = dw->toggleViewAction()->data().toByteArray(); + dw->setVisible(d->_hPref->GetBool(dockName, dw->isVisible())); + } + } + }); } DockWindowManager::~DockWindowManager() @@ -126,9 +222,17 @@ DockWindowManager::~DockWindowManager() */ QDockWidget* DockWindowManager::addDockWindow(const char* name, QWidget* widget, Qt::DockWidgetArea pos) { + if(!widget) + return nullptr; + QDockWidget *dw = qobject_cast(widget->parentWidget()); + if(dw) + return dw; + // creates the dock widget as container to embed this widget MainWindow* mw = getMainWindow(); - auto dw = new QDockWidget(mw); + dw = new QDockWidget(mw); + OverlayManager::instance()->setupTitleBar(dw); + // Note: By default all dock widgets are hidden but the user can show them manually in the view menu. // First, hide immediately the dock widget to avoid flickering, after setting up the dock widgets // MainWindow::loadLayoutSettings() is called to restore the layout. @@ -152,13 +256,28 @@ QDockWidget* DockWindowManager::addDockWindow(const char* name, QWidget* widget, dw->setWidget(widget); // set object name and window title needed for i18n stuff - dw->setObjectName(QLatin1String(name)); - dw->setWindowTitle(QDockWidget::tr(name)); + dw->setObjectName(QString::fromUtf8(name)); + QString title = widget->windowTitle(); + if (title.isEmpty()) + title = QDockWidget::tr(name); + dw->setWindowTitle(title); dw->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); d->_dockedWindows.push_back(dw); + + OverlayManager::instance()->initDockWidget(dw); + + connect(dw->toggleViewAction(), &QAction::triggered, [this, dw](){ + Base::ConnectionBlocker block(d->_connParam); + QByteArray dockName = dw->toggleViewAction()->data().toByteArray(); + d->_hPref->SetBool(dockName.constData(), dw->isVisible()); + }); + + auto cb = []() {getMainWindow()->saveWindowSettings(true);}; + connect(dw, &QDockWidget::topLevelChanged, cb); + connect(dw, &QDockWidget::dockLocationChanged, cb); return dw; } @@ -169,7 +288,7 @@ QDockWidget* DockWindowManager::addDockWindow(const char* name, QWidget* widget, QWidget* DockWindowManager::getDockWindow(const char* name) const { for (QList::Iterator it = d->_dockedWindows.begin(); it != d->_dockedWindows.end(); ++it) { - if ((*it)->objectName() == QLatin1String(name)) + if ((*it)->objectName() == QString::fromUtf8(name)) return (*it)->widget(); } @@ -208,9 +327,10 @@ QWidget* DockWindowManager::removeDockWindow(const char* name) { QWidget* widget=nullptr; for (QList::Iterator it = d->_dockedWindows.begin(); it != d->_dockedWindows.end(); ++it) { - if ((*it)->objectName() == QLatin1String(name)) { + if ((*it)->objectName() == QString::fromUtf8(name)) { QDockWidget* dw = *it; d->_dockedWindows.erase(it); + OverlayManager::instance()->unsetupDockWidget(dw); getMainWindow()->removeDockWidget(dw); // avoid to destruct the embedded widget widget = dw->widget(); @@ -234,10 +354,13 @@ QWidget* DockWindowManager::removeDockWindow(const char* name) */ void DockWindowManager::removeDockWindow(QWidget* widget) { + if (!widget) + return; for (QList::Iterator it = d->_dockedWindows.begin(); it != d->_dockedWindows.end(); ++it) { if ((*it)->widget() == widget) { QDockWidget* dw = *it; d->_dockedWindows.erase(it); + OverlayManager::instance()->unsetupDockWidget(dw); getMainWindow()->removeDockWidget(dw); // avoid to destruct the embedded widget widget->setParent(nullptr); @@ -283,7 +406,11 @@ void DockWindowManager::activate(QWidget* widget) void DockWindowManager::retranslate() { for (QList::Iterator it = d->_dockedWindows.begin(); it != d->_dockedWindows.end(); ++it) { - (*it)->setWindowTitle(QDockWidget::tr((*it)->objectName().toLatin1())); + QString title = (*it)->windowTitle(); + if (title.isEmpty()) + (*it)->setWindowTitle(QDockWidget::tr((*it)->objectName().toUtf8())); + else + (*it)->setWindowTitle(title); } } @@ -308,10 +435,10 @@ void DockWindowManager::retranslate() */ bool DockWindowManager::registerDockWindow(const char* name, QWidget* widget) { - QMap >::Iterator it = d->_dockWindows.find(QLatin1String(name)); + QMap >::Iterator it = d->_dockWindows.find(QString::fromUtf8(name)); if (it != d->_dockWindows.end() || !widget) return false; - d->_dockWindows[QLatin1String(name)] = widget; + d->_dockWindows[QString::fromUtf8(name)] = widget; widget->hide(); // hide the widget if not used return true; } @@ -319,14 +446,22 @@ bool DockWindowManager::registerDockWindow(const char* name, QWidget* widget) QWidget* DockWindowManager::unregisterDockWindow(const char* name) { QWidget* widget = nullptr; - QMap >::Iterator it = d->_dockWindows.find(QLatin1String(name)); + QMap >::Iterator it = d->_dockWindows.find(QString::fromUtf8(name)); if (it != d->_dockWindows.end()) { - widget = d->_dockWindows.take(QLatin1String(name)); + widget = d->_dockWindows.take(QString::fromUtf8(name)); } return widget; } +QWidget* DockWindowManager::findRegisteredDockWindow(const char* name) +{ + QMap >::Iterator it = d->_dockWindows.find(QString::fromUtf8(name)); + if (it != d->_dockWindows.end()) + return it.value(); + return nullptr; +} + /** Sets up the dock windows of the activated workbench. */ void DockWindowManager::setup(DockWindowItems* items) { @@ -334,14 +469,12 @@ void DockWindowManager::setup(DockWindowItems* items) saveState(); d->_dockWindowItems = *items; - ParameterGrp::handle hPref = App::GetApplication().GetUserParameter().GetGroup("BaseApp") - ->GetGroup("MainWindow")->GetGroup("DockWindows"); QList docked = d->_dockedWindows; const QList& dws = items->dockWidgets(); for (const auto& it : dws) { QDockWidget* dw = findDockWidget(docked, it.name); QByteArray dockName = it.name.toLatin1(); - bool visible = hPref->GetBool(dockName.constData(), it.visibility); + bool visible = d->_hPref->GetBool(dockName.constData(), it.visibility); if (!dw) { QMap >::Iterator jt = d->_dockWindows.find(it.name); @@ -358,6 +491,9 @@ void DockWindowManager::setup(DockWindowItems* items) int index = docked.indexOf(dw); docked.removeAt(index); } + + if(dw && visible) + OverlayManager::instance()->setupDockWidget(dw); } tabifyDockWidgets(items); @@ -417,15 +553,12 @@ void DockWindowManager::tabifyDockWidgets(DockWindowItems* items) void DockWindowManager::saveState() { - ParameterGrp::handle hPref = App::GetApplication().GetUserParameter().GetGroup("BaseApp") - ->GetGroup("MainWindow")->GetGroup("DockWindows"); - const QList& dockItems = d->_dockWindowItems.dockWidgets(); for (QList::ConstIterator it = dockItems.begin(); it != dockItems.end(); ++it) { QDockWidget* dw = findDockWidget(d->_dockedWindows, it->name); if (dw) { QByteArray dockName = dw->toggleViewAction()->data().toByteArray(); - hPref->SetBool(dockName.constData(), dw->isVisible()); + d->_hPref->SetBool(dockName.constData(), dw->isVisible()); } } } @@ -438,7 +571,7 @@ void DockWindowManager::loadState() for (QList::ConstIterator it = dockItems.begin(); it != dockItems.end(); ++it) { QDockWidget* dw = findDockWidget(d->_dockedWindows, it->name); if (dw) { - QByteArray dockName = it->name.toLatin1(); + QByteArray dockName = it->name.toUtf8(); bool visible = hPref->GetBool(dockName.constData(), it->visibility); dw->setVisible(visible); } diff --git a/src/Gui/DockWindowManager.h b/src/Gui/DockWindowManager.h index 76cc3c2c22..38de218e46 100644 --- a/src/Gui/DockWindowManager.h +++ b/src/Gui/DockWindowManager.h @@ -26,7 +26,6 @@ #include #include - class QDockWidget; class QWidget; @@ -70,6 +69,7 @@ public: bool registerDockWindow(const char* name, QWidget* widget); QWidget* unregisterDockWindow(const char* name); + QWidget* findRegisteredDockWindow(const char* name); void setup(DockWindowItems*); /// Adds a QDockWidget to the main window and sets \a widget as its widget diff --git a/src/Gui/Icons/cursor-through.svg b/src/Gui/Icons/cursor-through.svg new file mode 100644 index 0000000000..e2df7b96d2 --- /dev/null +++ b/src/Gui/Icons/cursor-through.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/Gui/Icons/resource.qrc b/src/Gui/Icons/resource.qrc index 792194c0ce..d7f3d83772 100644 --- a/src/Gui/Icons/resource.qrc +++ b/src/Gui/Icons/resource.qrc @@ -255,6 +255,7 @@ folder.svg document-python.svg document-package.svg + cursor-through.svg Std_Alignment.svg Std_DuplicateSelection.svg Std_UserEditModeDefault.svg diff --git a/src/Gui/MainWindow.cpp b/src/Gui/MainWindow.cpp index a99dadab81..65ee8af19e 100644 --- a/src/Gui/MainWindow.cpp +++ b/src/Gui/MainWindow.cpp @@ -31,6 +31,7 @@ # include # include # include +# include # include # include # include @@ -58,6 +59,8 @@ # include #endif +#include + #include #include #include @@ -68,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -83,6 +87,7 @@ #include "FileDialog.h" #include "MenuManager.h" #include "NotificationArea.h" +#include "OverlayManager.h" #include "ProgressBar.h" #include "PropertyView.h" #include "PythonConsole.h" @@ -243,6 +248,8 @@ struct MainWindowP QTimer* actionTimer; QTimer* statusTimer; QTimer* activityTimer; + QTimer saveStateTimer; + QTimer restoreStateTimer; QMdiArea* mdiArea; QPointer activeView; QSignalMapper* windowMapper; @@ -254,6 +261,13 @@ struct MainWindowP int currentStatusType = 100; int actionUpdateDelay = 0; QMap > urlHandler; + std::string hiddenDockWindows; + int screen = -1; + boost::signals2::scoped_connection connParam; + ParameterGrp::handle hGrp; + bool _restoring = false; + QTime _showNormal; + void restoreWindowState(const QByteArray &); }; class MDITabbar : public QTabBar @@ -352,6 +366,32 @@ MainWindow::MainWindow(QWidget * parent, Qt::WindowFlags f) // global access instance = this; + d->connParam = App::GetApplication().GetUserParameter().signalParamChanged.connect( + [this](ParameterGrp *Param, ParameterGrp::ParamType, const char *Name, const char *) { + if (Param != d->hGrp || !Name) + return; + if (boost::equals(Name, "StatusBar")) { + if(auto sb = getMainWindow()->statusBar()) + sb->setVisible(d->hGrp->GetBool("StatusBar", sb->isVisible())); + } + else if (boost::equals(Name, "MainWindowState")) { + OverlayManager::instance()->reload(OverlayManager::ReloadMode::ReloadPause); + d->restoreStateTimer.start(100); + } + }); + + d->hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/MainWindow"); + d->saveStateTimer.setSingleShot(true); + connect(&d->saveStateTimer, &QTimer::timeout, [this](){this->saveWindowSettings();}); + + d->restoreStateTimer.setSingleShot(true); + connect(&d->restoreStateTimer, &QTimer::timeout, [this](){ + d->restoreWindowState(QByteArray::fromBase64(d->hGrp->GetASCII("MainWindowState").c_str())); + ToolBarManager::getInstance()->restoreState(); + OverlayManager::instance()->reload(OverlayManager::ReloadMode::ReloadResume); + }); + // support for grouped dragging of dockwidgets // https://woboq.com/blog/qdockwidget-changes-in-56.html setDockOptions(dockOptions() | QMainWindow::GroupedDragging); @@ -461,31 +501,46 @@ MainWindow* MainWindow::getInstance() return instance; } -void MainWindow::setupDockWindows() +// Helper function to update dock widget according to the user parameter +// settings, e.g. register/unregister, enable/disable, show/hide. +template +static inline void _updateDockWidget(const char *name, + bool enabled, + bool show, + Qt::DockWidgetArea pos, + T callback) { - std::string hiddenDockWindows; - const std::map& config = App::Application::Config(); - auto ht = config.find("HiddenDockWindow"); - if (ht != config.end()) - hiddenDockWindows = ht->second; - - setupTreeView(hiddenDockWindows); - setupPropertyView(hiddenDockWindows); - setupTaskView(hiddenDockWindows); - setupSelectionView(hiddenDockWindows); - setupComboView(hiddenDockWindows); - - // Report view must be created before PythonConsole! - setupReportView(hiddenDockWindows); - setupPythonConsole(hiddenDockWindows); - setupDAGView(hiddenDockWindows); - - this->setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::North); + auto pDockMgr = DockWindowManager::instance(); + auto widget = pDockMgr->findRegisteredDockWindow(name); + if (!enabled) { + if(widget) { + pDockMgr->removeDockWindow(widget); + pDockMgr->unregisterDockWindow(name); + widget->deleteLater(); + } + return; + } + // Use callback to perform specific update for each type of dock widget + widget = callback(widget); + if(!widget) + return; + DockWindowManager::instance()->registerDockWindow(name, widget); + if(show) { + auto dock = pDockMgr->addDockWindow( + widget->objectName().toUtf8().constData(), widget, pos); + if(dock) { + if(!dock->toggleViewAction()->isChecked()) + dock->toggleViewAction()->activate(QAction::Trigger); + OverlayManager::instance()->refresh(dock); + } + } } -bool MainWindow::setupTreeView(const std::string& hiddenDockWindows) +void MainWindow::initDockWindows(bool show) { - if (hiddenDockWindows.find("Std_TreeView") == std::string::npos) { + bool treeView = false; + + if (d->hiddenDockWindows.find("Std_TreeView") == std::string::npos) { //work through parameter. ParameterGrp::handle group = App::GetApplication().GetUserParameter(). GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("DockWindows")->GetGroup("TreeView"); @@ -495,111 +550,82 @@ bool MainWindow::setupTreeView(const std::string& hiddenDockWindows) ->GetGroup("MainWindow")->GetGroup("DockWindows")->GetBool("Std_TreeView", false); } group->SetBool("Enabled", enabled); //ensure entry exists. - if (enabled) { - auto tree = new TreeDockWidget(nullptr, this); - tree->setObjectName - (QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","Tree view"))); - tree->setMinimumWidth(210); - - DockWindowManager* pDockMgr = DockWindowManager::instance(); - pDockMgr->registerDockWindow("Std_TreeView", tree); - return true; - } + treeView = enabled; + _updateDockWidget("Std_TreeView", enabled, show, Qt::RightDockWidgetArea, + [](QWidget *widget) { + if(widget) + return widget; + TreeDockWidget* tree = new TreeDockWidget(0,getMainWindow()); + tree->setObjectName(QStringLiteral(QT_TRANSLATE_NOOP("QDockWidget","Tree view"))); + tree->setMinimumWidth(210); + widget = tree; + return widget; + }); } - return false; -} - -bool MainWindow::setupTaskView(const std::string& hiddenDockWindows) -{ - // Task view - if (hiddenDockWindows.find("Std_TaskView") == std::string::npos) { - auto taskView = new Gui::TaskView::TaskView(this); - taskView->setObjectName - (QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","Tasks"))); - taskView->setMinimumWidth(210); - - DockWindowManager* pDockMgr = DockWindowManager::instance(); - pDockMgr->registerDockWindow("Std_TaskView", taskView); - return true; - } - - return false; -} - -bool MainWindow::setupPropertyView(const std::string& hiddenDockWindows) -{ // Property view - if (hiddenDockWindows.find("Std_PropertyView") == std::string::npos) { + if (d->hiddenDockWindows.find("Std_PropertyView") == std::string::npos) { //work through parameter. ParameterGrp::handle group = App::GetApplication().GetUserParameter(). GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("DockWindows")->GetGroup("PropertyView"); - bool enabled = group->GetBool("Enabled", true); - if (enabled != group->GetBool("Enabled", false)) { + bool enabled = treeView || group->GetBool("Enabled", false); + if (!treeView && enabled != group->GetBool("Enabled", true)) { enabled = App::GetApplication().GetUserParameter().GetGroup("BaseApp") ->GetGroup("MainWindow")->GetGroup("DockWindows")->GetBool("Std_PropertyView", false); } group->SetBool("Enabled", enabled); //ensure entry exists. - if (enabled) { - auto pcPropView = new PropertyDockView(nullptr, this); - pcPropView->setObjectName - (QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","Property view"))); - pcPropView->setMinimumWidth(210); - - DockWindowManager* pDockMgr = DockWindowManager::instance(); - pDockMgr->registerDockWindow("Std_PropertyView", pcPropView); - return true; - } + _updateDockWidget("Std_PropertyView", enabled, show, Qt::RightDockWidgetArea, + [](QWidget *widget) { + if(widget) + return widget; + PropertyDockView* pcPropView = new PropertyDockView(0, getMainWindow()); + pcPropView->setObjectName(QStringLiteral(QT_TRANSLATE_NOOP("QDockWidget","Property view"))); + pcPropView->setMinimumWidth(210); + widget = pcPropView; + return widget; + }); } - return false; -} - -bool MainWindow::setupSelectionView(const std::string& hiddenDockWindows) -{ - // Selection view - if (hiddenDockWindows.find("Std_SelectionView") == std::string::npos) { - auto pcSelectionView = new SelectionView(nullptr, this); - pcSelectionView->setObjectName - (QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","Selection view"))); - pcSelectionView->setMinimumWidth(210); - - DockWindowManager* pDockMgr = DockWindowManager::instance(); - pDockMgr->registerDockWindow("Std_SelectionView", pcSelectionView); - return true; - } - - return false; -} - -bool MainWindow::setupComboView(const std::string& hiddenDockWindows) -{ // Combo view - if (hiddenDockWindows.find("Std_ComboView") == std::string::npos) { - ParameterGrp::handle group = App::GetApplication().GetUserParameter(). - GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("DockWindows")->GetGroup("ComboView"); - bool enable = group->GetBool("Enabled", true); - - if (enable) { - auto pcComboView = new ComboView(nullptr, this); - pcComboView->setObjectName(QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget", "Model"))); - pcComboView->setMinimumWidth(150); - - DockWindowManager* pDockMgr = DockWindowManager::instance(); - pDockMgr->registerDockWindow("Std_ComboView", pcComboView); - return true; + if (d->hiddenDockWindows.find("Std_ComboView") == std::string::npos) { + bool enable = !treeView; + if (!enable) { + ParameterGrp::handle group = App::GetApplication().GetUserParameter(). + GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("DockWindows")->GetGroup("ComboView"); + enable = group->GetBool("Enabled", true); } + _updateDockWidget("Std_ComboView", enable, show, Qt::LeftDockWidgetArea, + [](QWidget *widget) { + auto pcComboView = qobject_cast(widget); + if(widget) + return widget; + pcComboView = new ComboView(nullptr, getMainWindow()); + pcComboView->setObjectName(QStringLiteral(QT_TRANSLATE_NOOP("QDockWidget", "Model"))); + pcComboView->setMinimumWidth(150); + widget = pcComboView; + return widget; + }); } - return false; -} - -bool MainWindow::setupDAGView(const std::string& hiddenDockWindows) -{ - //TODO: Add external object support for DAGView + //Task List (task watcher). + if (d->hiddenDockWindows.find("Std_TaskWatcher") == std::string::npos) { + //work through parameter. + ParameterGrp::handle group = App::GetApplication().GetUserParameter(). + GetGroup("BaseApp/Preferences/DockWindows/TaskWatcher"); + bool enabled = group->GetBool("Enabled", false); + group->SetBool("Enabled", enabled); //ensure entry exists. + _updateDockWidget("Std_TaskWatcher", enabled, show, Qt::RightDockWidgetArea, + [](QWidget *widget) { + if(widget) + return widget; + widget = new TaskView::TaskView(getMainWindow()); + widget->setObjectName(QStringLiteral(QT_TRANSLATE_NOOP("QDockWidget","Task List"))); + return widget; + }); + } //Dag View. - if (hiddenDockWindows.find("Std_DAGView") == std::string::npos) { + if (d->hiddenDockWindows.find("Std_DAGView") == std::string::npos) { //work through parameter. // old group name ParameterGrp::handle deprecateGroup = App::GetApplication().GetUserParameter(). @@ -614,24 +640,68 @@ bool MainWindow::setupDAGView(const std::string& hiddenDockWindows) GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("DockWindows")->GetGroup("DAGView"); enabled = group->GetBool("Enabled", enabled); group->SetBool("Enabled", enabled); //ensure entry exists. - if (enabled) { - auto dagDockWindow = new DAG::DockWindow(nullptr, this); - dagDockWindow->setObjectName - (QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","DAG View"))); - DockWindowManager* pDockMgr = DockWindowManager::instance(); - pDockMgr->registerDockWindow("Std_DAGView", dagDockWindow); - return true; - } + _updateDockWidget("Std_DAGView", enabled, show, Qt::RightDockWidgetArea, + [](QWidget *widget) { + if(widget) + return widget; + DAG::DockWindow *dagDockWindow = new DAG::DockWindow(nullptr, getMainWindow()); + dagDockWindow->setObjectName(QStringLiteral(QT_TRANSLATE_NOOP("QDockWidget","DAG View"))); + widget = dagDockWindow; + return widget; + }); + } +} + +void MainWindow::setupDockWindows() +{ + // Report view must be created before PythonConsole! + setupReportView(); + setupPythonConsole(); + setupSelectionView(); + setupTaskView(); + + initDockWindows(false); +} + +bool MainWindow::setupTaskView() +{ + // Task view + if (d->hiddenDockWindows.find("Std_TaskView") == std::string::npos) { + auto taskView = new Gui::TaskView::TaskView(this); + taskView->setObjectName + (QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","Tasks"))); + taskView->setMinimumWidth(210); + + DockWindowManager* pDockMgr = DockWindowManager::instance(); + pDockMgr->registerDockWindow("Std_TaskView", taskView); + return true; } return false; } -bool MainWindow::setupReportView(const std::string& hiddenDockWindows) +bool MainWindow::setupSelectionView() +{ + // Selection view + if (d->hiddenDockWindows.find("Std_SelectionView") == std::string::npos) { + auto pcSelectionView = new SelectionView(nullptr, this); + pcSelectionView->setObjectName + (QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","Selection view"))); + pcSelectionView->setMinimumWidth(210); + + DockWindowManager* pDockMgr = DockWindowManager::instance(); + pDockMgr->registerDockWindow("Std_SelectionView", pcSelectionView); + return true; + } + + return false; +} + +bool MainWindow::setupReportView() { // Report view - if (hiddenDockWindows.find("Std_ReportView") == std::string::npos) { + if (d->hiddenDockWindows.find("Std_ReportView") == std::string::npos) { auto pcReport = new ReportOutput(this); pcReport->setWindowIcon(BitmapFactory().pixmap("MacroEditor")); pcReport->setObjectName @@ -648,10 +718,10 @@ bool MainWindow::setupReportView(const std::string& hiddenDockWindows) return false; } -bool MainWindow::setupPythonConsole(const std::string& hiddenDockWindows) +bool MainWindow::setupPythonConsole() { // Python console - if (hiddenDockWindows.find("Std_PythonView") == std::string::npos) { + if (d->hiddenDockWindows.find("Std_PythonView") == std::string::npos) { auto pcPython = new PythonConsole(this); pcPython->setWindowIcon(Gui::BitmapFactory().iconFromTheme("applications-python")); pcPython->setObjectName @@ -1589,37 +1659,49 @@ void MainWindow::switchToDockedMode() void MainWindow::loadWindowSettings() { - QString vendor = QString::fromLatin1(App::Application::Config()["ExeVendor"].c_str()); - QString application = QString::fromLatin1(App::Application::Config()["ExeName"].c_str()); + QString vendor = QString::fromUtf8(App::Application::Config()["ExeVendor"].c_str()); + QString application = QString::fromUtf8(App::Application::Config()["ExeName"].c_str()); int major = (QT_VERSION >> 0x10) & 0xff; int minor = (QT_VERSION >> 0x08) & 0xff; - QString qtver = QString::fromLatin1("Qt%1.%2").arg(major).arg(minor); + QString qtver = QStringLiteral("Qt%1.%2").arg(major).arg(minor); QSettings config(vendor, application); - QRect rect = QApplication::primaryScreen()->availableGeometry(); - int maxHeight = rect.height(); - int maxWidth = rect.width(); + QRect rect = QApplication::desktop()->availableGeometry(d->screen); config.beginGroup(qtver); - QPoint pos = config.value(QString::fromLatin1("Position"), this->pos()).toPoint(); - maxWidth -= pos.x(); - maxHeight -= pos.y(); - this->resize(config.value(QString::fromLatin1("Size"), QSize(maxWidth, maxHeight)).toSize()); + QPoint pos = config.value(QStringLiteral("Position"), this->pos()).toPoint(); + QSize size = config.value(QStringLiteral("Size"), rect.size()).toSize(); + bool max = config.value(QStringLiteral("Maximized"), false).toBool(); + bool showStatusBar = config.value(QStringLiteral("StatusBar"), true).toBool(); + QByteArray windowState = config.value(QStringLiteral("MainWindowState")).toByteArray(); + config.endGroup(); - int x1,x2,y1,y2; - // make sure that the main window is not totally out of the visible rectangle - rect.getCoords(&x1, &y1, &x2, &y2); - pos.setX(qMin(qMax(pos.x(),x1-this->width()+30),x2-30)); - pos.setY(qMin(qMax(pos.y(),y1-10),y2-10)); - this->move(pos); - { - // tmp. disable the report window to suppress some bothering warnings - const Base::ILoggerBlocker blocker("ReportOutput", Base::ConsoleSingleton::MsgType_Wrn); - this->restoreState(config.value(QString::fromLatin1("MainWindowState")).toByteArray()); + std::string geometry = d->hGrp->GetASCII("Geometry"); + std::istringstream iss(geometry); + int x,y,w,h; + if (iss >> x >> y >> w >> h) { + pos = QPoint(x,y); + size = QSize(w,h); } + max = d->hGrp->GetBool("Maximized", max); + showStatusBar = d->hGrp->GetBool("StatusBar", showStatusBar); + std::string wstate = d->hGrp->GetASCII("MainWindowState"); + if (wstate.size()) + windowState = QByteArray::fromBase64(wstate.c_str()); + + x = std::max(rect.left(), std::min(rect.left()+rect.width()/2, pos.x())); + y = std::max(rect.top(), std::min(rect.top()+rect.height()/2, pos.y())); + w = std::min(rect.width(), size.width()); + h = std::min(rect.height(), size.height()); + + this->move(x, y); + this->resize(w, h); + + Base::StateLocker guard(d->_restoring); + + d->restoreWindowState(windowState); std::clog << "Main window restored" << std::endl; - bool max = config.value(QString::fromLatin1("Maximized"), false).toBool(); max ? showMaximized() : show(); // make menus and tooltips usable in fullscreen under Windows, see issue #7563 @@ -1629,31 +1711,84 @@ void MainWindow::loadWindowSettings() } #endif - statusBar()->setVisible(config.value(QString::fromLatin1("StatusBar"), true).toBool()); - config.endGroup(); + statusBar()->setVisible(showStatusBar); ToolBarManager::getInstance()->restoreState(); std::clog << "Toolbars restored" << std::endl; + + OverlayManager::instance()->restore(); } -void MainWindow::saveWindowSettings() +bool MainWindow::isRestoringWindowState() const { - QString vendor = QString::fromLatin1(App::Application::Config()["ExeVendor"].c_str()); - QString application = QString::fromLatin1(App::Application::Config()["ExeName"].c_str()); + return d->_restoring; +} + +void MainWindowP::restoreWindowState(const QByteArray &windowState) +{ + if (windowState.isEmpty()) + return; + + Base::StateLocker guard(_restoring); + + // tmp. disable the report window to suppress some bothering warnings + if (Base::Console().IsMsgTypeEnabled("ReportOutput", Base::ConsoleSingleton::MsgType_Wrn)) { + Base::Console().SetEnabledMsgType("ReportOutput", Base::ConsoleSingleton::MsgType_Wrn, false); + getMainWindow()->restoreState(windowState); + Base::Console().SetEnabledMsgType("ReportOutput", Base::ConsoleSingleton::MsgType_Wrn, true); + } else + getMainWindow()->restoreState(windowState); + + Base::ConnectionBlocker block(connParam); + // as a notification for user code on window state restore + hGrp->SetBool("WindowStateRestored", !hGrp->GetBool("WindowStateRestored", false)); +} + +void MainWindow::saveWindowSettings(bool canDelay) +{ + if (isRestoringWindowState()) + return; + + if (canDelay) { + d->saveStateTimer.start(100); + return; + } + + QString vendor = QString::fromUtf8(App::Application::Config()["ExeVendor"].c_str()); + QString application = QString::fromUtf8(App::Application::Config()["ExeName"].c_str()); int major = (QT_VERSION >> 0x10) & 0xff; int minor = (QT_VERSION >> 0x08) & 0xff; - QString qtver = QString::fromLatin1("Qt%1.%2").arg(major).arg(minor); + QString qtver = QStringLiteral("Qt%1.%2").arg(major).arg(minor); QSettings config(vendor, application); +#if 0 config.beginGroup(qtver); - config.setValue(QString::fromLatin1("Size"), this->size()); - config.setValue(QString::fromLatin1("Position"), this->pos()); - config.setValue(QString::fromLatin1("Maximized"), this->isMaximized()); - config.setValue(QString::fromLatin1("MainWindowState"), this->saveState()); - config.setValue(QString::fromLatin1("StatusBar"), this->statusBar()->isVisible()); + config.setValue(QStringLiteral("Size"), this->size()); + config.setValue(QStringLiteral("Position"), this->pos()); + config.setValue(QStringLiteral("Maximized"), this->isMaximized()); + config.setValue(QStringLiteral("MainWindowState"), this->saveState()); + config.setValue(QStringLiteral("StatusBar"), this->statusBar()->isVisible()); config.endGroup(); +#else + // We are migrating from saving qt main window layout state in QSettings to + // FreeCAD parameters, for more control. The old settings is explicitly + // remove from old QSettings conf to allow easier complete reset of + // application state by just removing FC user.cfg file. + config.remove(qtver); +#endif + + Base::ConnectionBlocker block(d->connParam); + d->hGrp->SetBool("Maximized", this->isMaximized()); + d->hGrp->SetBool("StatusBar", this->statusBar()->isVisible()); + d->hGrp->SetASCII("MainWindowState", this->saveState().toBase64().constData()); + + std::ostringstream ss; + QRect rect(this->pos(), this->size()); + ss << rect.left() << " " << rect.top() << " " << rect.width() << " " << rect.height(); + d->hGrp->SetASCII("Geometry", ss.str().c_str()); DockWindowManager::instance()->saveState(); + OverlayManager::instance()->save(); ToolBarManager::getInstance()->saveState(); } @@ -1669,6 +1804,7 @@ void MainWindow::startSplasher() if (hGrp->GetBool("ShowSplasher", true)) { d->splashscreen = new SplashScreen(this->splashImage()); d->splashscreen->show(); + d->screen = QApplication::desktop()->screenNumber(d->splashscreen); } else d->splashscreen = nullptr; @@ -2296,6 +2432,11 @@ void MainWindow::customEvent(QEvent* e) } } +QMdiArea *MainWindow::getMdiArea() const +{ + return d->mdiArea; +} + // ---------------------------------------------------------- StatusBarObserver::StatusBarObserver() diff --git a/src/Gui/MainWindow.h b/src/Gui/MainWindow.h index 9b8405a4fb..248c49094d 100644 --- a/src/Gui/MainWindow.h +++ b/src/Gui/MainWindow.h @@ -105,6 +105,10 @@ public: * Returns a list of all MDI windows in the worpspace. */ QList windows(QMdiArea::WindowOrder order = QMdiArea::CreationOrder) const; + /** + * Returns the internal QMdiArea instance. + */ + QMdiArea *getMdiArea() const; /** * Can be called after the caption of an MDIView has changed to update the tab's caption. */ @@ -152,7 +156,7 @@ public: /// Loads the main window settings. void loadWindowSettings(); /// Saves the main window settings. - void saveWindowSettings(); + void saveWindowSettings(bool canDelay = false); //@} /** @name Menu @@ -203,6 +207,8 @@ public: enum StatusType {None, Err, Wrn, Pane, Msg, Log, Tmp, Critical}; void showStatus(int type, const QString & message); + void initDockWindows(bool show); + public Q_SLOTS: /** * Updates the standard actions of a text editor such as Cut, Copy, Paste, Undo and Redo. @@ -254,6 +260,8 @@ public Q_SLOTS: void showMessage (const QString & message, int timeout = 0); + bool isRestoringWindowState() const; + protected: /** * This method checks if the main window can be closed by checking all open documents and views. @@ -283,14 +291,10 @@ protected: private: void setupDockWindows(); - bool setupTreeView(const std::string&); - bool setupTaskView(const std::string&); - bool setupPropertyView(const std::string&); - bool setupSelectionView(const std::string&); - bool setupComboView(const std::string&); - bool setupDAGView(const std::string&); - bool setupReportView(const std::string&); - bool setupPythonConsole(const std::string&); + bool setupTaskView(); + bool setupSelectionView(); + bool setupReportView(); + bool setupPythonConsole(); static void renderDevBuildWarning(QPainter &painter, const QPoint startPosition, const QSize maxSize); diff --git a/src/Gui/NaviCube.cpp b/src/Gui/NaviCube.cpp index 92389f736e..c2eb3acfab 100644 --- a/src/Gui/NaviCube.cpp +++ b/src/Gui/NaviCube.cpp @@ -162,7 +162,7 @@ private: public: - int m_CubeWidgetSize = 132; + static int m_CubeWidgetSize; QColor m_BaseColor; QColor m_EmphaseColor; QColor m_HiliteColor; @@ -201,6 +201,13 @@ private: QMenu* m_Menu; }; +int NaviCubeImplementation::m_CubeWidgetSize = 132; + +int NaviCube::getNaviCubeSize() +{ + return NaviCubeImplementation::m_CubeWidgetSize; +} + NaviCube::NaviCube(Gui::View3DInventorViewer* viewer) { m_NaviCubeImplementation = new NaviCubeImplementation(viewer); } diff --git a/src/Gui/NaviCube.h b/src/Gui/NaviCube.h index 0384b63c17..c86a3f36cc 100644 --- a/src/Gui/NaviCube.h +++ b/src/Gui/NaviCube.h @@ -68,6 +68,7 @@ public: // Label order: front, top, right, rear, bottom, left void setNaviCubeLabels(const std::vector& labels); static void setNaviCubeCommands(const std::vector& cmd); + static int getNaviCubeSize(); private: NaviCubeImplementation* m_NaviCubeImplementation; diff --git a/src/Gui/OverlayManager.cpp b/src/Gui/OverlayManager.cpp new file mode 100644 index 0000000000..2df795ee5d --- /dev/null +++ b/src/Gui/OverlayManager.cpp @@ -0,0 +1,2052 @@ +/**************************************************************************** + * Copyright (c) 2022 Zheng Lei (realthunder) * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ****************************************************************************/ + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include + +#include +#include + +#include "OverlayManager.h" + +#include +#include +#include +#include +#include "Application.h" +#include "BitmapFactory.h" +#include "Control.h" +#include "MainWindow.h" +#include "MDIView.h" +#include "NaviCube.h" +#include "OverlayParams.h" +#include "OverlayWidgets.h" +#include "TaskView/TaskView.h" +#include "Tree.h" +#include "TreeParams.h" +#include "View3DInventorViewer.h" + +FC_LOG_LEVEL_INIT("Dock", true, true); + +using namespace Gui; + +static std::array _Overlays; + +static inline OverlayTabWidget *findTabWidget(QWidget *widget=nullptr, bool filterDialog=false) +{ + if(!widget) + widget = qApp->focusWidget(); + for(auto w=widget; w; w=w->parentWidget()) { + auto tabWidget = qobject_cast(w); + if(tabWidget) + return tabWidget; + auto proxy = qobject_cast(w); + if(proxy) + return proxy->getOwner(); + if(filterDialog && w->windowType() != Qt::Widget) + break; + } + return nullptr; +} + +class OverlayStyleSheet: public ParameterGrp::ObserverType { +public: + + OverlayStyleSheet() { + handle = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/MainWindow"); + update(); + handle->Attach(this); + } + + static OverlayStyleSheet *instance() { + static OverlayStyleSheet *inst; + if(!inst) + inst = new OverlayStyleSheet; + return inst; + } + + void OnChange(Base::Subject &, const char* sReason) { + if(!sReason) + return; + if(strcmp(sReason, "StyleSheet")==0 + || strcmp(sReason, "OverlayActiveStyleSheet")==0) + { + OverlayManager::instance()->refresh(nullptr, true); + } + } + + void update() { + QString mainstyle = QString::fromUtf8(handle->GetASCII("StyleSheet").c_str()); + + QString prefix; + + if(!mainstyle.isEmpty()) { + int dark = mainstyle.indexOf(QStringLiteral("dark"),0,Qt::CaseInsensitive); + prefix = QStringLiteral("overlay:%1").arg( + dark<0 ? QStringLiteral("Light") : QStringLiteral("Dark")); + } + + QString name; + name = QString::fromUtf8(handle->GetASCII("OverlayActiveStyleSheet").c_str()); + if(name.isEmpty() && !prefix.isEmpty()) + name = prefix + QStringLiteral(".qss"); + else if (!QFile::exists(name)) + name = QStringLiteral("overlay:%1").arg(name); + activeStyleSheet.clear(); + if(QFile::exists(name)) { + QFile f(name); + if(f.open(QFile::ReadOnly)) { + QTextStream str(&f); + activeStyleSheet = str.readAll(); + } + } + if(activeStyleSheet.isEmpty()) { + static QString _default = QStringLiteral( + "* {alternate-background-color: rgba(250,250,250,120)}" + + "QComboBox, QComboBox:editable, QComboBox:!editable, QLineEdit," + "QTextEdit, QPlainTextEdit, QAbstractSpinBox, QDateEdit, QDateTimeEdit," + "Gui--PropertyEditor--PropertyEditor QLabel " + "{background : palette(base);}" + + "QScrollBar { background: rgba(0,0,0,10);}" + "QTabWidget::pane { background-color: transparent; border: transparent }" + "Gui--OverlayTabWidget { qproperty-effectColor: rgba(0,0,0,0) }" + "Gui--OverlayTabWidget::pane { background-color: rgba(250,250,250,80) }" + + "QTabBar {border : none;}" + "QTabBar::tab {color: palette(text);" + "background-color: rgba(100,100,100,50);" + "padding: 5px}" + "QTabBar::tab:selected {background-color: rgba(250,250,250,80);}" + "QTabBar::tab:hover {background-color: rgba(250,250,250,200);}" + + "QHeaderView { background:transparent }" + "QHeaderView::section {color: palette(text);" + "background-color: rgba(250,250,250,50);" + "border: 1px solid palette(dark);" + "padding: 2px}" + + "QTreeView, QListView, QTableView {" + "background: rgb(250,250,250);" + "border: transparent;" + "selection-background-color: rgba(94, 144, 250, 0.7);}" + "QListView::item:selected, QTreeView::item:selected {" + "background-color: rgba(94, 144, 250, 0.7);}" + + "Gui--PropertyEditor--PropertyEditor {" + "border: 1px solid palette(dark);" + "qproperty-groupTextColor: rgb(100, 100, 100);" + "qproperty-groupBackground: rgba(180, 180, 180, 0.7);}" + + "QToolTip {background-color: rgba(250,250,250,180);}" + + "Gui--TreeWidget QHeaderView:section {" + "height: 0px;" + "background-color: transparent;" + "padding: 0px;" + "border: transparent;}" + + "Gui--CallTipsList::item { background-color: rgba(200,200,200,200);}" + "Gui--CallTipsList::item::selected { background-color: palette(highlight);}" + + "QPushButton { background: rgba(250,250,250,80);padding: 2px 4px;}" + "QPushButton::hover { background: rgba(250,250,250,200);}" + "QPushButton::focus { background: rgba(250,250,250,255);}" + "QPushButton::pressed { background-color: #5e90fa;" + "border: 1px inset palette(dark) }" + "QPushButton::checked { background: rgba(100,100,100,100);" + "border: 1px inset palette(dark) }" + "QPushButton::checked:hover { background: rgba(150,150,150,200);" + "border: 1px inset palette(dark) }" + "Gui--OverlayToolButton { background: transparent; padding: 0px; border: none }" + "Gui--OverlayTitleBar," + "Gui--OverlaySplitterHandle { background-color: rgba(200, 200, 200, 150); }" + "QWidget#ClippingScrollAreaContents, " + "QScrollArea#ClippingScrollArea { border: none; background-color: #a0e6e6e6; }" + "Gui--PropertyEditor--PropertyEditor > QWidget > QPushButton {text-align:left;padding-left:2px;}" + ); + activeStyleSheet = _default; + } + } + + ParameterGrp::handle handle; + QString activeStyleSheet; + bool hideTab = false; +}; + +// ----------------------------------------------------------- + +struct OverlayInfo { + const char *name; + OverlayTabWidget *tabWidget; + Qt::DockWidgetArea dockArea; + std::unordered_map &overlayMap; + ParameterGrp::handle hGrp; + boost::signals2::scoped_connection conn; + + OverlayInfo(QWidget *parent, + const char *name, + Qt::DockWidgetArea pos, + std::unordered_map &map) + : name(name), dockArea(pos), overlayMap(map) + { + tabWidget = new OverlayTabWidget(parent, dockArea); + tabWidget->setObjectName(QString::fromUtf8(name)); + tabWidget->getProxyWidget()->setObjectName(tabWidget->objectName() + QStringLiteral("Proxy")); + tabWidget->setMovable(true); + hGrp = App::GetApplication().GetUserParameter().GetGroup("BaseApp") + ->GetGroup("MainWindow")->GetGroup("DockWindows")->GetGroup(name); + conn = App::GetApplication().GetUserParameter().signalParamChanged.connect( + [this](ParameterGrp *Param, ParameterGrp::ParamType, const char *Name, const char *) { + if (hGrp == Param && Name && !tabWidget->isSaving()) { + // This will prevent saving settings which will mess up the + // just restored ones + tabWidget->restore(nullptr); + OverlayManager::instance()->reload(); + } + }); + } + + bool addWidget(QDockWidget *dock, bool forced=true) { + if(!dock) + return false; + if(tabWidget->dockWidgetIndex(dock) >= 0) + return false; + overlayMap[dock] = this; + bool visible = dock->isVisible(); + + auto focus = qApp->focusWidget(); + if(focus && findTabWidget(focus) != tabWidget) + focus = nullptr; + + tabWidget->addWidget(dock, dock->windowTitle()); + + if(focus) { + tabWidget->setCurrent(dock); + focus = qApp->focusWidget(); + if(focus) + focus->clearFocus(); + } + + if(forced) { + auto mw = getMainWindow(); + for(auto d : mw->findChildren()) { + if(mw->dockWidgetArea(d) == dockArea + && d->toggleViewAction()->isChecked()) + { + addWidget(d, false); + } + } + if(visible) { + dock->show(); + tabWidget->setCurrent(dock); + } + } else + tabWidget->saveTabs(); + return true; + } + + void removeWidget() { + if(!tabWidget->count()) + return; + + tabWidget->hide(); + + QPointer focus = qApp->focusWidget(); + + QDockWidget *lastDock = tabWidget->currentDockWidget(); + if(lastDock) + tabWidget->removeWidget(lastDock); + while(tabWidget->count()) { + QDockWidget *dock = tabWidget->dockWidget(0); + if(!dock) { + tabWidget->removeTab(0); + continue; + } + tabWidget->removeWidget(dock, lastDock); + lastDock = dock; + } + + if(focus) + focus->setFocus(); + + tabWidget->saveTabs(); + } + + void save() + { + } + + void restore() + { + tabWidget->restore(hGrp); + for(int i=0,c=tabWidget->count();idockWidget(i); + if(dock) + overlayMap[dock] = this; + } + } +}; + +enum class ToggleMode { + Unset, + Set, + Toggle, + Transparent, + Check, +}; + +class OverlayManager::Private { +public: + + QPointer lastIntercept; + QTimer _timer; + QTimer _reloadTimer; + + bool mouseTransparent = false; + bool intercepting = false; + + std::unordered_map _overlayMap; + OverlayInfo _left; + OverlayInfo _right; + OverlayInfo _top; + OverlayInfo _bottom; + std::array _overlayInfos; + QCursor _cursor; + + QPoint _lastPos; + + QAction _actClose; + QAction _actFloat; + QAction _actOverlay; + QList _actions; + + QPointer _trackingWidget; + OverlayTabWidget *_trackingOverlay = nullptr; + + bool updateStyle = false; + QTime wheelDelay; + QPoint wheelPos; + + std::map _dockWidgetNameMap; + + bool raising = false; + + OverlayManager::ReloadMode curReloadMode = OverlayManager::ReloadMode::ReloadPending; + + Private(OverlayManager *host, QWidget *parent) + :_left(parent,"OverlayLeft", Qt::LeftDockWidgetArea,_overlayMap) + ,_right(parent,"OverlayRight", Qt::RightDockWidgetArea,_overlayMap) + ,_top(parent,"OverlayTop", Qt::TopDockWidgetArea,_overlayMap) + ,_bottom(parent,"OverlayBottom",Qt::BottomDockWidgetArea,_overlayMap) + ,_overlayInfos({&_left,&_right,&_top,&_bottom}) + ,_actions({&_actOverlay,&_actFloat,&_actClose}) + { + _Overlays = {OverlayTabWidget::_LeftOverlay, + OverlayTabWidget::_RightOverlay, + OverlayTabWidget::_TopOverlay, + OverlayTabWidget::_BottomOverlay}; + + connect(&_timer, &QTimer::timeout, [this](){onTimer();}); + _timer.setSingleShot(true); + + _reloadTimer.setSingleShot(true); + connect(&_reloadTimer, &QTimer::timeout, [this]() { + for (auto &o : _overlayInfos) { + o->tabWidget->restore(nullptr); // prevent saving setting first + o->removeWidget(); + } + for (auto &o : _overlayInfos) + o->restore(); + refresh(); + }); + + connect(qApp, &QApplication::focusChanged, host, &OverlayManager::onFocusChanged); + + Application::Instance->signalActivateView.connect([this](const MDIView *) { + refresh(); + }); + Application::Instance->signalInEdit.connect([this](const ViewProviderDocumentObject &) { + refresh(); + }); + Application::Instance->signalResetEdit.connect([this](const ViewProviderDocumentObject &) { + refresh(); + }); + + _actOverlay.setData(QStringLiteral("OBTN Overlay")); + _actFloat.setData(QStringLiteral("OBTN Float")); + _actClose.setData(QStringLiteral("OBTN Close")); + + retranslate(); + refreshIcons(); + + for(auto action : _actions) { + QObject::connect(action, &QAction::triggered, host, &OverlayManager::onAction); + } + for(auto o : _overlayInfos) { + for(auto action : o->tabWidget->actions()) { + QObject::connect(action, &QAction::triggered, host, &OverlayManager::onAction); + } + o->tabWidget->setTitleBar(createTitleBar(o->tabWidget)); + } + + QIcon px = BitmapFactory().pixmap("cursor-through"); + _cursor = QCursor(px.pixmap(32,32), 10, 9); + } + + void refreshIcons() + { + _actFloat.setIcon(BitmapFactory().pixmap("qss:overlay/float.svg")); + _actOverlay.setIcon(BitmapFactory().pixmap("qss:overlay/overlay.svg")); + _actClose.setIcon(BitmapFactory().pixmap("qss:overlay/close.svg")); + for (OverlayTabWidget *tabWidget : _Overlays) { + tabWidget->refreshIcons(); + for (auto handle : tabWidget->findChildren()) + handle->refreshIcons(); + } + } + + void interceptEvent(QWidget *, QEvent *); + + void setMouseTransparent(bool enabled) + { + if (mouseTransparent == enabled) + return; + mouseTransparent = enabled; + for (OverlayTabWidget *tabWidget : _Overlays) { + tabWidget->setAttribute( + Qt::WA_TransparentForMouseEvents, enabled); + tabWidget->touch(); + } + refresh(); + if(!enabled) + qApp->restoreOverrideCursor(); + else + qApp->setOverrideCursor(_cursor); + } + + bool toggleOverlay(QDockWidget *dock, ToggleMode toggle, int dockPos=Qt::NoDockWidgetArea) + { + if(!dock) + return false; + + auto it = _overlayMap.find(dock); + if(it != _overlayMap.end()) { + auto o = it->second; + switch(toggle) { + case ToggleMode::Transparent: + o->tabWidget->setTransparent(!o->tabWidget->isTransparent()); + break; + case ToggleMode::Unset: + case ToggleMode::Toggle: + _overlayMap.erase(it); + o->tabWidget->removeWidget(dock); + return false; + default: + break; + } + return true; + } + + if(toggle == ToggleMode::Unset) + return false; + + if(dockPos == Qt::NoDockWidgetArea) + dockPos = getMainWindow()->dockWidgetArea(dock); + OverlayInfo *o; + switch(dockPos) { + case Qt::LeftDockWidgetArea: + o = &_left; + break; + case Qt::RightDockWidgetArea: + o = &_right; + break; + case Qt::TopDockWidgetArea: + o = &_top; + break; + case Qt::BottomDockWidgetArea: + o = &_bottom; + break; + default: + return false; + } + if(toggle == ToggleMode::Check && !o->tabWidget->count()) + return false; + if(o->addWidget(dock)) { + if(toggle == ToggleMode::Transparent) + o->tabWidget->setTransparent(true); + } + refresh(); + return true; + } + + void refresh(QWidget *widget=nullptr, bool refreshStyle=false) + { + if(refreshStyle) { + OverlayStyleSheet::instance()->update(); + updateStyle = true; + } + + if(widget) { + auto tabWidget = findTabWidget(widget); + if(tabWidget && tabWidget->count()) { + for(auto o : _overlayInfos) { + if(tabWidget == o->tabWidget) { + tabWidget->touch(); + break; + } + } + } + } + _timer.start(OverlayParams::getDockOverlayDelay()); + } + + void save() + { + _left.save(); + _right.save(); + _top.save(); + _bottom.save(); + } + + void restore() + { + _left.restore(); + _right.restore(); + _top.restore(); + _bottom.restore(); + refresh(); + } + + void onTimer() + { + auto mdi = getMainWindow() ? getMainWindow()->getMdiArea() : nullptr; + if(!mdi) + return; + + auto focus = findTabWidget(qApp->focusWidget()); + if (focus && !focus->getSplitter()->isVisible()) + focus = nullptr; + auto active = findTabWidget(qApp->widgetAt(QCursor::pos())); + if (active && !active->getSplitter()->isVisible()) + active = nullptr; + OverlayTabWidget *reveal = nullptr; + + bool updateFocus = false; + bool updateActive = false; + + for(auto o : _overlayInfos) { + if(o->tabWidget->isTouched() || updateStyle) { + if(o->tabWidget == focus) + updateFocus = true; + else if(o->tabWidget == active) + updateActive = true; + else + o->tabWidget->setOverlayMode(true); + } + if(!o->tabWidget->getRevealTime().isNull()) { + if(o->tabWidget->getRevealTime()<= QTime::currentTime()) + o->tabWidget->setRevealTime(QTime()); + else + reveal = o->tabWidget; + } + } + updateStyle = false; + + if (focus) { + if (focus->isOverlaid(OverlayTabWidget::QueryOption::TransparencyChanged) || updateFocus) { + focus->setOverlayMode(false); + focus->raise(); + if(reveal == focus) + reveal = nullptr; + } else + focus->updateSplitterHandles(); + } + + if(active) { + if(active != focus && (active->isOverlaid(OverlayTabWidget::QueryOption::TransparencyChanged) || updateActive)) + active->setOverlayMode(false); + active->raise(); + if(reveal == active) + reveal = nullptr; + } + + if(reveal && !reveal->splitter->isVisible()) { + reveal->setOverlayMode(false); + reveal->raise(); + } + + for(auto o : _overlayInfos) { + if(o->tabWidget != focus + && o->tabWidget != active + && o->tabWidget != reveal + && o->tabWidget->count() + && !o->tabWidget->isOverlaid(OverlayTabWidget::QueryOption::TransparencyNotChanged)) + { + o->tabWidget->setOverlayMode(true); + } + } + + int w = mdi->geometry().width(); + int h = mdi->geometry().height(); + auto tabbar = mdi->findChild(); + if(tabbar) + h -= tabbar->height(); + + int naviCubeSize = NaviCube::getNaviCubeSize(); + int naviCorner = OverlayParams::getDockOverlayCheckNaviCube() ? + OverlayParams::getCornerNaviCube() : -1; + + QRect rect; + QRect rectBottom(0,0,0,0); + + rect = _bottom.tabWidget->getRect(); + + QSize ofs = _bottom.tabWidget->getOffset(); + int delta = _bottom.tabWidget->getSizeDelta(); + h -= ofs.height(); + + auto getCubeSize = [naviCubeSize](OverlayInfo &info) -> int { + float scale = info.tabWidget->_imageScale; + if (scale == 0.0) { + scale = info.tabWidget->titleBar->grab().devicePixelRatio(); + if (scale == 0.0) + scale = 1.0; + } + return naviCubeSize/scale + 10; + }; + + int cubeSize = getCubeSize(_bottom); + if(naviCorner == 2) + ofs.setWidth(ofs.width()+cubeSize); + int bw = w-10-ofs.width()-delta; + if(naviCorner == 3) + bw -= cubeSize; + if(bw < 10) + bw = 10; + + // Bottom width is maintain the same to reduce QTextEdit re-layout + // which may be expensive if there are lots of text, e.g. for + // ReportView or PythonConsole. + _bottom.tabWidget->setRect(QRect(ofs.width(),h-rect.height(),bw,rect.height())); + + if (_bottom.tabWidget->count() + && _bottom.tabWidget->isVisible() + && _bottom.tabWidget->getState() <= OverlayTabWidget::State::Normal) + rectBottom = _bottom.tabWidget->getRect(); + + QRect rectLeft(0,0,0,0); + rect = _left.tabWidget->getRect(); + + ofs = _left.tabWidget->getOffset(); + cubeSize = getCubeSize(_left); + if(naviCorner == 0) + ofs.setWidth(ofs.width()+cubeSize); + delta = _left.tabWidget->getSizeDelta()+rectBottom.height(); + if(naviCorner == 2 && cubeSize > rectBottom.height()) + delta += cubeSize - rectBottom.height(); + int lh = std::max(h-ofs.width()-delta, 10); + + _left.tabWidget->setRect(QRect(ofs.height(),ofs.width(),rect.width(),lh)); + + if (_left.tabWidget->count() + && _left.tabWidget->isVisible() + && _left.tabWidget->getState() <= OverlayTabWidget::State::Normal) + rectLeft = _left.tabWidget->getRect(); + + QRect rectRight(0,0,0,0); + rect = _right.tabWidget->getRect(); + + ofs = _right.tabWidget->getOffset(); + cubeSize = getCubeSize(_right); + if(naviCorner == 1) + ofs.setWidth(ofs.width()+cubeSize); + delta = _right.tabWidget->getSizeDelta()+rectBottom.height(); + if(naviCorner == 3 && cubeSize > rectBottom.height()) + delta += cubeSize - rectBottom.height(); + int rh = std::max(h-ofs.width()-delta, 10); + w -= ofs.height(); + + _right.tabWidget->setRect(QRect(w-rect.width(),ofs.width(),rect.width(),rh)); + + if (_right.tabWidget->count() + && _right.tabWidget->isVisible() + && _right.tabWidget->getState() <= OverlayTabWidget::State::Normal) + rectRight = _right.tabWidget->getRect(); + + rect = _top.tabWidget->getRect(); + + ofs = _top.tabWidget->getOffset(); + cubeSize = getCubeSize(_top); + delta = _top.tabWidget->getSizeDelta(); + if(naviCorner == 0) + rectLeft.setWidth(std::max(rectLeft.width(), cubeSize)); + else if(naviCorner == 1) + rectRight.setWidth(std::max(rectRight.width(), cubeSize)); + int tw = w-rectLeft.width()-rectRight.width()-ofs.width()-delta; + + _top.tabWidget->setRect(QRect(rectLeft.width()-ofs.width(),ofs.height(),tw,rect.height())); + } + + void setOverlayMode(OverlayMode mode) + { + switch(mode) { + case OverlayManager::OverlayMode::DisableAll: + case OverlayManager::OverlayMode::EnableAll: { + auto docks = getMainWindow()->findChildren(); + // put visible dock widget first + std::sort(docks.begin(),docks.end(), + [](const QDockWidget *a, const QDockWidget *b) { + return !a->visibleRegion().isEmpty() && b->visibleRegion().isEmpty(); + }); + for(auto dock : docks) { + if(mode == OverlayManager::OverlayMode::DisableAll) + toggleOverlay(dock, ToggleMode::Unset); + else + toggleOverlay(dock, ToggleMode::Set); + } + return; + } + case OverlayManager::OverlayMode::ToggleAll: + for(auto o : _overlayInfos) { + if(o->tabWidget->count()) { + setOverlayMode(OverlayManager::OverlayMode::DisableAll); + return; + } + } + setOverlayMode(OverlayManager::OverlayMode::EnableAll); + return; + case OverlayManager::OverlayMode::TransparentAll: { + bool found = false; + for(auto o : _overlayInfos) { + if(o->tabWidget->count()) + found = true; + } + if(!found) + setOverlayMode(OverlayManager::OverlayMode::EnableAll); + } + // fall through + case OverlayManager::OverlayMode::TransparentNone: + for(auto o : _overlayInfos) + o->tabWidget->setTransparent(mode == OverlayManager::OverlayMode::TransparentAll); + refresh(); + return; + case OverlayManager::OverlayMode::ToggleTransparentAll: + for(auto o : _overlayInfos) { + if(o->tabWidget->count() && o->tabWidget->isTransparent()) { + setOverlayMode(OverlayManager::OverlayMode::TransparentNone); + return; + } + } + setOverlayMode(OverlayManager::OverlayMode::TransparentAll); + return; + case OverlayManager::OverlayMode::ToggleLeft: + if (OverlayTabWidget::_LeftOverlay->isVisible()) + OverlayTabWidget::_LeftOverlay->setState(OverlayTabWidget::State::Hidden); + else + OverlayTabWidget::_LeftOverlay->setState(OverlayTabWidget::State::Showing); + break; + case OverlayManager::OverlayMode::ToggleRight: + if (OverlayTabWidget::_RightOverlay->isVisible()) + OverlayTabWidget::_RightOverlay->setState(OverlayTabWidget::State::Hidden); + else + OverlayTabWidget::_RightOverlay->setState(OverlayTabWidget::State::Showing); + break; + case OverlayManager::OverlayMode::ToggleTop: + if (OverlayTabWidget::_TopOverlay->isVisible()) + OverlayTabWidget::_TopOverlay->setState(OverlayTabWidget::State::Hidden); + else + OverlayTabWidget::_TopOverlay->setState(OverlayTabWidget::State::Showing); + break; + case OverlayManager::OverlayMode::ToggleBottom: + if (OverlayTabWidget::_BottomOverlay->isVisible()) + OverlayTabWidget::_BottomOverlay->setState(OverlayTabWidget::State::Hidden); + else + OverlayTabWidget::_BottomOverlay->setState(OverlayTabWidget::State::Showing); + break; + default: + break; + } + + ToggleMode m; + QDockWidget *dock = nullptr; + for(auto w=qApp->widgetAt(QCursor::pos()); w; w=w->parentWidget()) { + dock = qobject_cast(w); + if(dock) + break; + auto tabWidget = qobject_cast(w); + if(tabWidget) { + dock = tabWidget->currentDockWidget(); + if(dock) + break; + } + } + if(!dock) { + for(auto w=qApp->focusWidget(); w; w=w->parentWidget()) { + dock = qobject_cast(w); + if(dock) + break; + } + } + + switch(mode) { + case OverlayManager::OverlayMode::ToggleActive: + m = ToggleMode::Toggle; + break; + case OverlayManager::OverlayMode::ToggleTransparent: + m = ToggleMode::Transparent; + break; + case OverlayManager::OverlayMode::EnableActive: + m = ToggleMode::Set; + break; + case OverlayManager::OverlayMode::DisableActive: + m = ToggleMode::Unset; + break; + default: + return; + } + toggleOverlay(dock, m); + } + + void onToggleDockWidget(QDockWidget *dock, int checked) + { + if(!dock) + return; + + auto it = _overlayMap.find(dock); + if(it == _overlayMap.end()) + return; + + OverlayTabWidget *tabWidget = it->second->tabWidget; + int index = tabWidget->dockWidgetIndex(dock); + if(index < 0) + return; + auto sizes = tabWidget->getSplitter()->sizes(); + while(index >= sizes.size()) + sizes.append(0); + + if (checked < -1) + checked = 0; + else if (checked == 3) { + checked = 1; + sizes[index] = 0; // force expand the tab in full + } else if (checked <= 1) { + if (sizes[index] != 0 && tabWidget->isHidden()) + checked = 1; + else { + // child widget inside splitter by right shouldn't been hidden, so + // we ignore the given toggle bit, but rely on its splitter size to + // decide. + checked = sizes[index] == 0 ? 1 : 0; + } + } + if(sizes[index]==0) { + if (!checked) + return; + tabWidget->setCurrent(dock); + tabWidget->onCurrentChanged(tabWidget->dockWidgetIndex(dock)); + } else if (!checked) { + if (sizes[index] > 0 && sizes.size() > 1) { + int newtotal = 0; + int total = 0; + auto newsizes = sizes; + newsizes[index] = 0; + for (int i=0; idockWidget(i); + auto it = tabWidget->_sizemap.find(d); + if (it == tabWidget->_sizemap.end()) + newsizes[i] = 0; + else { + if (newtotal == 0) + tabWidget->setCurrent(d); + newsizes[i] = it->second; + newtotal += it->second; + } + } + } + if (!newtotal) { + int expand = 0; + for (int i=0; i 0) { + ++expand; + break; + } + } + if (expand) { + int expansion = sizes[index]; + int step = expansion / expand; + for (int i=0; isplitter->setSizes(newsizes); + tabWidget->saveTabs(); + } + } + for (int i=0; i_sizemap[tabWidget->dockWidget(i)] = sizes[i]; + } + if (checked) + tabWidget->setRevealTime(QTime::currentTime().addMSecs( + OverlayParams::getDockOverlayRevealDelay())); + refresh(); + } + + void onFocusChanged(QWidget *, QWidget *) { + refresh(); + } + + void setupTitleBar(QDockWidget *dock) + { + if(!dock->titleBarWidget()) + dock->setTitleBarWidget(createTitleBar(dock)); + } + + QWidget *createTitleBar(QWidget *parent) + { + OverlayTitleBar *widget = new OverlayTitleBar(parent); + widget->setObjectName(QStringLiteral("OverlayTitle")); + QList actions; + if (auto tabWidget = qobject_cast(parent)) + actions = tabWidget->actions(); + else + actions = _actions; + widget->setTitleItem(OverlayTabWidget::prepareTitleWidget(widget, actions)); + return widget; + } + + void onAction(QAction *action) { + if(action == &_actOverlay) { + OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleActive); + } else if(action == &_actFloat || action == &_actClose) { + for(auto w=qApp->widgetAt(QCursor::pos());w;w=w->parentWidget()) { + auto dock = qobject_cast(w); + if(!dock) + continue; + setFocusView(); + if(action == &_actClose) { + dock->toggleViewAction()->activate(QAction::Trigger); + } else { + auto it = _overlayMap.find(dock); + if(it != _overlayMap.end()) { + it->second->tabWidget->removeWidget(dock); + getMainWindow()->addDockWidget(it->second->dockArea, dock); + _overlayMap.erase(it); + dock->show(); + dock->setFloating(true); + refresh(); + } else + dock->setFloating(!dock->isFloating()); + } + return; + } + } else { + auto tabWidget = qobject_cast(action->parent()); + if(tabWidget) + tabWidget->onAction(action); + } + } + + void retranslate() + { + _actOverlay.setToolTip(QObject::tr("Toggle overlay")); + _actFloat.setToolTip(QObject::tr("Toggle floating window")); + _actClose.setToolTip(QObject::tr("Close dock window")); + } + + void floatDockWidget(QDockWidget *dock) + { + setFocusView(); + auto it = _overlayMap.find(dock); + if (it != _overlayMap.end()) { + it->second->tabWidget->removeWidget(dock); + _overlayMap.erase(it); + } + dock->setFloating(true); + dock->show(); + } + + // Warning, the caller may be deleted during the call. So do not pass + // parameter using reference, pass by value instead. + void dragDockWidget(QPoint pos, + QWidget *srcWidget, + QPoint dragOffset, + QSize dragSize, + bool drop) + { + if (!getMainWindow()) + return; + auto mdi = getMainWindow()->getMdiArea(); + if (!mdi) + return; + + auto dock = qobject_cast(srcWidget); + if (dock && dock->isFloating()) + dock->move(pos - dragOffset); + + OverlayTabWidget *src = nullptr; + int srcIndex = -1; + if (dock) { + auto it = _overlayMap.find(dock); + if (it != _overlayMap.end()) { + src = it->second->tabWidget; + srcIndex = src->dockWidgetIndex(dock); + } + } + else { + src = qobject_cast(srcWidget); + if (!src) + return; + for(int size : src->getSplitter()->sizes()) { + ++srcIndex; + if (size) { + dock = src->dockWidget(srcIndex); + break; + } + } + if (!dock) + return; + } + + OverlayTabWidget *tabWidget = nullptr; + int resizeOffset = 0; + int index = -1; + QRect rect; + QRect rectMain(getMainWindow()->mapToGlobal(QPoint()), + getMainWindow()->size()); + QRect rectMdi(mdi->mapToGlobal(QPoint()), mdi->size()); + + for (OverlayTabWidget *overlay : _Overlays) { + rect = QRect(mdi->mapToGlobal(overlay->rectOverlay.topLeft()), + overlay->rectOverlay.size()); + + QSize size(rect.width()*3/4, rect.height()*3/4); + QSize sideSize(rect.width()/4, rect.height()/4); + + int dockArea = overlay->getDockArea(); + + if (dockArea == Qt::BottomDockWidgetArea) { + if (pos.y() < rect.bottom() && rect.bottom() - pos.y() < sideSize.height()) { + rect.setTop(rect.bottom() - OverlayParams::getDockOverlayMinimumSize()); + rect.setBottom(rectMain.bottom()); + rect.setLeft(rectMdi.left()); + rect.setRight(rectMdi.right()); + tabWidget = overlay; + index = -2; + break; + } + } + if (dockArea == Qt::LeftDockWidgetArea) { + if (pos.x() >= rect.left() && pos.x() - rect.left() < sideSize.width()) { + rect.setRight(rect.left() + OverlayParams::getDockOverlayMinimumSize()); + rect.setLeft(rectMain.left()); + rect.setTop(rectMdi.top()); + rect.setBottom(rectMdi.bottom()); + tabWidget = overlay; + index = -2; + break; + } + } + else if (dockArea == Qt::RightDockWidgetArea) { + if (pos.x() < rect.right() && rect.right() - pos.x() < sideSize.width()) { + rect.setLeft(rect.right() - OverlayParams::getDockOverlayMinimumSize()); + rect.setRight(rectMain.right()); + rect.setTop(rectMdi.top()); + rect.setBottom(rectMdi.bottom()); + tabWidget = overlay; + index = -2; + break; + } + } + else if (dockArea == Qt::TopDockWidgetArea) { + if (pos.y() >= rect.top() && pos.y() - rect.top() < sideSize.height()) { + rect.setBottom(rect.top() + OverlayParams::getDockOverlayMinimumSize()); + rect.setTop(rectMain.top()); + rect.setLeft(rectMdi.left()); + rect.setRight(rectMdi.right()); + tabWidget = overlay; + index = -2; + break; + } + } + + switch(dockArea) { + case Qt::LeftDockWidgetArea: + rect.setWidth(size.width()); + break; + case Qt::RightDockWidgetArea: + rect.setLeft(rect.right() - size.width()); + break; + case Qt::TopDockWidgetArea: + rect.setHeight(size.height()); + break; + default: + rect.setTop(rect.bottom() - size.height()); + break; + } + + if (!rect.contains(pos)) + continue; + + tabWidget = overlay; + index = -1; + int i = -1; + + for (int size : overlay->getSplitter()->sizes()) { + ++i; + auto handle = overlay->getSplitter()->handle(i); + QWidget *w = overlay->dockWidget(i); + if (!handle || !w) + continue; + if (handle->rect().contains(handle->mapFromGlobal(pos))) { + QPoint pt = handle->mapToGlobal(QPoint()); + QSize s = handle->size(); + if (!size) + size = OverlayParams::getDockOverlayMinimumSize(); + if (tabWidget != src) + size /= 2; + if (overlay->getSplitter()->orientation() == Qt::Vertical) + s.setHeight(s.height() + size); + else + s.setWidth(s.width() + size); + rect = QRect(pt, s); + index = i; + break; + } + if (!size) + continue; + if (w->rect().contains(w->mapFromGlobal(pos))) { + QPoint pt = overlay->getSplitter()->mapToGlobal(w->pos()); + rect = QRect(pt, w->size()); + if (tabWidget != src) { + if (overlay->getSplitter()->orientation() == Qt::Vertical) { + if (pos.y() > pt.y() + size/2) { + rect.setTop(rect.top() + size/2); + resizeOffset = -1; + ++i; + } + else + rect.setHeight(size/2); + } + else if (pos.x() > pt.x() + size/2) { + rect.setLeft(rect.left() + size/2); + resizeOffset = -1; + ++i; + } + else + rect.setWidth(size/2); + } + index = i; + break; + } + } + break; + }; + + OverlayTabWidget *dst = nullptr; + int dstIndex = -1; + QDockWidget *dstDock = nullptr; + Qt::DockWidgetArea dstDockArea; + + if (!tabWidget) { + rect = QRect(pos - dragOffset, dragSize); + if (rect.width() < 50) + rect.setWidth(50); + if (rect.height() < 50) + rect.setHeight(50); + + for(auto dockWidget : getMainWindow()->findChildren()) { + if (dockWidget == dock + || !dockWidget->isVisible() + || dockWidget->isFloating() + || _overlayMap.count(dockWidget)) + continue; + if (dockWidget->rect().contains(dockWidget->mapFromGlobal(pos))) { + dstDock = dockWidget; + dstDockArea = getMainWindow()->dockWidgetArea(dstDock); + rect = QRect(dockWidget->mapToGlobal(QPoint()), + dockWidget->size()); + break; + } + } + } + else { + dst = tabWidget; + dstIndex = index; + if (dstIndex == -1) + rect = QRect(mdi->mapToGlobal(tabWidget->rectOverlay.topLeft()), + tabWidget->rectOverlay.size()); + } + + bool outside = false; + if (!rectMain.contains(pos)) { + outside = true; + if (drop) { + if (!dock->isFloating()) { + if (src) { + _overlayMap.erase(dock); + src->removeWidget(dock); + } + setFocusView(); + dock->setFloating(true); + dock->move(pos - dragOffset); + dock->show(); + } + if (OverlayTabWidget::_DragFloating) + OverlayTabWidget::_DragFloating->hide(); + } else if (!dock->isFloating()) { + if (!OverlayTabWidget::_DragFloating) { + OverlayTabWidget::_DragFloating = new QDockWidget(getMainWindow()); + OverlayTabWidget::_DragFloating->setFloating(true); + } + OverlayTabWidget::_DragFloating->resize(dock->size()); + OverlayTabWidget::_DragFloating->setWindowTitle(dock->windowTitle()); + OverlayTabWidget::_DragFloating->show(); + OverlayTabWidget::_DragFloating->move(pos - dragOffset); + } + if (OverlayTabWidget::_DragFrame) + OverlayTabWidget::_DragFrame->hide(); + return; + + } else if (!drop && OverlayTabWidget::_DragFrame && !OverlayTabWidget::_DragFrame->isVisible()) { + OverlayTabWidget::_DragFrame->raise(); + OverlayTabWidget::_DragFrame->show(); + if (OverlayTabWidget::_DragFloating) + OverlayTabWidget::_DragFloating->hide(); + } + + int insertDock = 0; // 0: tabify, -1: insert before, 1: insert after + if (!dst && dstDock) { + switch(dstDockArea) { + case Qt::LeftDockWidgetArea: + case Qt::RightDockWidgetArea: + if (pos.y() < rect.top() + rect.height()/4) { + insertDock = -1; + rect.setBottom(rect.top() + rect.height()/2); + } + else if (pos.y() > rect.bottom() - rect.height()/4) { + insertDock = 1; + int height = rect.height(); + rect.setTop(rect.bottom() - height/4); + rect.setHeight(height/2); + } + break; + default: + if (pos.x() < rect.left() + rect.width()/4) { + insertDock = -1; + rect.setRight(rect.left() + rect.width()/2); + } + else if (pos.x() > rect.right() - rect.width()/4) { + insertDock = 1; + int width = rect.width(); + rect.setLeft(rect.right() - width/4); + rect.setWidth(width/2); + } + break; + } + } + + if (!drop) { + if (!OverlayTabWidget::_DragFrame) + OverlayTabWidget::_DragFrame = new OverlayDragFrame(getMainWindow()); + + rect = QRect(getMainWindow()->mapFromGlobal(rect.topLeft()), rect.size()); + OverlayTabWidget::_DragFrame->setGeometry(rect); + if (!outside && !OverlayTabWidget::_DragFrame->isVisible()) { + OverlayTabWidget::_DragFrame->raise(); + OverlayTabWidget::_DragFrame->show(); + } + return; + } + + if (src && src == dst && dstIndex != -2){ + auto splitter = src->getSplitter(); + if (dstIndex == -1) { + src->tabBar()->moveTab(srcIndex, 0); + src->setCurrentIndex(0); + src->onCurrentChanged(0); + } + else if (srcIndex != dstIndex) { + auto sizes = splitter->sizes(); + src->tabBar()->moveTab(srcIndex, dstIndex); + splitter->setSizes(sizes); + src->onSplitterResize(dstIndex); + src->saveTabs(); + } + return; + } + + if (src) { + _overlayMap.erase(dock); + src->removeWidget(dock); + } + + setFocusView(); + if (!dst) { + if (dstDock) { + dock->setFloating(false); + if(insertDock == 0) + getMainWindow()->tabifyDockWidget(dstDock, dock); + else { + std::map docks; + for(auto dockWidget : getMainWindow()->findChildren()) { + if (dockWidget == dock + || !dockWidget->isVisible() + || getMainWindow()->dockWidgetArea(dockWidget) != dstDockArea) + continue; + auto pos = dockWidget->mapToGlobal(QPoint(0,0)); + if (dstDockArea == Qt::LeftDockWidgetArea || dstDockArea == Qt::RightDockWidgetArea) + docks[pos.y()] = dockWidget; + else + docks[pos.x()] = dockWidget; + } + auto it = docks.begin(); + for (;it != docks.end(); ++it) { + if (it->second == dstDock) + break; + } + if (insertDock > 0 && it != docks.end()) + ++it; + for (auto iter = it; iter != docks.end(); ++iter) + getMainWindow()->removeDockWidget(iter->second); + getMainWindow()->addDockWidget(dstDockArea, dock); + dock->show(); + for (auto iter = it; iter != docks.end(); ++iter) { + getMainWindow()->addDockWidget(dstDockArea, iter->second); + iter->second->show(); + } + } + } + else { + dock->setFloating(true); + if(OverlayTabWidget::_DragFrame) + dock->setGeometry(QRect(OverlayTabWidget::_DragFrame->mapToGlobal(QPoint()), + OverlayTabWidget::_DragFrame->size())); + } + dock->show(); + } + else if (dstIndex == -2) { + getMainWindow()->addDockWidget(dst->getDockArea(), dock); + dock->setFloating(false); + } + else { + auto sizes = dst->getSplitter()->sizes(); + for (auto o : _overlayInfos) { + if (o->tabWidget == dst) { + o->addWidget(dock, false); + break; + } + } + index = dst->dockWidgetIndex(dock); + if (index >= 0) { + if (dstIndex < 0) { + dst->tabBar()->moveTab(index, 0); + dst->setCurrentIndex(0); + dst->onCurrentChanged(0); + } + else { + dst->tabBar()->moveTab(index, dstIndex); + int size = sizes[dstIndex + resizeOffset]; + if (size) { + size /= 2; + sizes[dstIndex + resizeOffset] = size; + } + else + size = 50; + sizes.insert(dstIndex, size); + dst->setCurrentIndex(dstIndex); + dst->getSplitter()->setSizes(sizes); + dst->onSplitterResize(dstIndex); + dst->saveTabs(); + } + dst->setRevealTime(QTime::currentTime().addMSecs( + OverlayParams::getDockOverlayRevealDelay())); + } + } + + refresh(); + } + + void raiseAll() + { + if (raising) + return; + Base::StateLocker guard(raising); + for (OverlayTabWidget *tabWidget : _Overlays) { + if (tabWidget->isVisible()) + tabWidget->raise(); + } + } + + void registerDockWidget(const QString &name, OverlayTabWidget *widget) { + if (name.size()) + _dockWidgetNameMap[name] = widget; + } + + void unregisterDockWidget(const QString &name, OverlayTabWidget *widget) { + auto it = _dockWidgetNameMap.find(name); + if (it != _dockWidgetNameMap.end() && it->second == widget) + _dockWidgetNameMap.erase(it); + } + + void reload(OverlayManager::ReloadMode mode) { + if (mode == OverlayManager::ReloadMode::ReloadResume) + curReloadMode = mode = OverlayManager::ReloadMode::ReloadPending; + if (mode == OverlayManager::ReloadMode::ReloadPending) { + if (curReloadMode != OverlayManager::ReloadMode::ReloadPause) { + FC_LOG("reload pending"); + _reloadTimer.start(100); + } + } + curReloadMode = mode; + if (mode == OverlayManager::ReloadMode::ReloadPause) { + FC_LOG("reload paused"); + _reloadTimer.stop(); + } + } +}; + + +static OverlayManager * _instance; + +OverlayManager* OverlayManager::instance() +{ + if ( _instance == 0 ) + _instance = new OverlayManager; + return _instance; +} + +void OverlayManager::destruct() +{ + delete _instance; + _instance = 0; +} + +OverlayManager::OverlayManager() +{ + d = new Private(this, getMainWindow()); + qApp->installEventFilter(this); +} + +OverlayManager::~OverlayManager() +{ + delete d; +} + +void OverlayManager::setOverlayMode(OverlayMode mode) +{ + d->setOverlayMode(mode); +} + + +void OverlayManager::initDockWidget(QDockWidget *dw) +{ + QObject::connect(dw->toggleViewAction(), &QAction::triggered, + this, &OverlayManager::onToggleDockWidget); + QObject::connect(dw, &QDockWidget::visibilityChanged, + this, &OverlayManager::onDockVisibleChange); + if (auto widget = dw->widget()) { + QObject::connect(widget, &QWidget::windowTitleChanged, + this, &OverlayManager::onDockWidgetTitleChange); + } + + QString name = dw->objectName(); + if (name.size()) { + auto it = d->_dockWidgetNameMap.find(dw->objectName()); + if (it != d->_dockWidgetNameMap.end()) { + for (auto o : d->_overlayInfos) { + if (o->tabWidget == it->second) { + o->addWidget(dw, true); + d->onToggleDockWidget(dw, 3); + break; + } + } + d->refresh(); + } + } +} + +void OverlayManager::setupDockWidget(QDockWidget *dw, int dockArea) +{ + (void)dockArea; + d->setupTitleBar(dw); +} + +void OverlayManager::unsetupDockWidget(QDockWidget *dw) +{ + d->toggleOverlay(dw, ToggleMode::Unset); +} + +void OverlayManager::onToggleDockWidget(bool checked) +{ + auto action = qobject_cast(sender()); + if(!action) + return; + d->onToggleDockWidget(qobject_cast(action->parent()), checked); +} + +void OverlayManager::onDockVisibleChange(bool visible) +{ + auto dock = qobject_cast(sender()); + if(!dock) + return; + FC_TRACE("dock " << dock->objectName().toUtf8().constData() + << " visible change " << visible << ", " << dock->isVisible()); +} + +void OverlayManager::onTaskViewUpdate() +{ + auto taskview = qobject_cast(sender()); + if (!taskview) + return; + QDockWidget *dock = nullptr; + for (QWidget *w=taskview; w; w=w->parentWidget()) { + if ((dock = qobject_cast(w))) + break; + } + if (dock) { + auto it = d->_overlayMap.find(dock); + if (it == d->_overlayMap.end() + || it->second->tabWidget->count() < 2 + || it->second->tabWidget->getAutoMode() != OverlayTabWidget::AutoMode::TaskShow) + return; + d->onToggleDockWidget(dock, taskview->isEmpty() ? -2 : 2); + } +} + +void OverlayManager::onDockWidgetTitleChange(const QString &title) +{ + if (title.isEmpty()) + return; + auto widget = qobject_cast(sender()); + QDockWidget *dock = nullptr; + for (QWidget *w=widget; w; w=w->parentWidget()) { + if ((dock = qobject_cast(w))) + break; + } + if(!dock) + return; + auto tabWidget = findTabWidget(dock); + if (!tabWidget) + return; + int index = tabWidget->dockWidgetIndex(dock); + if (index >= 0) + tabWidget->setTabText(index, title); +} + +void OverlayManager::retranslate() +{ + d->retranslate(); +} + +void OverlayManager::refreshIcons() +{ + d->refreshIcons(); +} + +void OverlayManager::reload(ReloadMode mode) +{ + d->reload(mode); +} + +void OverlayManager::raiseAll() +{ + d->raiseAll(); +} + +static inline bool +isNear(const QPoint &a, const QPoint &b, int tol = 16) +{ + QPoint d = a - b; + return d.x()*d.x() + d.y()*d.y() < tol; +} + +bool OverlayManager::eventFilter(QObject *o, QEvent *ev) +{ + if (d->intercepting || !getMainWindow() || !o->isWidgetType()) + return false; + auto mdi = getMainWindow()->getMdiArea(); + if (!mdi) + return false; + + switch(ev->type()) { + case QEvent::Enter: + if (Selection().hasPreselection() + && !qobject_cast(o) + && !isUnderOverlay()) + { + Selection().rmvPreselect(); + } + break; + case QEvent::ZOrderChange: { + if(!d->raising && getMainWindow() && o == mdi) { + // On Windows, for some reason, it will raise mdi window on tab + // change in any docked widget, which will then obscure any overlay + // docked widget here. + for (auto child : getMainWindow()->children()) { + if (child == mdi || qobject_cast(child)) { + QMetaObject::invokeMethod(this, "raiseAll", Qt::QueuedConnection); + break; + } + if (qobject_cast(child)) + break; + } + } + break; + } + case QEvent::Resize: { + if(getMainWindow() && o == mdi) + refresh(); + return false; + } + case QEvent::KeyPress: { + QKeyEvent *ke = static_cast(ev); + bool accepted = false; + if (ke->modifiers() == Qt::NoModifier && ke->key() == Qt::Key_Escape) { + if (d->mouseTransparent) { + d->setMouseTransparent(false); + accepted = true; + } else if (OverlayTabWidget::_Dragging && OverlayTabWidget::_Dragging != o) { + if (auto titleBar = qobject_cast(OverlayTabWidget::_Dragging)) + titleBar->endDrag(); + else if (auto splitHandle = qobject_cast(OverlayTabWidget::_Dragging)) + splitHandle->endDrag(); + } + else if (!OverlayTabWidget::_Dragging) { + for (OverlayTabWidget *tabWidget : _Overlays) { + if (tabWidget->onEscape()) + accepted = true; + } + } + } + if (accepted) { + ke->accept(); + return true; + } + break; + } + case QEvent::Paint: + if (auto widget = qobject_cast(o)) { + // QAbstractItemView optimize redraw using its item delegate's + // visualRect(). However, if we are using QGraphicsEffects, the + // effect may touch areas outside of visualRect(), so + // OverlayTabWidget offers a timer for a delayed redraw. + widget = qobject_cast(widget->parentWidget()); + if(widget) { + auto tabWidget = findTabWidget(widget, true); + if (tabWidget) + tabWidget->scheduleRepaint(); + } + } + break; + // case QEvent::NativeGesture: + case QEvent::Wheel: + if (!OverlayParams::getDockOverlayWheelPassThrough()) + return false; + // fall through + case QEvent::ContextMenu: { + auto cev = static_cast(ev); + if (cev->reason() != QContextMenuEvent::Mouse) + return false; + } // fall through + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonPress: + case QEvent::MouseMove: { + if (OverlayTabWidget::_Dragging && OverlayTabWidget::_Dragging != o) { + if (auto titleBar = qobject_cast(OverlayTabWidget::_Dragging)) + titleBar->endDrag(); + else if (auto splitHandle = qobject_cast(OverlayTabWidget::_Dragging)) + splitHandle->endDrag(); + } + QWidget *grabber = QWidget::mouseGrabber(); + d->lastIntercept = nullptr; + if (d->mouseTransparent || (grabber && grabber != d->_trackingOverlay)) + return false; + if (qobject_cast(o)) + return false; + if (ev->type() != QEvent::Wheel) { + if (qobject_cast(o)) + return false; + } else if (qobject_cast(o)) + return false; + + if (d->_trackingWidget) { + if(!isTreeViewDragging()) + d->interceptEvent(d->_trackingWidget, ev); + if(isTreeViewDragging() + || ev->type() == QEvent::MouseButtonRelease + || QApplication::mouseButtons() == Qt::NoButton) + { + d->_trackingWidget = nullptr; + if (d->_trackingOverlay == grabber + && ev->type() == QEvent::MouseButtonRelease) + { + d->_trackingOverlay = nullptr; + // Must not release mouse here, because otherwise the event + // will find its way to the actual widget under cursor. + // Instead, return false here to let OverlayTabWidget::event() + // release the mouse. + return false; + } + if(d->_trackingOverlay && grabber == d->_trackingOverlay) + d->_trackingOverlay->releaseMouse(); + d->_trackingOverlay = nullptr; + } + // Must return true here to filter the event, otherwise ContextMenu + // event may be routed to the actual widget. Other types of event + // probably do not matter. + return true; + } else if (ev->type() != QEvent::MouseButtonPress + && ev->type() != QEvent::MouseButtonDblClick + && QApplication::mouseButtons() != Qt::NoButton) + return false; + + if(isTreeViewDragging()) + return false; + + OverlayTabWidget *activeTabWidget = nullptr; + int hit = 0; + QPoint pos = QCursor::pos(); + if (OverlayParams::getDockOverlayAutoMouseThrough() + && ev->type() != QEvent::Wheel + && pos == d->_lastPos) + { + hit = 1; + } else if (ev->type() == QEvent::Wheel + && !d->wheelDelay.isNull() + && (isNear(pos, d->wheelPos) || d->wheelDelay > QTime::currentTime())) + { + d->wheelDelay = QTime::currentTime().addMSecs( + OverlayParams::getDockOverlayWheelDelay()); + d->wheelPos = pos; + return false; + } else { + for(auto widget=qApp->widgetAt(pos); widget ; widget=widget->parentWidget()) { + int type = widget->windowType(); + if (type != Qt::Widget && type != Qt::Window) { + if (type != Qt::SubWindow) + hit = -1; + break; + } + if (ev->type() == QEvent::Wheel) { + if (qobject_cast(widget)) + activeTabWidget = qobject_cast(widget->parentWidget()); + else if (qobject_cast(widget)) { + auto parent = widget->parentWidget(); + if (parent) + activeTabWidget = qobject_cast(parent->parentWidget()); + } + if (activeTabWidget) + break; + } + if (auto tabWidget = qobject_cast(widget)) { + if (tabWidget->testAlpha(pos, ev->type() == QEvent::Wheel ? 4 : 1) == 0) + activeTabWidget = tabWidget; + break; + } + } + if (activeTabWidget) { + hit = OverlayParams::getDockOverlayAutoMouseThrough(); + d->_lastPos = pos; + } + } + + for (OverlayTabWidget *tabWidget : _Overlays) { + if (tabWidget->getProxyWidget()->hitTest(pos) == OverlayProxyWidget::HitTest::HitInner) { + if ((ev->type() == QEvent::MouseButtonRelease + || ev->type() == QEvent::MouseButtonPress) + && static_cast(ev)->button() == Qt::LeftButton) + { + if (ev->type() == QEvent::MouseButtonRelease) + tabWidget->getProxyWidget()->onMousePress(); + return true; + } + } + } + + if (hit <= 0) { + d->_lastPos.setX(INT_MAX); + if (ev->type() == QEvent::Wheel) { + d->wheelDelay = QTime::currentTime().addMSecs(OverlayParams::getDockOverlayWheelDelay()); + d->wheelPos = pos; + } + return false; + } + + auto hitWidget = mdi->childAt(mdi->mapFromGlobal(pos)); + if (!hitWidget) + return false; + + if (!activeTabWidget) + activeTabWidget = findTabWidget(qApp->widgetAt(QCursor::pos())); + if(!activeTabWidget || !activeTabWidget->isTransparent()) + return false; + + ev->accept(); + d->interceptEvent(hitWidget, ev); + if (ev->isAccepted() && ev->type() == QEvent::MouseButtonPress) { + hitWidget->setFocus(); + d->_trackingWidget = hitWidget; + d->_trackingOverlay = activeTabWidget; + d->_trackingOverlay->grabMouse(); + } + return true; + } + default: + break; + } + return false; +} + +namespace { +class MouseGrabberGuard { +public: + MouseGrabberGuard(QWidget *grabber) + { + if (grabber && grabber == QWidget::mouseGrabber()) { + _grabber = grabber; + _grabber->releaseMouse(); + } + } + ~MouseGrabberGuard() + { + if (_grabber) + _grabber->grabMouse(); + } + + QPointer _grabber; +}; +}// anonymous namespace + +void OverlayManager::Private::interceptEvent(QWidget *widget, QEvent *ev) +{ + Base::StateLocker guard(this->intercepting); + MouseGrabberGuard grabberGuard(_trackingOverlay); + + lastIntercept = nullptr; + auto getChildAt = [](QWidget *w, const QPoint &pos) { + QWidget *res = w; + for (; w; w = w->childAt(w->mapFromGlobal(pos))) { + if (auto scrollArea = qobject_cast(w)) { + return scrollArea->viewport(); + } + res = w; + } + return res; + }; + + switch(ev->type()) { + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonDblClick: { + auto me = static_cast(ev); + lastIntercept = getChildAt(widget, me->globalPos()); + QMouseEvent mouseEvent(ev->type(), + lastIntercept->mapFromGlobal(me->globalPos()), + me->screenPos(), + me->button(), + me->buttons(), + me->modifiers()); + QApplication::sendEvent(lastIntercept, &mouseEvent); + break; + } + case QEvent::Wheel: { + auto we = static_cast(ev); + lastIntercept = getChildAt(widget, we->globalPos()); +#if QT_VERSION >= QT_VERSION_CHECK(5,12,0) + QWheelEvent wheelEvent(lastIntercept->mapFromGlobal(we->globalPos()), + we->globalPos(), + we->pixelDelta(), + we->angleDelta(), + we->buttons(), + we->modifiers(), + we->phase(), + we->inverted(), + we->source()); +#else + QWheelEvent wheelEvent(lastIntercept->mapFromGlobal(we->globalPos()), + we->globalPos(), + we->pixelDelta(), + we->angleDelta(), + 0, + Qt::Vertical, + we->buttons(), + we->modifiers(), + we->phase(), + we->source(), + we->inverted()); +#endif + QApplication::sendEvent(lastIntercept, &wheelEvent); + break; + } + case QEvent::ContextMenu: { + auto ce = static_cast(ev); + lastIntercept = getChildAt(widget, ce->globalPos()); + QContextMenuEvent contextMenuEvent(ce->reason(), + lastIntercept->mapFromGlobal(ce->globalPos()), + ce->globalPos()); + QApplication::sendEvent(lastIntercept, &contextMenuEvent); + break; + } + default: + break; + } +} + +void OverlayManager::refresh(QWidget *widget, bool refreshStyle) +{ + d->refresh(widget, refreshStyle); +} + +void OverlayManager::setMouseTransparent(bool enabled) +{ + d->setMouseTransparent(enabled); +} + +bool OverlayManager::isMouseTransparent() const +{ + return d->mouseTransparent; +} + +bool OverlayManager::isUnderOverlay() const +{ + return OverlayParams::getDockOverlayAutoMouseThrough() + && findTabWidget(qApp->widgetAt(QCursor::pos()), true); +} + +void OverlayManager::save() +{ + d->save(); +} + +void OverlayManager::restore() +{ + d->restore(); + + if (Control().taskPanel()) + connect(Control().taskPanel(), &TaskView::TaskView::taskUpdate, + this, &OverlayManager::onTaskViewUpdate); +} + +void OverlayManager::setupTitleBar(QDockWidget *dock) +{ + d->setupTitleBar(dock); +} + +void OverlayManager::onFocusChanged(QWidget *old, QWidget *now) +{ + d->onFocusChanged(old, now); +} + +void OverlayManager::onAction() +{ + QAction *action = qobject_cast(sender()); + if(action) + d->onAction(action); +} + +void OverlayManager::dragDockWidget(const QPoint &pos, + QWidget *src, + const QPoint &offset, + const QSize &size, + bool drop) +{ + d->dragDockWidget(pos, src, offset, size, drop); +} + +void OverlayManager::floatDockWidget(QDockWidget *dock) +{ + d->floatDockWidget(dock); +} + +void OverlayManager::registerDockWidget(const QString &name, OverlayTabWidget *widget) +{ + d->registerDockWidget(name, widget); +} + +void OverlayManager::unregisterDockWidget(const QString &name, OverlayTabWidget *widget) +{ + d->unregisterDockWidget(name, widget); +} + +QWidget *OverlayManager::getLastMouseInterceptWidget() const +{ + return d->lastIntercept; +} + +const QString &OverlayManager::getStyleSheet() const +{ + return OverlayStyleSheet::instance()->activeStyleSheet; +} + +bool OverlayManager::getHideTab() const +{ + return OverlayStyleSheet::instance()->hideTab; +} + +void OverlayManager::setFocusView() +{ + auto view = getMainWindow()->activeWindow(); + if (!view) + view = Application::Instance->activeView(); + if (view) + view->setFocus(); +} + +#include "moc_OverlayManager.cpp" diff --git a/src/Gui/OverlayManager.h b/src/Gui/OverlayManager.h new file mode 100644 index 0000000000..26e89fe947 --- /dev/null +++ b/src/Gui/OverlayManager.h @@ -0,0 +1,190 @@ +/**************************************************************************** + * Copyright (c) 2022 Zheng Lei (realthunder) * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ****************************************************************************/ + +#ifndef FC_OVERLAYMANAGER_H +#define FC_OVERLAYMANAGER_H + +#include + +class QAction; +class QDockWidget; +class QString; +class QPoint; +class QWidget; + +namespace Gui { + +class OverlayTabWidget; + +/// Class that manages overlay dockable widgets +class GuiExport OverlayManager : public QObject { + Q_OBJECT +public: + OverlayManager(); + virtual ~OverlayManager(); + + /// restore states + void restore(); + /// save states + void save(); + /// Retranslate text on language change + void retranslate(); + /// Reload icon images + void refreshIcons(); + + /// Reload mode + enum class ReloadMode { + /// Reload is pending + ReloadPending = 0, + /// Reload is paused + ReloadPause = 1, + /// Resume state reload + ReloadResume = 2, + }; + /** Set reload mode + * + * An internal timer is used to batch handle all relevant parameter + * changes. The function is provided to let other code temporarily disable + * the timer before changing the parameter, and then resume it after done. + */ + void reload(ReloadMode mode = ReloadMode::ReloadPending); + + /** Refresh overlay internal layout + * @param widget: optional source widget that triggers the refresh + * @param refreshStyle: whether to reload stylesheet + */ + void refresh(QWidget *widget=nullptr, bool refreshStyle=false); + + /// Setup title bar for a QDockWidget + void setupTitleBar(QDockWidget *); + + /// Overlay mode + enum class OverlayMode { + /// Toggle the focused widget between normal and overlay on top of MDI client area + ToggleActive, + /// Toggle overlay dock widget background between opaque and transparent + ToggleTransparent, + /// Make the focused widget switch to overlay on top of MDI client area + EnableActive, + /// Make the focused widget switch back to normal dockable widget + DisableActive, + /// Make all docked widget switch to overlay + EnableAll, + /// Make all docked widget switch back to normal + DisableAll, + /// Toggle all docked widget between normal and overlay + ToggleAll, + /// Set all overlay dock widget to transparent background + TransparentAll, + /// Set all overlay dock widget to opaque background + TransparentNone, + /// Toggle all overlay dock widget background between opaque and transparent + ToggleTransparentAll, + /// Toggle show/hide of the left docked widgets + ToggleLeft, + /// Toggle show/hide of the right docked widgets + ToggleRight, + /// Toggle show/hide of the top docked widgets + ToggleTop, + /// Toggle show/hide of the bottom docked widgets + ToggleBottom, + }; + /// Set overlay mode + void setOverlayMode(OverlayMode mode); + + /// Enable/disable mouse transparent mode + void setMouseTransparent(bool enabled); + /// Report if mouse transparent mode is active + bool isMouseTransparent() const; + + /// Check if the cursor is within an overlay docked widget + bool isUnderOverlay() const; + + /// Initialize a newly created dock widget + void initDockWidget(QDockWidget *); + /// Prepare a dock widget for overlay display + void setupDockWidget(QDockWidget *, int dockArea = Qt::NoDockWidgetArea); + /// Switch a dock widget back to normal display + void unsetupDockWidget(QDockWidget *); + + /** Mouse event handler for dragging a dock widget + * @param pos: mouse cursor position + * @param widget: dragging widget, can either be a QDockWidget if it is + * floating, or a OverlayTabWidget if docked in overlay mode + * @param offset: offset from the mouse cursor to the widget origin + * @param size: widget size before dragging start + * @param drop: whether to drop after drag to the position + */ + void dragDockWidget(const QPoint &pos, + QWidget *widget, + const QPoint &offset, + const QSize &size, + bool drop = false); + + /// Float an overlay docked widget + void floatDockWidget(QDockWidget *); + + /// Return the last widget whose mouse event got intercepted by the overlay manager for mouse pass through + QWidget *getLastMouseInterceptWidget() const; + + /// Return the stylesheet for overlay widgets + const QString &getStyleSheet() const; + + /// Check whether to hide tab in overlay dock widget + bool getHideTab() const; + + /// Helper function to set focus when switching active sub window + static void setFocusView(); + + /// Return the singleton instance of the overlay manager + static OverlayManager * instance(); + /// Destroy the overlay manager + static void destruct(); + + class Private; + +protected: + bool eventFilter(QObject *, QEvent *ev); + + /// Register a named docked widget with an overlay tab widget + void registerDockWidget(const QString &name, OverlayTabWidget *); + /// Unregister a named docked widget with an overlay tab widget + void unregisterDockWidget(const QString &name, OverlayTabWidget *); + +private: + void onToggleDockWidget(bool checked); + void onDockVisibleChange(bool visible); + void onDockWidgetTitleChange(const QString &); + void onTaskViewUpdate(); + void onFocusChanged(QWidget *, QWidget *); + void onAction(); + void raiseAll(); + +private: + friend class Private; + friend class OverlayTabWidget; + Private * d; +}; + +} // namespace Gui + +#endif // FC_OVERLAYMANAGER_H diff --git a/src/Gui/OverlayParams.cpp b/src/Gui/OverlayParams.cpp new file mode 100644 index 0000000000..ae92fd6e1e --- /dev/null +++ b/src/Gui/OverlayParams.cpp @@ -0,0 +1,1151 @@ +/**************************************************************************** + * Copyright (c) 2022 Zheng Lei (realthunder) * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ****************************************************************************/ + +#include "PreCompiled.h" +#include "OverlayManager.h" + +/*[[[cog +import OverlayParams +OverlayParams.define() +]]]*/ + +// Auto generated code (Tools/params_utils.py:166) +#include +#include +#include +#include "OverlayParams.h" +using namespace Gui; + +// Auto generated code (Tools/params_utils.py:175) +namespace { +class OverlayParamsP: public ParameterGrp::ObserverType { +public: + ParameterGrp::handle handle; + std::unordered_map funcs; + + long CornerNaviCube; + bool DockOverlayAutoView; + long DockOverlayDelay; + long DockOverlayRevealDelay; + long DockOverlaySplitterHandleTimeout; + bool DockOverlayActivateOnHover; + bool DockOverlayAutoMouseThrough; + bool DockOverlayWheelPassThrough; + long DockOverlayWheelDelay; + long DockOverlayAlphaRadius; + bool DockOverlayCheckNaviCube; + long DockOverlayHintTriggerSize; + long DockOverlayHintSize; + long DockOverlayHintLeftLength; + long DockOverlayHintRightLength; + long DockOverlayHintTopLength; + long DockOverlayHintBottomLength; + long DockOverlayHintLeftOffset; + long DockOverlayHintRightOffset; + long DockOverlayHintTopOffset; + long DockOverlayHintBottomOffset; + bool DockOverlayHintTabBar; + bool DockOverlayHideTabBar; + long DockOverlayHintDelay; + long DockOverlayAnimationDuration; + long DockOverlayAnimationCurve; + bool DockOverlayHidePropertyViewScrollBar; + long DockOverlayMinimumSize; + + // Auto generated code (Tools/params_utils.py:203) + OverlayParamsP() { + handle = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); + handle->Attach(this); + + CornerNaviCube = handle->GetInt("CornerNaviCube", 1); + funcs["CornerNaviCube"] = &OverlayParamsP::updateCornerNaviCube; + DockOverlayAutoView = handle->GetBool("DockOverlayAutoView", true); + funcs["DockOverlayAutoView"] = &OverlayParamsP::updateDockOverlayAutoView; + DockOverlayDelay = handle->GetInt("DockOverlayDelay", 200); + funcs["DockOverlayDelay"] = &OverlayParamsP::updateDockOverlayDelay; + DockOverlayRevealDelay = handle->GetInt("DockOverlayRevealDelay", 2000); + funcs["DockOverlayRevealDelay"] = &OverlayParamsP::updateDockOverlayRevealDelay; + DockOverlaySplitterHandleTimeout = handle->GetInt("DockOverlaySplitterHandleTimeout", 0); + funcs["DockOverlaySplitterHandleTimeout"] = &OverlayParamsP::updateDockOverlaySplitterHandleTimeout; + DockOverlayActivateOnHover = handle->GetBool("DockOverlayActivateOnHover", true); + funcs["DockOverlayActivateOnHover"] = &OverlayParamsP::updateDockOverlayActivateOnHover; + DockOverlayAutoMouseThrough = handle->GetBool("DockOverlayAutoMouseThrough", true); + funcs["DockOverlayAutoMouseThrough"] = &OverlayParamsP::updateDockOverlayAutoMouseThrough; + DockOverlayWheelPassThrough = handle->GetBool("DockOverlayWheelPassThrough", true); + funcs["DockOverlayWheelPassThrough"] = &OverlayParamsP::updateDockOverlayWheelPassThrough; + DockOverlayWheelDelay = handle->GetInt("DockOverlayWheelDelay", 1000); + funcs["DockOverlayWheelDelay"] = &OverlayParamsP::updateDockOverlayWheelDelay; + DockOverlayAlphaRadius = handle->GetInt("DockOverlayAlphaRadius", 2); + funcs["DockOverlayAlphaRadius"] = &OverlayParamsP::updateDockOverlayAlphaRadius; + DockOverlayCheckNaviCube = handle->GetBool("DockOverlayCheckNaviCube", true); + funcs["DockOverlayCheckNaviCube"] = &OverlayParamsP::updateDockOverlayCheckNaviCube; + DockOverlayHintTriggerSize = handle->GetInt("DockOverlayHintTriggerSize", 16); + funcs["DockOverlayHintTriggerSize"] = &OverlayParamsP::updateDockOverlayHintTriggerSize; + DockOverlayHintSize = handle->GetInt("DockOverlayHintSize", 8); + funcs["DockOverlayHintSize"] = &OverlayParamsP::updateDockOverlayHintSize; + DockOverlayHintLeftLength = handle->GetInt("DockOverlayHintLeftLength", 100); + funcs["DockOverlayHintLeftLength"] = &OverlayParamsP::updateDockOverlayHintLeftLength; + DockOverlayHintRightLength = handle->GetInt("DockOverlayHintRightLength", 100); + funcs["DockOverlayHintRightLength"] = &OverlayParamsP::updateDockOverlayHintRightLength; + DockOverlayHintTopLength = handle->GetInt("DockOverlayHintTopLength", 100); + funcs["DockOverlayHintTopLength"] = &OverlayParamsP::updateDockOverlayHintTopLength; + DockOverlayHintBottomLength = handle->GetInt("DockOverlayHintBottomLength", 100); + funcs["DockOverlayHintBottomLength"] = &OverlayParamsP::updateDockOverlayHintBottomLength; + DockOverlayHintLeftOffset = handle->GetInt("DockOverlayHintLeftOffset", 0); + funcs["DockOverlayHintLeftOffset"] = &OverlayParamsP::updateDockOverlayHintLeftOffset; + DockOverlayHintRightOffset = handle->GetInt("DockOverlayHintRightOffset", 0); + funcs["DockOverlayHintRightOffset"] = &OverlayParamsP::updateDockOverlayHintRightOffset; + DockOverlayHintTopOffset = handle->GetInt("DockOverlayHintTopOffset", 0); + funcs["DockOverlayHintTopOffset"] = &OverlayParamsP::updateDockOverlayHintTopOffset; + DockOverlayHintBottomOffset = handle->GetInt("DockOverlayHintBottomOffset", 0); + funcs["DockOverlayHintBottomOffset"] = &OverlayParamsP::updateDockOverlayHintBottomOffset; + DockOverlayHintTabBar = handle->GetBool("DockOverlayHintTabBar", false); + funcs["DockOverlayHintTabBar"] = &OverlayParamsP::updateDockOverlayHintTabBar; + DockOverlayHideTabBar = handle->GetBool("DockOverlayHideTabBar", true); + funcs["DockOverlayHideTabBar"] = &OverlayParamsP::updateDockOverlayHideTabBar; + DockOverlayHintDelay = handle->GetInt("DockOverlayHintDelay", 200); + funcs["DockOverlayHintDelay"] = &OverlayParamsP::updateDockOverlayHintDelay; + DockOverlayAnimationDuration = handle->GetInt("DockOverlayAnimationDuration", 200); + funcs["DockOverlayAnimationDuration"] = &OverlayParamsP::updateDockOverlayAnimationDuration; + DockOverlayAnimationCurve = handle->GetInt("DockOverlayAnimationCurve", 7); + funcs["DockOverlayAnimationCurve"] = &OverlayParamsP::updateDockOverlayAnimationCurve; + DockOverlayHidePropertyViewScrollBar = handle->GetBool("DockOverlayHidePropertyViewScrollBar", false); + funcs["DockOverlayHidePropertyViewScrollBar"] = &OverlayParamsP::updateDockOverlayHidePropertyViewScrollBar; + DockOverlayMinimumSize = handle->GetInt("DockOverlayMinimumSize", 30); + funcs["DockOverlayMinimumSize"] = &OverlayParamsP::updateDockOverlayMinimumSize; + } + + // Auto generated code (Tools/params_utils.py:217) + ~OverlayParamsP() { + } + + // Auto generated code (Tools/params_utils.py:222) + void OnChange(Base::Subject &, const char* sReason) { + if(!sReason) + return; + auto it = funcs.find(sReason); + if(it == funcs.end()) + return; + it->second(this); + + } + + + // Auto generated code (Tools/params_utils.py:244) + static void updateCornerNaviCube(OverlayParamsP *self) { + auto v = self->handle->GetInt("CornerNaviCube", 1); + if (self->CornerNaviCube != v) { + self->CornerNaviCube = v; + OverlayParams::onCornerNaviCubeChanged(); + } + } + // Auto generated code (Tools/params_utils.py:244) + static void updateDockOverlayAutoView(OverlayParamsP *self) { + auto v = self->handle->GetBool("DockOverlayAutoView", true); + if (self->DockOverlayAutoView != v) { + self->DockOverlayAutoView = v; + OverlayParams::onDockOverlayAutoViewChanged(); + } + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayDelay(OverlayParamsP *self) { + self->DockOverlayDelay = self->handle->GetInt("DockOverlayDelay", 200); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayRevealDelay(OverlayParamsP *self) { + self->DockOverlayRevealDelay = self->handle->GetInt("DockOverlayRevealDelay", 2000); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlaySplitterHandleTimeout(OverlayParamsP *self) { + self->DockOverlaySplitterHandleTimeout = self->handle->GetInt("DockOverlaySplitterHandleTimeout", 0); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayActivateOnHover(OverlayParamsP *self) { + self->DockOverlayActivateOnHover = self->handle->GetBool("DockOverlayActivateOnHover", true); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayAutoMouseThrough(OverlayParamsP *self) { + self->DockOverlayAutoMouseThrough = self->handle->GetBool("DockOverlayAutoMouseThrough", true); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayWheelPassThrough(OverlayParamsP *self) { + self->DockOverlayWheelPassThrough = self->handle->GetBool("DockOverlayWheelPassThrough", true); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayWheelDelay(OverlayParamsP *self) { + self->DockOverlayWheelDelay = self->handle->GetInt("DockOverlayWheelDelay", 1000); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayAlphaRadius(OverlayParamsP *self) { + self->DockOverlayAlphaRadius = self->handle->GetInt("DockOverlayAlphaRadius", 2); + } + // Auto generated code (Tools/params_utils.py:244) + static void updateDockOverlayCheckNaviCube(OverlayParamsP *self) { + auto v = self->handle->GetBool("DockOverlayCheckNaviCube", true); + if (self->DockOverlayCheckNaviCube != v) { + self->DockOverlayCheckNaviCube = v; + OverlayParams::onDockOverlayCheckNaviCubeChanged(); + } + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayHintTriggerSize(OverlayParamsP *self) { + self->DockOverlayHintTriggerSize = self->handle->GetInt("DockOverlayHintTriggerSize", 16); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayHintSize(OverlayParamsP *self) { + self->DockOverlayHintSize = self->handle->GetInt("DockOverlayHintSize", 8); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayHintLeftLength(OverlayParamsP *self) { + self->DockOverlayHintLeftLength = self->handle->GetInt("DockOverlayHintLeftLength", 100); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayHintRightLength(OverlayParamsP *self) { + self->DockOverlayHintRightLength = self->handle->GetInt("DockOverlayHintRightLength", 100); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayHintTopLength(OverlayParamsP *self) { + self->DockOverlayHintTopLength = self->handle->GetInt("DockOverlayHintTopLength", 100); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayHintBottomLength(OverlayParamsP *self) { + self->DockOverlayHintBottomLength = self->handle->GetInt("DockOverlayHintBottomLength", 100); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayHintLeftOffset(OverlayParamsP *self) { + self->DockOverlayHintLeftOffset = self->handle->GetInt("DockOverlayHintLeftOffset", 0); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayHintRightOffset(OverlayParamsP *self) { + self->DockOverlayHintRightOffset = self->handle->GetInt("DockOverlayHintRightOffset", 0); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayHintTopOffset(OverlayParamsP *self) { + self->DockOverlayHintTopOffset = self->handle->GetInt("DockOverlayHintTopOffset", 0); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayHintBottomOffset(OverlayParamsP *self) { + self->DockOverlayHintBottomOffset = self->handle->GetInt("DockOverlayHintBottomOffset", 0); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayHintTabBar(OverlayParamsP *self) { + self->DockOverlayHintTabBar = self->handle->GetBool("DockOverlayHintTabBar", false); + } + // Auto generated code (Tools/params_utils.py:244) + static void updateDockOverlayHideTabBar(OverlayParamsP *self) { + auto v = self->handle->GetBool("DockOverlayHideTabBar", true); + if (self->DockOverlayHideTabBar != v) { + self->DockOverlayHideTabBar = v; + OverlayParams::onDockOverlayHideTabBarChanged(); + } + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayHintDelay(OverlayParamsP *self) { + self->DockOverlayHintDelay = self->handle->GetInt("DockOverlayHintDelay", 200); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayAnimationDuration(OverlayParamsP *self) { + self->DockOverlayAnimationDuration = self->handle->GetInt("DockOverlayAnimationDuration", 200); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayAnimationCurve(OverlayParamsP *self) { + self->DockOverlayAnimationCurve = self->handle->GetInt("DockOverlayAnimationCurve", 7); + } + // Auto generated code (Tools/params_utils.py:238) + static void updateDockOverlayHidePropertyViewScrollBar(OverlayParamsP *self) { + self->DockOverlayHidePropertyViewScrollBar = self->handle->GetBool("DockOverlayHidePropertyViewScrollBar", false); + } + // Auto generated code (Tools/params_utils.py:244) + static void updateDockOverlayMinimumSize(OverlayParamsP *self) { + auto v = self->handle->GetInt("DockOverlayMinimumSize", 30); + if (self->DockOverlayMinimumSize != v) { + self->DockOverlayMinimumSize = v; + OverlayParams::onDockOverlayMinimumSizeChanged(); + } + } +}; + +// Auto generated code (Tools/params_utils.py:256) +OverlayParamsP *instance() { + static OverlayParamsP *inst = new OverlayParamsP; + return inst; +} + +} // Anonymous namespace + +// Auto generated code (Tools/params_utils.py:265) +ParameterGrp::handle OverlayParams::getHandle() { + return instance()->handle; +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docCornerNaviCube() { + return ""; +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getCornerNaviCube() { + return instance()->CornerNaviCube; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultCornerNaviCube() { + const static long def = 1; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setCornerNaviCube(const long &v) { + instance()->handle->SetInt("CornerNaviCube",v); + instance()->CornerNaviCube = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeCornerNaviCube() { + instance()->handle->RemoveInt("CornerNaviCube"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayAutoView() { + return ""; +} + +// Auto generated code (Tools/params_utils.py:294) +const bool & OverlayParams::getDockOverlayAutoView() { + return instance()->DockOverlayAutoView; +} + +// Auto generated code (Tools/params_utils.py:300) +const bool & OverlayParams::defaultDockOverlayAutoView() { + const static bool def = true; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayAutoView(const bool &v) { + instance()->handle->SetBool("DockOverlayAutoView",v); + instance()->DockOverlayAutoView = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayAutoView() { + instance()->handle->RemoveBool("DockOverlayAutoView"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayDelay() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Overlay dock (re),layout delay."); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayDelay() { + return instance()->DockOverlayDelay; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayDelay() { + const static long def = 200; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayDelay(const long &v) { + instance()->handle->SetInt("DockOverlayDelay",v); + instance()->DockOverlayDelay = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayDelay() { + instance()->handle->RemoveInt("DockOverlayDelay"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayRevealDelay() { + return ""; +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayRevealDelay() { + return instance()->DockOverlayRevealDelay; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayRevealDelay() { + const static long def = 2000; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayRevealDelay(const long &v) { + instance()->handle->SetInt("DockOverlayRevealDelay",v); + instance()->DockOverlayRevealDelay = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayRevealDelay() { + instance()->handle->RemoveInt("DockOverlayRevealDelay"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlaySplitterHandleTimeout() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Overlay splitter handle auto hide delay. Set zero to disable auto hiding."); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlaySplitterHandleTimeout() { + return instance()->DockOverlaySplitterHandleTimeout; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlaySplitterHandleTimeout() { + const static long def = 0; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlaySplitterHandleTimeout(const long &v) { + instance()->handle->SetInt("DockOverlaySplitterHandleTimeout",v); + instance()->DockOverlaySplitterHandleTimeout = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlaySplitterHandleTimeout() { + instance()->handle->RemoveInt("DockOverlaySplitterHandleTimeout"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayActivateOnHover() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Show auto hidden dock overlay on mouse over.\n" +"If disabled, then show on mouse click."); +} + +// Auto generated code (Tools/params_utils.py:294) +const bool & OverlayParams::getDockOverlayActivateOnHover() { + return instance()->DockOverlayActivateOnHover; +} + +// Auto generated code (Tools/params_utils.py:300) +const bool & OverlayParams::defaultDockOverlayActivateOnHover() { + const static bool def = true; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayActivateOnHover(const bool &v) { + instance()->handle->SetBool("DockOverlayActivateOnHover",v); + instance()->DockOverlayActivateOnHover = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayActivateOnHover() { + instance()->handle->RemoveBool("DockOverlayActivateOnHover"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayAutoMouseThrough() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Auto mouse click through transparent part of dock overlay."); +} + +// Auto generated code (Tools/params_utils.py:294) +const bool & OverlayParams::getDockOverlayAutoMouseThrough() { + return instance()->DockOverlayAutoMouseThrough; +} + +// Auto generated code (Tools/params_utils.py:300) +const bool & OverlayParams::defaultDockOverlayAutoMouseThrough() { + const static bool def = true; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayAutoMouseThrough(const bool &v) { + instance()->handle->SetBool("DockOverlayAutoMouseThrough",v); + instance()->DockOverlayAutoMouseThrough = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayAutoMouseThrough() { + instance()->handle->RemoveBool("DockOverlayAutoMouseThrough"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayWheelPassThrough() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Auto pass through mouse wheel event on transparent dock overlay."); +} + +// Auto generated code (Tools/params_utils.py:294) +const bool & OverlayParams::getDockOverlayWheelPassThrough() { + return instance()->DockOverlayWheelPassThrough; +} + +// Auto generated code (Tools/params_utils.py:300) +const bool & OverlayParams::defaultDockOverlayWheelPassThrough() { + const static bool def = true; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayWheelPassThrough(const bool &v) { + instance()->handle->SetBool("DockOverlayWheelPassThrough",v); + instance()->DockOverlayWheelPassThrough = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayWheelPassThrough() { + instance()->handle->RemoveBool("DockOverlayWheelPassThrough"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayWheelDelay() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Delay capturing mouse wheel event for passing through if it is\n" +"previously handled by other widget."); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayWheelDelay() { + return instance()->DockOverlayWheelDelay; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayWheelDelay() { + const static long def = 1000; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayWheelDelay(const long &v) { + instance()->handle->SetInt("DockOverlayWheelDelay",v); + instance()->DockOverlayWheelDelay = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayWheelDelay() { + instance()->handle->RemoveInt("DockOverlayWheelDelay"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayAlphaRadius() { + return QT_TRANSLATE_NOOP("OverlayParams", +"If auto mouse click through is enabled, then this radius\n" +"defines a region of alpha test under the mouse cursor.\n" +"Auto click through is only activated if all pixels within\n" +"the region are non-opaque."); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayAlphaRadius() { + return instance()->DockOverlayAlphaRadius; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayAlphaRadius() { + const static long def = 2; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayAlphaRadius(const long &v) { + instance()->handle->SetInt("DockOverlayAlphaRadius",v); + instance()->DockOverlayAlphaRadius = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayAlphaRadius() { + instance()->handle->RemoveInt("DockOverlayAlphaRadius"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayCheckNaviCube() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Leave space for Navigation Cube in dock overlay"); +} + +// Auto generated code (Tools/params_utils.py:294) +const bool & OverlayParams::getDockOverlayCheckNaviCube() { + return instance()->DockOverlayCheckNaviCube; +} + +// Auto generated code (Tools/params_utils.py:300) +const bool & OverlayParams::defaultDockOverlayCheckNaviCube() { + const static bool def = true; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayCheckNaviCube(const bool &v) { + instance()->handle->SetBool("DockOverlayCheckNaviCube",v); + instance()->DockOverlayCheckNaviCube = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayCheckNaviCube() { + instance()->handle->RemoveBool("DockOverlayCheckNaviCube"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayHintTriggerSize() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Auto hide hint visual display triggering width"); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayHintTriggerSize() { + return instance()->DockOverlayHintTriggerSize; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayHintTriggerSize() { + const static long def = 16; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayHintTriggerSize(const long &v) { + instance()->handle->SetInt("DockOverlayHintTriggerSize",v); + instance()->DockOverlayHintTriggerSize = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayHintTriggerSize() { + instance()->handle->RemoveInt("DockOverlayHintTriggerSize"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayHintSize() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Auto hide hint visual display width"); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayHintSize() { + return instance()->DockOverlayHintSize; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayHintSize() { + const static long def = 8; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayHintSize(const long &v) { + instance()->handle->SetInt("DockOverlayHintSize",v); + instance()->DockOverlayHintSize = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayHintSize() { + instance()->handle->RemoveInt("DockOverlayHintSize"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayHintLeftLength() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Auto hide hint visual display length for left panel. Set to zero to fill the space."); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayHintLeftLength() { + return instance()->DockOverlayHintLeftLength; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayHintLeftLength() { + const static long def = 100; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayHintLeftLength(const long &v) { + instance()->handle->SetInt("DockOverlayHintLeftLength",v); + instance()->DockOverlayHintLeftLength = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayHintLeftLength() { + instance()->handle->RemoveInt("DockOverlayHintLeftLength"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayHintRightLength() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Auto hide hint visual display length for right panel. Set to zero to fill the space."); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayHintRightLength() { + return instance()->DockOverlayHintRightLength; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayHintRightLength() { + const static long def = 100; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayHintRightLength(const long &v) { + instance()->handle->SetInt("DockOverlayHintRightLength",v); + instance()->DockOverlayHintRightLength = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayHintRightLength() { + instance()->handle->RemoveInt("DockOverlayHintRightLength"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayHintTopLength() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Auto hide hint visual display length for top panel. Set to zero to fill the space."); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayHintTopLength() { + return instance()->DockOverlayHintTopLength; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayHintTopLength() { + const static long def = 100; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayHintTopLength(const long &v) { + instance()->handle->SetInt("DockOverlayHintTopLength",v); + instance()->DockOverlayHintTopLength = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayHintTopLength() { + instance()->handle->RemoveInt("DockOverlayHintTopLength"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayHintBottomLength() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Auto hide hint visual display length for bottom panel. Set to zero to fill the space."); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayHintBottomLength() { + return instance()->DockOverlayHintBottomLength; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayHintBottomLength() { + const static long def = 100; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayHintBottomLength(const long &v) { + instance()->handle->SetInt("DockOverlayHintBottomLength",v); + instance()->DockOverlayHintBottomLength = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayHintBottomLength() { + instance()->handle->RemoveInt("DockOverlayHintBottomLength"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayHintLeftOffset() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Auto hide hint visual display offset for left panel"); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayHintLeftOffset() { + return instance()->DockOverlayHintLeftOffset; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayHintLeftOffset() { + const static long def = 0; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayHintLeftOffset(const long &v) { + instance()->handle->SetInt("DockOverlayHintLeftOffset",v); + instance()->DockOverlayHintLeftOffset = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayHintLeftOffset() { + instance()->handle->RemoveInt("DockOverlayHintLeftOffset"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayHintRightOffset() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Auto hide hint visual display offset for right panel"); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayHintRightOffset() { + return instance()->DockOverlayHintRightOffset; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayHintRightOffset() { + const static long def = 0; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayHintRightOffset(const long &v) { + instance()->handle->SetInt("DockOverlayHintRightOffset",v); + instance()->DockOverlayHintRightOffset = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayHintRightOffset() { + instance()->handle->RemoveInt("DockOverlayHintRightOffset"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayHintTopOffset() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Auto hide hint visual display offset for top panel"); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayHintTopOffset() { + return instance()->DockOverlayHintTopOffset; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayHintTopOffset() { + const static long def = 0; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayHintTopOffset(const long &v) { + instance()->handle->SetInt("DockOverlayHintTopOffset",v); + instance()->DockOverlayHintTopOffset = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayHintTopOffset() { + instance()->handle->RemoveInt("DockOverlayHintTopOffset"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayHintBottomOffset() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Auto hide hint visual display offset for bottom panel"); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayHintBottomOffset() { + return instance()->DockOverlayHintBottomOffset; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayHintBottomOffset() { + const static long def = 0; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayHintBottomOffset(const long &v) { + instance()->handle->SetInt("DockOverlayHintBottomOffset",v); + instance()->DockOverlayHintBottomOffset = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayHintBottomOffset() { + instance()->handle->RemoveInt("DockOverlayHintBottomOffset"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayHintTabBar() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Show tab bar on mouse over when auto hide"); +} + +// Auto generated code (Tools/params_utils.py:294) +const bool & OverlayParams::getDockOverlayHintTabBar() { + return instance()->DockOverlayHintTabBar; +} + +// Auto generated code (Tools/params_utils.py:300) +const bool & OverlayParams::defaultDockOverlayHintTabBar() { + const static bool def = false; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayHintTabBar(const bool &v) { + instance()->handle->SetBool("DockOverlayHintTabBar",v); + instance()->DockOverlayHintTabBar = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayHintTabBar() { + instance()->handle->RemoveBool("DockOverlayHintTabBar"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayHideTabBar() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Hide tab bar in dock overlay"); +} + +// Auto generated code (Tools/params_utils.py:294) +const bool & OverlayParams::getDockOverlayHideTabBar() { + return instance()->DockOverlayHideTabBar; +} + +// Auto generated code (Tools/params_utils.py:300) +const bool & OverlayParams::defaultDockOverlayHideTabBar() { + const static bool def = true; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayHideTabBar(const bool &v) { + instance()->handle->SetBool("DockOverlayHideTabBar",v); + instance()->DockOverlayHideTabBar = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayHideTabBar() { + instance()->handle->RemoveBool("DockOverlayHideTabBar"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayHintDelay() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Delay before show hint visual"); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayHintDelay() { + return instance()->DockOverlayHintDelay; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayHintDelay() { + const static long def = 200; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayHintDelay(const long &v) { + instance()->handle->SetInt("DockOverlayHintDelay",v); + instance()->DockOverlayHintDelay = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayHintDelay() { + instance()->handle->RemoveInt("DockOverlayHintDelay"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayAnimationDuration() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Auto hide animation duration, 0 to disable"); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayAnimationDuration() { + return instance()->DockOverlayAnimationDuration; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayAnimationDuration() { + const static long def = 200; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayAnimationDuration(const long &v) { + instance()->handle->SetInt("DockOverlayAnimationDuration",v); + instance()->DockOverlayAnimationDuration = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayAnimationDuration() { + instance()->handle->RemoveInt("DockOverlayAnimationDuration"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayAnimationCurve() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Auto hide animation curve type"); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayAnimationCurve() { + return instance()->DockOverlayAnimationCurve; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayAnimationCurve() { + const static long def = 7; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayAnimationCurve(const long &v) { + instance()->handle->SetInt("DockOverlayAnimationCurve",v); + instance()->DockOverlayAnimationCurve = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayAnimationCurve() { + instance()->handle->RemoveInt("DockOverlayAnimationCurve"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayHidePropertyViewScrollBar() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Hide property view scroll bar in dock overlay"); +} + +// Auto generated code (Tools/params_utils.py:294) +const bool & OverlayParams::getDockOverlayHidePropertyViewScrollBar() { + return instance()->DockOverlayHidePropertyViewScrollBar; +} + +// Auto generated code (Tools/params_utils.py:300) +const bool & OverlayParams::defaultDockOverlayHidePropertyViewScrollBar() { + const static bool def = false; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayHidePropertyViewScrollBar(const bool &v) { + instance()->handle->SetBool("DockOverlayHidePropertyViewScrollBar",v); + instance()->DockOverlayHidePropertyViewScrollBar = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayHidePropertyViewScrollBar() { + instance()->handle->RemoveBool("DockOverlayHidePropertyViewScrollBar"); +} + +// Auto generated code (Tools/params_utils.py:288) +const char *OverlayParams::docDockOverlayMinimumSize() { + return QT_TRANSLATE_NOOP("OverlayParams", +"Minimum overlay dock widget width/height"); +} + +// Auto generated code (Tools/params_utils.py:294) +const long & OverlayParams::getDockOverlayMinimumSize() { + return instance()->DockOverlayMinimumSize; +} + +// Auto generated code (Tools/params_utils.py:300) +const long & OverlayParams::defaultDockOverlayMinimumSize() { + const static long def = 30; + return def; +} + +// Auto generated code (Tools/params_utils.py:307) +void OverlayParams::setDockOverlayMinimumSize(const long &v) { + instance()->handle->SetInt("DockOverlayMinimumSize",v); + instance()->DockOverlayMinimumSize = v; +} + +// Auto generated code (Tools/params_utils.py:314) +void OverlayParams::removeDockOverlayMinimumSize() { + instance()->handle->RemoveInt("DockOverlayMinimumSize"); +} + +// Auto generated code (Gui/OverlayParams.py:172) +const std::vector OverlayParams::AnimationCurveTypes = { + QStringLiteral("Linear"), + QStringLiteral("InQuad"), + QStringLiteral("OutQuad"), + QStringLiteral("InOutQuad"), + QStringLiteral("OutInQuad"), + QStringLiteral("InCubic"), + QStringLiteral("OutCubic"), + QStringLiteral("InOutCubic"), + QStringLiteral("OutInCubic"), + QStringLiteral("InQuart"), + QStringLiteral("OutQuart"), + QStringLiteral("InOutQuart"), + QStringLiteral("OutInQuart"), + QStringLiteral("InQuint"), + QStringLiteral("OutQuint"), + QStringLiteral("InOutQuint"), + QStringLiteral("OutInQuint"), + QStringLiteral("InSine"), + QStringLiteral("OutSine"), + QStringLiteral("InOutSine"), + QStringLiteral("OutInSine"), + QStringLiteral("InExpo"), + QStringLiteral("OutExpo"), + QStringLiteral("InOutExpo"), + QStringLiteral("OutInExpo"), + QStringLiteral("InCirc"), + QStringLiteral("OutCirc"), + QStringLiteral("InOutCirc"), + QStringLiteral("OutInCirc"), + QStringLiteral("InElastic"), + QStringLiteral("OutElastic"), + QStringLiteral("InOutElastic"), + QStringLiteral("OutInElastic"), + QStringLiteral("InBack"), + QStringLiteral("OutBack"), + QStringLiteral("InOutBack"), + QStringLiteral("OutInBack"), + QStringLiteral("InBounce"), + QStringLiteral("OutBounce"), + QStringLiteral("InOutBounce"), + QStringLiteral("OutInBounce"), +}; +//[[[end]]] + +void OverlayParams::onDockOverlayAutoViewChanged() { + OverlayManager::instance()->refresh(); +} + +void OverlayParams::onCornerNaviCubeChanged() { + OverlayManager::instance()->refresh(); +} + +void OverlayParams::onDockOverlayCheckNaviCubeChanged() { + OverlayManager::instance()->refresh(); +} + +void OverlayParams::onDockOverlayHideTabBarChanged() { + OverlayManager::instance()->refresh(nullptr, true); +} + +void OverlayParams::onDockOverlayMinimumSizeChanged() { + OverlayManager::instance()->refresh(); +} + diff --git a/src/Gui/OverlayParams.h b/src/Gui/OverlayParams.h new file mode 100644 index 0000000000..461d082863 --- /dev/null +++ b/src/Gui/OverlayParams.h @@ -0,0 +1,422 @@ +/**************************************************************************** + * Copyright (c) 2022 Zheng Lei (realthunder) * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ****************************************************************************/ + +#ifndef GUI_OVERLAY_PARAMS_H +#define GUI_OVERLAY_PARAMS_H + +/*[[[cog +import OverlayParams +OverlayParams.declare() +]]]*/ + +// Auto generated code (Gui/OverlayParams.py:158) +#include + +// Auto generated code (Tools/params_utils.py:72) +#include + + +// Auto generated code (Tools/params_utils.py:78) +namespace Gui { +/** Convenient class to obtain overlay widgets related parameters + + * The parameters are under group "User parameter:BaseApp/Preferences/View" + * + * This class is auto generated by Gui/OverlayParams.py. Modify that file + * instead of this one, if you want to add any parameter. You need + * to install Cog Python package for code generation: + * @code + * pip install cogapp + * @endcode + * + * Once modified, you can regenerate the header and the source file, + * @code + * python3 -m cogapp -r Gui/OverlayParams.h Gui/OverlayParams.cpp + * @endcode + * + * You can add a new parameter by adding lines in Gui/OverlayParams.py. Available + * parameter types are 'Int, UInt, String, Bool, Float'. For example, to add + * a new Int type parameter, + * @code + * ParamInt(parameter_name, default_value, documentation, on_change=False) + * @endcode + * + * If there is special handling on parameter change, pass in on_change=True. + * And you need to provide a function implementation in Gui/OverlayParams.cpp with + * the following signature. + * @code + * void OverlayParams:onChanged() + * @endcode + */ +class GuiExport OverlayParams { +public: + static ParameterGrp::handle getHandle(); + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter CornerNaviCube + static const long & getCornerNaviCube(); + static const long & defaultCornerNaviCube(); + static void removeCornerNaviCube(); + static void setCornerNaviCube(const long &v); + static const char *docCornerNaviCube(); + static void onCornerNaviCubeChanged(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayAutoView + static const bool & getDockOverlayAutoView(); + static const bool & defaultDockOverlayAutoView(); + static void removeDockOverlayAutoView(); + static void setDockOverlayAutoView(const bool &v); + static const char *docDockOverlayAutoView(); + static void onDockOverlayAutoViewChanged(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayDelay + /// + /// Overlay dock (re),layout delay. + static const long & getDockOverlayDelay(); + static const long & defaultDockOverlayDelay(); + static void removeDockOverlayDelay(); + static void setDockOverlayDelay(const long &v); + static const char *docDockOverlayDelay(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayRevealDelay + static const long & getDockOverlayRevealDelay(); + static const long & defaultDockOverlayRevealDelay(); + static void removeDockOverlayRevealDelay(); + static void setDockOverlayRevealDelay(const long &v); + static const char *docDockOverlayRevealDelay(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlaySplitterHandleTimeout + /// + /// Overlay splitter handle auto hide delay. Set zero to disable auto hiding. + static const long & getDockOverlaySplitterHandleTimeout(); + static const long & defaultDockOverlaySplitterHandleTimeout(); + static void removeDockOverlaySplitterHandleTimeout(); + static void setDockOverlaySplitterHandleTimeout(const long &v); + static const char *docDockOverlaySplitterHandleTimeout(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayActivateOnHover + /// + /// Show auto hidden dock overlay on mouse over. + /// If disabled, then show on mouse click. + static const bool & getDockOverlayActivateOnHover(); + static const bool & defaultDockOverlayActivateOnHover(); + static void removeDockOverlayActivateOnHover(); + static void setDockOverlayActivateOnHover(const bool &v); + static const char *docDockOverlayActivateOnHover(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayAutoMouseThrough + /// + /// Auto mouse click through transparent part of dock overlay. + static const bool & getDockOverlayAutoMouseThrough(); + static const bool & defaultDockOverlayAutoMouseThrough(); + static void removeDockOverlayAutoMouseThrough(); + static void setDockOverlayAutoMouseThrough(const bool &v); + static const char *docDockOverlayAutoMouseThrough(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayWheelPassThrough + /// + /// Auto pass through mouse wheel event on transparent dock overlay. + static const bool & getDockOverlayWheelPassThrough(); + static const bool & defaultDockOverlayWheelPassThrough(); + static void removeDockOverlayWheelPassThrough(); + static void setDockOverlayWheelPassThrough(const bool &v); + static const char *docDockOverlayWheelPassThrough(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayWheelDelay + /// + /// Delay capturing mouse wheel event for passing through if it is + /// previously handled by other widget. + static const long & getDockOverlayWheelDelay(); + static const long & defaultDockOverlayWheelDelay(); + static void removeDockOverlayWheelDelay(); + static void setDockOverlayWheelDelay(const long &v); + static const char *docDockOverlayWheelDelay(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayAlphaRadius + /// + /// If auto mouse click through is enabled, then this radius + /// defines a region of alpha test under the mouse cursor. + /// Auto click through is only activated if all pixels within + /// the region are non-opaque. + static const long & getDockOverlayAlphaRadius(); + static const long & defaultDockOverlayAlphaRadius(); + static void removeDockOverlayAlphaRadius(); + static void setDockOverlayAlphaRadius(const long &v); + static const char *docDockOverlayAlphaRadius(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayCheckNaviCube + /// + /// Leave space for Navigation Cube in dock overlay + static const bool & getDockOverlayCheckNaviCube(); + static const bool & defaultDockOverlayCheckNaviCube(); + static void removeDockOverlayCheckNaviCube(); + static void setDockOverlayCheckNaviCube(const bool &v); + static const char *docDockOverlayCheckNaviCube(); + static void onDockOverlayCheckNaviCubeChanged(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayHintTriggerSize + /// + /// Auto hide hint visual display triggering width + static const long & getDockOverlayHintTriggerSize(); + static const long & defaultDockOverlayHintTriggerSize(); + static void removeDockOverlayHintTriggerSize(); + static void setDockOverlayHintTriggerSize(const long &v); + static const char *docDockOverlayHintTriggerSize(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayHintSize + /// + /// Auto hide hint visual display width + static const long & getDockOverlayHintSize(); + static const long & defaultDockOverlayHintSize(); + static void removeDockOverlayHintSize(); + static void setDockOverlayHintSize(const long &v); + static const char *docDockOverlayHintSize(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayHintLeftLength + /// + /// Auto hide hint visual display length for left panel. Set to zero to fill the space. + static const long & getDockOverlayHintLeftLength(); + static const long & defaultDockOverlayHintLeftLength(); + static void removeDockOverlayHintLeftLength(); + static void setDockOverlayHintLeftLength(const long &v); + static const char *docDockOverlayHintLeftLength(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayHintRightLength + /// + /// Auto hide hint visual display length for right panel. Set to zero to fill the space. + static const long & getDockOverlayHintRightLength(); + static const long & defaultDockOverlayHintRightLength(); + static void removeDockOverlayHintRightLength(); + static void setDockOverlayHintRightLength(const long &v); + static const char *docDockOverlayHintRightLength(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayHintTopLength + /// + /// Auto hide hint visual display length for top panel. Set to zero to fill the space. + static const long & getDockOverlayHintTopLength(); + static const long & defaultDockOverlayHintTopLength(); + static void removeDockOverlayHintTopLength(); + static void setDockOverlayHintTopLength(const long &v); + static const char *docDockOverlayHintTopLength(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayHintBottomLength + /// + /// Auto hide hint visual display length for bottom panel. Set to zero to fill the space. + static const long & getDockOverlayHintBottomLength(); + static const long & defaultDockOverlayHintBottomLength(); + static void removeDockOverlayHintBottomLength(); + static void setDockOverlayHintBottomLength(const long &v); + static const char *docDockOverlayHintBottomLength(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayHintLeftOffset + /// + /// Auto hide hint visual display offset for left panel + static const long & getDockOverlayHintLeftOffset(); + static const long & defaultDockOverlayHintLeftOffset(); + static void removeDockOverlayHintLeftOffset(); + static void setDockOverlayHintLeftOffset(const long &v); + static const char *docDockOverlayHintLeftOffset(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayHintRightOffset + /// + /// Auto hide hint visual display offset for right panel + static const long & getDockOverlayHintRightOffset(); + static const long & defaultDockOverlayHintRightOffset(); + static void removeDockOverlayHintRightOffset(); + static void setDockOverlayHintRightOffset(const long &v); + static const char *docDockOverlayHintRightOffset(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayHintTopOffset + /// + /// Auto hide hint visual display offset for top panel + static const long & getDockOverlayHintTopOffset(); + static const long & defaultDockOverlayHintTopOffset(); + static void removeDockOverlayHintTopOffset(); + static void setDockOverlayHintTopOffset(const long &v); + static const char *docDockOverlayHintTopOffset(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayHintBottomOffset + /// + /// Auto hide hint visual display offset for bottom panel + static const long & getDockOverlayHintBottomOffset(); + static const long & defaultDockOverlayHintBottomOffset(); + static void removeDockOverlayHintBottomOffset(); + static void setDockOverlayHintBottomOffset(const long &v); + static const char *docDockOverlayHintBottomOffset(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayHintTabBar + /// + /// Show tab bar on mouse over when auto hide + static const bool & getDockOverlayHintTabBar(); + static const bool & defaultDockOverlayHintTabBar(); + static void removeDockOverlayHintTabBar(); + static void setDockOverlayHintTabBar(const bool &v); + static const char *docDockOverlayHintTabBar(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayHideTabBar + /// + /// Hide tab bar in dock overlay + static const bool & getDockOverlayHideTabBar(); + static const bool & defaultDockOverlayHideTabBar(); + static void removeDockOverlayHideTabBar(); + static void setDockOverlayHideTabBar(const bool &v); + static const char *docDockOverlayHideTabBar(); + static void onDockOverlayHideTabBarChanged(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayHintDelay + /// + /// Delay before show hint visual + static const long & getDockOverlayHintDelay(); + static const long & defaultDockOverlayHintDelay(); + static void removeDockOverlayHintDelay(); + static void setDockOverlayHintDelay(const long &v); + static const char *docDockOverlayHintDelay(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayAnimationDuration + /// + /// Auto hide animation duration, 0 to disable + static const long & getDockOverlayAnimationDuration(); + static const long & defaultDockOverlayAnimationDuration(); + static void removeDockOverlayAnimationDuration(); + static void setDockOverlayAnimationDuration(const long &v); + static const char *docDockOverlayAnimationDuration(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayAnimationCurve + /// + /// Auto hide animation curve type + static const long & getDockOverlayAnimationCurve(); + static const long & defaultDockOverlayAnimationCurve(); + static void removeDockOverlayAnimationCurve(); + static void setDockOverlayAnimationCurve(const long &v); + static const char *docDockOverlayAnimationCurve(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayHidePropertyViewScrollBar + /// + /// Hide property view scroll bar in dock overlay + static const bool & getDockOverlayHidePropertyViewScrollBar(); + static const bool & defaultDockOverlayHidePropertyViewScrollBar(); + static void removeDockOverlayHidePropertyViewScrollBar(); + static void setDockOverlayHidePropertyViewScrollBar(const bool &v); + static const char *docDockOverlayHidePropertyViewScrollBar(); + //@} + + // Auto generated code (Tools/params_utils.py:122) + //@{ + /// Accessor for parameter DockOverlayMinimumSize + /// + /// Minimum overlay dock widget width/height + static const long & getDockOverlayMinimumSize(); + static const long & defaultDockOverlayMinimumSize(); + static void removeDockOverlayMinimumSize(); + static void setDockOverlayMinimumSize(const long &v); + static const char *docDockOverlayMinimumSize(); + static void onDockOverlayMinimumSizeChanged(); + //@} + + // Auto generated code (Gui/OverlayParams.py:164) + static const std::vector AnimationCurveTypes; + +// Auto generated code (Tools/params_utils.py:150) +}; // class OverlayParams +} // namespace Gui +//[[[end]]] + +#endif // GUI_OVERLAY_PARAMS_H diff --git a/src/Gui/OverlayParams.py b/src/Gui/OverlayParams.py new file mode 100644 index 0000000000..33e731ff19 --- /dev/null +++ b/src/Gui/OverlayParams.py @@ -0,0 +1,182 @@ +# -*- coding: utf-8 -*- +# *************************************************************************** +# * Copyright (c) 2022 Zheng Lei (realthunder) * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * This program is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with this program; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** +'''Auto code generator for overlay widgets related parameters in Preferences/View +''' +import cog +import inspect, sys +from os import sys, path + +# import Tools/params_utils.py +sys.path.append(path.join(path.dirname(path.dirname(path.abspath(__file__))), 'Tools')) +import params_utils + +from params_utils import ParamBool, ParamInt, ParamString, ParamUInt, ParamHex, \ + ParamFloat, ParamProxy, ParamLinePattern, ParamFile, \ + ParamComboBox, ParamColor, ParamSpinBox, auto_comment + +NameSpace = 'Gui' +ClassName = 'OverlayParams' +ParamPath = 'User parameter:BaseApp/Preferences/View' +ClassDoc = 'Convenient class to obtain overlay widgets related parameters' + +AnimationCurveTypes = ( + "Linear", + "InQuad", + "OutQuad", + "InOutQuad", + "OutInQuad", + "InCubic", + "OutCubic", + "InOutCubic", + "OutInCubic", + "InQuart", + "OutQuart", + "InOutQuart", + "OutInQuart", + "InQuint", + "OutQuint", + "InOutQuint", + "OutInQuint", + "InSine", + "OutSine", + "InOutSine", + "OutInSine", + "InExpo", + "OutExpo", + "InOutExpo", + "OutInExpo", + "InCirc", + "OutCirc", + "InOutCirc", + "OutInCirc", + "InElastic", + "OutElastic", + "InOutElastic", + "OutInElastic", + "InBack", + "OutBack", + "InOutBack", + "OutInBack", + "InBounce", + "OutBounce", + "InOutBounce", + "OutInBounce", +) + +class ParamAnimationCurve(ParamProxy): + WidgetType = 'Gui::PrefComboBox' + + def widget_setter(self, _param): + return None + + def init_widget(self, param, row, group_name): + super().init_widget(param, row, group_name) + cog.out(f''' + {auto_comment()} + for (const auto &item : OverlayParams::AnimationCurveTypes) + {param.widget_name}->addItem(item);''') + cog.out(f''' + {param.widget_name}->setCurrentIndex({param.namespace}::{param.class_name}::default{param.name}());''') + +Params = [ + ParamInt('CornerNaviCube', 1, on_change=True), + ParamBool('DockOverlayAutoView', True, on_change=True, title="Auto hide in non 3D view"), + ParamInt('DockOverlayDelay', 200, "Overlay dock (re),layout delay.", title="Layout delay (ms)", proxy=ParamSpinBox(0, 5000, 100)), + ParamInt('DockOverlayRevealDelay', 2000), + ParamInt('DockOverlaySplitterHandleTimeout', 0, title="Splitter auto hide delay (ms)", proxy=ParamSpinBox(0, 99999, 100), + doc="Overlay splitter handle auto hide delay. Set zero to disable auto hiding."), + ParamBool('DockOverlayActivateOnHover', True, title="Activate on hover", + doc="Show auto hidden dock overlay on mouse over.\n" + "If disabled, then show on mouse click."), + ParamBool('DockOverlayAutoMouseThrough', True, + "Auto mouse click through transparent part of dock overlay.", title="Auto mouse pass through"), + ParamBool('DockOverlayWheelPassThrough', True, + "Auto pass through mouse wheel event on transparent dock overlay.", title="Auto mouse wheel pass through"), + ParamInt('DockOverlayWheelDelay', 1000, title="Delay mouse wheel pass through (ms)", proxy=ParamSpinBox(0, 99999, 1), + doc="Delay capturing mouse wheel event for passing through if it is\n" + "previously handled by other widget."), + ParamInt('DockOverlayAlphaRadius', 2, title="Alpha test radius", proxy=ParamSpinBox(1, 100, 1), doc=\ + "If auto mouse click through is enabled, then this radius\n" + "defines a region of alpha test under the mouse cursor.\n" + "Auto click through is only activated if all pixels within\n" + "the region are non-opaque."), + ParamBool('DockOverlayCheckNaviCube', True, on_change=True, title="Check Navigation Cube", + doc="Leave space for Navigation Cube in dock overlay"), + ParamInt('DockOverlayHintTriggerSize', 16, title="Hint trigger size", proxy=ParamSpinBox(1, 100, 1), + doc="Auto hide hint visual display triggering width"), + ParamInt('DockOverlayHintSize', 8, title="Hint width", proxy=ParamSpinBox(1, 100, 1), + doc="Auto hide hint visual display width"), + ParamInt('DockOverlayHintLeftLength', 100, title='Left panel hint length', proxy=ParamSpinBox(0, 10000, 10), + doc="Auto hide hint visual display length for left panel. Set to zero to fill the space."), + ParamInt('DockOverlayHintRightLength', 100, title='Right panel hint length', proxy=ParamSpinBox(0, 10000, 10), + doc="Auto hide hint visual display length for right panel. Set to zero to fill the space."), + ParamInt('DockOverlayHintTopLength', 100, title='Top panel hint length', proxy=ParamSpinBox(0, 10000, 10), + doc="Auto hide hint visual display length for top panel. Set to zero to fill the space."), + ParamInt('DockOverlayHintBottomLength', 100, title='Bottom panel hint length', proxy=ParamSpinBox(0, 10000, 10), + doc="Auto hide hint visual display length for bottom panel. Set to zero to fill the space."), + ParamInt('DockOverlayHintLeftOffset', 0, title='Left panel hint offset', proxy=ParamSpinBox(0, 10000, 10), + doc="Auto hide hint visual display offset for left panel"), + ParamInt('DockOverlayHintRightOffset', 0, title='Right panel hint offset', proxy=ParamSpinBox(0, 10000, 10), + doc="Auto hide hint visual display offset for right panel"), + ParamInt('DockOverlayHintTopOffset', 0, title='Top panel hint offset', proxy=ParamSpinBox(0, 10000, 10), + doc="Auto hide hint visual display offset for top panel"), + ParamInt('DockOverlayHintBottomOffset', 0, title='Bottom panel hint offset', proxy=ParamSpinBox(0, 10000, 10), + doc="Auto hide hint visual display offset for bottom panel"), + ParamBool('DockOverlayHintTabBar', False, "Show tab bar on mouse over when auto hide", title="Hint show tab bar"), + ParamBool('DockOverlayHideTabBar', True, on_change=True, doc="Hide tab bar in dock overlay", title='Hide tab bar'), + ParamInt('DockOverlayHintDelay', 200, "Delay before show hint visual", title="Hint delay (ms)", proxy=ParamSpinBox(0, 1000, 100)), + ParamInt('DockOverlayAnimationDuration', 200, "Auto hide animation duration, 0 to disable", + title="Animation duration (ms)", proxy=ParamSpinBox(0, 5000, 100)), + ParamInt('DockOverlayAnimationCurve', 7, "Auto hide animation curve type", title="Animation curve type", proxy=ParamAnimationCurve()), + ParamBool('DockOverlayHidePropertyViewScrollBar', False, "Hide property view scroll bar in dock overlay", title="Hide property view scroll bar"), + ParamInt('DockOverlayMinimumSize', 30, on_change=True, + doc="Minimum overlay dock widget width/height", + title="Minimum dock widget size"), +] + +def declare(): + cog.out(f''' +{auto_comment()} +#include +''') + + params_utils.declare_begin(sys.modules[__name__]) + cog.out(f''' + {auto_comment()} + static const std::vector AnimationCurveTypes; +''') + params_utils.declare_end(sys.modules[__name__]) + +def define(): + params_utils.define(sys.modules[__name__]) + cog.out(f''' +{auto_comment()} +const std::vector OverlayParams::AnimationCurveTypes = {{''') + for item in AnimationCurveTypes: + cog.out(f''' + QStringLiteral("{item}"),''') + cog.out(f''' +}}; +''') + + +params_utils.init_params(Params, NameSpace, ClassName, ParamPath) diff --git a/src/Gui/OverlayWidgets.cpp b/src/Gui/OverlayWidgets.cpp new file mode 100644 index 0000000000..9144cc0b63 --- /dev/null +++ b/src/Gui/OverlayWidgets.cpp @@ -0,0 +1,2487 @@ +/**************************************************************************** + * Copyright (c) 2020 Zheng Lei (realthunder) * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ****************************************************************************/ + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include + +#include +#include + +#include "OverlayWidgets.h" + +#include +#include +#include +#include "Application.h" +#include "BitmapFactory.h" +#include "Clipping.h" +#include "ComboView.h" +#include "Command.h" +#include "Control.h" +#include "MainWindow.h" +#include "MDIView.h" +#include "NaviCube.h" +#include "OverlayManager.h" +#include "OverlayParams.h" +#include "TaskView/TaskView.h" +#include "Tree.h" +#include "TreeParams.h" +#include "propertyeditor/PropertyEditor.h" + +FC_LOG_LEVEL_INIT("Dock", true, true); + +using namespace Gui; + +OverlayDragFrame *OverlayTabWidget::_DragFrame; +QDockWidget *OverlayTabWidget::_DragFloating; +QWidget *OverlayTabWidget::_Dragging; +OverlayTabWidget *OverlayTabWidget::_LeftOverlay; +OverlayTabWidget *OverlayTabWidget::_RightOverlay; +OverlayTabWidget *OverlayTabWidget::_TopOverlay; +OverlayTabWidget *OverlayTabWidget::_BottomOverlay; + +static inline int widgetMinSize(const QWidget *widget, bool margin=false) +{ + return widget->fontMetrics().ascent() + + widget->fontMetrics().descent() + (margin?4:0); +} + +// ----------------------------------------------------------- + +OverlayProxyWidget::OverlayProxyWidget(OverlayTabWidget *tabOverlay) + :QWidget(tabOverlay->parentWidget()), owner(tabOverlay), _hintColor(QColor(50,50,50,150)) +{ + dockArea = owner->getDockArea(); + timer.setSingleShot(true); + QObject::connect(&timer, &QTimer::timeout, this, &OverlayProxyWidget::onTimer); + setAttribute(Qt::WA_TransparentForMouseEvents, true); +} + +bool OverlayProxyWidget::isActivated() const +{ + return drawLine && isVisible(); +} + +OverlayProxyWidget::HitTest OverlayProxyWidget::hitTest(const QPoint &globalPt, bool delay) +{ + if (!isVisible() || !owner->count()) + return HitTest::HitNone; + + auto pt = mapFromGlobal(globalPt); + + QTabBar *tabbar = owner->tabBar(); + if (tabbar->isVisible() && tabbar->tabAt(pt)>=0) { + ToolTip::showText(globalPt, QObject::tr("Press ESC to hide hint"), this); + return HitTest::HitOuter; + } + + HitTest hit = HitTest::HitNone; + QRect rect = this->getRect(); + QSize s = this->size(); + int hintSize = OverlayParams::getDockOverlayHintTriggerSize(); + // if (owner->getState() == OverlayTabWidget::State::HintHidden) + // hintSize *= 2; + switch(dockArea) { + case Qt::LeftDockWidgetArea: + if (pt.y() >= 0 && pt.y() <= s.height() && pt.x() > 0 && pt.x() < hintSize) + hit = HitTest::HitOuter; + break; + case Qt::RightDockWidgetArea: + if (pt.y() >= 0 && pt.y() <= s.height() && pt.x() < s.width() && pt.x() > -hintSize) + hit = HitTest::HitOuter; + break; + case Qt::TopDockWidgetArea: + if (pt.x() >= 0 && pt.x() <= s.width() && pt.y() > 0 && pt.y() < hintSize) + hit = HitTest::HitOuter; + break; + case Qt::BottomDockWidgetArea: + if (pt.x() >= 0 && pt.x() <= s.width() && pt.y() < s.height() && pt.y() > -hintSize) + hit = HitTest::HitOuter; + break; + } + if (rect.contains(pt)) { + hit = HitTest::HitInner; + ToolTip::showText(globalPt, QObject::tr("Press ESC to hide hint"), this); + } else if (drawLine) + ToolTip::hideText(); + + if (owner->getState() == OverlayTabWidget::State::HintHidden) { + if (hit == HitTest::HitNone) + owner->setState(OverlayTabWidget::State::Normal); + else { + hit = HitTest::HitNone; + ToolTip::hideText(); + } + } + if (hit != HitTest::HitNone) { + if (drawLine) + timer.stop(); + else if (delay) { + if (!timer.isActive()) + timer.start(OverlayParams::getDockOverlayHintDelay()); + return hit; + } else { + timer.stop(); + owner->setState(OverlayTabWidget::State::Hint); + drawLine = true; + update(); + } + if(owner->getState() != OverlayTabWidget::State::Hidden + && hit == HitTest::HitOuter + && OverlayParams::getDockOverlayActivateOnHover()) { + if (owner->isVisible() && owner->tabBar()->isVisible()) { + QSize size = owner->tabBar()->size(); + QPoint pt = owner->tabBar()->mapToGlobal( + QPoint(size.width(), size.height())); + QPoint pos = QCursor::pos(); + switch(this->dockArea) { + case Qt::LeftDockWidgetArea: + case Qt::RightDockWidgetArea: + if (pos.y() < pt.y()) + return HitTest::HitNone; + break; + case Qt::TopDockWidgetArea: + case Qt::BottomDockWidgetArea: + if (pos.x() < pt.x()) + return HitTest::HitNone; + break; + default: + break; + } + } + owner->setState(OverlayTabWidget::State::Showing); + } + + } else if (!drawLine) { + timer.stop(); + } else if (delay) { + if (!timer.isActive()) + timer.start(OverlayParams::getDockOverlayHintDelay()); + } else { + timer.stop(); + owner->setState(OverlayTabWidget::State::Normal); + drawLine = false; + ToolTip::hideText(); + update(); + } + return hit; +} + +void OverlayProxyWidget::onTimer() +{ + hitTest(QCursor::pos(), false); +} + +void OverlayProxyWidget::enterEvent(QEvent *) +{ + if(!owner->count()) + return; + + if (!drawLine) { + if (!timer.isActive()) + timer.start(OverlayParams::getDockOverlayHintDelay()); + } +} + +void OverlayProxyWidget::hideEvent(QHideEvent *) +{ + drawLine = false; +} + +void OverlayProxyWidget::onMousePress() +{ + if(!owner->count()) + return; + + if (owner->getState() == OverlayTabWidget::State::HintHidden) + return; + + owner->setState(OverlayTabWidget::State::Showing); +} + +QBrush OverlayProxyWidget::hintColor() const +{ + return _hintColor; +} + +void OverlayProxyWidget::setHintColor(const QBrush &brush) +{ + _hintColor = brush; +} + +QRect OverlayProxyWidget::getRect() const +{ + QRect rect = this->rect(); + if (owner->isVisible() && owner->tabBar()->isVisible()) { + QSize size = owner->tabBar()->size(); + QPoint pt = owner->tabBar()->mapToGlobal( + QPoint(size.width(), size.height())); + pt = this->mapFromGlobal(pt); + switch(this->dockArea) { + case Qt::LeftDockWidgetArea: + case Qt::RightDockWidgetArea: + rect.setTop(pt.y()); + break; + case Qt::TopDockWidgetArea: + case Qt::BottomDockWidgetArea: + rect.setLeft(pt.x()); + break; + default: + break; + } + } + switch(this->dockArea) { + case Qt::LeftDockWidgetArea: + if (int offset = OverlayParams::getDockOverlayHintLeftOffset()) + rect.moveTop(std::max(rect.top()+offset, rect.bottom()-10)); + if (int length = OverlayParams::getDockOverlayHintLeftLength()) + rect.setHeight(std::min(length, rect.height())); + break; + case Qt::RightDockWidgetArea: + if (int offset = OverlayParams::getDockOverlayHintRightOffset()) + rect.moveTop(std::max(rect.top()+offset, rect.bottom()-10)); + if (int length = OverlayParams::getDockOverlayHintRightLength()) + rect.setHeight(std::min(length, rect.height())); + break; + case Qt::TopDockWidgetArea: + if (int offset = OverlayParams::getDockOverlayHintTopOffset()) + rect.moveLeft(std::max(rect.left()+offset, rect.right()-10)); + if (int length = OverlayParams::getDockOverlayHintTopLength()) + rect.setWidth(std::min(length, rect.width())); + break; + case Qt::BottomDockWidgetArea: + if (int offset = OverlayParams::getDockOverlayHintBottomOffset()) + rect.moveLeft(std::max(rect.left()+offset, rect.right()-10)); + if (int length = OverlayParams::getDockOverlayHintBottomLength()) + rect.setWidth(std::min(length, rect.width())); + break; + default: + break; + } + return rect; +} + +void OverlayProxyWidget::paintEvent(QPaintEvent *) +{ + if(!drawLine) + return; + QPainter painter(this); + painter.setOpacity(_hintColor.color().alphaF()); + painter.setPen(Qt::transparent); + painter.setBrush(_hintColor); + + QRect rect = this->getRect(); + painter.drawRect(rect); +} + +OverlayToolButton::OverlayToolButton(QWidget *parent) + :QToolButton(parent) +{ + setCursor(Qt::ArrowCursor); +} + +// -------------------------------------------------------------------- + +OverlayTabWidget::OverlayTabWidget(QWidget *parent, Qt::DockWidgetArea pos) + :QTabWidget(parent), dockArea(pos) +{ + // This is necessary to capture any focus lost from switching the tab, + // otherwise the lost focus will leak to the parent, i.e. MdiArea, which may + // cause unexpected Mdi sub window switching. + // setFocusPolicy(Qt::StrongFocus); + + _imageScale = 0.0; + + splitter = new OverlaySplitter(this); + + _graphicsEffect = new OverlayGraphicsEffect(splitter); + splitter->setGraphicsEffect(_graphicsEffect); + + _graphicsEffectTab = new OverlayGraphicsEffect(this); + _graphicsEffectTab->setEnabled(false); + tabBar()->setGraphicsEffect(_graphicsEffectTab); + + Command *cmdHide = nullptr; + switch(pos) { + case Qt::LeftDockWidgetArea: + _LeftOverlay = this; + setTabPosition(QTabWidget::West); + splitter->setOrientation(Qt::Vertical); + cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleLeft"); + break; + case Qt::RightDockWidgetArea: + _RightOverlay = this; + setTabPosition(QTabWidget::East); + splitter->setOrientation(Qt::Vertical); + cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleRight"); + break; + case Qt::TopDockWidgetArea: + _TopOverlay = this; + setTabPosition(QTabWidget::North); + splitter->setOrientation(Qt::Horizontal); + cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleTop"); + break; + case Qt::BottomDockWidgetArea: + _BottomOverlay = this; + setTabPosition(QTabWidget::South); + splitter->setOrientation(Qt::Horizontal); + cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleBottom"); + break; + default: + break; + } + + proxyWidget = new OverlayProxyWidget(this); + proxyWidget->hide(); + _setOverlayMode(proxyWidget,OverlayOption::Enable); + + setOverlayMode(true); + hide(); + + actTransparent.setCheckable(true); + actTransparent.setData(QStringLiteral("OBTN Transparent")); + actTransparent.setParent(this); + addAction(&actTransparent); + + actAutoHide.setData(QStringLiteral("OBTN AutoHide")); + + actEditHide.setData(QStringLiteral("OBTN EditHide")); + + actEditShow.setData(QStringLiteral("OBTN EditShow")); + + actTaskShow.setData(QStringLiteral("OBTN TaskShow")); + + actNoAutoMode.setData(QStringLiteral("OBTN NoAutoMode")); + + actAutoMode.setData(QStringLiteral("OBTN AutoMode")); + actAutoMode.setParent(this); + autoModeMenu.hide(); + autoModeMenu.setToolTipsVisible(true); + autoModeMenu.addAction(&actNoAutoMode); + autoModeMenu.addAction(&actAutoHide); + autoModeMenu.addAction(&actEditShow); + autoModeMenu.addAction(&actEditHide); + autoModeMenu.addAction(&actTaskShow); + addAction(&actAutoMode); + + actOverlay.setData(QStringLiteral("OBTN Overlay")); + actOverlay.setParent(this); + addAction(&actOverlay); + + if (cmdHide) + cmdHide->addTo(this); + + retranslate(); + refreshIcons(); + + connect(tabBar(), &QTabBar::tabBarClicked, this, &OverlayTabWidget::onCurrentChanged); + connect(tabBar(), &QTabBar::tabMoved, this, &OverlayTabWidget::onTabMoved); + tabBar()->installEventFilter(this); + + timer.setSingleShot(true); + connect(&timer, &QTimer::timeout, this, &OverlayTabWidget::setupLayout); + + repaintTimer.setSingleShot(true); + connect(&repaintTimer, &QTimer::timeout, this, &OverlayTabWidget::onRepaint); + + _animator = new QPropertyAnimation(this, "animation", this); + _animator->setStartValue(0.0); + _animator->setEndValue(1.0); + connect(_animator, &QAbstractAnimation::stateChanged, + this, &OverlayTabWidget::onAnimationStateChanged); +} + +void OverlayTabWidget::refreshIcons() +{ + actOverlay.setIcon(BitmapFactory().pixmap("qss:overlay/overlay.svg")); + actNoAutoMode.setIcon(BitmapFactory().pixmap("qss:overlay/mode.svg")); + actTaskShow.setIcon(BitmapFactory().pixmap("qss:overlay/taskshow.svg")); + actEditShow.setIcon(BitmapFactory().pixmap("qss:overlay/editshow.svg")); + actEditHide.setIcon(BitmapFactory().pixmap("qss:overlay/edithide.svg")); + actTransparent.setIcon(BitmapFactory().pixmap("qss:overlay/transparent.svg")); + QPixmap pxAutoHide = BitmapFactory().pixmap("qss:overlay/autohide.svg"); + switch(dockArea) { + case Qt::LeftDockWidgetArea: + actAutoHide.setIcon(pxAutoHide); + break; + case Qt::RightDockWidgetArea: + actAutoHide.setIcon(pxAutoHide.transformed(QTransform().scale(-1,1))); + break; + case Qt::TopDockWidgetArea: + actAutoHide.setIcon(pxAutoHide.transformed(QTransform().rotate(90))); + break; + case Qt::BottomDockWidgetArea: + actAutoHide.setIcon(pxAutoHide.transformed(QTransform().rotate(-90))); + break; + default: + break; + } + syncAutoMode(); +} + +void OverlayTabWidget::onAnimationStateChanged() +{ + if (_animator->state() != QAbstractAnimation::Running) { + setAnimation(0); + if (_animator->startValue().toReal() == 0.0) { + hide(); + OverlayManager::instance()->refresh(); + } + if (_state == State::Showing) + setState(State::Normal); + } +} + +void OverlayTabWidget::setAnimation(qreal t) +{ + if (t != _animation) { + _animation = t; + setupLayout(); + } +} + +void OverlayTabWidget::startShow() +{ + if (isVisible() || _state > State::Normal) + return; + int duration = OverlayParams::getDockOverlayAnimationDuration(); + bool setmode = _state != State::Showing; + if (duration) { + _animator->setStartValue(1.0); + _animator->setEndValue(0.0); + _animator->setDuration(duration); + _animator->setEasingCurve((QEasingCurve::Type)OverlayParams::getDockOverlayAnimationCurve()); + _animator->start(); + } + else if (_state == State::Showing) + setState(State::Normal); + proxyWidget->hide(); + show(); + raise(); + if (setmode) + setOverlayMode(overlaid); +} + +QWidget *OverlayTabWidget::createTitleButton(QAction *action, int size) +{ + auto button = new OverlayToolButton(nullptr); + button->setObjectName(action->data().toString()); + button->setDefaultAction(action); + button->setAutoRaise(true); + button->setContentsMargins(0,0,0,0); + button->setFixedSize(size,size); + return button; +} + +void OverlayTabWidget::startHide() +{ + if (!isVisible() + || _state > State::Normal + || (_animator->state() == QAbstractAnimation::Running + && _animator->startValue().toReal() == 0.0)) + return; + int duration = OverlayParams::getDockOverlayAnimationDuration(); + if (!duration) + hide(); + else { + _animator->setStartValue(0.0); + _animator->setEndValue(1.0); + _animator->setDuration(duration); + _animator->setEasingCurve((QEasingCurve::Type)OverlayParams::getDockOverlayAnimationCurve()); + _animator->start(); + } +} + +bool OverlayTabWidget::event(QEvent *ev) +{ + switch(ev->type()) { + case QEvent::MouseButtonRelease: + if(mouseGrabber() == this) { + releaseMouse(); + ev->accept(); + return true; + } + break; + case QEvent::MouseMove: + case QEvent::ContextMenu: + if(QApplication::mouseButtons() == Qt::NoButton && mouseGrabber() == this) { + releaseMouse(); + ev->accept(); + return true; + } + break; + case QEvent::MouseButtonPress: + ev->accept(); + return true; + default: + break; + } + return QTabWidget::event(ev); +} + +int OverlayTabWidget::testAlpha(const QPoint &_pos, int radiusScale) +{ + if (!count() || (!isOverlaid() && !isTransparent()) || !isVisible()) + return -1; + + if (tabBar()->isVisible() && tabBar()->tabAt(tabBar()->mapFromGlobal(_pos))>=0) + return -1; + + if (titleBar->isVisible() && titleBar->rect().contains(titleBar->mapFromGlobal(_pos))) + return -1; + + if (!splitter->isVisible()) + return 0; + + auto pos = splitter->mapFromGlobal(_pos); + QSize size = splitter->size(); + if (pos.x() < 0 || pos.y() < 0 + || pos.x() >= size.width() + || pos.y() >= size.height()) + { + if (this->rect().contains(this->mapFromGlobal(_pos))) + return 0; + return -1; + } + + if (_image.isNull()) { + auto pixmap = splitter->grab(); + _imageScale = pixmap.devicePixelRatio(); + _image = pixmap.toImage(); + } + + int res = qAlpha(_image.pixel(pos*_imageScale)); + int radius = OverlayParams::getDockOverlayAlphaRadius() * radiusScale; + if (res || radius<=0 ) + return res; + + radius *= _imageScale; + for (int i=-radius; i= size.width() + || pos.y()+j >= size.height()) + continue; + res = qAlpha(_image.pixel(pos*_imageScale + QPoint(i,j))); + if (res) + return res; + } + } + return 0; +} + +void OverlayTabWidget::paintEvent(QPaintEvent *ev) +{ + Base::StateLocker guard(repainting); + repaintTimer.stop(); + if (!_image.isNull()) + _image = QImage(); + QTabWidget::paintEvent(ev); +} + +void OverlayTabWidget::onRepaint() +{ + Base::StateLocker guard(repainting); + repaintTimer.stop(); + if (!_image.isNull()) + _image = QImage(); + splitter->repaint(); +} + +void OverlayTabWidget::scheduleRepaint() +{ + if(!repainting + && isVisible() + && _graphicsEffect) + { + repaintTimer.start(100); + } +} + +QColor OverlayTabWidget::effectColor() const +{ + return _graphicsEffect->color(); +} + +void OverlayTabWidget::setEffectColor(const QColor &color) +{ + _graphicsEffect->setColor(color); + _graphicsEffectTab->setColor(color); +} + +int OverlayTabWidget::effectWidth() const +{ + return _graphicsEffect->size().width(); +} + +void OverlayTabWidget::setEffectWidth(int s) +{ + auto size = _graphicsEffect->size(); + size.setWidth(s); + _graphicsEffect->setSize(size); + _graphicsEffectTab->setSize(size); +} + +int OverlayTabWidget::effectHeight() const +{ + return _graphicsEffect->size().height(); +} + +void OverlayTabWidget::setEffectHeight(int s) +{ + auto size = _graphicsEffect->size(); + size.setHeight(s); + _graphicsEffect->setSize(size); + _graphicsEffectTab->setSize(size); +} + +qreal OverlayTabWidget::effectOffsetX() const +{ + return _graphicsEffect->offset().x(); +} + +void OverlayTabWidget::setEffectOffsetX(qreal d) +{ + auto offset = _graphicsEffect->offset(); + offset.setX(d); + _graphicsEffect->setOffset(offset); + _graphicsEffectTab->setOffset(offset); +} + +qreal OverlayTabWidget::effectOffsetY() const +{ + return _graphicsEffect->offset().y(); +} + +void OverlayTabWidget::setEffectOffsetY(qreal d) +{ + auto offset = _graphicsEffect->offset(); + offset.setY(d); + _graphicsEffect->setOffset(offset); + _graphicsEffectTab->setOffset(offset); +} + +qreal OverlayTabWidget::effectBlurRadius() const +{ + return _graphicsEffect->blurRadius(); +} + +void OverlayTabWidget::setEffectBlurRadius(qreal r) +{ + _graphicsEffect->setBlurRadius(r); + _graphicsEffectTab->setBlurRadius(r); +} + +bool OverlayTabWidget::effectEnabled() const +{ + return _effectEnabled; +} + +void OverlayTabWidget::setEffectEnabled(bool enable) +{ + _effectEnabled = enable; +} + +bool OverlayTabWidget::eventFilter(QObject *o, QEvent *ev) +{ + if(ev->type() == QEvent::Resize && o == tabBar()) { + if (_state <= State::Normal) + timer.start(10); + } + return QTabWidget::eventFilter(o, ev); +} + +void OverlayTabWidget::restore(ParameterGrp::handle handle) +{ + if (!handle) { + hGrp = handle; + return; + } + if (!parentWidget()) + return; + std::string widgets = handle->GetASCII("Widgets",""); + for(auto &name : QString::fromUtf8(widgets.c_str()).split(QLatin1Char(','))) { + if(name.isEmpty()) + continue; + OverlayManager::instance()->registerDockWidget(name, this); + auto dock = getMainWindow()->findChild(name); + if(dock) + addWidget(dock, dock->windowTitle()); + } + int width = handle->GetInt("Width", 0); + int height = handle->GetInt("Height", 0); + int offset1 = handle->GetInt("Offset1", 0); + int offset2 = handle->GetInt("Offset3", 0); + setOffset(QSize(offset1,offset2)); + setSizeDelta(handle->GetInt("Offset2", 0)); + if(width && height) { + QRect rect(0, 0, width, height); + switch(dockArea) { + case Qt::RightDockWidgetArea: + rect.moveRight(parentWidget()->size().width()); + break; + case Qt::BottomDockWidgetArea: + rect.moveBottom(parentWidget()->size().height()); + break; + default: + break; + } + setRect(rect); + } + if (handle->GetBool("AutoHide", false)) + setAutoMode(AutoMode::AutoHide); + else if (handle->GetBool("EditHide", false)) + setAutoMode(AutoMode::EditHide); + else if (handle->GetBool("EditShow", false)) + setAutoMode(AutoMode::EditShow); + else if (handle->GetBool("TaskShow", false)) + setAutoMode(AutoMode::TaskShow); + else + setAutoMode(AutoMode::NoAutoMode); + + setTransparent(handle->GetBool("Transparent", false)); + + _sizemap.clear(); + std::string savedSizes = handle->GetASCII("Sizes",""); + QList sizes; + int idx = 0; + for(auto &size : QString::fromUtf8(savedSizes.c_str()).split(QLatin1Char(','))) { + sizes.append(size.toInt()); + _sizemap[dockWidget(idx++)] = sizes.back(); + } + + FC_LOG("restore " << objectName().toUtf8().constData() << " " << savedSizes); + + getSplitter()->setSizes(sizes); + hGrp = handle; +} + +void OverlayTabWidget::saveTabs() +{ + if(!hGrp) + return; + + std::ostringstream os, os2; + _sizemap.clear(); + auto sizes = splitter->sizes(); + bool first = true; + for(int i=0,c=splitter->count(); iobjectName().size()) { + os << dock->objectName().toUtf8().constData() << ","; + if (first) + first = false; + else + os2 << ","; + os2 << sizes[i]; + } + _sizemap[dock] = sizes[i]; + } + Base::StateLocker lock(_saving); + hGrp->SetASCII("Widgets", os.str().c_str()); + hGrp->SetASCII("Sizes", os2.str().c_str()); + FC_LOG("save " << objectName().toUtf8().constData() << " " << os2.str()); +} + +void OverlayTabWidget::onTabMoved(int from, int to) +{ + QWidget *w = splitter->widget(from); + splitter->insertWidget(to,w); + saveTabs(); +} + +void OverlayTabWidget::setTitleBar(QWidget *w) +{ + titleBar = w; +} + +void OverlayTabWidget::changeEvent(QEvent *e) +{ + QTabWidget::changeEvent(e); + if (e->type() == QEvent::LanguageChange) + retranslate(); +} + +void OverlayTabWidget::retranslate() +{ + actTransparent.setToolTip(tr("Toggle transparent mode")); + actNoAutoMode.setText(tr("None")); + actNoAutoMode.setToolTip(tr("Turn off auto hide/show")); + actAutoHide.setText(tr("Auto hide")); + actAutoHide.setToolTip(tr("Auto hide docked widgets on leave")); + actEditHide.setText(tr("Hide on edit")); + actEditHide.setToolTip(tr("Auto hide docked widgets on editing")); + actEditShow.setText(tr("Show on edit")); + actEditShow.setToolTip(tr("Auto show docked widgets on editing")); + actTaskShow.setText(tr("Auto task")); + actTaskShow.setToolTip(tr("Auto show task view for any current task, and hide the view when there is no task.")); + actOverlay.setToolTip(tr("Toggle overlay")); + syncAutoMode(); +} + +void OverlayTabWidget::syncAutoMode() +{ + QAction *action = nullptr; + switch(autoMode) { + case AutoMode::AutoHide: + action = &actAutoHide; + break; + case AutoMode::EditShow: + action = &actEditShow; + break; + case AutoMode::TaskShow: + action = &actTaskShow; + break; + case AutoMode::EditHide: + action = &actEditHide; + break; + default: + action = &actNoAutoMode; + break; + } + actAutoMode.setIcon(action->icon()); + if (action == &actNoAutoMode) + actAutoMode.setToolTip(tr("Select auto show/hide mode")); + else + actAutoMode.setToolTip(action->toolTip()); +} + +void OverlayTabWidget::onAction(QAction *action) +{ + if (action == &actAutoMode) { + action = autoModeMenu.exec(QCursor::pos()); + if (action == &actNoAutoMode) + setAutoMode(AutoMode::NoAutoMode); + else if (action == &actAutoHide) + setAutoMode(AutoMode::AutoHide); + else if (action == &actEditShow) + setAutoMode(AutoMode::EditShow); + else if (action == &actTaskShow) + setAutoMode(AutoMode::TaskShow); + else if (action == &actEditHide) + setAutoMode(AutoMode::EditHide); + return; + } + else if(action == &actOverlay) { + OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleActive); + return; + } else if(action == &actTransparent) { + if(hGrp) { + Base::StateLocker lock(_saving); + hGrp->SetBool("Transparent", actTransparent.isChecked()); + } + } + OverlayManager::instance()->refresh(this); +} + +void OverlayTabWidget::setState(State state) +{ + if (_state == state) + return; + switch(state) { + case State::Normal: + if (_state == State::Hidden) { + // Only unhide through State::Showing, not State::Normal + return; + } + else if (_state == State::Showing) { + _state = state; + return; + } + // fall through + case State::Showing: + _state = state; + hide(); + if (dockArea == Qt::RightDockWidgetArea) + setTabPosition(East); + else if (dockArea == Qt::BottomDockWidgetArea) + setTabPosition(South); + if (this->count() == 1) + tabBar()->hide(); + _graphicsEffectTab->setEnabled(false); + titleBar->show(); + splitter->show(); + if (state == State::Showing) + OverlayManager::instance()->refresh(this); + break; + case State::Hint: + if (_state == State::HintHidden || _state == State::Hidden) + break; + _state = state; + if (this->count() && OverlayParams::getDockOverlayHintTabBar()) { + tabBar()->setToolTip(proxyWidget->toolTip()); + tabBar()->show(); + titleBar->hide(); + splitter->hide(); + _graphicsEffectTab->setEnabled(true); + show(); + raise(); + proxyWidget->raise(); + if (dockArea == Qt::RightDockWidgetArea) + setTabPosition(West); + else if (dockArea == Qt::BottomDockWidgetArea) + setTabPosition(North); + OverlayManager::instance()->refresh(this); + } + break; + case State::HintHidden: + if (_state != State::Hidden) + _state = state; + proxyWidget->hide(); + hide(); + _graphicsEffectTab->setEnabled(true); + break; + case State::Hidden: + startHide(); + _state = state; + break; + default: + break; + } +} + +bool OverlayTabWidget::checkAutoHide() const +{ + if(autoMode == AutoMode::AutoHide) + return true; + + if(OverlayParams::getDockOverlayAutoView()) { + auto view = getMainWindow()->activeWindow(); + if (!view) return true; + if(!view->onHasMsg("CanPan") + && view->parentWidget() + && view->parentWidget()->isMaximized()) + return true; + } + + if(autoMode == AutoMode::EditShow) { + return !Application::Instance->editDocument() + && (!Control().taskPanel() || Control().taskPanel()->isEmpty(false)); + } + + if(autoMode == AutoMode::EditHide && Application::Instance->editDocument()) + return true; + + return false; +} + +void OverlayTabWidget::leaveEvent(QEvent*) +{ + if (titleBar && QWidget::mouseGrabber() == titleBar) + return; + OverlayManager::instance()->refresh(); +} + +void OverlayTabWidget::enterEvent(QEvent*) +{ + revealTime = QTime(); + OverlayManager::instance()->refresh(); +} + +void OverlayTabWidget::setRevealTime(const QTime &time) +{ + revealTime = time; +} + +void OverlayTabWidget::_setOverlayMode(QWidget *widget, OverlayOption option) +{ + if(!widget) + return; + +#if QT_VERSION>QT_VERSION_CHECK(5,12,2) && QT_VERSION < QT_VERSION_CHECK(5,12,6) + // Work around Qt bug https://bugreports.qt.io/browse/QTBUG-77006 + widget->setStyleSheet(OverlayManager::instance()->getStyleSheet()); +#endif + + if (qobject_cast(widget)) { + auto parent = widget->parentWidget(); + if (parent) { + parent = parent->parentWidget(); + if (qobject_cast(parent)) { + auto scrollArea = static_cast(parent); + if (scrollArea->verticalScrollBar() == widget) { + if (!OverlayParams::getDockOverlayHidePropertyViewScrollBar() + || option == OverlayOption::Disable) + widget->setStyleSheet(QString()); + else { + static QString _style = QStringLiteral("*{width:0}"); + widget->setStyleSheet(_style); + } + } + } + auto treeView = qobject_cast(parent); + if (treeView) { + auto scrollArea = static_cast(parent); + if (scrollArea->verticalScrollBar() == widget) { + if (!TreeParams::getHideScrollBar() || option == OverlayOption::Disable) + widget->setStyleSheet(QString()); + else { + static QString _style = QStringLiteral("*{width:0}"); + widget->setStyleSheet(_style); + } + } + } + + if (treeView) { + auto header = treeView->header(); + if (!TreeParams::getHideHeaderView() || option==OverlayOption::Disable) + header->setStyleSheet(QString()); + else { + static QString _style = QStringLiteral( + "QHeaderView:section {" + "height: 0px;" + "background-color: transparent;" + "padding: 0px;" + "border: transparent;}"); + header->setStyleSheet(_style); + } + } + } + } + + auto tabbar = qobject_cast(widget); + if(tabbar) { + if(!tabbar->autoHide() || tabbar->count()>1) { + if(!OverlayManager::instance()->getHideTab()) + tabbar->setVisible(true); + else + tabbar->setVisible(option == OverlayOption::Disable + || (option == OverlayOption::ShowTab && tabbar->count()>1)); + return; + } + } + + if (!qobject_cast(widget) + || !qobject_cast(widget->parentWidget())) { + if(option != OverlayOption::Disable) { + widget->setWindowFlags(widget->windowFlags() | Qt::FramelessWindowHint); + } else { + widget->setWindowFlags(widget->windowFlags() & ~Qt::FramelessWindowHint); + } + widget->setAttribute(Qt::WA_NoSystemBackground, option != OverlayOption::Disable); + widget->setAttribute(Qt::WA_TranslucentBackground, option != OverlayOption::Disable); + } +} + +void OverlayTabWidget::setOverlayMode(QWidget *widget, OverlayOption option) +{ + if(!widget || (qobject_cast(widget) + && !qobject_cast(widget)) + || qobject_cast(widget)) + return; + + if(widget != tabBar()) { + if(OverlayParams::getDockOverlayAutoMouseThrough() + && option == OverlayOption::ShowTab) { + widget->setMouseTracking(true); + } + } + + _setOverlayMode(widget, option); + + if(qobject_cast(widget)) { + // do not set child QAbstractItemView of QComboBox, otherwise the drop down box + // won't be shown + return; + } + for(auto child : widget->children()) + setOverlayMode(qobject_cast(child), option); +} + +void OverlayTabWidget::setTransparent(bool enable) +{ + if(actTransparent.isChecked() == enable) + return; + if(hGrp) { + Base::StateLocker lock(_saving); + hGrp->SetBool("Transparent", enable); + } + actTransparent.setChecked(enable); + OverlayManager::instance()->refresh(this); +} + +bool OverlayTabWidget::isTransparent() const +{ + if (!actTransparent.isChecked()) + return false; + if(OverlayParams::getDockOverlayAutoView()) { + auto view = getMainWindow()->activeWindow(); + if (!view) return false; + if(!view->onHasMsg("CanPan") + && view->parentWidget() + && view->parentWidget()->isMaximized()) + return false; + } + return true; +} + +bool OverlayTabWidget::isOverlaid(QueryOption option) const +{ + if (option != QueryOption::QueryOverlay + && currentTransparent != isTransparent()) + return option == QueryOption::TransparencyChanged; + return overlaid; +} + +void OverlayTabWidget::setAutoMode(AutoMode mode) +{ + if (autoMode == mode) + return; + autoMode = mode; + + if (hGrp) { + bool autohide = false, editshow = false, edithide = false, taskshow = false; + switch (mode) { + case AutoMode::AutoHide: + autohide = true; + break; + case AutoMode::EditShow: + editshow = true; + break; + case AutoMode::EditHide: + edithide = true; + break; + case AutoMode::TaskShow: + taskshow = true; + break; + default: + break; + } + Base::StateLocker lock(_saving); + hGrp->SetBool("AutoHide", autohide); + hGrp->SetBool("EditShow", editshow); + hGrp->SetBool("EditHide", edithide); + hGrp->SetBool("TaskShow", taskshow); + } + syncAutoMode(); + OverlayManager::instance()->refresh(this); +} + +QDockWidget *OverlayTabWidget::currentDockWidget() const +{ + int index = -1; + for(int size : splitter->sizes()) { + ++index; + if(size>0) + return dockWidget(index); + } + return dockWidget(currentIndex()); +} + +QDockWidget *OverlayTabWidget::dockWidget(int index) const +{ + if(index < 0 || index >= splitter->count()) + return nullptr; + return qobject_cast(splitter->widget(index)); +} + +void OverlayTabWidget::updateSplitterHandles() +{ + if (overlaid || _state > State::Normal) + return; + for (int i=0, c=splitter->count(); i(splitter->handle(i)); + if (handle) + handle->showTitle(true); + } +} + +bool OverlayTabWidget::onEscape() +{ + if (getState() == OverlayTabWidget::State::Hint + || getState() == OverlayTabWidget::State::Hidden) { + setState(OverlayTabWidget::State::HintHidden); + return true; + } + if (!isVisible()) + return false; + if (titleBar->isVisible() && titleBar->underMouse()) { + titleBar->hide(); + return true; + } + for (int i=0, c=splitter->count(); i(splitter->handle(i)); + if (handle->isVisible() && handle->underMouse()) { + handle->showTitle(false); + return true; + } + } + return false; +} + +void OverlayTabWidget::setOverlayMode(bool enable) +{ + overlaid = enable; + + if(!isVisible() || !count()) + return; + + touched = false; + + if (_state <= State::Normal) { + titleBar->setVisible(!enable || OverlayManager::instance()->isMouseTransparent()); + for (int i=0, c=splitter->count(); i(splitter->handle(i)); + if (handle) + handle->showTitle(!enable); + } + } + + QString stylesheet; + stylesheet = OverlayManager::instance()->getStyleSheet(); + currentTransparent = isTransparent(); + + OverlayOption option; + if(!enable && isTransparent()) { + option = OverlayOption::ShowTab; + } else if (enable + && !isTransparent() + && (autoMode == AutoMode::EditShow || autoMode == AutoMode::AutoHide)) { + option = OverlayOption::Disable; + } else { + option = enable?OverlayOption::Enable:OverlayOption::Disable; + } + + proxyWidget->setStyleSheet(stylesheet); + this->setStyleSheet(stylesheet); + setOverlayMode(this, option); + + _graphicsEffect->setEnabled(effectEnabled() && (enable || isTransparent())); + + if (_state == State::Hint && OverlayParams::getDockOverlayHintTabBar()) { + tabBar()->setToolTip(proxyWidget->toolTip()); + tabBar()->show(); + } else if (OverlayParams::getDockOverlayHideTabBar() || count()==1) { + tabBar()->hide(); + } else { + tabBar()->setToolTip(QString()); + tabBar()->setVisible(!enable || !OverlayManager::instance()->getHideTab()); + } + + setRect(rectOverlay); +} + +const QRect &OverlayTabWidget::getRect() +{ + return rectOverlay; +} + +bool OverlayTabWidget::getAutoHideRect(QRect &rect) const +{ + rect = rectOverlay; + int hintWidth = OverlayParams::getDockOverlayHintSize(); + switch(dockArea) { + case Qt::LeftDockWidgetArea: + case Qt::RightDockWidgetArea: + if (_TopOverlay->isVisible() && _TopOverlay->_state <= State::Normal) + rect.setTop(std::max(rect.top(), _TopOverlay->rectOverlay.bottom())); + if (dockArea == Qt::RightDockWidgetArea) + rect.setLeft(rect.left() + std::max(rect.width()-hintWidth,0)); + else + rect.setRight(rect.right() - std::max(rect.width()-hintWidth,0)); + break; + case Qt::TopDockWidgetArea: + case Qt::BottomDockWidgetArea: + if (_LeftOverlay->isVisible() && _LeftOverlay->_state <= State::Normal) + rect.setLeft(std::max(rect.left(),_LeftOverlay->rectOverlay.right())); + if (dockArea == Qt::TopDockWidgetArea) + rect.setBottom(rect.bottom() - std::max(rect.height()-hintWidth,0)); + else { + rect.setTop(rect.top() + std::max(rect.height()-hintWidth,0)); + if (_RightOverlay->isVisible() && _RightOverlay->_state <= State::Normal) { + QPoint offset = getMainWindow()->getMdiArea()->pos(); + rect.setRight(std::min(rect.right(), _RightOverlay->x()-offset.x())); + } + } + break; + default: + break; + } + return _state != State::Showing && overlaid && checkAutoHide(); +} + +void OverlayTabWidget::setOffset(const QSize &ofs) +{ + if(offset != ofs) { + offset = ofs; + if(hGrp) { + Base::StateLocker lock(_saving); + hGrp->SetInt("Offset1", ofs.width()); + hGrp->SetInt("Offset3", ofs.height()); + } + } +} + +void OverlayTabWidget::setSizeDelta(int delta) +{ + if(sizeDelta != delta) { + if(hGrp) { + Base::StateLocker lock(_saving); + hGrp->SetInt("Offset2", delta); + } + sizeDelta = delta; + } +} + +void OverlayTabWidget::setRect(QRect rect) +{ + if(busy || !parentWidget() || !getMainWindow() || !getMainWindow()->getMdiArea()) + return; + + if (rect.width() == 0) + rect.setWidth(OverlayParams::getDockOverlayMinimumSize()*3); + if (rect.height() == 0) + rect.setHeight(OverlayParams::getDockOverlayMinimumSize()*3); + + switch(dockArea) { + case Qt::LeftDockWidgetArea: + rect.moveLeft(0); + if (rect.width() < OverlayParams::getDockOverlayMinimumSize()) + rect.setWidth(OverlayParams::getDockOverlayMinimumSize()); + break; + case Qt::RightDockWidgetArea: + if (rect.width() < OverlayParams::getDockOverlayMinimumSize()) + rect.setLeft(rect.right()-OverlayParams::getDockOverlayMinimumSize()); + break; + case Qt::TopDockWidgetArea: + rect.moveTop(0); + if (rect.height() < OverlayParams::getDockOverlayMinimumSize()) + rect.setHeight(OverlayParams::getDockOverlayMinimumSize()); + break; + case Qt::BottomDockWidgetArea: + if (rect.height() < OverlayParams::getDockOverlayMinimumSize()) + rect.setTop(rect.bottom()-OverlayParams::getDockOverlayMinimumSize()); + break; + default: + break; + } + + if(hGrp && rect.size() != rectOverlay.size()) { + Base::StateLocker lock(_saving); + hGrp->SetInt("Width", rect.width()); + hGrp->SetInt("Height", rect.height()); + } + rectOverlay = rect; + + QPoint offset = getMainWindow()->getMdiArea()->pos(); + + if(getAutoHideRect(rect) || _state == State::Hint || _state == State::Hidden) { + QRect rectHint = rect; + if (_state != State::Hint && _state != State::Hidden) + startHide(); + else if (count() && OverlayParams::getDockOverlayHintTabBar()) { + switch(dockArea) { + case Qt::LeftDockWidgetArea: + case Qt::RightDockWidgetArea: + if (dockArea == Qt::LeftDockWidgetArea) + rect.setWidth(tabBar()->width()); + else + rect.setLeft(rect.left() + rect.width() - tabBar()->width()); + rect.setHeight(std::max(rect.height(), + tabBar()->y() + tabBar()->sizeHint().height() + 5)); + break; + case Qt::BottomDockWidgetArea: + case Qt::TopDockWidgetArea: + if (dockArea == Qt::TopDockWidgetArea) + rect.setHeight(tabBar()->height()); + else + rect.setTop(rect.top() + rect.height() - tabBar()->height()); + rect.setWidth(std::max(rect.width(), + tabBar()->x() + tabBar()->sizeHint().width() + 5)); + break; + default: + break; + } + + setGeometry(rect.translated(offset)); + } + proxyWidget->setGeometry(rectHint.translated(offset)); + if (count()) { + proxyWidget->show(); + proxyWidget->raise(); + } else + proxyWidget->hide(); + + } else { + setGeometry(rectOverlay.translated(offset)); + + for(int i=0, count=splitter->count(); iwidget(i)->show(); + + if(!isVisible() && count()) { + proxyWidget->hide(); + startShow(); + } + } +} + +void OverlayTabWidget::addWidget(QDockWidget *dock, const QString &title) +{ + if (!getMainWindow() || !getMainWindow()->getMdiArea()) + return; + + OverlayManager::instance()->registerDockWidget(dock->objectName(), this); + + OverlayManager::setFocusView(); + getMainWindow()->removeDockWidget(dock); + + auto titleWidget = dock->titleBarWidget(); + if(titleWidget && titleWidget->objectName()==QStringLiteral("OverlayTitle")) { + // replace the title bar with an invisible widget to hide it. The + // OverlayTabWidget uses its own title bar for all docks. + auto w = new QWidget(); + w->setObjectName(QStringLiteral("OverlayTitle")); + dock->setTitleBarWidget(w); + w->hide(); + titleWidget->deleteLater(); + } + + dock->show(); + splitter->addWidget(dock); + auto dummyWidget = new QWidget(this); + addTab(dummyWidget, title); + connect(dock, &QObject::destroyed, dummyWidget, &QObject::deleteLater); + + dock->setFeatures(dock->features() & ~QDockWidget::DockWidgetFloatable); + if(count() == 1) { + QRect rect = dock->geometry(); + QSize sizeMain = getMainWindow()->getMdiArea()->size(); + switch(dockArea) { + case Qt::LeftDockWidgetArea: + case Qt::RightDockWidgetArea: + if (rect.width() > sizeMain.width()/3) + rect.setWidth(sizeMain.width()/3); + break; + case Qt::TopDockWidgetArea: + case Qt::BottomDockWidgetArea: + if (rect.height() > sizeMain.height()/3) + rect.setHeight(sizeMain.height()/3); + break; + default: + break; + } + setRect(rect); + } + + saveTabs(); +} + +int OverlayTabWidget::dockWidgetIndex(QDockWidget *dock) const +{ + return splitter->indexOf(dock); +} + +void OverlayTabWidget::removeWidget(QDockWidget *dock, QDockWidget *lastDock) +{ + int index = dockWidgetIndex(dock); + if(index < 0) + return; + + OverlayManager::instance()->unregisterDockWidget(dock->objectName(), this); + + OverlayManager::setFocusView(); + dock->show(); + if(lastDock) + getMainWindow()->tabifyDockWidget(lastDock, dock); + else + getMainWindow()->addDockWidget(dockArea, dock); + + auto w = this->widget(index); + removeTab(index); + w->deleteLater(); + + if(!count()) + hide(); + + w = dock->titleBarWidget(); + if(w && w->objectName() == QStringLiteral("OverlayTitle")) { + dock->setTitleBarWidget(nullptr); + w->deleteLater(); + } + OverlayManager::instance()->setupTitleBar(dock); + + dock->setFeatures(dock->features() | QDockWidget::DockWidgetFloatable); + + setOverlayMode(dock, OverlayOption::Disable); + + saveTabs(); +} + +void OverlayTabWidget::resizeEvent(QResizeEvent *ev) +{ + QTabWidget::resizeEvent(ev); + if (_state <= State::Normal) + timer.start(10); +} + +void OverlayTabWidget::setupLayout() +{ + if (_state > State::Normal) + return; + + if(count() == 1) + tabSize = 0; + else { + int tsize; + if(dockArea==Qt::LeftDockWidgetArea || dockArea==Qt::RightDockWidgetArea) + tsize = tabBar()->width(); + else + tsize = tabBar()->height(); + tabSize = tsize; + } + int titleBarSize = widgetMinSize(this, true); + QRect rect, rectTitle; + switch(tabPosition()) { + case West: + rectTitle = QRect(tabSize, 0, this->width()-tabSize, titleBarSize); + rect = QRect(rectTitle.left(), rectTitle.bottom(), + rectTitle.width(), this->height()-rectTitle.height()); + break; + case East: + rectTitle = QRect(0, 0, this->width()-tabSize, titleBarSize); + rect = QRect(rectTitle.left(), rectTitle.bottom(), + rectTitle.width(), this->height()-rectTitle.height()); + break; + case North: + rectTitle = QRect(0, tabSize, titleBarSize, this->height()-tabSize); + rect = QRect(rectTitle.right(), rectTitle.top(), + this->width()-rectTitle.width(), rectTitle.height()); + break; + case South: + rectTitle = QRect(0, 0, titleBarSize, this->height()-tabSize); + rect = QRect(rectTitle.right(), rectTitle.top(), + this->width()-rectTitle.width(), rectTitle.height()); + break; + } + if (_animation != 0.0) { + switch(dockArea) { + case Qt::LeftDockWidgetArea: + rect.moveLeft(rect.left() - _animation * rect.width()); + break; + case Qt::RightDockWidgetArea: + rect.moveLeft(rect.left() + _animation * rect.width()); + break; + case Qt::TopDockWidgetArea: + rect.moveTop(rect.top() - _animation * rect.height()); + break; + case Qt::BottomDockWidgetArea: + rect.moveTop(rect.top() + _animation * rect.height()); + break; + default: + break; + } + } + splitter->setGeometry(rect); + titleBar->setGeometry(rectTitle); +} + +void OverlayTabWidget::setCurrent(QDockWidget *widget) +{ + int index = dockWidgetIndex(widget); + if(index >= 0) + setCurrentIndex(index); +} + +void OverlayTabWidget::onSplitterResize(int index) +{ + const auto &sizes = splitter->sizes(); + if (index >= 0 && index < sizes.count()) { + if (sizes[index] == 0) { + if (currentIndex() == index) { + bool done = false; + for (int i=index+1; i 0) { + setCurrentIndex(i); + done = true; + break; + } + } + if (!done) { + for (int i=index-1; i>=0 ;--i) { + if (sizes[i] > 0) { + setCurrentIndex(i); + break; + } + } + } + } + } else + setCurrentIndex(index); + } + + saveTabs(); +} + +void OverlayTabWidget::onCurrentChanged(int index) +{ + setState(State::Showing); + + auto sizes = splitter->sizes(); + int i=0; + int size = splitter->orientation()==Qt::Vertical ? + height()-tabBar()->height() : width()-tabBar()->width(); + for(auto &s : sizes) { + if(i++ == index) + s = size; + else + s = 0; + } + splitter->setSizes(sizes); + onSplitterResize(index); + saveTabs(); +} + +void OverlayTabWidget::onSizeGripMove(const QPoint &p) +{ + if (!getMainWindow() || !getMainWindow()->getMdiArea()) + return; + + QPoint pos = mapFromGlobal(p) + this->pos(); + QPoint offset = getMainWindow()->getMdiArea()->pos(); + QRect rect = this->rectOverlay.translated(offset); + + switch(dockArea) { + case Qt::LeftDockWidgetArea: + if (pos.x() - rect.left() < OverlayParams::getDockOverlayMinimumSize()) + return; + rect.setRight(pos.x()); + break; + case Qt::RightDockWidgetArea: + if (rect.right() - pos.x() < OverlayParams::getDockOverlayMinimumSize()) + return; + rect.setLeft(pos.x()); + break; + case Qt::TopDockWidgetArea: + if (pos.y() - rect.top() < OverlayParams::getDockOverlayMinimumSize()) + return; + rect.setBottom(pos.y()); + break; + default: + if (rect.bottom() - pos.y() < OverlayParams::getDockOverlayMinimumSize()) + return; + rect.setTop(pos.y()); + break; + } + this->setRect(rect.translated(-offset)); + OverlayManager::instance()->refresh(); +} + +QLayoutItem *OverlayTabWidget::prepareTitleWidget(QWidget *widget, const QList &actions) +{ + bool vertical = false; + QBoxLayout *layout = nullptr; + auto tabWidget = qobject_cast(widget->parentWidget()); + if(!tabWidget) { + layout = new QBoxLayout(QBoxLayout::LeftToRight, widget); + } else { + switch(tabWidget->getDockArea()) { + case Qt::LeftDockWidgetArea: + layout = new QBoxLayout(QBoxLayout::LeftToRight, widget); + break; + case Qt::RightDockWidgetArea: + layout = new QBoxLayout(QBoxLayout::RightToLeft, widget); + break; + case Qt::TopDockWidgetArea: + layout = new QBoxLayout(QBoxLayout::TopToBottom, widget); + vertical = true; + break; + case Qt::BottomDockWidgetArea: + layout = new QBoxLayout(QBoxLayout::BottomToTop, widget); + vertical = true; + break; + default: + break; + } + } + + layout->addSpacing(5); + layout->setContentsMargins(1,1,1,1); + int buttonSize = widgetMinSize(widget); + auto spacer = new QSpacerItem(buttonSize,buttonSize, + vertical?QSizePolicy::Minimum:QSizePolicy::Expanding, + vertical?QSizePolicy::Expanding:QSizePolicy::Minimum); + layout->addSpacerItem(spacer); + + for(auto action : actions) + layout->addWidget(OverlayTabWidget::createTitleButton(action, buttonSize)); + + if (tabWidget) { + auto grip = new OverlaySizeGrip(tabWidget, vertical); + QObject::connect(grip, &OverlaySizeGrip::dragMove, + tabWidget, &OverlayTabWidget::onSizeGripMove); + layout->addWidget(grip); + grip->raise(); + } + + return spacer; +} + +// ----------------------------------------------------------- + +OverlayTitleBar::OverlayTitleBar(QWidget * parent) + :QWidget(parent) +{ + setFocusPolicy(Qt::ClickFocus); + setMouseTracking(true); + setCursor(Qt::OpenHandCursor); +} + +void OverlayTitleBar::setTitleItem(QLayoutItem *item) +{ + titleItem = item; +} + +void OverlayTitleBar::paintEvent(QPaintEvent *) +{ + if (!titleItem) + return; + + QDockWidget *dock = qobject_cast(parentWidget()); + int vertical = false; + int flags = Qt::AlignCenter; + if (!dock) { + OverlayTabWidget *tabWidget = qobject_cast(parentWidget()); + if (tabWidget) { + switch(tabWidget->getDockArea()) { + case Qt::TopDockWidgetArea: + vertical = true; + // fallthrough + case Qt::RightDockWidgetArea: + flags = Qt::AlignRight; + break; + case Qt::BottomDockWidgetArea: + vertical = true; + // fallthrough + case Qt::LeftDockWidgetArea: + flags = Qt::AlignLeft; + break; + default: + break; + } + dock = tabWidget->dockWidget(0); + } + } + if (!dock) + return; + + QPainter painter(this); + if (qobject_cast(parentWidget())) + painter.fillRect(this->rect(), painter.background()); + + QRect r = titleItem->geometry(); + if (vertical) { + r = r.transposed(); + painter.translate(r.left(), r.top() + r.width()); + painter.rotate(-90); + painter.translate(-r.left(), -r.top()); + } + + QString title; + if (OverlayManager::instance()->isMouseTransparent()) { + if (timerId == 0) + timerId = startTimer(500); + title = blink ? tr("Mouse pass through, ESC to stop") : dock->windowTitle(); + } else { + if (timerId != 0) { + killTimer(timerId); + timerId = 0; + } + title = dock->windowTitle(); + } + QString text = painter.fontMetrics().elidedText( + title, Qt::ElideRight, r.width()); + painter.drawText(r, flags, text); +} + +void OverlayTitleBar::timerEvent(QTimerEvent *ev) +{ + if (timerId == ev->timerId()) { + update(); + blink = !blink; + } +} + +static inline bool +isNear(const QPoint &a, const QPoint &b, int tol = 16) +{ + QPoint d = a - b; + return d.x()*d.x() + d.y()*d.y() < tol; +} + +void OverlayTitleBar::endDrag() +{ + if (OverlayTabWidget::_Dragging == this) { + OverlayTabWidget::_Dragging = nullptr; + setCursor(Qt::OpenHandCursor); + if (OverlayTabWidget::_DragFrame) + OverlayTabWidget::_DragFrame->hide(); + if (OverlayTabWidget::_DragFloating) + OverlayTabWidget::_DragFrame->hide(); + } +} + +void OverlayTitleBar::mouseMoveEvent(QMouseEvent *me) +{ + if (ignoreMouse) { + if (!(me->buttons() & Qt::LeftButton)) + ignoreMouse = false; + else { + me->ignore(); + return; + } + } + + if (OverlayTabWidget::_Dragging != this && mouseMovePending && (me->buttons() & Qt::LeftButton)) { + if (isNear(dragOffset, me->pos())) + return; + mouseMovePending = false; + OverlayTabWidget::_Dragging = this; + } + + if (OverlayTabWidget::_Dragging != this) + return; + + if (!(me->buttons() & Qt::LeftButton)) { + endDrag(); + return; + } + OverlayManager::instance()->dragDockWidget(me->globalPos(), + parentWidget(), + dragOffset, + dragSize); +} + +void OverlayTitleBar::mousePressEvent(QMouseEvent *me) +{ + mouseMovePending = false; + QWidget *parent = parentWidget(); + if (OverlayTabWidget::_Dragging || !parent || !getMainWindow() || me->button() != Qt::LeftButton) + return; + + dragSize = parent->size(); + OverlayTabWidget *tabWidget = qobject_cast(parent); + if (!tabWidget) { + if(QApplication::queryKeyboardModifiers() == Qt::ShiftModifier) { + ignoreMouse = true; + me->ignore(); + return; + } + } + else { + for (int s : tabWidget->getSplitter()->sizes()) { + if (!s) + continue; + if (tabWidget == OverlayTabWidget::_TopOverlay + || tabWidget == OverlayTabWidget::_BottomOverlay) { + dragSize.setWidth(s + this->width()); + dragSize.setHeight(tabWidget->height()); + } + else { + dragSize.setHeight(s + this->height()); + dragSize.setWidth(tabWidget->width()); + } + } + } + ignoreMouse = false; + QSize mwSize = getMainWindow()->size(); + dragSize.setWidth(std::max(OverlayParams::getDockOverlayMinimumSize(), + static_cast(std::min(mwSize.width()/2, dragSize.width())))); + dragSize.setHeight(std::max(OverlayParams::getDockOverlayMinimumSize(), + static_cast(std::min(mwSize.height()/2, dragSize.height())))); + + dragOffset = me->pos(); + setCursor(Qt::ClosedHandCursor); + mouseMovePending = true; +} + +void OverlayTitleBar::mouseReleaseEvent(QMouseEvent *me) +{ + if (ignoreMouse) { + me->ignore(); + return; + } + + setCursor(Qt::OpenHandCursor); + mouseMovePending = false; + if (OverlayTabWidget::_Dragging != this) + return; + + if (me->button() != Qt::LeftButton) + return; + + OverlayTabWidget::_Dragging = nullptr; + OverlayManager::instance()->dragDockWidget(me->globalPos(), + parentWidget(), + dragOffset, + dragSize, + true); + if (OverlayTabWidget::_DragFrame) + OverlayTabWidget::_DragFrame->hide(); + if (OverlayTabWidget::_DragFloating) + OverlayTabWidget::_DragFloating->hide(); +} + +void OverlayTitleBar::keyPressEvent(QKeyEvent *ke) +{ + if (OverlayTabWidget::_Dragging == this && ke->key() == Qt::Key_Escape) + endDrag(); +} + + +// ----------------------------------------------------------- + +OverlayDragFrame::OverlayDragFrame(QWidget * parent) + :QWidget(parent) +{ +} + +void OverlayDragFrame::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + painter.drawRect(0, 0, this->width()-1, this->height()-1); + painter.setOpacity(0.3); + painter.setBrush(QBrush(Qt::blue)); + painter.drawRect(0, 0, this->width()-1, this->height()-1); +} + +QSize OverlayDragFrame::sizeHint() const +{ + return size(); +} + +QSize OverlayDragFrame::minimumSizeHint() const +{ + return minimumSize(); +} + +// ----------------------------------------------------------- + +OverlaySizeGrip::OverlaySizeGrip(QWidget * parent, bool vertical) + :QWidget(parent), vertical(vertical) +{ + if (vertical) { + this->setFixedHeight(6); + this->setMinimumWidth(widgetMinSize(this,true)); + this->setCursor(Qt::SizeVerCursor); + } + else { + this->setFixedWidth(6); + this->setMinimumHeight(widgetMinSize(this,true)); + this->setCursor(Qt::SizeHorCursor); + } + setMouseTracking(true); +} + +void OverlaySizeGrip::paintEvent(QPaintEvent*) +{ + QPainter painter(this); + painter.setPen(Qt::transparent); + painter.setOpacity(0.5); + painter.setBrush(QBrush(Qt::black, Qt::Dense6Pattern)); + QRect rect(this->rect()); + painter.drawRect(rect); +} + +void OverlaySizeGrip::mouseMoveEvent(QMouseEvent *me) +{ + if ((me->buttons() & Qt::LeftButton)) + Q_EMIT dragMove(me->globalPos()); +} + +void OverlaySizeGrip::mousePressEvent(QMouseEvent *) +{ +} + +void OverlaySizeGrip::mouseReleaseEvent(QMouseEvent *) +{ +} + +// ----------------------------------------------------------- + +OverlaySplitter::OverlaySplitter(QWidget *parent) + : QSplitter(parent) +{ +} + +QSplitterHandle * OverlaySplitter::createHandle() +{ + auto widget = new OverlaySplitterHandle(this->orientation(), this); + widget->setObjectName(QStringLiteral("OverlaySplitHandle")); + QList actions; + actions.append(&widget->actFloat); + widget->setTitleItem(OverlayTabWidget::prepareTitleWidget(widget, actions)); + return widget; +} + +// ----------------------------------------------------------- + +OverlaySplitterHandle::OverlaySplitterHandle(Qt::Orientation orientation, QSplitter *parent) + : QSplitterHandle(orientation, parent) +{ + setMouseTracking(true); + setFocusPolicy(Qt::ClickFocus); + retranslate(); + refreshIcons(); + QObject::connect(&actFloat, &QAction::triggered, this, &OverlaySplitterHandle::onAction); + timer.setSingleShot(true); + QObject::connect(&timer, &QTimer::timeout, this, &OverlaySplitterHandle::onTimer); +} + +void OverlaySplitterHandle::refreshIcons() +{ + actFloat.setIcon(BitmapFactory().pixmap("qss:overlay/float.svg")); +} + +void OverlaySplitterHandle::onTimer() +{ + if (isVisible() && qApp->widgetAt(QCursor::pos()) != this) + showTitle(false); +} + +void OverlaySplitterHandle::showEvent(QShowEvent *ev) +{ + if (OverlayParams::getDockOverlaySplitterHandleTimeout() > 0 + && qApp->widgetAt(QCursor::pos()) != this) + timer.start(OverlayParams::getDockOverlaySplitterHandleTimeout()); + QSplitterHandle::showEvent(ev); +} + +void OverlaySplitterHandle::enterEvent(QEvent *ev) +{ + timer.stop(); + QSplitterHandle::enterEvent(ev); +} + +void OverlaySplitterHandle::leaveEvent(QEvent *ev) +{ + if (OverlayParams::getDockOverlaySplitterHandleTimeout() > 0) + timer.start(OverlayParams::getDockOverlaySplitterHandleTimeout()); + QSplitterHandle::leaveEvent(ev); +} + +QSize OverlaySplitterHandle::sizeHint() const +{ + QSize size = QSplitterHandle::sizeHint(); + int minSize = widgetMinSize(this,true); + if (this->orientation() == Qt::Vertical) + size.setHeight(std::max(minSize, size.height())); + else + size.setWidth(std::max(minSize, size.width())); + return size; +} + +void OverlaySplitterHandle::onAction() +{ + auto action = qobject_cast(sender()); + if(action == &actFloat) { + QDockWidget *dock = dockWidget(); + if (dock) + OverlayManager::instance()->floatDockWidget(dock); + } +} + +QDockWidget *OverlaySplitterHandle::dockWidget() +{ + QSplitter *parent = splitter(); + if (!parent) + return nullptr; + + if (parent->handle(this->idx) != this) { + this->idx = -1; + for (int i=0, c=parent->count(); ihandle(i) == this) { + this->idx = i; + break; + } + } + } + return qobject_cast(parent->widget(this->idx)); +} + +void OverlaySplitterHandle::retranslate() +{ + actFloat.setToolTip(QObject::tr("Toggle floating window")); +} + +void OverlaySplitterHandle::changeEvent(QEvent *e) +{ + QSplitterHandle::changeEvent(e); + if (e->type() == QEvent::LanguageChange) + retranslate(); +} + +void OverlaySplitterHandle::setTitleItem(QLayoutItem *item) +{ + titleItem = item; +} + +void OverlaySplitterHandle::showTitle(bool enable) +{ + if (_showTitle == enable) + return; + if (!enable) + unsetCursor(); + else { + setCursor(this->orientation() == Qt::Horizontal + ? Qt::SizeHorCursor : Qt::SizeVerCursor); + if (OverlayParams::getDockOverlaySplitterHandleTimeout() > 0 + && qApp->widgetAt(QCursor::pos()) != this) + timer.start(OverlayParams::getDockOverlaySplitterHandleTimeout()); + } + _showTitle = enable; + for (auto child : findChildren(QString(), Qt::FindDirectChildrenOnly)) + child->setVisible(enable); + update(); +} + +void OverlaySplitterHandle::paintEvent(QPaintEvent *e) +{ + if (!_showTitle) + return; + + if (!titleItem) { + QSplitterHandle::paintEvent(e); + return; + } + + int flags = Qt::AlignCenter; + auto tabWidget = qobject_cast( + splitter() ? splitter()->parentWidget() : nullptr); + + if (tabWidget) { + switch(tabWidget->getDockArea()) { + case Qt::TopDockWidgetArea: + case Qt::RightDockWidgetArea: + flags = Qt::AlignRight; + break; + case Qt::BottomDockWidgetArea: + case Qt::LeftDockWidgetArea: + flags = Qt::AlignLeft; + break; + default: + break; + } + } + + QDockWidget *dock = dockWidget(); + if (!dock) { + QSplitterHandle::paintEvent(e); + return; + } + + QPainter painter(this); + painter.fillRect(this->rect(), painter.background()); + + QRect r = titleItem->geometry(); + if (this->orientation() != Qt::Vertical) { + r = r.transposed(); + painter.translate(r.left(), r.top() + r.width()); + painter.rotate(-90); + painter.translate(-r.left(), -r.top()); + } + QString text = painter.fontMetrics().elidedText( + dock->windowTitle(), Qt::ElideRight, r.width()); + + painter.drawText(r, flags, text); +} + +void OverlaySplitterHandle::endDrag() +{ + auto tabWidget = qobject_cast(splitter()->parentWidget()); + if (tabWidget) { + dockWidget(); + tabWidget->onSplitterResize(this->idx); + } + OverlayTabWidget::_Dragging = nullptr; + setCursor(this->orientation() == Qt::Horizontal + ? Qt::SizeHorCursor : Qt::SizeVerCursor); + if (OverlayTabWidget::_DragFrame) + OverlayTabWidget::_DragFrame->hide(); + if (OverlayTabWidget::_DragFloating) + OverlayTabWidget::_DragFloating->hide(); +} + +void OverlaySplitterHandle::keyPressEvent(QKeyEvent *ke) +{ + if (OverlayTabWidget::_Dragging == this && ke->key() == Qt::Key_Escape) + endDrag(); +} + +void OverlaySplitterHandle::mouseMoveEvent(QMouseEvent *me) +{ + if (OverlayTabWidget::_Dragging != this) + return; + + if (!(me->buttons() & Qt::LeftButton)) { + endDrag(); + return; + } + + if (dragging == 1) { + OverlayTabWidget *overlay = qobject_cast( + splitter()->parentWidget()); + QPoint pos = me->pos(); + if (overlay) { + switch(overlay->getDockArea()) { + case Qt::LeftDockWidgetArea: + case Qt::RightDockWidgetArea: + if (pos.x() < 0 || pos.x() > overlay->width()) + dragging = 2; + break; + case Qt::TopDockWidgetArea: + case Qt::BottomDockWidgetArea: + if (pos.y() < 0 || pos.y() > overlay->height()) + dragging = 2; + break; + default: + break; + } + } + if (dragging == 1) { + QPoint offset = parentWidget()->mapFromGlobal(me->globalPos()) - dragOffset; + moveSplitter(this->orientation() == Qt::Horizontal ? offset.x() : offset.y()); + return; + } + setCursor(Qt::ClosedHandCursor); + } + + OverlayManager::instance()->dragDockWidget(me->globalPos(), + dockWidget(), + dragOffset, + dragSize); +} + +void OverlaySplitterHandle::mousePressEvent(QMouseEvent *me) +{ + if (OverlayTabWidget::_Dragging || !getMainWindow() || me->button() != Qt::LeftButton) + return; + + OverlayTabWidget::_Dragging = this; + dragging = 1; + dragOffset = me->pos(); + auto dock = dockWidget(); + if (dock) { + dragSize = dock->size(); + dock->show(); + } else + dragSize = QSize(); + + QSize mwSize = getMainWindow()->size(); + dragSize.setWidth(std::max(OverlayParams::getDockOverlayMinimumSize(), + static_cast(std::min(mwSize.width()/2, dragSize.width())))); + dragSize.setHeight(std::max(OverlayParams::getDockOverlayMinimumSize(), + static_cast(std::min(mwSize.height()/2, dragSize.height())))); + +} + +void OverlaySplitterHandle::mouseReleaseEvent(QMouseEvent *me) +{ + if (OverlayTabWidget::_Dragging != this || me->button() != Qt::LeftButton) + return; + + if (dragging == 1) { + endDrag(); + return; + } + endDrag(); + OverlayManager::instance()->dragDockWidget(me->globalPos(), + dockWidget(), + dragOffset, + dragSize, + true); + // Warning! the handle itself maybe deleted after return from + // dragDockWidget(). +} + +// ----------------------------------------------------------- + +OverlayGraphicsEffect::OverlayGraphicsEffect(QObject *parent) : + QGraphicsEffect(parent), + _enabled(false), + _size(1,1), + _blurRadius(2.0f), + _color(0, 0, 0, 80) +{ +} + +QT_BEGIN_NAMESPACE + extern Q_WIDGETS_EXPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0 ); +QT_END_NAMESPACE + +void OverlayGraphicsEffect::draw(QPainter* painter) +{ + // if nothing to show outside the item, just draw source + if (!_enabled || _blurRadius + _size.height() <= 0 || _blurRadius + _size.width() <= 0) { + drawSource(painter); + return; + } + + PixmapPadMode mode = QGraphicsEffect::PadToEffectiveBoundingRect; + QPoint offset; + QPixmap px = sourcePixmap(Qt::DeviceCoordinates, &offset, mode); + + // return if no source + if (px.isNull()) + return; + +#if 0 + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + static int count; + getMainWindow()->showMessage( + QStringLiteral("dock overlay redraw %1").arg(count++)); + } +#endif + + QTransform restoreTransform = painter->worldTransform(); + painter->setWorldTransform(QTransform()); + + // Calculate size for the background image + QImage tmp(px.size(), QImage::Format_ARGB32_Premultiplied); + tmp.setDevicePixelRatio(px.devicePixelRatioF()); + tmp.fill(0); + QPainter tmpPainter(&tmp); + QPainterPath clip; + tmpPainter.setCompositionMode(QPainter::CompositionMode_Source); + if(_size.width() == 0 && _size.height() == 0) + tmpPainter.drawPixmap(QPoint(0, 0), px); + else { + // exclude splitter handles + auto splitter = qobject_cast(parent()); + if (splitter) { + int i = -1; + for (int size : splitter->sizes()) { + ++i; + if (!size) + continue; + QWidget *w = splitter->widget(i); + if (w->findChild()) + continue; + QRect rect = w->geometry(); + if (splitter->orientation() == Qt::Vertical) + clip.addRect(rect.x(), rect.y()+4, + rect.width(), rect.height()-4); + else + clip.addRect(rect.x()+4, rect.y(), + rect.width()-4, rect.height()); + } + if (clip.isEmpty()) { + drawSource(painter); + return; + } + tmpPainter.setClipPath(clip); + } + + for (int x=-_size.width();x<=_size.width();++x) { + for (int y=-_size.height();y<=_size.height();++y) { + if (x || y) { + tmpPainter.drawPixmap(QPoint(x, y), px); + tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceOver); + } + } + } + } + tmpPainter.end(); + + // blur the alpha channel + QImage blurred(tmp.size(), QImage::Format_ARGB32_Premultiplied); + blurred.setDevicePixelRatio(px.devicePixelRatioF()); + blurred.fill(0); + QPainter blurPainter(&blurred); + qt_blurImage(&blurPainter, tmp, blurRadius(), false, true); + blurPainter.end(); + + tmp = blurred; + + // blacken the image... + tmpPainter.begin(&tmp); + tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + tmpPainter.fillRect(tmp.rect(), color()); + tmpPainter.end(); + + // draw the blurred shadow... + painter->drawImage(QPointF(offset.x()+_offset.x(), offset.y()+_offset.y()), tmp); + + // draw the actual pixmap... + painter->drawPixmap(offset, px, QRectF()); + +#if 0 + QWidget *focus = qApp->focusWidget(); + if (focus) { + QWidget *widget = qobject_cast(this->parent()); + if (auto *edit = qobject_cast(focus)) { + if (!edit->isReadOnly() && edit->isEnabled()) { + for(auto w=edit->parentWidget(); w; w=w->parentWidget()) { + if (w == widget) { + QRect r = edit->cursorRect(); + QRect rect(edit->viewport()->mapTo(widget, r.topLeft()), + edit->viewport()->mapTo(widget, r.bottomRight())); + // painter->fillRect(rect, edit->textColor()); + // painter->fillRect(rect, edit->currentCharFormat().foreground()); + painter->fillRect(rect.translated(offset), Qt::white); + } + } + } + } + } +#endif + + // restore world transform + painter->setWorldTransform(restoreTransform); +} + +QRectF OverlayGraphicsEffect::boundingRectFor(const QRectF& rect) const +{ + if (!_enabled) + return rect; + return rect.united(rect.adjusted(-_blurRadius - _size.width() + _offset.x(), + -_blurRadius - _size.height()+ _offset.y(), + _blurRadius + _size.width() + _offset.x(), + _blurRadius + _size.height() + _offset.y())); +} + +#include "moc_OverlayWidgets.cpp" diff --git a/src/Gui/OverlayWidgets.h b/src/Gui/OverlayWidgets.h new file mode 100644 index 0000000000..9f71ced9c3 --- /dev/null +++ b/src/Gui/OverlayWidgets.h @@ -0,0 +1,630 @@ +/**************************************************************************** + * Copyright (c) 2020 Zheng Lei (realthunder) * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ****************************************************************************/ + +#ifndef FC_OVERLAYWIDGETS_H +#define FC_OVERLAYWIDGETS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "OverlayManager.h" + +class QPropertyAnimation; +class QLayoutItem; + +namespace Gui { + +class OverlayTabWidget; +class OverlayTitleBar; +class OverlaySplitterHandle; +class OverlaySizeGrip; +class OverlayProxyWidget; +class OverlayGraphicsEffect; +class OverlayDragFrame; + +/// Tab widget to contain dock widgets in overlay mode +class OverlayTabWidget: public QTabWidget +{ + Q_OBJECT + + /** @name Graphics effect properties for customization through stylesheet */ + //@{ + Q_PROPERTY(QColor effectColor READ effectColor WRITE setEffectColor) // clazy:exclude=qproperty-without-notify + Q_PROPERTY(int effectWidth READ effectWidth WRITE setEffectWidth) // clazy:exclude=qproperty-without-notify + Q_PROPERTY(int effectHeight READ effectHeight WRITE setEffectHeight) // clazy:exclude=qproperty-without-notify + Q_PROPERTY(qreal effectOffsetX READ effectOffsetX WRITE setEffectOffsetX) // clazy:exclude=qproperty-without-notify + Q_PROPERTY(qreal effectOffsetY READ effectOffsetY WRITE setEffectOffsetY) // clazy:exclude=qproperty-without-notify + Q_PROPERTY(qreal effectBlurRadius READ effectBlurRadius WRITE setEffectBlurRadius) // clazy:exclude=qproperty-without-notify + Q_PROPERTY(bool enableEffect READ effectEnabled WRITE setEffectEnabled) // clazy:exclude=qproperty-without-notify + Q_PROPERTY(qreal animation READ animation WRITE setAnimation) // clazy:exclude=qproperty-without-notify + //@} + +public: + /** Constructor + * @param parent: parent widget + * @param pos: docking position + */ + OverlayTabWidget(QWidget *parent, Qt::DockWidgetArea pos); + + /// Enable/disable overlay mode for this tab widget + void setOverlayMode(bool enable); + + /// Update splitter handle according to overlay status + void updateSplitterHandles(); + + /** Add a dock widget + * @param widget: dock widget to be added + * @param title: title for the dock widget + */ + void addWidget(QDockWidget *widget, const QString &title); + + /** Remove a dock widget + * @param widget: dock widget to be removed + * @param last: optional non overlaid widget. If given, then the removed + * dock widget will be tabified together with this one. + */ + void removeWidget(QDockWidget *widget, QDockWidget *last=nullptr); + + /** Set the current dock widget + * @param widget: an overlay dock widget + * + * All other dock widget in the same tab widget will be collapsed, and the + * give widget will take the whole space. This is actually handled inside + * onCurrentChanged(). + */ + void setCurrent(QDockWidget *widget); + + /** Handle ESC key press + * + * If the this overlay tab widget is hidden and its hint/tabs are visible, + * pressing ESC will hide the hint and tabs. + * + * If this overlay tab widget is visible and the current mouse cursor is on + * top of the tab widget title bar or any of its split handler, then + * pressing ESC will hide the title bar and split handler. + */ + bool onEscape(); + + /// Enable/disable transparent background mode + void setTransparent(bool enable); + + /// Check if transparent background mode is active + bool isTransparent() const; + + /// Auto mode to show or hide the tab widget + enum class AutoMode { + /// No auto show or hide + NoAutoMode, + /// Auto hide tab widget on lost of focus + AutoHide, + /// Auto show tab widget on any new editing task + EditShow, + /// Auto hide tab widget on any new editing task + EditHide, + /// Auto show on any task panel e.g. suggestive task panel in PartDesign + TaskShow, + }; + /// Set auto mode to show or hide the tab widget + void setAutoMode(AutoMode mode); + /// Get current auto mode + AutoMode getAutoMode() const { return autoMode; } + + /// Touch the tab widget to trigger saving of settings + void touch() {touched = true;} + /// Check if the tab widget settings need to be saved + bool isTouched() const {return touched;} + + /// Set geometry of this tab widget + void setRect(QRect rect); + + /// Get the geometry of this tab widget + const QRect &getRect(); + + /// Overlay query option + enum class QueryOption { + /// Report the current overlay status + QueryOverlay, + /// Report true if transparency status has been changed + TransparencyChanged, + /// Report true if transparency status has not been changed + TransparencyNotChanged, + }; + /// Query overlay status + bool isOverlaid(QueryOption option=QueryOption::QueryOverlay) const; + /** Check if needs to auto hide this tab widget + * + * Besides when auto hide mode is activated by user, the tab widget will + * also auto hide if the current view does not have panning capability + * (queried through MdiView::hasMsg("CanPan")). The user can explicitly + * disable this auto hide if no pan by setting user parameter + * View/DockOverlayAutoView, or preference option Display -> UI -> Auto + * hide in non 3D view. + */ + bool checkAutoHide() const; + /** Obtain geometry of auto hiding tab widget + * @param rect: output geometry of the tab widget + * @return Return true if the tab widget should be auto hiding + */ + bool getAutoHideRect(QRect &rect) const; + /// Handler of various actions exposed as buttons on title bar + void onAction(QAction *); + /// Sync relevant actions status with the current auto mode + void syncAutoMode(); + + /** Set tab widget position offset + * @param ofs: the offset size. Width is the x offset for top and bottom + * docking tab widget, and y offset for left and right ones. Height is the y + * offset for top and bottom, and x offset for left and right ones. + */ + void setOffset(const QSize &ofs); + /// Get the tab widget position offset + const QSize &getOffset() const {return offset;} + + /** Set tab widget size delta + * @param delta: the size delta. For left and right widget, the delta is + * added to the height of the tab widget. For top and bottom widget, it is + * added to the width. + */ + void setSizeDelta(int delta); + /// Get the tab widget size delta + int getSizeDelta() const {return sizeDelta;} + + /// Obtain the proxy widget + OverlayProxyWidget *getProxyWidget() {return proxyWidget;} + + /// Obtain the current dock widget + QDockWidget *currentDockWidget() const; + /// Obtain the dock widget by index + QDockWidget *dockWidget(int index) const; + /// Obtain the index of a given dock widget + int dockWidgetIndex(QDockWidget *) const; + + /// Set the title bar for this tab widget + void setTitleBar(QWidget *); + + /// Get the splitter + QSplitter *getSplitter() const {return splitter;} + /// Get the title bar + QWidget *getTitleBar() const {return titleBar;} + + /// Get the docking position of this tab widget + Qt::DockWidgetArea getDockArea() const {return dockArea;} + + /// Get delay time for animated reveal + const QTime &getRevealTime() const {return revealTime;} + /// Set delay time for animated reveal + void setRevealTime(const QTime &time); + + /// Restore state + void restore(ParameterGrp::handle handle); + /// Save tab orders and positions + void saveTabs(); + + /** @name Graphics effect properties setters and getters */ + //@{ + QColor effectColor() const; + void setEffectColor(const QColor&); + int effectWidth() const; + void setEffectWidth(int); + int effectHeight() const; + void setEffectHeight(int); + qreal effectOffsetX() const; + void setEffectOffsetX(qreal); + qreal effectOffsetY() const; + void setEffectOffsetY(qreal); + qreal effectBlurRadius() const; + void setEffectBlurRadius(qreal); + bool effectEnabled() const; + void setEffectEnabled(bool); + qreal animation() const {return _animation;} + void setAnimation(qreal); + //@} + + /// Schedule for repaint + void scheduleRepaint(); + + /** Return the pixel alpha value at the give position + * @param pos: position + * @param radiusScale: scale of the radius to check for alpha. + * + * @return Returns the largest alpha value of a circular area of 'pos' as + * center and radius as defined by user parameter + * View/DockOverlayAlphaRadius. May return -1 if out side of the widget, or + * zero if transparent. + */ + int testAlpha(const QPoint &pos, int radiusScale); + + /// Start animated showing + void startShow(); + /// Start animated hiding + void startHide(); + + /// Internal state of the tab widget + enum class State { + /// The tab widget is showing + Showing, + /// Normal visible state + Normal, + /// Visual hint is visible + Hint, + /// Hint is hidden by user after pressing ESC + HintHidden, + /// The tab widget is explicitly hidden by user + Hidden, + }; + /// Set state of the tab widget + void setState(State); + /// Get the state of the widget + State getState() const {return _state;} + + /// Handle splitter resize + void onSplitterResize(int index); + + /// Check if the tab widget is saving its state + bool isSaving() const {return _saving;} + + /// Helper function to create title bar for a dock widget + static QWidget *createTitleButton(QAction *action, int size); + /// Helper function to prepare a widget as a title widget + static QLayoutItem *prepareTitleWidget(QWidget *widget, const QList &actions); + +protected: + void leaveEvent(QEvent*); + void enterEvent(QEvent*); + void changeEvent(QEvent*); + void resizeEvent(QResizeEvent*); + void paintEvent(QPaintEvent *); + bool event(QEvent *ev); + bool eventFilter(QObject *, QEvent *ev); + + void retranslate(); + void refreshIcons(); + + /// Overlay mode options + enum class OverlayOption { + /// Enable overlay + Enable, + /// Disable overlay + Disable, + /// Enable overlay and show tab bar + ShowTab, + }; + /// Toggle overlay mode for a given widget + void setOverlayMode(QWidget *widget, OverlayOption option); + /// Helper function to set overlay mode for a give widget + static void _setOverlayMode(QWidget *widget, OverlayOption option); + +protected: + void onCurrentChanged(int index); + void onTabMoved(int from, int to); + void onRepaint(); + void onAnimationStateChanged(); + void setupLayout(); + void onSizeGripMove(const QPoint &); + +private: + friend class OverlayProxyWidget; + friend class OverlayTitleBar; + friend class OverlayManager; + friend class OverlayManager::Private; + friend class OverlaySplitterHandle; + friend class OverlaySizeGrip; + + QSize offset; + int sizeDelta = 0; + QRect rectOverlay; + OverlayProxyWidget *proxyWidget; + QSplitter *splitter = nullptr; + QWidget *titleBar = nullptr; + QAction actNoAutoMode; + QAction actAutoHide; + QAction actEditHide; + QAction actEditShow; + QAction actTaskShow; + QAction actAutoMode; + QMenu autoModeMenu; + QAction actTransparent; + QAction actIncrease; + QAction actDecrease; + QAction actOverlay; + QTimer timer; + QTimer repaintTimer; + AutoMode autoMode = AutoMode::NoAutoMode; + bool repainting = false; + bool overlaid = false; + bool currentTransparent = false; + bool touched = false; + bool busy = false; + Qt::DockWidgetArea dockArea; + int tabSize = 0; + QTime revealTime; + ParameterGrp::handle hGrp; + + OverlayGraphicsEffect *_graphicsEffect = nullptr; + OverlayGraphicsEffect *_graphicsEffectTab = nullptr; + bool _effectEnabled = false; + + QImage _image; + qreal _imageScale; + + qreal _animation = 0; + QPropertyAnimation *_animator = nullptr; + + State _state = State::Normal; + + std::map _sizemap; + bool _saving = false; + + static OverlayDragFrame *_DragFrame; + static QDockWidget *_DragFloating; + static QWidget *_Dragging; + static OverlayTabWidget *_LeftOverlay; + static OverlayTabWidget *_RightOverlay; + static OverlayTabWidget *_TopOverlay; + static OverlayTabWidget *_BottomOverlay; +}; + +/// A translucent frame as a visual indicator when dragging a dock widget +class OverlayDragFrame: public QWidget +{ + Q_OBJECT +public: + OverlayDragFrame(QWidget * parent); + QSize sizeHint() const; + QSize minimumSizeHint() const; + +protected: + void paintEvent(QPaintEvent*); +}; + +/// Title bar for OverlayTabWidget +class OverlayTitleBar: public QWidget +{ + Q_OBJECT +public: + OverlayTitleBar(QWidget * parent); + void setTitleItem(QLayoutItem *); + void endDrag(); + +protected: + void mouseMoveEvent(QMouseEvent *); + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void paintEvent(QPaintEvent*); + void keyPressEvent(QKeyEvent *ke); + void timerEvent(QTimerEvent *); + +private: + QPoint dragOffset; + QSize dragSize; + QLayoutItem *titleItem = nullptr; + QColor textcolor; + int timerId = 0; + bool blink = false; + bool mouseMovePending = false; + bool ignoreMouse = false; +}; + +/// Size grip for title bar and split handler of OverlayTabWidget +class OverlaySizeGrip: public QWidget +{ + Q_OBJECT +public: + OverlaySizeGrip(QWidget *parent, bool vertical); + +Q_SIGNALS: + void dragMove(const QPoint &globalPos); + +protected: + void paintEvent(QPaintEvent*); + void mouseMoveEvent(QMouseEvent *); + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + const QPixmap &pixmap() const; + +private: + bool vertical; +}; + +/// Splitter for OverlayTabWidget +class OverlaySplitter : public QSplitter +{ + Q_OBJECT +public: + OverlaySplitter(QWidget *parent); + void retranslate(); + +protected: + virtual QSplitterHandle *createHandle(); +}; + + +/// Splitter handle for dragging the splitter +class OverlaySplitterHandle : public QSplitterHandle +{ + Q_OBJECT +public: + friend class OverlaySplitter; + + OverlaySplitterHandle(Qt::Orientation, QSplitter *parent); + void setTitleItem(QLayoutItem *); + void retranslate(); + void refreshIcons(); + QDockWidget * dockWidget(); + + void showTitle(bool enable); + bool isShowing() const { return _showTitle; } + void endDrag(); + +protected: + virtual void showEvent(QShowEvent *); + virtual void enterEvent(QEvent *); + virtual void leaveEvent(QEvent *); + virtual void paintEvent(QPaintEvent*); + virtual void changeEvent(QEvent*); + virtual void mouseMoveEvent(QMouseEvent *); + virtual void mousePressEvent(QMouseEvent *); + virtual void mouseReleaseEvent(QMouseEvent *); + virtual void keyPressEvent(QKeyEvent *); + virtual QSize sizeHint() const; + +protected: + void onAction(); + void onTimer(); + +private: + QLayoutItem * titleItem = nullptr; + int idx = -1; + QAction actFloat; + bool _showTitle = true; + int dragging = 0; + QPoint dragOffset; + QSize dragSize; + QTimer timer; +}; + + +/// Tool button for the title bar of the OverlayTabWidget +class OverlayToolButton: public QToolButton +{ + Q_OBJECT +public: + OverlayToolButton(QWidget *parent); +}; + +/** Class for handling visual hint for bringing back hidden overlay dock widget + * + * The proxy widget is transparent except a customizable rectangle area with a + * selectable color shown as the visual hint. The hint is normally hidden, and + * is shown only if the mouse hovers within the widget. When the hint area is + * clicked, it will bring back hidden overlay dock panel. Note that the proxy + * widget itself is mouse transparent as well, meaning that it will not receive + * any mouse event. It is handled in the OverlayManager event filter. + */ +class OverlayProxyWidget: public QWidget +{ + Q_OBJECT + + Q_PROPERTY(QBrush hintColor READ hintColor WRITE setHintColor) // clazy:exclude=qproperty-without-notify + +public: + OverlayProxyWidget(OverlayTabWidget *); + + OverlayTabWidget *getOwner() const {return owner;} + + /// For representing hit region + enum class HitTest { + /// Not hitting + HitNone = 0, + /// Hitting the proxy widget size but not within the visible hint area. + HitOuter = 1, + /// Hitting the visible hint area. + HitInner = 2, + }; + /** Mouse cursor hit test + * @param pos: cursor position + * @param delay: Whether to delay showing hint on mouse hit + */ + HitTest hitTest(const QPoint &pos, bool delay=true); + + /// Check if the visual hint is showing + bool isActivated() const; + + QBrush hintColor() const; + void setHintColor(const QBrush &); + + QRect getRect() const; + + void onMousePress(); + +protected: + void enterEvent(QEvent*); + void hideEvent(QHideEvent*); + void paintEvent(QPaintEvent*); + +protected: + void onTimer(); + +private: + OverlayTabWidget* owner; + int drawLine = false; + int dockArea; + QTimer timer; + QBrush _hintColor; +}; + +/** Graphic effects for drawing shadow and outline of text on transparent background + * + * Modified from https://stackoverflow.com/a/23752747 + */ +class OverlayGraphicsEffect: public QGraphicsEffect +{ + Q_OBJECT +public: + OverlayGraphicsEffect(QObject *parent); + + virtual void draw(QPainter* painter); + virtual QRectF boundingRectFor(const QRectF& rect) const; + + inline void setSize(const QSize &size) + { if(_size!=size){_size = size; updateBoundingRect(); } } + + inline QSize size() const { return _size; } + + inline void setOffset(const QPointF &offset) + { if(_offset!=offset) {_offset = offset; updateBoundingRect(); } } + + inline QPointF offset() const { return _offset; } + + inline void setBlurRadius(qreal blurRadius) + { if(_blurRadius!=blurRadius) {_blurRadius = blurRadius; updateBoundingRect();} } + + inline qreal blurRadius() const { return _blurRadius; } + + inline void setColor(const QColor& color) { _color = color; } + inline QColor color() const { return _color; } + + inline bool enabled() const {return _enabled;} + inline void setEnabled(bool enabled) + { if(_enabled!=enabled) {_enabled = enabled; updateBoundingRect();} } + +private: + bool _enabled; + QSize _size; + qreal _blurRadius; + QColor _color; + QPointF _offset; +}; + +} // namespace Gui + +#endif // FC_OVERLAYWIDGETS_H diff --git a/src/Gui/Stylesheets/CMakeLists.txt b/src/Gui/Stylesheets/CMakeLists.txt index 07eed65c12..6e5de676a7 100644 --- a/src/Gui/Stylesheets/CMakeLists.txt +++ b/src/Gui/Stylesheets/CMakeLists.txt @@ -10,6 +10,22 @@ SET(Stylesheets_Files "Light-modern.qss" ) +SET(Overlay_Stylesheets_Files + "overlay/Dark.qss" + "overlay/Light.qss" + "overlay/Dark-Outline.qss" + "overlay/Light-Outline.qss" + "overlay/close.svg" + "overlay/overlay.svg" + "overlay/float.svg" + "overlay/autohide.svg" + "overlay/editshow.svg" + "overlay/taskshow.svg" + "overlay/edithide.svg" + "overlay/mode.svg" + "overlay/transparent.svg" +) + # Find all the image files FILE(GLOB Images_Files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/images_dark-light/*.svg") @@ -17,11 +33,13 @@ FILE(GLOB Images_Files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" SOURCE_GROUP("images_dark-light" FILES ${Images_Files}) ADD_CUSTOM_TARGET(Stylesheets_data ALL - SOURCES ${Stylesheets_Files} ${Images_Files} + SOURCES ${Stylesheets_Files} ${Images_Files} ${Overlay_Stylesheets_Files} ) fc_copy_sources(Stylesheets_data "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Gui/Stylesheets" - ${Stylesheets_Files} ${Images_Files}) + ${Stylesheets_Files} + ${Images_Files} + ${Overlay_Stylesheets_Files}) INSTALL( FILES @@ -29,6 +47,12 @@ INSTALL( DESTINATION ${CMAKE_INSTALL_DATADIR}/Gui/Stylesheets ) +INSTALL( + FILES + ${Overlay_Stylesheets_Files} + DESTINATION + ${CMAKE_INSTALL_DATADIR}/Gui/Stylesheets/overlay +) INSTALL( FILES ${Images_Files} diff --git a/src/Gui/Stylesheets/overlay/Dark-Outline.qss b/src/Gui/Stylesheets/overlay/Dark-Outline.qss new file mode 100644 index 0000000000..ad962b7bea --- /dev/null +++ b/src/Gui/Stylesheets/overlay/Dark-Outline.qss @@ -0,0 +1,190 @@ +* { + color: #f0f0f0; + alternate-background-color: rgba(50,50,50,120); +} + +QComboBox, +QComboBox:editable, +QComboBox:!editable, +QLineEdit, +QTextEdit, +QPlainTextEdit, +QAbstractSpinBox, +QDateEdit, +QDateTimeEdit, +Gui--PropertyEditor--PropertyEditor QLabel { + background : #6e6e6e; +} +Gui--PropertyEditor--PropertyEditor { + gridline-color: #20a0a0a0; +} + +QComboBox:disabled, +QAbstractSpinBox:disabled, +QLineEdit:disabled, +QTextEdit:disabled, +QPlainTextEdit:disabled, +QTimeEdit:disabled, +QDateEdit:disabled, +QDateTimeEdit:disabled { + color: darkgray; +} + +QTabWidget::pane { + background-color: transparent; + border: transparent; +} + +QTabBar { + border : none; +} + +QTabBar::tab { + color: rgb(180,180,180); + background-color: rgba(100,100,100,10); + padding: 5px; +} + +QTabBar::tab:selected { + color: rgb(250,250,250); + background-color: rgba(100,100,100,50); +} + +QTabBar::tab:hover { + color: rgb(250,250,250); + background-color: rgba(150,150,150,150); +} + +/* The OverlayTabWidget is named as OverlayLeft, OverlayRight, OverlayTop, OverlayBottom. +To customize for each overlay docking site, use the following selector + +Gui--OverlayTabWidget#OverlayLeft {} +*/ + +Gui--OverlayTabWidget { + qproperty-effectColor: rgba(50, 50, 50, 200); + qproperty-effectBlurRadius: 2.0; + qproperty-effectOffsetX: 0.0; + qproperty-effectOffsetY: 0.0; + qproperty-effectWidth: 1; + qproperty-effectHeight: 1; + qproperty-enableEffect: 1; +} + +Gui--OverlayTabWidget::tab-bar:top, +Gui--OverlayTabWidget::tab-bar:bottom { + left: 10px; + alignment: left; +} + +Gui--OverlayTabWidget::tab-bar:left, +Gui--OverlayTabWidget::tab-bar:right { + top: 10px; + alignment: top; +} + +Gui--OverlayTabWidget::pane { + background-color: transparent; +} + +QHeaderView { background:transparent } +QHeaderView::section { + color: rgb(200,200,200); + background-color: rgba(50,50,50,10); + padding: 2px; + border: 1px solid rgb(150,150,150); +} + +QToolTip { + background-color: rgba(100,100,100,180); + border: 1px solid darkgray; + border-radius:2px; +} + +Gui--OverlayProxyWidget { + qproperty-hintColor: rgba(50, 50, 50, 0.6); +} + +Gui--OverlayToolButton { + background: transparent; + padding: 0px; + border: none; +} + +Gui--OverlayToolButton::hover { + background: rgba(150,150,150,200); +} + +Gui--OverlayToolButton::focus { + background: rgba(150,150,150,155); +} + +Gui--OverlayToolButton::pressed { + background: rgba(50,50,50,80); + border: 1px inset darkgray; +} + +Gui--OverlayToolButton::checked { + background: rgba(50,50,50,80); + border: 1px inset darkgray; +} + +Gui--OverlayToolButton::checked:hover { + background: rgba(150,150,150,200); + border: 1px inset darkgray; +} + +QTreeView, +QListView, +QTableView { + background: rgb(100,100,100); + border: transparent; + selection-background-color: rgba(94, 144, 250, 0.7); +} + +QListView::item:selected, +QTreeView::item:selected { + background-color: rgba(94, 144, 250, 0.7); +} + +Gui--PropertyEditor--PropertyEditor { + qproperty-groupTextColor: rgb(50, 50, 50); + qproperty-groupBackground: rgba(140, 140, 140, 0.6); + qproperty-itemBackground: rgba(0, 0, 0, 0.01); +} + +Gui--CallTipsList::item { + background-color: rgba(100, 100, 100, 200); +} + +Gui--CallTipsList::item::selected { + background-color: rgb(94, 144, 250); +} + +/* Use the following selector to customize title bar for each side */ +/* +Gui--OverlayTabWidget#OverlayBottom Gui--OverlayTitleBar, +Gui--OverlayTabWidget#OverlayBottom QSplitter Gui--OverlaySplitterHandle { + background-color: qlineargradient( + spread:pad, x1:0, y1:1, x2:0, y2:0, + stop:0 #80202020, stop:1 #00202020); +} +*/ + +Gui--OverlayTitleBar, +Gui--OverlaySplitterHandle { + background-color: rgba(80, 80, 80, 150) +} + +QScrollArea#ClippingScrollArea, +QWidget#ClippingScrollAreaContents { + background-color: #808c8c8c; +} + +QSint--ActionGroup QFrame[class="content"] { + background-color: #a08c8c8c; /* Task Panel background color */ +} + +QSint--ActionGroup QFrame[class="content"] > QWidget { + background-color: #808c8c8c; /* Task Panel background color */ +} diff --git a/src/Gui/Stylesheets/overlay/Dark-off.qss b/src/Gui/Stylesheets/overlay/Dark-off.qss new file mode 100644 index 0000000000..a11272af5b --- /dev/null +++ b/src/Gui/Stylesheets/overlay/Dark-off.qss @@ -0,0 +1,34 @@ +Gui--OverlayToolButton { + background: transparent; + padding: 0px; + border: none; +} + +Gui--OverlayToolButton::hover { + background: rgba(150,150,150,200); +} + +Gui--OverlayToolButton::focus { + background: rgba(150,150,150,155); +} + +Gui--OverlayToolButton::pressed { + background: rgba(50,50,50,80); + border: 1px inset palette(dark); +} + +Gui--OverlayToolButton::checked { + background: rgba(50,50,50,80); + border: 1px inset palette(dark); +} + +Gui--OverlayToolButton::checked:hover { + background: rgba(150,150,150,200); + border: 1px inset palette(dark); +} + +Gui--OverlayTitleBar, +Gui--OverlaySplitterHandle { + background-color: rgba(80, 80, 80, 150) +} + diff --git a/src/Gui/Stylesheets/overlay/Dark.qss b/src/Gui/Stylesheets/overlay/Dark.qss new file mode 100644 index 0000000000..3e74e12b36 --- /dev/null +++ b/src/Gui/Stylesheets/overlay/Dark.qss @@ -0,0 +1,158 @@ +* { + color: #f0f0f0; + alternate-background-color: rgba(50,50,50,120); +} + +QComboBox, +QComboBox:editable, +QComboBox:!editable, +QLineEdit, +QTextEdit, +QPlainTextEdit, +QAbstractSpinBox, +QDateEdit, +QDateTimeEdit, +Gui--PropertyEditor--PropertyEditor QLabel { + background : #6e6e6e; +} + +QComboBox:disabled, +QAbstractSpinBox:disabled, +QLineEdit:disabled, +QTextEdit:disabled, +QPlainTextEdit:disabled, +QTimeEdit:disabled, +QDateEdit:disabled, +QDateTimeEdit:disabled { + color: darkgray; +} + +QTabWidget::pane { + background-color: transparent; + border: transparent; +} + +Gui--OverlayTabWidget { + qproperty-enableEffect: 0; +} + +Gui--OverlayTabWidget::pane { + background-color: rgba(100,100,100,150) +} + +QTabBar { + border : none; +} + +QTabBar::tab { + color: #f0f0f0; + background-color: rgba(100,100,100,50); + padding: 5px; +} + +QTabBar::tab:selected { + background-color: rgba(100,100,100,150); +} + +QTabBar::tab:hover { + background-color: rgba(150,150,150,150); +} + +QHeaderView { background:transparent } +QHeaderView::section { + color: rgb(200,200,200); + background-color: rgba(50,50,50,50); + padding: 2px; + border: 1px solid rgb(100,100,100); +} + +QToolTip { + background-color: rgba(100,100,100,180); + border: 1px solid darkgray; + border-radius:2px; +} + +Gui--OverlayToolButton { + background: transparent; + padding: 0px; + border: none; +} + +Gui--OverlayToolButton::hover { + background: rgba(150,150,150,200); +} + +Gui--OverlayToolButton::focus { + background: rgba(150,150,150,155); +} + +Gui--OverlayToolButton::pressed { + background: rgba(50,50,50,80); + border: 1px inset darkgray; +} + +Gui--OverlayToolButton::checked { + background: rgba(50,50,50,80); + border: 1px inset darkgray; +} + +Gui--OverlayToolButton::checked:hover { + background: rgba(150,150,150,200); + border: 1px inset darkgray; +} + +QTreeView, +QListView, +QTableView { + background: rgb(100,100,100); + selection-background-color: rgba(94, 144, 250, 0.7); + border: transparent; +} + +QListView::item:selected, +QTreeView::item:selected { + background-color: rgba(94, 144, 250, 0.7); +} + +Gui--PropertyEditor--PropertyEditor { + qproperty-groupTextColor: rgb(50, 50, 50); + qproperty-groupBackground: rgba(140, 140, 140, 0.7); + qproperty-itemBackground: rgba(0, 0, 0, 0.01); +} + +Gui--CallTipsList::item { + background-color: rgba(100, 100, 100, 200); +} + +Gui--CallTipsList::item::selected { + background-color: rgb(94, 144, 250); +} + +/* Use the following selector to customize title bar for each side */ +/* +Gui--OverlayTabWidget#OverlayBottom Gui--OverlayTitleBar, +Gui--OverlayTabWidget#OverlayBottom QSplitter Gui--OverlaySplitterHandle { + background-color: qlineargradient( + spread:pad, x1:0, y1:1, x2:0, y2:0, + stop:0 #80202020, stop:1 #00202020); +} +*/ + +Gui--OverlayTitleBar, +Gui--OverlaySplitterHandle { + background-color: rgba(80, 80, 80, 150) +} + +QScrollArea#ClippingScrollArea, +QWidget#ClippingScrollAreaContents { + background-color: #808c8c8c; +} + +QSint--ActionGroup QFrame[class="content"] { + background-color: #808c8c8c; /* Task Panel background color */ +} + +QSint--ActionGroup QFrame[class="content"] > QWidget { + background-color: #808c8c8c; /* Task Panel background color */ +} + diff --git a/src/Gui/Stylesheets/overlay/Light-Outline.qss b/src/Gui/Stylesheets/overlay/Light-Outline.qss new file mode 100644 index 0000000000..b3c76bbfd3 --- /dev/null +++ b/src/Gui/Stylesheets/overlay/Light-Outline.qss @@ -0,0 +1,183 @@ +* { + color: #202020; + alternate-background-color: rgba(250,250,250,120); +} + +QComboBox, +QComboBox:editable, +QComboBox:!editable, +QLineEdit, +QTextEdit, +QPlainTextEdit, +QAbstractSpinBox, +QDateEdit, +QDateTimeEdit, +Gui--PropertyEditor--PropertyEditor QLabel { + background-color: #e0e0e0; +} + +QComboBox:disabled, +QAbstractSpinBox:disabled, +QLineEdit:disabled, +QTextEdit:disabled, +QPlainTextEdit:disabled, +QTimeEdit:disabled, +QDateEdit:disabled, +QDateTimeEdit:disabled { + color: gray; +} + +QTabWidget::pane { + background-color: transparent; + border: transparent; +} + +QTabBar { + border : none; +} + +QTabBar::tab { + color: #202020; + background-color: rgba(100,100,100,50); + padding: 5px; +} + +QTabBar::tab:selected { + background-color: rgba(250,250,250,80); +} + +QTabBar::tab:hover { + background-color: rgba(250,250,250,200); +} + +/* The OverlayTabWidget is named as OverlayLeft, OverlayRight, OverlayTop, OverlayBottom. +To customize for each overlay docking site, use the following selector + +Gui--OverlayTabWidget#OverlayLeft {} +*/ + +Gui--OverlayTabWidget { + qproperty-effectColor: rgba(200, 200, 200, 100); + qproperty-effectBlurRadius: 2.0; + qproperty-effectOffsetX: 0.0; + qproperty-effectOffsetY: 0.0; + qproperty-effectWidth: 1; + qproperty-effectHeight: 1; + qproperty-enableEffect: 1; +} + +Gui--OverlayTabWidget::pane { + background-color: transparent +} + +Gui--OverlayTabWidget::tab-bar:top, +Gui--OverlayTabWidget::tab-bar:bottom { + left: 10px; + alignment: left; +} + +Gui--OverlayTabWidget::tab-bar:left, +Gui--OverlayTabWidget::tab-bar:right { + top: 10px; + alignment: top; +} + +QHeaderView { background:transparent } +QHeaderView::section { + color: rgb(80, 80, 80); + background-color: rgba(128,128,128,50); + border: 1px solid rgb(100,100,100); + padding: 2px; +} + +QToolTip { + background-color: rgba(250,250,250,180); + border: 1px solid rgb(80,80,80); + border-radius:2px; +} + +Gui--OverlayProxyWidget { + qproperty-hintColor: rgba(250, 250, 250, 0.6); +} + +Gui--OverlayToolButton { + background: transparent; + padding: 0px; + border: none; +} + +Gui--OverlayToolButton::hover { + background: rgba(250,250,250,200); +} + +Gui--OverlayToolButton::focus { + background: rgba(250,250,250,255); +} + +Gui--OverlayToolButton::pressed, +Gui--OverlayToolButton::checked { + background: rgba(150,150,150,80); + border: 1px inset #f5f5f5; +} + +Gui--OverlayToolButton::checked:hover { + background: rgba(150,150,150,200); + border: 1px inset #f5f5f5; +} + +QTreeView, +QListView, +QTableView { + background: rgb(250,250,250); + selection-background-color: rgba(94, 144, 250, 0.7); + border: transparent; +} + +QListView::item:selected, +QTreeView::item:selected { + background-color: rgba(94, 144, 250, 0.7); +} + +/* Property Editor QTreeView (FreeCAD custom widget) */ +Gui--PropertyEditor--PropertyEditor { + gridline-color: #20d2d2de; + qproperty-groupTextColor: rgb(100, 100, 100); + qproperty-groupBackground: rgba(180, 180, 180, 0.7); + qproperty-itemBackground: rgba(0, 0, 0, 0.01); +} + +Gui--CallTipsList::item { + background-color: rgba(200, 200, 200, 200); +} + +Gui--CallTipsList::item::selected { + background-color: rgba(94, 144, 250); +} + +/* Use the following selector to customize title bar for each side */ +/* +Gui--OverlayTabWidget#OverlayBottom Gui--OverlayTitleBar, +Gui--OverlayTabWidget#OverlayBottom QSplitter Gui--OverlaySplitterHandle { + background-color: qlineargradient( + spread:pad, x1:0, y1:1, x2:0, y2:0, + stop:0 #80202020, stop:1 #00202020); +} +*/ + +Gui--OverlayTitleBar, +Gui--OverlaySplitterHandle { + background-color: rgba(200, 200, 200, 150) +} + +QScrollArea#ClippingScrollArea, +QWidget#ClippingScrollAreaContents { + background-color: #80e6e6e6; +} + +QSint--ActionGroup QFrame[class="content"] { + background-color: #a0e6e6e6; /* Task Panel background color */ +} + +QSint--ActionGroup QFrame[class="content"] > QWidget { + background-color: #80e6e6e6; /* Task Panel background color */ +} diff --git a/src/Gui/Stylesheets/overlay/Light-off.qss b/src/Gui/Stylesheets/overlay/Light-off.qss new file mode 100644 index 0000000000..0c5ec79e65 --- /dev/null +++ b/src/Gui/Stylesheets/overlay/Light-off.qss @@ -0,0 +1,33 @@ +Gui--OverlayToolButton { + background: transparent; + padding: 0px; + border: none; +} + +Gui--OverlayToolButton::hover { + background: rgba(150,150,150,200); +} + +Gui--OverlayToolButton::focus { + background: rgba(150,150,150,155); +} + +Gui--OverlayToolButton::pressed { + background: rgba(50,50,50,80); + border: 1px inset palette(dark); +} + +Gui--OverlayToolButton::checked { + background: rgba(50,50,50,80); + border: 1px inset palette(dark); +} + +Gui--OverlayToolButton::checked:hover { + background: rgba(150,150,150,200); + border: 1px inset palette(dark); +} + +Gui--OverlayTitleBar, +Gui--OverlaySplitterHandle { + background-color: rgba(200, 200, 200, 150) +} diff --git a/src/Gui/Stylesheets/overlay/Light.qss b/src/Gui/Stylesheets/overlay/Light.qss new file mode 100644 index 0000000000..6ee020ac01 --- /dev/null +++ b/src/Gui/Stylesheets/overlay/Light.qss @@ -0,0 +1,155 @@ +* { + color: #202020; + alternate-background-color: rgba(250,250,250,120); +} + +QComboBox, +QComboBox:editable, +QComboBox:!editable, +QLineEdit, +QTextEdit, +QPlainTextEdit, +QAbstractSpinBox, +QDateEdit, +QDateTimeEdit, +Gui--PropertyEditor--PropertyEditor QLabel { + background-color: #e0e0e0; +} + +QComboBox:disabled, +QAbstractSpinBox:disabled, +QLineEdit:disabled, +QTextEdit:disabled, +QPlainTextEdit:disabled, +QTimeEdit:disabled, +QDateEdit:disabled, +QDateTimeEdit:disabled { + color: gray; +} + +QTabWidget::pane { + background-color: transparent; + border: transparent; +} + +Gui--OverlayTabWidget { + qproperty-enableEffect: 0; +} + +Gui--OverlayTabWidget::pane { + background-color: rgba(255,255,255,80) +} + +QTabBar { + border : none; +} + +QTabBar::tab { + color: #202020; + background-color: rgba(100,100,100,50); + padding: 5px; +} + +QTabBar::tab:selected { + background-color: rgba(250,250,250,80); +} + +QTabBar::tab:hover { + background-color: rgba(250,250,250,200); +} + +QHeaderView { background:transparent } +QHeaderView::section { + color: rgb(80, 80, 80); + background-color: rgba(128,128,128,50); + border: 1px solid darkgray; + padding: 2px; +} + +QToolTip { + background-color: rgba(250,250,250,180); + border: 1px solid rgb(80,80,80); + border-radius:2px; +} + +Gui--OverlayToolButton { + background: transparent; + padding: 0px; + border: none; +} + +Gui--OverlayToolButton::hover { + background: rgba(250,250,250,200); +} + +Gui--OverlayToolButton::focus { + background: rgba(250,250,250,255); +} + +Gui--OverlayToolButton::pressed, +Gui--OverlayToolButton::checked { + background: rgba(150,150,150,80); + border: 1px inset #f5f5f5; +} + +Gui--OverlayToolButton::checked:hover { + background: rgba(150,150,150,200); + border: 1px inset #f5f5f5; +} + +QTreeView, +QListView, +QTableView { + background: rgb(250,250,250); + selection-background-color: rgba(94, 144, 250, 0.7); + border: transparent; +} + +QListView::item:selected, +QTreeView::item:selected { + background-color: rgba(94, 144, 250, 0.7); +} + +/* Property Editor QTreeView (FreeCAD custom widget) */ +Gui--PropertyEditor--PropertyEditor { + /* gridline-color: #6e6e6e; */ + qproperty-groupTextColor: rgb(100, 100, 100); + qproperty-groupBackground: rgba(180, 180, 180, 0.7); + qproperty-itemBackground: rgba(0, 0, 0, 0.01); +} + +Gui--CallTipsList::item { + background-color: rgba(200, 200, 200, 200); +} + +Gui--CallTipsList::item::selected { + background-color: rgba(94, 144, 250); +} + +/* Use the following selector to customize title bar for each side */ +/* +Gui--OverlayTabWidget#OverlayBottom Gui--OverlayTitleBar, +Gui--OverlayTabWidget#OverlayBottom QSplitter Gui--OverlaySplitterHandle { + background-color: qlineargradient( + spread:pad, x1:0, y1:1, x2:0, y2:0, + stop:0 #80202020, stop:1 #00202020); +} +*/ + +Gui--OverlayTitleBar, +Gui--OverlaySplitterHandle { + background-color: rgba(200, 200, 200, 150) +} + +QScrollArea#ClippingScrollArea, +QWidget#ClippingScrollAreaContents { + background-color: #80e6e6e6 +} + +QSint--ActionGroup QFrame[class="content"] { + background-color: #80e6e6e6; /* Task Panel background color */ +} + +QSint--ActionGroup QFrame[class="content"] > QWidget { + background-color: #80e6e6e6; /* Task Panel background color */ +} diff --git a/src/Gui/Stylesheets/overlay/autohide.svg b/src/Gui/Stylesheets/overlay/autohide.svg new file mode 100644 index 0000000000..978084bd13 --- /dev/null +++ b/src/Gui/Stylesheets/overlay/autohide.svg @@ -0,0 +1,86 @@ + + + + + + + + + + image/svg+xml + + + + + Pablo Gil + + + + + SVG + template + + + + + + + + + diff --git a/src/Gui/Stylesheets/overlay/close.svg b/src/Gui/Stylesheets/overlay/close.svg new file mode 100644 index 0000000000..c907bc39db --- /dev/null +++ b/src/Gui/Stylesheets/overlay/close.svg @@ -0,0 +1,47 @@ + + + + + + + image/svg+xml + + + + + Pablo Gil + + + + + SVG + template + + + + + + + + + diff --git a/src/Gui/Stylesheets/overlay/edithide.svg b/src/Gui/Stylesheets/overlay/edithide.svg new file mode 100644 index 0000000000..4352bc3196 --- /dev/null +++ b/src/Gui/Stylesheets/overlay/edithide.svg @@ -0,0 +1,44 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/Gui/Stylesheets/overlay/editshow.svg b/src/Gui/Stylesheets/overlay/editshow.svg new file mode 100644 index 0000000000..78234e3cd4 --- /dev/null +++ b/src/Gui/Stylesheets/overlay/editshow.svg @@ -0,0 +1,69 @@ + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/Gui/Stylesheets/overlay/float.svg b/src/Gui/Stylesheets/overlay/float.svg new file mode 100644 index 0000000000..645a956ab6 --- /dev/null +++ b/src/Gui/Stylesheets/overlay/float.svg @@ -0,0 +1,53 @@ + + + + + + + image/svg+xml + + + + + Pablo Gil + + + + + SVG + template + + + + + + + + + + diff --git a/src/Gui/Stylesheets/overlay/mode.svg b/src/Gui/Stylesheets/overlay/mode.svg new file mode 100644 index 0000000000..1853f1670d --- /dev/null +++ b/src/Gui/Stylesheets/overlay/mode.svg @@ -0,0 +1,430 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/src/Gui/Stylesheets/overlay/overlay.svg b/src/Gui/Stylesheets/overlay/overlay.svg new file mode 100644 index 0000000000..bb2f24a3a7 --- /dev/null +++ b/src/Gui/Stylesheets/overlay/overlay.svg @@ -0,0 +1,82 @@ + + + + + + + + + + image/svg+xml + + + + + Pablo Gil + + + + + SVG + template + + + + + + + + diff --git a/src/Gui/Stylesheets/overlay/taskshow.svg b/src/Gui/Stylesheets/overlay/taskshow.svg new file mode 100644 index 0000000000..0c1c297672 --- /dev/null +++ b/src/Gui/Stylesheets/overlay/taskshow.svg @@ -0,0 +1,89 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/Gui/Stylesheets/overlay/transparent.svg b/src/Gui/Stylesheets/overlay/transparent.svg new file mode 100644 index 0000000000..5e3adff690 --- /dev/null +++ b/src/Gui/Stylesheets/overlay/transparent.svg @@ -0,0 +1,52 @@ + + + + + + + image/svg+xml + + + + + Pablo Gil + + + + + SVG + template + + + + + + + + diff --git a/src/Gui/TaskView/TaskView.cpp b/src/Gui/TaskView/TaskView.cpp index 712df39ee2..9e5a0a30e3 100644 --- a/src/Gui/TaskView/TaskView.cpp +++ b/src/Gui/TaskView/TaskView.cpp @@ -41,6 +41,7 @@ #include "TaskView.h" #include "TaskDialog.h" #include "TaskEditControl.h" +#include #include #include @@ -299,6 +300,11 @@ TaskView::TaskView(QWidget *parent) App::GetApplication().signalRedoDocument.connect (std::bind(&Gui::TaskView::TaskView::slotRedoDocument, this, sp::_1)); //NOLINTEND + + this->timer = new QTimer(this); + this->timer->setSingleShot(true); + QObject::connect(this->timer, &QTimer::timeout, [this](){onUpdateWatcher();}); + updateWatcher(); } TaskView::~TaskView() @@ -310,6 +316,20 @@ TaskView::~TaskView() Gui::Selection().Detach(this); } +bool TaskView::isEmpty(bool includeWatcher) const +{ + if (ActiveCtrl || ActiveDialog) + return false; + + if (includeWatcher) { + for (auto * watcher : ActiveWatcher) { + if (watcher->shouldShow()) + return false; + } + } + return true; +} + bool TaskView::event(QEvent* event) { // Workaround for a limitation in Qt (#0003794) @@ -542,7 +562,10 @@ void TaskView::showDialog(TaskDialog *dlg) ActiveDialog->open(); getMainWindow()->updateActions(); + triggerMinimumSizeHint(); + + Q_EMIT taskUpdate(); } void TaskView::removeDialog() @@ -585,8 +608,23 @@ void TaskView::removeDialog() triggerMinimumSizeHint(); } -void TaskView::updateWatcher() +void TaskView::updateWatcher(void) { + this->timer->start(200); +} + +void TaskView::onUpdateWatcher(void) +{ + if (ActiveCtrl || ActiveDialog) + return; + + if (ActiveWatcher.empty()) { + auto panel = Gui::Control().taskPanel(); + if (panel && panel->ActiveWatcher.size()) + takeTaskWatcher(panel); + } + this->timer->stop(); + // In case a child of the TaskView has the focus and get hidden we have // to make sure to set the focus on a widget that won't be hidden or // deleted because otherwise Qt may forward the focus via focusNextPrevChild() @@ -622,6 +660,8 @@ void TaskView::updateWatcher() fwp->setFocus(); triggerMinimumSizeHint(); + + Q_EMIT taskUpdate(); } void TaskView::addTaskWatcher(const std::vector &Watcher) @@ -631,7 +671,17 @@ void TaskView::addTaskWatcher(const std::vector &Watcher) delete tw; ActiveWatcher = Watcher; - addTaskWatcher(); + if (!ActiveCtrl && !ActiveDialog) + addTaskWatcher(); +} + +void TaskView::takeTaskWatcher(TaskView *other) +{ + clearTaskWatcher(); + ActiveWatcher.swap(other->ActiveWatcher); + other->clearTaskWatcher(); + if (isEmpty(false)) + addTaskWatcher(); } void TaskView::clearTaskWatcher() diff --git a/src/Gui/TaskView/TaskView.h b/src/Gui/TaskView/TaskView.h index c199b3ea4e..44a336dfcb 100644 --- a/src/Gui/TaskView/TaskView.h +++ b/src/Gui/TaskView/TaskView.h @@ -154,17 +154,24 @@ public: void addTaskWatcher(const std::vector &Watcher); void clearTaskWatcher(); + void takeTaskWatcher(TaskView *other); + + bool isEmpty(bool includeWatcher = true) const; void clearActionStyle(); void restoreActionStyle(); QSize minimumSizeHint() const override; +Q_SIGNALS: + void taskUpdate(); + protected Q_SLOTS: void accept(); void reject(); void helpRequested(); void clicked (QAbstractButton * button); + void onUpdateWatcher(); private: void triggerMinimumSizeHint(); @@ -193,6 +200,7 @@ protected: QSint::ActionPanel* taskPanel; TaskDialog *ActiveDialog; TaskEditControl *ActiveCtrl; + QTimer *timer; Connection connectApplicationActiveDocument; Connection connectApplicationDeleteDocument; diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index cb8c2cc721..d1b25390c4 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -32,6 +32,7 @@ # include # include # include +# include # include # include # include @@ -89,6 +90,7 @@ std::set TreeWidget::Instances; static TreeWidget* _LastSelectedTreeWidget; const int TreeWidget::DocumentType = 1000; const int TreeWidget::ObjectType = 1001; +static bool _DraggingActive; static bool _DragEventFilter; void TreeParams::onItemBackgroundChanged() @@ -355,13 +357,95 @@ public: // --------------------------------------------------------------------------- -TreeWidgetEditDelegate::TreeWidgetEditDelegate(QObject* parent) +namespace Gui { +/** + * TreeWidget item delegate for editing + */ +class TreeWidgetItemDelegate: public QStyledItemDelegate { + typedef QStyledItemDelegate inherited; +public: + TreeWidgetItemDelegate(QObject* parent=0); + + virtual QWidget* createEditor(QWidget *parent, + const QStyleOptionViewItem &, const QModelIndex &index) const; + + virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + + virtual void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const; + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +} // namespace Gui + +TreeWidgetItemDelegate::TreeWidgetItemDelegate(QObject* parent) : QStyledItemDelegate(parent) { } -QWidget* TreeWidgetEditDelegate::createEditor( - QWidget* parent, const QStyleOptionViewItem&, const QModelIndex& index) const +void TreeWidgetItemDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItem opt = option; + initStyleOption(&opt, index); + + TreeWidget * tree = static_cast(parent()); + auto style = tree->style(); + + // If the second column is not shown, we'll trim the color background when + // rendering as transparent overlay. + bool trimBG = TreeParams::getHideColumn(); + QRect rect = opt.rect; + + if (index.column() == 0) { + if (tree->testAttribute(Qt::WA_NoSystemBackground) + && (trimBG || (opt.backgroundBrush.style() == Qt::NoBrush + && _TreeItemBackground.style() != Qt::NoBrush))) + { + const int margin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, tree) + 1; + // 2 margin for text, 2 margin for decoration (icon) + int width = 4*margin + opt.fontMetrics.boundingRect(opt.text).width() + + opt.decorationSize.width() + TreeParams::getItemBackgroundPadding(); + if (TreeParams::getCheckBoxesSelection()) { + // another 2 margin for checkbox + width += 2*margin + style->pixelMetric(QStyle::PM_IndicatorWidth) + + style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing); + } + if (width < rect.width()) + rect.setWidth(width); + if (trimBG) { + rect.setWidth(rect.width() + 5); + opt.rect = rect; + if (opt.backgroundBrush.style() == Qt::NoBrush) + painter->fillRect(rect, _TreeItemBackground); + } else if (!opt.state.testFlag(QStyle::State_Selected)) + painter->fillRect(rect, _TreeItemBackground); + } + + } + + style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, tree); +} + +void TreeWidgetItemDelegate::initStyleOption(QStyleOptionViewItem *option, + const QModelIndex &index) const +{ + inherited::initStyleOption(option, index); + + TreeWidget * tree = static_cast(parent()); + QTreeWidgetItem * item = tree->itemFromIndex(index); + if (!item || item->type() != TreeWidget::ObjectType) + return; + + QSize size; + size = option->icon.actualSize(QSize(0xffff, 0xffff)); + if (size.height()) + option->decorationSize = QSize(size.width()*TreeWidget::iconSize()/size.height(), + TreeWidget::iconSize()); +} + +QWidget* TreeWidgetItemDelegate::createEditor( + QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const { auto ti = static_cast(index.internalPointer()); if (ti->type() != TreeWidget::ObjectType || index.column() > 1) @@ -375,14 +459,27 @@ QWidget* TreeWidgetEditDelegate::createEditor( App::GetApplication().setActiveTransaction(str.str().c_str()); FC_LOG("create editor transaction " << App::GetApplication().getActiveTransaction()); - auto le = new ExpLineEdit(parent); - le->setFrame(false); - le->setReadOnly(prop.isReadOnly()); - le->bind(App::ObjectIdentifier(prop)); - le->setAutoApply(true); - return le; + QLineEdit *editor; + if(TreeParams::getLabelExpression()) { + ExpLineEdit *le = new ExpLineEdit(parent); + le->setAutoApply(true); + le->setFrame(false); + le->bind(App::ObjectIdentifier(prop)); + editor = le; + } else { + editor = new QLineEdit(parent); + } + editor->setReadOnly(prop.isReadOnly()); + return editor; } +QSize TreeWidgetItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QSize size = QStyledItemDelegate::sizeHint(option, index); + int spacing = std::max(0, static_cast(TreeParams::getItemSpacing())); + size.setHeight(size.height() + spacing); + return size; +} // --------------------------------------------------------------------------- TreeWidget::TreeWidget(const char* name, QWidget* parent) @@ -404,7 +501,7 @@ TreeWidget::TreeWidget(const char* name, QWidget* parent) this->setDropIndicatorShown(false); this->setDragDropMode(QTreeWidget::InternalMove); this->setColumnCount(2); - this->setItemDelegate(new TreeWidgetEditDelegate(this)); + this->setItemDelegate(new TreeWidgetItemDelegate(this)); this->showHiddenAction = new QAction(this); this->showHiddenAction->setCheckable(true); @@ -1170,6 +1267,47 @@ void TreeWidget::selectAllInstances(const ViewProviderDocumentObject& vpd) { v.second->selectAllInstances(vpd); } +static int &treeIconSize() +{ + static int _treeIconSize = -1; + + if (_treeIconSize < 0) + _treeIconSize = TreeParams::getIconSize(); + return _treeIconSize; +} + +int TreeWidget::iconHeight() const +{ + return treeIconSize(); +} + +void TreeWidget::setIconHeight(int height) +{ + if (treeIconSize() == height) + return; + + treeIconSize() = height; + if (treeIconSize() <= 0) + treeIconSize() = std::max(10, iconSize()); + + for(auto tree : Instances) + tree->setIconSize(QSize(treeIconSize(), treeIconSize())); +} + +int TreeWidget::iconSize() { + static int defaultSize; + if (defaultSize == 0) { + auto tree = instance(); + if(tree) + defaultSize = tree->viewOptions().decorationSize.width(); + else + defaultSize = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize); + } + if (treeIconSize() > 0) + return std::max(10, treeIconSize()); + return defaultSize; +} + TreeWidget* TreeWidget::instance() { auto res = _LastSelectedTreeWidget; if (res && res->isVisible()) @@ -1336,6 +1474,15 @@ bool TreeWidget::eventFilter(QObject*, QEvent* ev) { return false; } +namespace Gui { + +bool isTreeViewDragging() +{ + return _DraggingActive; +} + +} // namespace Gui + void TreeWidget::keyPressEvent(QKeyEvent* event) { if (event->matches(QKeySequence::Find)) { @@ -1477,6 +1624,7 @@ void TreeWidget::startDragging() { void TreeWidget::startDrag(Qt::DropActions supportedActions) { + Base::StateLocker guard(_DraggingActive); QTreeWidget::startDrag(supportedActions); if (_DragEventFilter) { _DragEventFilter = false; diff --git a/src/Gui/Tree.h b/src/Gui/Tree.h index f10b618d07..6d35edfec2 100644 --- a/src/Gui/Tree.h +++ b/src/Gui/Tree.h @@ -46,9 +46,12 @@ class ViewProviderDocumentObject; class DocumentObjectItem; class DocumentObjectData; using DocumentObjectDataPtr = std::shared_ptr; +class TreeWidgetItemDelegate; class DocumentItem; +GuiExport bool isTreeViewDragging(); + /** Tree view that allows drag & drop of document objects. * @author Werner Mayer */ @@ -66,6 +69,13 @@ public: void selectLinkedObject(App::DocumentObject *linked); void selectAllLinks(App::DocumentObject *obj); void expandSelectedItems(TreeItemMode mode); + static int iconSize(); + + int iconHeight() const; + void setIconHeight(int height); + + int itemSpacing() const; + void setItemSpacing(int); bool eventFilter(QObject *, QEvent *ev) override; @@ -241,6 +251,7 @@ private: friend class DocumentItem; friend class DocumentObjectItem; friend class TreeParams; + friend class TreeWidgetItemDelegate; using Connection = boost::signals2::connection; Connection connectNewDocument; @@ -480,19 +491,6 @@ public: ~TreeDockWidget() override; }; - -/** - * TreeWidget item delegate for editing - */ -class TreeWidgetEditDelegate: public QStyledItemDelegate { - Q_OBJECT -public: - explicit TreeWidgetEditDelegate(QObject* parent=nullptr); - QWidget* createEditor(QWidget *parent, - const QStyleOptionViewItem &, const QModelIndex &index) const override; -}; - - } #endif // GUI_TREE_H diff --git a/src/Gui/TreeParams.cpp b/src/Gui/TreeParams.cpp index 784f0905ce..a83548fa4e 100644 --- a/src/Gui/TreeParams.cpp +++ b/src/Gui/TreeParams.cpp @@ -140,7 +140,7 @@ public: funcs["ItemSpacing"] = &TreeParamsP::updateItemSpacing; ItemBackground = handle->GetUnsigned("ItemBackground", 0x00000000); funcs["ItemBackground"] = &TreeParamsP::updateItemBackground; - ItemBackgroundPadding = handle->GetInt("ItemBackgroundPadding", 10); + ItemBackgroundPadding = handle->GetInt("ItemBackgroundPadding", 0); funcs["ItemBackgroundPadding"] = &TreeParamsP::updateItemBackgroundPadding; HideColumn = handle->GetBool("HideColumn", true); funcs["HideColumn"] = &TreeParamsP::updateHideColumn; @@ -347,7 +347,7 @@ public: } // Auto generated code (Tools/params_utils.py:244) static void updateItemBackgroundPadding(TreeParamsP *self) { - auto v = self->handle->GetInt("ItemBackgroundPadding", 10); + auto v = self->handle->GetInt("ItemBackgroundPadding", 0); if (self->ItemBackgroundPadding != v) { self->ItemBackgroundPadding = v; TreeParams::onItemBackgroundPaddingChanged(); @@ -1174,7 +1174,7 @@ const long & TreeParams::getItemBackgroundPadding() { // Auto generated code (Tools/params_utils.py:300) const long & TreeParams::defaultItemBackgroundPadding() { - const static long def = 10; + const static long def = 0; return def; } @@ -1403,10 +1403,9 @@ void TreeParams::onResizableColumnChanged() { } void TreeParams::onIconSizeChanged() { - // auto tree = TreeWidget::instance(); - // Commented out temporarily while merging PR #7888 - //if (tree) - //tree->setIconHeight(TreeParams::getIconSize()); + auto tree = TreeWidget::instance(); + if (tree) + tree->setIconHeight(TreeParams::getIconSize()); } void TreeParams::onFontSizeChanged() { diff --git a/src/Gui/TreeParams.py b/src/Gui/TreeParams.py index 8ff5b485e6..f06991df84 100644 --- a/src/Gui/TreeParams.py +++ b/src/Gui/TreeParams.py @@ -66,8 +66,8 @@ Params = [ ParamInt('FontSize', 0, on_change=True), ParamInt('ItemSpacing', 0, on_change=True), ParamHex('ItemBackground', 0, on_change=True, title='Item background color', proxy=ParamColor(), - doc = "Tree view item background. Only effective in overlay."), - ParamInt('ItemBackgroundPadding', 10, on_change=True, title="Item background padding", proxy=ParamSpinBox(0, 100, 1), + doc = "Tree view item background. Only effecitve in overlay."), + ParamInt('ItemBackgroundPadding', 0, on_change=True, title="Item background padding", proxy=ParamSpinBox(0, 100, 1), doc = "Tree view item background padding."), ParamBool('HideColumn', True, on_change=True, title="Hide extra column", doc = "Hide extra tree view column for item description."), diff --git a/src/Gui/Widgets.cpp b/src/Gui/Widgets.cpp index 8667a628bd..64855bd53d 100644 --- a/src/Gui/Widgets.cpp +++ b/src/Gui/Widgets.cpp @@ -40,6 +40,7 @@ # include #endif +#include #include #include #include @@ -1164,11 +1165,17 @@ void ToolTip::showText(const QPoint & pos, const QString & text, QWidget * w) tip->displayTime.start(); } else { - // do immediately - QToolTip::showText(pos, text, w); + hideText(); } } +void ToolTip::hideText() +{ + instance()->tooltipTimer.stop(); + instance()->hidden = true; + QToolTip::hideText(); +} + void ToolTip::timerEvent(QTimerEvent *e) { if (e->timerId() == tooltipTimer.timerId()) { @@ -1180,25 +1187,45 @@ void ToolTip::timerEvent(QTimerEvent *e) bool ToolTip::eventFilter(QObject* o, QEvent*e) { - // This is a trick to circumvent that the tooltip gets hidden immediately - // after it gets visible. We just filter out all timer events to keep the - // label visible. - if (o->inherits("QLabel")) { - auto label = qobject_cast(o); - // Ignore the timer events to prevent from being closed - if (label->windowFlags() & Qt::ToolTip) { - if (e->type() == QEvent::Show) { - this->hidden = false; - } - else if (e->type() == QEvent::Hide) { - removeEventFilter(); - this->hidden = true; - } - else if (e->type() == QEvent::Timer && - !this->hidden && displayTime.elapsed() < 5000) { - return true; + if (!o->isWidgetType()) + return false; + switch(e->type()) { + case QEvent::MouseButtonPress: + hideText(); + break; + case QEvent::KeyPress: + if (static_cast(e)->key() == Qt::Key_Escape) + hideText(); + break; + case QEvent::Leave: + hideText(); + break; + case QEvent::Timer: + case QEvent::Show: + case QEvent::Hide: + if (auto label = qobject_cast(o)) { + if (label->objectName() == QStringLiteral("qtooltip_label")) { + // This is a trick to circumvent that the tooltip gets hidden immediately + // after it gets visible. We just filter out all timer events to keep the + // label visible. + + // Ignore the timer events to prevent from being closed + if (e->type() == QEvent::Show) { + this->hidden = false; + } + else if (e->type() == QEvent::Hide) { + // removeEventFilter(); + this->hidden = true; + } + else if (e->type() == QEvent::Timer && + !this->hidden && displayTime.elapsed() < 5000) { + return true; + } } } + break; + default: + break; } return false; } diff --git a/src/Gui/Widgets.h b/src/Gui/Widgets.h index 1dae00d8b6..b9c83bcad0 100644 --- a/src/Gui/Widgets.h +++ b/src/Gui/Widgets.h @@ -449,6 +449,7 @@ class GuiExport ToolTip : public QObject { public: static void showText(const QPoint & pos, const QString & text, QWidget * w = nullptr); + static void hideText(); protected: static ToolTip* instance(); diff --git a/src/Gui/Workbench.cpp b/src/Gui/Workbench.cpp index b8767f7f44..612bb62b8b 100644 --- a/src/Gui/Workbench.cpp +++ b/src/Gui/Workbench.cpp @@ -699,7 +699,7 @@ MenuItem* StdWorkbench::setupMenuBar() const << "Separator" << visu << "Std_ToggleNavigation" << "Std_SetAppearance" << "Std_RandomColor" << "Separator" - << "Std_Workbench" << "Std_ToolBarMenu" << "Std_DockViewMenu" << "Separator" + << "Std_Workbench" << "Std_ToolBarMenu" << "Std_DockViewMenu" << "Std_DockOverlay" << "Separator" << "Std_LinkSelectActions" << "Std_TreeViewActions" << "Std_ViewStatusBar"; diff --git a/src/Gui/propertyeditor/PropertyEditor.cpp b/src/Gui/propertyeditor/PropertyEditor.cpp index fe86dffb65..3723e9dd75 100644 --- a/src/Gui/propertyeditor/PropertyEditor.cpp +++ b/src/Gui/propertyeditor/PropertyEditor.cpp @@ -83,6 +83,8 @@ PropertyEditor::PropertyEditor(QWidget *parent) this->background = opt.palette.dark(); this->groupColor = opt.palette.color(QPalette::BrightText); + this->_itemBackground.setColor(QColor(0,0,0,0)); + this->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(this, &QTreeView::activated, this, &PropertyEditor::onItemActivated); @@ -154,6 +156,16 @@ void PropertyEditor::setGroupTextColor(const QColor& c) this->groupColor = c; } +QBrush PropertyEditor::itemBackground() const +{ + return this->_itemBackground; +} + +void PropertyEditor::setItemBackground(const QBrush& c) +{ + this->_itemBackground = c; +} + #if QT_VERSION < QT_VERSION_CHECK(6,0,0) QStyleOptionViewItem PropertyEditor::viewOptions() const { diff --git a/src/Gui/propertyeditor/PropertyEditor.h b/src/Gui/propertyeditor/PropertyEditor.h index e3e97b39f3..6f67e71766 100644 --- a/src/Gui/propertyeditor/PropertyEditor.h +++ b/src/Gui/propertyeditor/PropertyEditor.h @@ -64,6 +64,7 @@ class PropertyEditor : public QTreeView Q_PROPERTY(QBrush groupBackground READ groupBackground WRITE setGroupBackground DESIGNABLE true SCRIPTABLE true) // clazy:exclude=qproperty-without-notify Q_PROPERTY(QColor groupTextColor READ groupTextColor WRITE setGroupTextColor DESIGNABLE true SCRIPTABLE true) // clazy:exclude=qproperty-without-notify + Q_PROPERTY(QBrush itemBackground READ itemBackground WRITE setItemBackground DESIGNABLE true SCRIPTABLE true) // clazy:exclude=qproperty-without-notify public: PropertyEditor(QWidget *parent = nullptr); @@ -84,6 +85,8 @@ public: void setGroupBackground(const QBrush& c); QColor groupTextColor() const; void setGroupTextColor(const QColor& c); + QBrush itemBackground() const; + void setItemBackground(const QBrush& c); bool isBinding() const { return binding; } void openEditor(const QModelIndex &index); @@ -135,6 +138,7 @@ private: QColor groupColor; QBrush background; + QBrush _itemBackground; QPointer activeEditor; QPersistentModelIndex editingIndex;