2046 lines
72 KiB
C++
2046 lines
72 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 <limits>
|
|
# 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();
|
|
|
|
const int paddedCubeSize = naviCubeSize + 10;
|
|
if(naviCorner == 2)
|
|
ofs.setWidth(ofs.width()+paddedCubeSize);
|
|
int bw = w-10-ofs.width()-delta;
|
|
if(naviCorner == 3)
|
|
bw -= paddedCubeSize;
|
|
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();
|
|
if(naviCorner == 0)
|
|
ofs.setWidth(ofs.width()+paddedCubeSize);
|
|
delta = _left.tabWidget->getSizeDelta()+rectBottom.height();
|
|
if(naviCorner == 2 && paddedCubeSize > rectBottom.height())
|
|
delta += paddedCubeSize - 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();
|
|
if(naviCorner == 1)
|
|
ofs.setWidth(ofs.width()+paddedCubeSize);
|
|
delta = _right.tabWidget->getSizeDelta()+rectBottom.height();
|
|
if(naviCorner == 3 && paddedCubeSize > rectBottom.height())
|
|
delta += paddedCubeSize - 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();
|
|
delta = _top.tabWidget->getSizeDelta();
|
|
if(naviCorner == 0)
|
|
rectLeft.setWidth(std::max(rectLeft.width(), paddedCubeSize));
|
|
else if(naviCorner == 1)
|
|
rectRight.setWidth(std::max(rectRight.width(), paddedCubeSize));
|
|
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.contains(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(std::numeric_limits<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);
|
|
QPoint globalPos = we->globalPosition().toPoint();
|
|
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;
|
|
}
|
|
}
|
|
|
|
QWheelEvent wheelEvent(lastIntercept->mapFromGlobal(globalPos),
|
|
globalPos,
|
|
we->pixelDelta(),
|
|
we->angleDelta(),
|
|
we->buttons(),
|
|
we->modifiers(),
|
|
we->phase(),
|
|
we->inverted(),
|
|
we->source());
|
|
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"
|