The hideTab is of type bool, which is primitive type and is thus unitialized if new
instance of the class is created. The exception would be to for static
instance of the class, but that is not the case (only the pointer to the instance is static).
It was originaly initialized, but the initialization was removed in
9d5390ed83.
2076 lines
73 KiB
C++
2076 lines
73 KiB
C++
/****************************************************************************
|
|
* Copyright (c) 2022 Zheng Lei (realthunder) <realthunder.dev@gmail.com> *
|
|
* *
|
|
* 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 <QAction>
|
|
# include <QApplication>
|
|
# include <QComboBox>
|
|
# include <QDockWidget>
|
|
# include <QFile>
|
|
# include <QGraphicsView>
|
|
# include <QHeaderView>
|
|
# include <QKeyEvent>
|
|
# include <QMdiArea>
|
|
# include <QMenu>
|
|
# include <QPainter>
|
|
# include <QPointer>
|
|
# include <QTextStream>
|
|
# include <QTimerEvent>
|
|
# include <QToolTip>
|
|
# include <QScrollBar>
|
|
#endif
|
|
|
|
#include <QPainterPath>
|
|
#include <QPropertyAnimation>
|
|
|
|
#include <array>
|
|
#include <unordered_map>
|
|
|
|
#include "OverlayManager.h"
|
|
|
|
#include <Base/Console.h>
|
|
#include <Base/Parameter.h>
|
|
#include <Base/Tools.h>
|
|
#include <App/Application.h>
|
|
#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<OverlayTabWidget*, 4> _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<OverlayTabWidget*>(w);
|
|
if(tabWidget)
|
|
return tabWidget;
|
|
auto proxy = qobject_cast<OverlayProxyWidget*>(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* instance;
|
|
|
|
if (!instance) {
|
|
instance = new OverlayStyleSheet;
|
|
}
|
|
|
|
return instance;
|
|
}
|
|
|
|
void OnChange(Base::Subject<const char*> &, const char* sReason) {
|
|
if (!sReason) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(sReason, "StyleSheet") == 0 ||
|
|
strcmp(sReason, "OverlayActiveStyleSheet") == 0) {
|
|
OverlayManager::instance()->refresh(nullptr, true);
|
|
}
|
|
}
|
|
|
|
void update() {
|
|
activeStyleSheet.clear();
|
|
|
|
QString overlayStylesheetFileName = detectOverlayStyleSheetFileName();
|
|
loadFromFile(overlayStylesheetFileName);
|
|
|
|
// If after loading the result is still empty we need to apply some defaults
|
|
if (activeStyleSheet.isEmpty()) {
|
|
activeStyleSheet = _default;
|
|
}
|
|
}
|
|
|
|
ParameterGrp::handle handle;
|
|
QString activeStyleSheet;
|
|
bool hideTab = false;
|
|
|
|
private:
|
|
QString detectOverlayStyleSheetFileName() const {
|
|
QString mainStyleSheet = QString::fromUtf8(handle->GetASCII("StyleSheet").c_str());
|
|
QString overlayStyleSheet = QString::fromUtf8(handle->GetASCII("OverlayActiveStyleSheet").c_str());
|
|
|
|
if (overlayStyleSheet.isEmpty()) {
|
|
// User did not choose any stylesheet, we need to choose one based on main stylesheet
|
|
if (mainStyleSheet.contains(QStringLiteral("light"), Qt::CaseInsensitive)) {
|
|
overlayStyleSheet = QStringLiteral("overlay:Light Theme + Dark Background.qss");
|
|
}
|
|
else {
|
|
// by default FreeCAD uses somewhat dark background for 3D View.
|
|
// if user has not explicitly selected light theme, the "Dark Outline" looks best
|
|
overlayStyleSheet = QStringLiteral("overlay:Dark Theme + Dark Background.qss");
|
|
}
|
|
}
|
|
else if (!overlayStyleSheet.isEmpty() && !QFile::exists(overlayStyleSheet)) {
|
|
// User did choose one of predefined stylesheets, we need to qualify it with namespace
|
|
overlayStyleSheet = QStringLiteral("overlay:%1").arg(overlayStyleSheet);
|
|
}
|
|
|
|
return overlayStyleSheet;
|
|
}
|
|
|
|
void loadFromFile(const QString& name) {
|
|
if (!QFile::exists(name)) {
|
|
return;
|
|
}
|
|
|
|
QFile file(name);
|
|
|
|
if (file.open(QFile::ReadOnly)) {
|
|
activeStyleSheet = QTextStream(&file).readAll();
|
|
}
|
|
}
|
|
|
|
static const QString _default;
|
|
};
|
|
|
|
const QString OverlayStyleSheet::_default = QStringLiteral("overlay:Light Theme + Dark Background.qss"
|
|
);
|
|
|
|
// -----------------------------------------------------------
|
|
|
|
struct OverlayInfo {
|
|
const char *name;
|
|
OverlayTabWidget *tabWidget;
|
|
Qt::DockWidgetArea dockArea;
|
|
std::unordered_map<QDockWidget*, OverlayInfo*> &overlayMap;
|
|
ParameterGrp::handle hGrp;
|
|
boost::signals2::scoped_connection conn;
|
|
|
|
OverlayInfo(QWidget *parent,
|
|
const char *name,
|
|
Qt::DockWidgetArea pos,
|
|
std::unordered_map<QDockWidget*, OverlayInfo*> &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<QDockWidget*>()) {
|
|
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<QWidget> 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();i<c;++i) {
|
|
auto dock = tabWidget->dockWidget(i);
|
|
if(dock)
|
|
overlayMap[dock] = this;
|
|
}
|
|
}
|
|
};
|
|
|
|
enum class ToggleMode {
|
|
Unset,
|
|
Set,
|
|
Toggle,
|
|
Transparent,
|
|
Check,
|
|
};
|
|
|
|
class OverlayManager::Private {
|
|
public:
|
|
|
|
QPointer<QWidget> lastIntercept;
|
|
QTimer _timer;
|
|
QTimer _reloadTimer;
|
|
|
|
bool mouseTransparent = false;
|
|
bool intercepting = false;
|
|
|
|
std::unordered_map<QDockWidget*, OverlayInfo*> _overlayMap;
|
|
OverlayInfo _left;
|
|
OverlayInfo _right;
|
|
OverlayInfo _top;
|
|
OverlayInfo _bottom;
|
|
std::array<OverlayInfo*,4> _overlayInfos;
|
|
QCursor _cursor;
|
|
|
|
QPoint _lastPos;
|
|
|
|
QAction _actClose;
|
|
QAction _actFloat;
|
|
QAction _actOverlay;
|
|
QList<QAction*> _actions;
|
|
|
|
QPointer<QWidget> _trackingWidget;
|
|
OverlayTabWidget *_trackingOverlay = nullptr;
|
|
|
|
bool updateStyle = false;
|
|
QTime wheelDelay;
|
|
QPoint wheelPos;
|
|
|
|
std::map<QString, OverlayTabWidget*> _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/icons/float.svg"));
|
|
_actOverlay.setIcon(BitmapFactory().pixmap("qss:overlay/icons/overlay.svg"));
|
|
_actClose.setIcon(BitmapFactory().pixmap("qss:overlay/icons/close.svg"));
|
|
for (OverlayTabWidget *tabWidget : _Overlays) {
|
|
tabWidget->refreshIcons();
|
|
for (auto handle : tabWidget->findChildren<OverlaySplitterHandle*>())
|
|
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<QTabBar*>();
|
|
if(tabbar)
|
|
h -= tabbar->height();
|
|
|
|
int naviCubeSize = NaviCube::getNaviCubeSize();
|
|
int naviCorner = OverlayParams::getDockOverlayCheckNaviCube()
|
|
? App::GetApplication()
|
|
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/NaviCube")
|
|
->GetInt("CornerNaviCube", 1)
|
|
: -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<QDockWidget*>();
|
|
// 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<QDockWidget*>(w);
|
|
if(dock)
|
|
break;
|
|
auto tabWidget = qobject_cast<OverlayTabWidget*>(w);
|
|
if(tabWidget) {
|
|
dock = tabWidget->currentDockWidget();
|
|
if(dock)
|
|
break;
|
|
}
|
|
}
|
|
if(!dock) {
|
|
for(auto w=qApp->focusWidget(); w; w=w->parentWidget()) {
|
|
dock = qobject_cast<QDockWidget*>(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;
|
|
auto newsizes = sizes;
|
|
newsizes[index] = 0;
|
|
for (int i=0; i<sizes.size(); ++i) {
|
|
if (i != index) {
|
|
auto d = tabWidget->dockWidget(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<sizes.size(); ++i) {
|
|
if (i != index && sizes[i] > 0) {
|
|
++expand;
|
|
break;
|
|
}
|
|
}
|
|
if (expand) {
|
|
int expansion = sizes[index];
|
|
int step = expansion / expand;
|
|
for (int i=0; i<sizes.size(); ++i) {
|
|
if (i == index)
|
|
newsizes[i] = 0;
|
|
else if (--expand) {
|
|
expansion -= step;
|
|
newsizes[i] += step;
|
|
} else {
|
|
newsizes[i] += expansion;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
newsizes[index] = 0;
|
|
}
|
|
tabWidget->splitter->setSizes(newsizes);
|
|
tabWidget->saveTabs();
|
|
}
|
|
}
|
|
for (int i=0; i<sizes.size(); ++i) {
|
|
if (sizes[i])
|
|
tabWidget->_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<QAction*> actions;
|
|
if (auto tabWidget = qobject_cast<OverlayTabWidget*>(parent))
|
|
actions = tabWidget->actions();
|
|
else if (auto dockWidget = qobject_cast<QDockWidget*>(parent))
|
|
{
|
|
const QDockWidget::DockWidgetFeatures features = dockWidget->features();
|
|
|
|
actions.append(&_actOverlay);
|
|
if (features.testFlag(QDockWidget::DockWidgetFloatable))
|
|
actions.append(&_actFloat);
|
|
if (features.testFlag(QDockWidget::DockWidgetClosable))
|
|
actions.append(&_actClose);
|
|
}
|
|
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<QDockWidget*>(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<OverlayTabWidget*>(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<QDockWidget*>(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<OverlayTabWidget*>(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<QDockWidget*>()) {
|
|
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<int, QDockWidget*> docks;
|
|
for(auto dockWidget : getMainWindow()->findChildren<QDockWidget*>()) {
|
|
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);
|
|
QObject::connect(dw, &QDockWidget::featuresChanged,
|
|
this, &OverlayManager::onDockFeaturesChange);
|
|
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<QAction*>(sender());
|
|
if(!action)
|
|
return;
|
|
d->onToggleDockWidget(qobject_cast<QDockWidget*>(action->parent()), checked);
|
|
}
|
|
|
|
void OverlayManager::onDockVisibleChange(bool visible)
|
|
{
|
|
auto dock = qobject_cast<QDockWidget*>(sender());
|
|
if(!dock)
|
|
return;
|
|
FC_TRACE("dock " << dock->objectName().toUtf8().constData()
|
|
<< " visible change " << visible << ", " << dock->isVisible());
|
|
}
|
|
|
|
void OverlayManager::onDockFeaturesChange(QDockWidget::DockWidgetFeatures features)
|
|
{
|
|
Q_UNUSED(features);
|
|
|
|
auto dw = qobject_cast<QDockWidget*>(sender());
|
|
|
|
if (!dw) {
|
|
return;
|
|
}
|
|
|
|
// Rebuild the title widget as it may have a different set of buttons shown.
|
|
if (auto *titleBarWidget = qobject_cast<OverlayTitleBar*>(dw->titleBarWidget())) {
|
|
dw->setTitleBarWidget(nullptr);
|
|
delete titleBarWidget;
|
|
}
|
|
|
|
setupTitleBar(dw);
|
|
}
|
|
|
|
void OverlayManager::onTaskViewUpdate()
|
|
{
|
|
auto taskview = qobject_cast<TaskView::TaskView*>(sender());
|
|
if (!taskview)
|
|
return;
|
|
QDockWidget *dock = nullptr;
|
|
for (QWidget *w=taskview; w; w=w->parentWidget()) {
|
|
if ((dock = qobject_cast<QDockWidget*>(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<QWidget*>(sender());
|
|
QDockWidget *dock = nullptr;
|
|
for (QWidget *w=widget; w; w=w->parentWidget()) {
|
|
if ((dock = qobject_cast<QDockWidget*>(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<View3DInventorViewer*>(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<QDockWidget*>(child)) {
|
|
QMetaObject::invokeMethod(this, "raiseAll", Qt::QueuedConnection);
|
|
break;
|
|
}
|
|
if (qobject_cast<OverlayTabWidget*>(child))
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case QEvent::Resize: {
|
|
if(getMainWindow() && o == mdi)
|
|
refresh();
|
|
return false;
|
|
}
|
|
case QEvent::KeyPress: {
|
|
QKeyEvent *ke = static_cast<QKeyEvent*>(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<OverlayTitleBar*>(OverlayTabWidget::_Dragging))
|
|
titleBar->endDrag();
|
|
else if (auto splitHandle = qobject_cast<OverlaySplitterHandle*>(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<QWidget*>(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<QAbstractItemView*>(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<QContextMenuEvent*>(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<OverlayTitleBar*>(OverlayTabWidget::_Dragging))
|
|
titleBar->endDrag();
|
|
else if (auto splitHandle = qobject_cast<OverlaySplitterHandle*>(OverlayTabWidget::_Dragging))
|
|
splitHandle->endDrag();
|
|
}
|
|
QWidget *grabber = QWidget::mouseGrabber();
|
|
d->lastIntercept = nullptr;
|
|
if (d->mouseTransparent || (grabber && grabber != d->_trackingOverlay))
|
|
return false;
|
|
if (qobject_cast<QAbstractButton*>(o))
|
|
return false;
|
|
if (ev->type() != QEvent::Wheel) {
|
|
if (qobject_cast<OverlayTitleBar*>(o))
|
|
return false;
|
|
} else if (qobject_cast<QScrollBar*>(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<OverlayTitleBar*>(widget))
|
|
activeTabWidget = qobject_cast<OverlayTabWidget*>(widget->parentWidget());
|
|
else if (qobject_cast<OverlaySplitterHandle*>(widget)) {
|
|
auto parent = widget->parentWidget();
|
|
if (parent)
|
|
activeTabWidget = qobject_cast<OverlayTabWidget*>(parent->parentWidget());
|
|
}
|
|
if (activeTabWidget)
|
|
break;
|
|
}
|
|
if (auto tabWidget = qobject_cast<OverlayTabWidget*>(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<QMouseEvent*>(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:
|
|
explicit MouseGrabberGuard(QWidget *grabber)
|
|
{
|
|
if (grabber && grabber == QWidget::mouseGrabber()) {
|
|
_grabber = grabber;
|
|
_grabber->releaseMouse();
|
|
}
|
|
}
|
|
~MouseGrabberGuard()
|
|
{
|
|
if (_grabber)
|
|
_grabber->grabMouse();
|
|
}
|
|
|
|
QPointer<QWidget> _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<QAbstractScrollArea*>(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<QMouseEvent*>(ev);
|
|
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
|
|
QPointF screenPos = me->screenPos();
|
|
QPoint point = me->globalPos();
|
|
#else
|
|
QPointF screenPos = me->globalPosition();
|
|
QPoint point = screenPos.toPoint();
|
|
#endif
|
|
lastIntercept = getChildAt(widget, point);
|
|
QMouseEvent mouseEvent(ev->type(),
|
|
lastIntercept->mapFromGlobal(point),
|
|
screenPos,
|
|
me->button(),
|
|
me->buttons(),
|
|
me->modifiers());
|
|
QApplication::sendEvent(lastIntercept, &mouseEvent);
|
|
break;
|
|
}
|
|
case QEvent::Wheel: {
|
|
auto we = static_cast<QWheelEvent*>(ev);
|
|
#if QT_VERSION < QT_VERSION_CHECK(5,15,0)
|
|
QPoint globalPos = we->globalPos();
|
|
#else
|
|
QPoint globalPos = we->globalPosition().toPoint();
|
|
#endif
|
|
lastIntercept = getChildAt(widget, globalPos);
|
|
|
|
// For some reason in case of 3D View we have to target it directly instead of targeting
|
|
// the viewport of QAbstractScrollArea like it works for all other widgets of that kind.
|
|
// That's why for this event we have to traverse up the widget tree to find if it is part
|
|
// of the 3D view.
|
|
for (auto parent = lastIntercept; parent; parent = parent->parentWidget()) {
|
|
if (qobject_cast<View3DInventorViewer*>(parent)) {
|
|
lastIntercept = parent;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5,12,0)
|
|
QWheelEvent wheelEvent(lastIntercept->mapFromGlobal(globalPos),
|
|
globalPos,
|
|
we->pixelDelta(),
|
|
we->angleDelta(),
|
|
we->buttons(),
|
|
we->modifiers(),
|
|
we->phase(),
|
|
we->inverted(),
|
|
we->source());
|
|
#else
|
|
QWheelEvent wheelEvent(lastIntercept->mapFromGlobal(globalPos),
|
|
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<QContextMenuEvent*>(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<QAction*>(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"
|