Files
create/src/Gui/OverlayWidgets.cpp
Markus Reitböck a72a0d6405 Gui: use CMake to generate precompiled headers on all platforms
"Professional CMake" book suggest the following:

"Targets should build successfully with or without compiler support for precompiled headers. It
should be considered an optimization, not a requirement. In particular, do not explicitly include a
precompile header (e.g. stdafx.h) in the source code, let CMake force-include an automatically
generated precompile header on the compiler command line instead. This is more portable across
the major compilers and is likely to be easier to maintain. It will also avoid warnings being
generated from certain code checking tools like iwyu (include what you use)."

Therefore, removed the "#include <PreCompiled.h>" from sources, also
there is no need for the "#ifdef _PreComp_" anymore
2025-09-14 09:47:03 +02:00

2685 lines
88 KiB
C++

/****************************************************************************
* Copyright (c) 2020 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 <QAction>
# include <QApplication>
# include <QBoxLayout>
# include <QComboBox>
# include <QDockWidget>
# include <QHeaderView>
# include <QKeyEvent>
# include <QMdiArea>
# include <QMenu>
# include <QPainter>
# include <QPointer>
# include <QSpacerItem>
# include <QSplitter>
# include <QTabBar>
# include <QTextStream>
# include <QTimerEvent>
# include <QToolTip>
# include <QTreeView>
# include <QScrollBar>
#include <QPainterPath>
#include <QPropertyAnimation>
#include <array>
#include <unordered_map>
#include "OverlayWidgets.h"
#include <Base/Tools.h>
#include <Base/Console.h>
#include <App/Application.h>
#include "Application.h"
#include "BitmapFactory.h"
#include "Clipping.h"
#include "ComboView.h"
#include "Command.h"
#include "Control.h"
#include "MainWindow.h"
#include "MDIView.h"
#include "NaviCube.h"
#include "OverlayManager.h"
#include "OverlayParams.h"
#include "TaskView/TaskView.h"
#include "Tree.h"
#include "TreeParams.h"
#include "propertyeditor/PropertyEditor.h"
FC_LOG_LEVEL_INIT("Dock", true, true);
using namespace Gui;
OverlayDragFrame *OverlayTabWidget::_DragFrame;
QDockWidget *OverlayTabWidget::_DragFloating;
QWidget *OverlayTabWidget::_Dragging;
OverlayTabWidget *OverlayTabWidget::_LeftOverlay;
OverlayTabWidget *OverlayTabWidget::_RightOverlay;
OverlayTabWidget *OverlayTabWidget::_TopOverlay;
OverlayTabWidget *OverlayTabWidget::_BottomOverlay;
static inline int widgetMinSize(const QWidget *widget, bool margin=false)
{
return widget->fontMetrics().ascent()
+ widget->fontMetrics().descent() + (margin?4:0);
}
// -----------------------------------------------------------
OverlayProxyWidget::OverlayProxyWidget(OverlayTabWidget *tabOverlay)
:QWidget(tabOverlay->parentWidget()), owner(tabOverlay), _hintColor(QColor(50,50,50,150))
{
dockArea = owner->getDockArea();
timer.setSingleShot(true);
QObject::connect(&timer, &QTimer::timeout, this, &OverlayProxyWidget::onTimer);
setAttribute(Qt::WA_TransparentForMouseEvents, true);
}
bool OverlayProxyWidget::isActivated() const
{
return drawLine && isVisible();
}
OverlayProxyWidget::HitTest OverlayProxyWidget::hitTest(const QPoint &globalPt, bool delay)
{
if (!isVisible() || !owner->count())
return HitTest::HitNone;
auto pt = mapFromGlobal(globalPt);
QTabBar *tabbar = owner->tabBar();
if (tabbar->isVisible() && tabbar->tabAt(pt)>=0) {
ToolTip::showText(globalPt, QObject::tr("Press Esc to hide hint"), this);
return HitTest::HitOuter;
}
HitTest hit = HitTest::HitNone;
QRect rect = this->getRect();
QSize s = this->size();
int hintSize = OverlayParams::getDockOverlayHintTriggerSize();
// if (owner->getState() == OverlayTabWidget::State::HintHidden)
// hintSize *= 2;
switch(dockArea) {
case Qt::LeftDockWidgetArea:
if (pt.y() >= 0 && pt.y() <= s.height() && pt.x() > 0 && pt.x() < hintSize)
hit = HitTest::HitOuter;
break;
case Qt::RightDockWidgetArea:
if (pt.y() >= 0 && pt.y() <= s.height() && pt.x() < s.width() && pt.x() > -hintSize)
hit = HitTest::HitOuter;
break;
case Qt::TopDockWidgetArea:
if (pt.x() >= 0 && pt.x() <= s.width() && pt.y() > 0 && pt.y() < hintSize)
hit = HitTest::HitOuter;
break;
case Qt::BottomDockWidgetArea:
if (pt.x() >= 0 && pt.x() <= s.width() && pt.y() < s.height() && pt.y() > -hintSize)
hit = HitTest::HitOuter;
break;
}
if (rect.contains(pt)) {
hit = HitTest::HitInner;
ToolTip::showText(globalPt, QObject::tr("Press Esc to hide hint"), this);
} else if (drawLine)
ToolTip::hideText();
if (owner->getState() == OverlayTabWidget::State::HintHidden) {
if (hit == HitTest::HitNone)
owner->setState(OverlayTabWidget::State::Normal);
else {
hit = HitTest::HitNone;
ToolTip::hideText();
}
}
if (hit != HitTest::HitNone) {
if (drawLine)
timer.stop();
else if (delay) {
if (!timer.isActive())
timer.start(OverlayParams::getDockOverlayHintDelay());
return hit;
} else {
timer.stop();
owner->setState(OverlayTabWidget::State::Hint);
drawLine = true;
update();
}
auto view = getMainWindow()->activeWindow();
auto overlayOnHoverAllowed = view && view->onHasMsg("AllowsOverlayOnHover");
if(owner->getState() != OverlayTabWidget::State::Hidden
&& hit == HitTest::HitOuter
&& overlayOnHoverAllowed
&& OverlayParams::getDockOverlayActivateOnHover()) {
if (owner->isVisible() && owner->tabBar()->isVisible()) {
QSize size = owner->tabBar()->size();
QPoint pt = owner->tabBar()->mapToGlobal(
QPoint(size.width(), size.height()));
QPoint pos = QCursor::pos();
switch(this->dockArea) {
case Qt::LeftDockWidgetArea:
case Qt::RightDockWidgetArea:
if (pos.y() < pt.y())
return HitTest::HitNone;
break;
case Qt::TopDockWidgetArea:
case Qt::BottomDockWidgetArea:
if (pos.x() < pt.x())
return HitTest::HitNone;
break;
default:
break;
}
}
owner->setState(OverlayTabWidget::State::Showing);
}
} else if (!drawLine) {
timer.stop();
} else if (delay) {
if (!timer.isActive())
timer.start(OverlayParams::getDockOverlayHintDelay());
} else {
timer.stop();
owner->setState(OverlayTabWidget::State::Normal);
drawLine = false;
ToolTip::hideText();
update();
}
return hit;
}
void OverlayProxyWidget::onTimer()
{
hitTest(QCursor::pos(), false);
}
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
void OverlayProxyWidget::enterEvent(QEvent *)
#else
void OverlayProxyWidget::enterEvent(QEnterEvent *)
#endif
{
if(!owner->count())
return;
if (!drawLine) {
if (!timer.isActive())
timer.start(OverlayParams::getDockOverlayHintDelay());
}
}
void OverlayProxyWidget::hideEvent(QHideEvent *)
{
drawLine = false;
}
void OverlayProxyWidget::onMousePress()
{
if(!owner->count())
return;
if (owner->getState() == OverlayTabWidget::State::HintHidden)
return;
owner->setState(OverlayTabWidget::State::Showing);
}
QBrush OverlayProxyWidget::hintColor() const
{
return _hintColor;
}
void OverlayProxyWidget::setHintColor(const QBrush &brush)
{
_hintColor = brush;
}
QRect OverlayProxyWidget::getRect() const
{
QRect rect = this->rect();
if (owner->isVisible() && owner->tabBar()->isVisible()) {
QSize size = owner->tabBar()->size();
QPoint pt = owner->tabBar()->mapToGlobal(
QPoint(size.width(), size.height()));
pt = this->mapFromGlobal(pt);
switch(this->dockArea) {
case Qt::LeftDockWidgetArea:
case Qt::RightDockWidgetArea:
rect.setTop(pt.y());
break;
case Qt::TopDockWidgetArea:
case Qt::BottomDockWidgetArea:
rect.setLeft(pt.x());
break;
default:
break;
}
}
switch(this->dockArea) {
case Qt::LeftDockWidgetArea:
if (int offset = OverlayParams::getDockOverlayHintLeftOffset())
rect.moveTop(std::max(rect.top()+offset, rect.bottom()-10));
if (int length = OverlayParams::getDockOverlayHintLeftLength())
rect.setHeight(std::min(length, rect.height()));
break;
case Qt::RightDockWidgetArea:
if (int offset = OverlayParams::getDockOverlayHintRightOffset())
rect.moveTop(std::max(rect.top()+offset, rect.bottom()-10));
if (int length = OverlayParams::getDockOverlayHintRightLength())
rect.setHeight(std::min(length, rect.height()));
break;
case Qt::TopDockWidgetArea:
if (int offset = OverlayParams::getDockOverlayHintTopOffset())
rect.moveLeft(std::max(rect.left()+offset, rect.right()-10));
if (int length = OverlayParams::getDockOverlayHintTopLength())
rect.setWidth(std::min(length, rect.width()));
break;
case Qt::BottomDockWidgetArea:
if (int offset = OverlayParams::getDockOverlayHintBottomOffset())
rect.moveLeft(std::max(rect.left()+offset, rect.right()-10));
if (int length = OverlayParams::getDockOverlayHintBottomLength())
rect.setWidth(std::min(length, rect.width()));
break;
default:
break;
}
return rect;
}
void OverlayProxyWidget::paintEvent(QPaintEvent *)
{
if(!drawLine)
return;
QPainter painter(this);
painter.setOpacity(_hintColor.color().alphaF());
painter.setPen(Qt::transparent);
painter.setBrush(_hintColor);
QRect rect = this->getRect();
painter.drawRect(rect);
}
OverlayToolButton::OverlayToolButton(QWidget *parent)
:QToolButton(parent)
{
setCursor(Qt::ArrowCursor);
}
// --------------------------------------------------------------------
OverlayTabWidget::OverlayTabWidget(QWidget *parent, Qt::DockWidgetArea pos)
:QTabWidget(parent), dockArea(pos)
{
// This is necessary to capture any focus lost from switching the tab,
// otherwise the lost focus will leak to the parent, i.e. MdiArea, which may
// cause unexpected Mdi sub window switching.
// setFocusPolicy(Qt::StrongFocus);
_imageScale = 0.0;
splitter = new OverlaySplitter(this);
_graphicsEffect = new OverlayGraphicsEffect(splitter);
splitter->setGraphicsEffect(_graphicsEffect);
_graphicsEffectTab = new OverlayGraphicsEffect(this);
_graphicsEffectTab->setEnabled(false);
tabBar()->setGraphicsEffect(_graphicsEffectTab);
Command *cmdHide = nullptr;
switch(pos) {
case Qt::LeftDockWidgetArea:
_LeftOverlay = this;
setTabPosition(QTabWidget::West);
splitter->setOrientation(Qt::Vertical);
cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleLeft");
break;
case Qt::RightDockWidgetArea:
_RightOverlay = this;
setTabPosition(QTabWidget::East);
splitter->setOrientation(Qt::Vertical);
cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleRight");
break;
case Qt::TopDockWidgetArea:
_TopOverlay = this;
setTabPosition(QTabWidget::North);
splitter->setOrientation(Qt::Horizontal);
cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleTop");
break;
case Qt::BottomDockWidgetArea:
_BottomOverlay = this;
setTabPosition(QTabWidget::South);
splitter->setOrientation(Qt::Horizontal);
cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleBottom");
break;
default:
break;
}
proxyWidget = new OverlayProxyWidget(this);
proxyWidget->hide();
_setOverlayMode(proxyWidget,OverlayOption::Enable);
setOverlayMode(true);
hide();
actTransparent.setCheckable(true);
actTransparent.setData(QStringLiteral("OBTN Transparent"));
actTransparent.setParent(this);
addAction(&actTransparent);
actAutoHide.setData(QStringLiteral("OBTN AutoHide"));
actEditHide.setData(QStringLiteral("OBTN EditHide"));
actEditShow.setData(QStringLiteral("OBTN EditShow"));
actTaskShow.setData(QStringLiteral("OBTN TaskShow"));
actNoAutoMode.setData(QStringLiteral("OBTN NoAutoMode"));
actAutoMode.setData(QStringLiteral("OBTN AutoMode"));
actAutoMode.setParent(this);
autoModeMenu.hide();
autoModeMenu.setToolTipsVisible(true);
autoModeMenu.addAction(&actNoAutoMode);
autoModeMenu.addAction(&actAutoHide);
autoModeMenu.addAction(&actEditShow);
autoModeMenu.addAction(&actEditHide);
autoModeMenu.addAction(&actTaskShow);
addAction(&actAutoMode);
actOverlay.setData(QStringLiteral("OBTN Overlay"));
actOverlay.setParent(this);
addAction(&actOverlay);
if (cmdHide)
cmdHide->addTo(this);
retranslate();
refreshIcons();
connect(tabBar(), &QTabBar::tabBarClicked, this, &OverlayTabWidget::onCurrentChanged);
connect(tabBar(), &QTabBar::tabMoved, this, &OverlayTabWidget::onTabMoved);
tabBar()->installEventFilter(this);
timer.setSingleShot(true);
connect(&timer, &QTimer::timeout, this, &OverlayTabWidget::setupLayout);
repaintTimer.setSingleShot(true);
connect(&repaintTimer, &QTimer::timeout, this, &OverlayTabWidget::onRepaint);
_animator = new QPropertyAnimation(this, "animation", this);
_animator->setStartValue(0.0);
_animator->setEndValue(1.0);
connect(_animator, &QAbstractAnimation::stateChanged,
this, &OverlayTabWidget::onAnimationStateChanged);
}
void OverlayTabWidget::refreshIcons()
{
auto curStyleSheet =
App::GetApplication()
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/MainWindow")
->GetASCII("StyleSheet", "None");
QPixmap pxAutoHide;
if (isStyleSheetDark(curStyleSheet)) {
actOverlay.setIcon(BitmapFactory().pixmap("qss:overlay/icons/overlay_light.svg"));
actNoAutoMode.setIcon(BitmapFactory().pixmap("qss:overlay/icons/mode_light.svg"));
actTaskShow.setIcon(BitmapFactory().pixmap("qss:overlay/icons/taskshow_light.svg"));
actEditShow.setIcon(BitmapFactory().pixmap("qss:overlay/icons/editshow_light.svg"));
actEditHide.setIcon(BitmapFactory().pixmap("qss:overlay/icons/edithide_light.svg"));
actTransparent.setIcon(BitmapFactory().pixmap("qss:overlay/icons/transparent_light.svg"));
pxAutoHide = BitmapFactory().pixmap("qss:overlay/icons/autohide_light.svg");
}
else {
actOverlay.setIcon(BitmapFactory().pixmap("qss:overlay/icons/overlay.svg"));
actNoAutoMode.setIcon(BitmapFactory().pixmap("qss:overlay/icons/mode.svg"));
actTaskShow.setIcon(BitmapFactory().pixmap("qss:overlay/icons/taskshow.svg"));
actEditShow.setIcon(BitmapFactory().pixmap("qss:overlay/icons/editshow.svg"));
actEditHide.setIcon(BitmapFactory().pixmap("qss:overlay/icons/edithide.svg"));
actTransparent.setIcon(BitmapFactory().pixmap("qss:overlay/icons/transparent.svg"));
pxAutoHide = BitmapFactory().pixmap("qss:overlay/icons/autohide.svg");
}
actAutoHide.setIcon(rotateAutoHideIcon(pxAutoHide, dockArea));
syncAutoMode();
}
void OverlayTabWidget::onAnimationStateChanged()
{
if (_animator->state() != QAbstractAnimation::Running) {
setAnimation(0);
if (_animator->startValue().toReal() == 0.0) {
hide();
OverlayManager::instance()->refresh();
}
if (_state == State::Showing)
setState(State::Normal);
}
}
void OverlayTabWidget::setAnimation(qreal t)
{
if (t != _animation) {
_animation = t;
setupLayout();
}
}
void OverlayTabWidget::startShow()
{
if (isVisible() || _state > State::Normal) {
return;
}
int duration = OverlayParams::getDockOverlayAnimationDuration();
bool setmode = _state != State::Showing;
if (duration) {
_animator->setStartValue(1.0);
_animator->setEndValue(0.0);
_animator->setDuration(duration);
_animator->setEasingCurve((QEasingCurve::Type)OverlayParams::getDockOverlayAnimationCurve());
_animator->start();
}
else if (_state == State::Showing)
setState(State::Normal);
proxyWidget->hide();
show();
raise();
if (setmode)
setOverlayMode(overlaid);
}
QWidget *OverlayTabWidget::createTitleButton(QAction *action, int size)
{
auto button = new OverlayToolButton(nullptr);
button->setObjectName(action->data().toString());
button->setDefaultAction(action);
button->setAutoRaise(true);
button->setContentsMargins(0,0,0,0);
button->setFixedSize(size,size);
return button;
}
void OverlayTabWidget::startHide()
{
if (!isVisible()
|| _state > State::Normal
|| (_animator->state() == QAbstractAnimation::Running
&& _animator->startValue().toReal() == 0.0))
return;
int duration = OverlayParams::getDockOverlayAnimationDuration();
if (!duration)
hide();
else {
_animator->setStartValue(0.0);
_animator->setEndValue(1.0);
_animator->setDuration(duration);
_animator->setEasingCurve((QEasingCurve::Type)OverlayParams::getDockOverlayAnimationCurve());
_animator->start();
}
}
bool OverlayTabWidget::event(QEvent *ev)
{
switch(ev->type()) {
case QEvent::MouseButtonRelease:
if(mouseGrabber() == this) {
releaseMouse();
ev->accept();
return true;
}
break;
case QEvent::MouseMove:
case QEvent::ContextMenu:
if(QApplication::mouseButtons() == Qt::NoButton && mouseGrabber() == this) {
releaseMouse();
ev->accept();
return true;
}
break;
case QEvent::MouseButtonPress:
ev->accept();
return true;
default:
break;
}
return QTabWidget::event(ev);
}
int OverlayTabWidget::testAlpha(const QPoint &_pos, int radiusScale)
{
if (!count() || (!isOverlaid() && !isTransparent()) || !isVisible())
return -1;
if (tabBar()->isVisible() && tabBar()->tabAt(tabBar()->mapFromGlobal(_pos))>=0)
return -1;
if (titleBar->isVisible() && titleBar->rect().contains(titleBar->mapFromGlobal(_pos)))
return -1;
if (!splitter->isVisible())
return 0;
auto pos = splitter->mapFromGlobal(_pos);
QSize size = splitter->size();
if (pos.x() < 0 || pos.y() < 0
|| pos.x() >= size.width()
|| pos.y() >= size.height())
{
if (this->rect().contains(this->mapFromGlobal(_pos)))
return 0;
return -1;
}
if (_image.isNull()) {
auto pixmap = splitter->grab();
_imageScale = pixmap.devicePixelRatio();
_image = pixmap.toImage();
}
int res = qAlpha(_image.pixel(pos*_imageScale));
int radius = OverlayParams::getDockOverlayAlphaRadius() * radiusScale;
if (res || radius<=0 )
return res;
radius *= _imageScale;
for (int i=-radius; i<radius; ++i) {
for (int j=-radius; j<radius; ++j) {
if (pos.x()+i < 0 || pos.y()+j < 0
|| pos.x()+i >= size.width()
|| pos.y()+j >= size.height())
continue;
res = qAlpha(_image.pixel(pos*_imageScale + QPoint(i,j)));
if (res)
return res;
}
}
return 0;
}
void OverlayTabWidget::paintEvent(QPaintEvent *ev)
{
Base::StateLocker guard(repainting);
repaintTimer.stop();
if (!_image.isNull())
_image = QImage();
QTabWidget::paintEvent(ev);
}
void OverlayTabWidget::onRepaint()
{
Base::StateLocker guard(repainting);
repaintTimer.stop();
if (!_image.isNull())
_image = QImage();
splitter->repaint();
}
void OverlayTabWidget::scheduleRepaint()
{
if(!repainting
&& isVisible()
&& _graphicsEffect)
{
repaintTimer.start(100);
}
}
QColor OverlayTabWidget::effectColor() const
{
return _graphicsEffect->color();
}
void OverlayTabWidget::setEffectColor(const QColor &color)
{
_graphicsEffect->setColor(color);
_graphicsEffectTab->setColor(color);
}
int OverlayTabWidget::effectWidth() const
{
return _graphicsEffect->size().width();
}
void OverlayTabWidget::setEffectWidth(int s)
{
auto size = _graphicsEffect->size();
size.setWidth(s);
_graphicsEffect->setSize(size);
_graphicsEffectTab->setSize(size);
}
int OverlayTabWidget::effectHeight() const
{
return _graphicsEffect->size().height();
}
void OverlayTabWidget::setEffectHeight(int s)
{
auto size = _graphicsEffect->size();
size.setHeight(s);
_graphicsEffect->setSize(size);
_graphicsEffectTab->setSize(size);
}
qreal OverlayTabWidget::effectOffsetX() const
{
return _graphicsEffect->offset().x();
}
void OverlayTabWidget::setEffectOffsetX(qreal d)
{
auto offset = _graphicsEffect->offset();
offset.setX(d);
_graphicsEffect->setOffset(offset);
_graphicsEffectTab->setOffset(offset);
}
qreal OverlayTabWidget::effectOffsetY() const
{
return _graphicsEffect->offset().y();
}
void OverlayTabWidget::setEffectOffsetY(qreal d)
{
auto offset = _graphicsEffect->offset();
offset.setY(d);
_graphicsEffect->setOffset(offset);
_graphicsEffectTab->setOffset(offset);
}
qreal OverlayTabWidget::effectBlurRadius() const
{
return _graphicsEffect->blurRadius();
}
void OverlayTabWidget::setEffectBlurRadius(qreal r)
{
_graphicsEffect->setBlurRadius(r);
_graphicsEffectTab->setBlurRadius(r);
}
bool OverlayTabWidget::effectEnabled() const
{
return _effectEnabled;
}
void OverlayTabWidget::setEffectEnabled(bool enable)
{
_effectEnabled = enable;
}
bool OverlayTabWidget::eventFilter(QObject *o, QEvent *ev)
{
if(ev->type() == QEvent::Resize && o == tabBar()) {
if (_state <= State::Normal)
timer.start(10);
}
return QTabWidget::eventFilter(o, ev);
}
void OverlayTabWidget::restore(ParameterGrp::handle handle)
{
if (!handle) {
hGrp = handle;
return;
}
if (!parentWidget())
return;
std::string widgets = handle->GetASCII("Widgets","");
for(auto &name : QString::fromUtf8(widgets.c_str()).split(QLatin1Char(','))) {
if(name.isEmpty())
continue;
OverlayManager::instance()->registerDockWidget(name, this);
auto dock = getMainWindow()->findChild<QDockWidget*>(name);
if(dock)
addWidget(dock, dock->windowTitle());
}
int width = handle->GetInt("Width", 0);
int height = handle->GetInt("Height", 0);
int offset1 = handle->GetInt("Offset1", 0);
int offset2 = handle->GetInt("Offset3", 0);
setOffset(QSize(offset1,offset2));
setSizeDelta(handle->GetInt("Offset2", 0));
if(width && height) {
QRect rect(0, 0, width, height);
switch(dockArea) {
case Qt::RightDockWidgetArea:
rect.moveRight(parentWidget()->size().width());
break;
case Qt::BottomDockWidgetArea:
rect.moveBottom(parentWidget()->size().height());
break;
default:
break;
}
setRect(rect);
}
if (handle->GetBool("AutoHide", false))
setAutoMode(AutoMode::AutoHide);
else if (handle->GetBool("EditHide", false))
setAutoMode(AutoMode::EditHide);
else if (handle->GetBool("EditShow", false))
setAutoMode(AutoMode::EditShow);
else if (handle->GetBool("TaskShow", false))
setAutoMode(AutoMode::TaskShow);
else
setAutoMode(AutoMode::NoAutoMode);
setTransparent(handle->GetBool("Transparent", false));
_sizemap.clear();
std::string savedSizes = handle->GetASCII("Sizes","");
QList<int> sizes;
int idx = 0;
for(auto &size : QString::fromUtf8(savedSizes.c_str()).split(QLatin1Char(','))) {
sizes.append(size.toInt());
_sizemap[dockWidget(idx++)] = sizes.back();
}
FC_LOG("restore " << objectName().toUtf8().constData() << " " << savedSizes);
getSplitter()->setSizes(sizes);
hGrp = handle;
}
void OverlayTabWidget::saveTabs()
{
if(!hGrp)
return;
std::ostringstream os, os2;
_sizemap.clear();
auto sizes = splitter->sizes();
bool first = true;
for(int i=0,c=splitter->count(); i<c; ++i) {
auto dock = dockWidget(i);
if (!dock)
continue;
if(dock->objectName().size()) {
os << dock->objectName().toUtf8().constData() << ",";
if (first)
first = false;
else
os2 << ",";
os2 << sizes[i];
}
_sizemap[dock] = sizes[i];
}
Base::StateLocker lock(_saving);
hGrp->SetASCII("Widgets", os.str().c_str());
hGrp->SetASCII("Sizes", os2.str().c_str());
FC_LOG("save " << objectName().toUtf8().constData() << " " << os2.str());
}
void OverlayTabWidget::onTabMoved(int from, int to)
{
QWidget *w = splitter->widget(from);
splitter->insertWidget(to,w);
saveTabs();
}
void OverlayTabWidget::setTitleBar(QWidget *w)
{
titleBar = w;
}
void OverlayTabWidget::changeEvent(QEvent *e)
{
QTabWidget::changeEvent(e);
if (e->type() == QEvent::LanguageChange)
retranslate();
}
void OverlayTabWidget::retranslate()
{
actTransparent.setToolTip(tr("Toggle transparent mode"));
actNoAutoMode.setText(tr("None"));
actNoAutoMode.setToolTip(tr("Turn off auto hide/show"));
actAutoHide.setText(tr("Auto hide"));
actAutoHide.setToolTip(tr("Auto hide docked widgets on leave"));
actEditHide.setText(tr("Hide on edit"));
actEditHide.setToolTip(tr("Auto hide docked widgets on editing"));
actEditShow.setText(tr("Show on edit"));
actEditShow.setToolTip(tr("Auto show docked widgets on editing"));
actTaskShow.setText(tr("Auto task"));
actTaskShow.setToolTip(tr("Auto show task view for any current task, and hide the view when there is no task."));
actOverlay.setToolTip(tr("Toggle overlay"));
syncAutoMode();
}
void OverlayTabWidget::syncAutoMode()
{
auto curStyleSheet =
App::GetApplication()
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/MainWindow")
->GetASCII("StyleSheet", "None");
QAction* action = nullptr;
switch (autoMode) {
case AutoMode::AutoHide:
action = &actAutoHide;
if (isStyleSheetDark(curStyleSheet)) {
QPixmap pxAutoHideMode =
BitmapFactory().pixmap("qss:overlay/icons/autohide_lighter.svg");
pxAutoHideMode = rotateAutoHideIcon(pxAutoHideMode, dockArea);
action->setIcon(pxAutoHideMode);
actNoAutoMode.setIcon(BitmapFactory().pixmap("qss:overlay/icons/mode_light.svg"));
actTaskShow.setIcon(BitmapFactory().pixmap("qss:overlay/icons/taskshow_light.svg"));
actEditShow.setIcon(BitmapFactory().pixmap("qss:overlay/icons/editshow_light.svg"));
actEditHide.setIcon(BitmapFactory().pixmap("qss:overlay/icons/edithide_light.svg"));
}
else {
QPixmap pxAutoHideMode = BitmapFactory().pixmap("qss:overlay/icons/autohide.svg");
pxAutoHideMode = rotateAutoHideIcon(pxAutoHideMode, dockArea);
action->setIcon(pxAutoHideMode);
actNoAutoMode.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/mode_lightgray.svg"));
actTaskShow.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/taskshow_lightgray.svg"));
actEditShow.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/editshow_lightgray.svg"));
actEditHide.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/edithide_lightgray.svg"));
}
break;
case AutoMode::EditShow:
action = &actEditShow;
if (isStyleSheetDark(curStyleSheet)) {
QPixmap pxEditShowMode =
BitmapFactory().pixmap("qss:overlay/icons/editshow_lighter.svg");
action->setIcon(pxEditShowMode);
QPixmap pxAutoHideMode =
BitmapFactory().pixmap("qss:overlay/icons/autohide_light.svg");
pxAutoHideMode = rotateAutoHideIcon(pxAutoHideMode, dockArea);
actAutoHide.setIcon(pxAutoHideMode);
actNoAutoMode.setIcon(BitmapFactory().pixmap("qss:overlay/icons/mode_light.svg"));
actTaskShow.setIcon(BitmapFactory().pixmap("qss:overlay/icons/taskshow_light.svg"));
actEditHide.setIcon(BitmapFactory().pixmap("qss:overlay/icons/edithide_light.svg"));
}
else {
QPixmap pxEditShowMode = BitmapFactory().pixmap("qss:overlay/icons/editshow.svg");
action->setIcon(pxEditShowMode);
QPixmap pxAutoHideMode =
BitmapFactory().pixmap("qss:overlay/icons/autohide_lightgray.svg");
pxAutoHideMode = rotateAutoHideIcon(pxAutoHideMode, dockArea);
actAutoHide.setIcon(pxAutoHideMode);
actNoAutoMode.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/mode_lightgray.svg"));
actTaskShow.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/taskshow_lightgray.svg"));
actEditHide.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/edithide_lightgray.svg"));
}
break;
case AutoMode::TaskShow:
action = &actTaskShow;
if (isStyleSheetDark(curStyleSheet)) {
QPixmap pxTaskShowMode =
BitmapFactory().pixmap("qss:overlay/icons/taskshow_lighter.svg");
action->setIcon(pxTaskShowMode);
QPixmap pxAutoHideMode =
BitmapFactory().pixmap("qss:overlay/icons/autohide_light.svg");
pxAutoHideMode = rotateAutoHideIcon(pxAutoHideMode, dockArea);
actNoAutoMode.setIcon(BitmapFactory().pixmap("qss:overlay/icons/mode_light.svg"));
actEditShow.setIcon(BitmapFactory().pixmap("qss:overlay/icons/editshow_light.svg"));
actAutoHide.setIcon(pxAutoHideMode);
actEditHide.setIcon(BitmapFactory().pixmap("qss:overlay/icons/edithide_light.svg"));
}
else {
QPixmap pxTaskShowMode = BitmapFactory().pixmap("qss:overlay/icons/taskshow.svg");
action->setIcon(pxTaskShowMode);
QPixmap pxAutoHideMode =
BitmapFactory().pixmap("qss:overlay/icons/autohide_lightgray.svg");
pxAutoHideMode = rotateAutoHideIcon(pxAutoHideMode, dockArea);
actNoAutoMode.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/mode_lightgray.svg"));
actEditShow.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/editshow_lightgray.svg"));
actAutoHide.setIcon(pxAutoHideMode);
actEditHide.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/edithide_lightgray.svg"));
}
break;
case AutoMode::EditHide:
action = &actEditHide;
if (isStyleSheetDark(curStyleSheet)) {
QPixmap pxEditHideMode =
BitmapFactory().pixmap("qss:overlay/icons/edithide_lighter.svg");
action->setIcon(pxEditHideMode);
QPixmap pxAutoHideMode =
BitmapFactory().pixmap("qss:overlay/icons/autohide_light.svg");
pxAutoHideMode = rotateAutoHideIcon(pxAutoHideMode, dockArea);
actNoAutoMode.setIcon(BitmapFactory().pixmap("qss:overlay/icons/mode_light.svg"));
actEditShow.setIcon(BitmapFactory().pixmap("qss:overlay/icons/editshow_light.svg"));
actAutoHide.setIcon(pxAutoHideMode);
actTaskShow.setIcon(BitmapFactory().pixmap("qss:overlay/icons/taskshow_light.svg"));
}
else {
QPixmap pxEditHideMode = BitmapFactory().pixmap("qss:overlay/icons/edithide.svg");
action->setIcon(pxEditHideMode);
QPixmap pxAutoHideMode =
BitmapFactory().pixmap("qss:overlay/icons/autohide_lightgray.svg");
pxAutoHideMode = rotateAutoHideIcon(pxAutoHideMode, dockArea);
actNoAutoMode.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/mode_lightgray.svg"));
actEditShow.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/editshow_lightgray.svg"));
actAutoHide.setIcon(pxAutoHideMode);
actTaskShow.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/taskshow_lightgray.svg"));
}
break;
default:
action = &actNoAutoMode;
if (isStyleSheetDark(curStyleSheet)) {
QPixmap pxNoAutoMode = BitmapFactory().pixmap("qss:overlay/icons/mode_lighter.svg");
action->setIcon(pxNoAutoMode);
QPixmap pxAutoHideMode =
BitmapFactory().pixmap("qss:overlay/icons/autohide_light.svg");
pxAutoHideMode = rotateAutoHideIcon(pxAutoHideMode, dockArea);
actTaskShow.setIcon(BitmapFactory().pixmap("qss:overlay/icons/taskshow_light.svg"));
actEditShow.setIcon(BitmapFactory().pixmap("qss:overlay/icons/editshow_light.svg"));
actAutoHide.setIcon(pxAutoHideMode);
actEditHide.setIcon(BitmapFactory().pixmap("qss:overlay/icons/edithide_light.svg"));
}
else {
QPixmap pxNoAutoMode = BitmapFactory().pixmap("qss:overlay/icons/mode.svg");
action->setIcon(pxNoAutoMode);
QPixmap pxAutoHideMode =
BitmapFactory().pixmap("qss:overlay/icons/autohide_lightgray.svg");
pxAutoHideMode = rotateAutoHideIcon(pxAutoHideMode, dockArea);
actTaskShow.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/taskshow_lightgray.svg"));
actEditShow.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/editshow_lightgray.svg"));
actAutoHide.setIcon(pxAutoHideMode);
actEditHide.setIcon(
BitmapFactory().pixmap("qss:overlay/icons/edithide_lightgray.svg"));
}
break;
}
actAutoMode.setIcon(action->icon());
if (action == &actNoAutoMode) {
actAutoMode.setToolTip(tr("Select auto show/hide mode"));
}
else {
actAutoMode.setToolTip(action->toolTip());
}
}
void OverlayTabWidget::onAction(QAction *action)
{
if (action == &actAutoMode) {
action = autoModeMenu.exec(QCursor::pos());
if (action == &actNoAutoMode)
setAutoMode(AutoMode::NoAutoMode);
else if (action == &actAutoHide)
setAutoMode(AutoMode::AutoHide);
else if (action == &actEditShow)
setAutoMode(AutoMode::EditShow);
else if (action == &actTaskShow)
setAutoMode(AutoMode::TaskShow);
else if (action == &actEditHide)
setAutoMode(AutoMode::EditHide);
return;
}
else if(action == &actOverlay) {
OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleActive);
return;
} else if(action == &actTransparent) {
if(hGrp) {
Base::StateLocker lock(_saving);
hGrp->SetBool("Transparent", actTransparent.isChecked());
}
}
OverlayManager::instance()->refresh(this);
}
void OverlayTabWidget::setState(State state)
{
if (_state == state)
return;
switch(state) {
case State::Normal:
if (_state == State::Hidden) {
// Only unhide through State::Showing, not State::Normal
return;
}
else if (_state == State::Showing) {
_state = state;
return;
}
// fall through
case State::Showing:
_state = state;
hide();
if (dockArea == Qt::RightDockWidgetArea)
setTabPosition(East);
else if (dockArea == Qt::BottomDockWidgetArea)
setTabPosition(South);
if (this->count() == 1)
tabBar()->hide();
_graphicsEffectTab->setEnabled(false);
titleBar->show();
splitter->show();
if (state == State::Showing)
OverlayManager::instance()->refresh(this);
break;
case State::Hint:
if (_state == State::HintHidden || _state == State::Hidden)
break;
_state = state;
if (this->count() && OverlayParams::getDockOverlayHintTabBar()) {
tabBar()->setToolTip(proxyWidget->toolTip());
tabBar()->show();
titleBar->hide();
splitter->hide();
_graphicsEffectTab->setEnabled(true);
show();
raise();
proxyWidget->raise();
if (dockArea == Qt::RightDockWidgetArea)
setTabPosition(West);
else if (dockArea == Qt::BottomDockWidgetArea)
setTabPosition(North);
OverlayManager::instance()->refresh(this);
}
break;
case State::HintHidden:
if (_state != State::Hidden)
_state = state;
proxyWidget->hide();
hide();
_graphicsEffectTab->setEnabled(true);
break;
case State::Hidden:
startHide();
_state = state;
break;
default:
break;
}
}
bool OverlayTabWidget::checkAutoHide() const
{
if (autoMode == AutoMode::AutoHide) {
return true;
}
if (OverlayParams::getDockOverlayAutoView()) {
auto view = getMainWindow()->activeWindow();
if (!view) {
return true;
}
if (!view->onHasMsg("AllowsOverlayOnHover")) {
return true;
}
if (!view->onHasMsg("CanPan")
&& view->parentWidget()
&& view->parentWidget()->isMaximized()) {
return true;
}
}
if (autoMode == AutoMode::EditShow) {
return !Application::Instance->editDocument()
&& (!Control().taskPanel() || Control().taskPanel()->isEmpty(false));
}
if (autoMode == AutoMode::EditHide && Application::Instance->editDocument()) {
return true;
}
return false;
}
void OverlayTabWidget::leaveEvent(QEvent*)
{
if (titleBar && QWidget::mouseGrabber() == titleBar)
return;
OverlayManager::instance()->refresh();
}
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
void OverlayTabWidget::enterEvent(QEvent*)
#else
void OverlayTabWidget::enterEvent(QEnterEvent*)
#endif
{
revealTime = QTime();
OverlayManager::instance()->refresh();
}
void OverlayTabWidget::setRevealTime(const QTime &time)
{
revealTime = time;
}
void OverlayTabWidget::_setOverlayMode(QWidget *widget, OverlayOption option)
{
if(!widget)
return;
if (qobject_cast<QScrollBar*>(widget)) {
auto parent = widget->parentWidget();
if (parent) {
parent = parent->parentWidget();
if (qobject_cast<PropertyEditor::PropertyEditor*>(parent)) {
auto scrollArea = static_cast<QAbstractScrollArea*>(parent);
if (scrollArea->verticalScrollBar() == widget) {
if (!OverlayParams::getDockOverlayHidePropertyViewScrollBar()
|| option == OverlayOption::Disable)
widget->setStyleSheet(QString());
else {
static QString _style = QStringLiteral("*{width:0}");
widget->setStyleSheet(_style);
}
}
}
auto treeView = qobject_cast<TreeWidget*>(parent);
if (treeView) {
auto scrollArea = static_cast<QAbstractScrollArea*>(parent);
if (scrollArea->verticalScrollBar() == widget) {
if (!TreeParams::getHideScrollBar() || option == OverlayOption::Disable)
widget->setStyleSheet(QString());
else {
static QString _style = QStringLiteral("*{width:0}");
widget->setStyleSheet(_style);
}
}
}
if (treeView) {
auto header = treeView->header();
if (!TreeParams::getHideHeaderView() || option==OverlayOption::Disable)
header->setStyleSheet(QString());
else {
static QString _style = QStringLiteral(
"QHeaderView:section {"
"height: 0px;"
"background-color: transparent;"
"padding: 0px;"
"border: transparent;}");
header->setStyleSheet(_style);
}
}
}
}
auto tabbar = qobject_cast<QTabBar*>(widget);
if(tabbar) {
if(!tabbar->autoHide() || tabbar->count()>1) {
if(!OverlayManager::instance()->getHideTab())
tabbar->setVisible(true);
else
tabbar->setVisible(option == OverlayOption::Disable
|| (option == OverlayOption::ShowTab && tabbar->count()>1));
return;
}
}
if (!qobject_cast<QScrollArea*>(widget)
|| !qobject_cast<Dialog::Clipping*>(widget->parentWidget())) {
if(option != OverlayOption::Disable) {
widget->setWindowFlags(widget->windowFlags() | Qt::FramelessWindowHint);
} else {
widget->setWindowFlags(widget->windowFlags() & ~Qt::FramelessWindowHint);
}
widget->setAttribute(Qt::WA_NoSystemBackground, option != OverlayOption::Disable);
widget->setAttribute(Qt::WA_TranslucentBackground, option != OverlayOption::Disable);
}
}
void OverlayTabWidget::setOverlayMode(QWidget *widget, OverlayOption option)
{
if(!widget || (qobject_cast<QDialog*>(widget)
&& !qobject_cast<Dialog::Clipping*>(widget))
|| qobject_cast<TaskView::TaskBox*>(widget))
return;
if(widget != tabBar()) {
if(OverlayParams::getDockOverlayAutoMouseThrough()
&& option == OverlayOption::ShowTab) {
widget->setMouseTracking(true);
}
}
_setOverlayMode(widget, option);
if(qobject_cast<QComboBox*>(widget)) {
// do not set child QAbstractItemView of QComboBox, otherwise the drop down box
// won't be shown
return;
}
for(auto child : widget->children())
setOverlayMode(qobject_cast<QWidget*>(child), option);
}
void OverlayTabWidget::setTransparent(bool enable)
{
if(actTransparent.isChecked() == enable)
return;
if(hGrp) {
Base::StateLocker lock(_saving);
hGrp->SetBool("Transparent", enable);
}
actTransparent.setChecked(enable);
OverlayManager::instance()->refresh(this);
}
bool OverlayTabWidget::isTransparent() const
{
if (!actTransparent.isChecked())
return false;
if(OverlayParams::getDockOverlayAutoView()) {
auto view = getMainWindow()->activeWindow();
if (!view) return false;
if(!view->onHasMsg("CanPan")
&& view->parentWidget()
&& view->parentWidget()->isMaximized())
return false;
}
return true;
}
bool OverlayTabWidget::isOverlaid(QueryOption option) const
{
if (option != QueryOption::QueryOverlay
&& currentTransparent != isTransparent())
return option == QueryOption::TransparencyChanged;
return overlaid;
}
void OverlayTabWidget::setAutoMode(AutoMode mode)
{
if (autoMode == mode)
return;
autoMode = mode;
if (hGrp) {
bool autohide = false, editshow = false, edithide = false, taskshow = false;
switch (mode) {
case AutoMode::AutoHide:
autohide = true;
break;
case AutoMode::EditShow:
editshow = true;
break;
case AutoMode::EditHide:
edithide = true;
break;
case AutoMode::TaskShow:
taskshow = true;
break;
default:
break;
}
Base::StateLocker lock(_saving);
hGrp->SetBool("AutoHide", autohide);
hGrp->SetBool("EditShow", editshow);
hGrp->SetBool("EditHide", edithide);
hGrp->SetBool("TaskShow", taskshow);
}
syncAutoMode();
OverlayManager::instance()->refresh(this);
}
QDockWidget *OverlayTabWidget::currentDockWidget() const
{
int index = -1;
for(int size : splitter->sizes()) {
++index;
if(size>0)
return dockWidget(index);
}
return dockWidget(currentIndex());
}
QDockWidget *OverlayTabWidget::dockWidget(int index) const
{
if(index < 0 || index >= splitter->count())
return nullptr;
return qobject_cast<QDockWidget*>(splitter->widget(index));
}
void OverlayTabWidget::updateSplitterHandles()
{
if (overlaid || _state > State::Normal)
return;
for (int i=0, c=splitter->count(); i<c; ++i) {
auto handle = qobject_cast<OverlaySplitterHandle*>(splitter->handle(i));
if (handle)
handle->showTitle(true);
}
}
bool OverlayTabWidget::onEscape()
{
if (getState() == OverlayTabWidget::State::Hint
|| getState() == OverlayTabWidget::State::Hidden) {
setState(OverlayTabWidget::State::HintHidden);
return true;
}
if (!isVisible())
return false;
if (titleBar->isVisible() && titleBar->underMouse()) {
titleBar->hide();
return true;
}
for (int i=0, c=splitter->count(); i<c; ++i) {
auto handle = qobject_cast<OverlaySplitterHandle*>(splitter->handle(i));
if (handle->isVisible() && handle->underMouse()) {
handle->showTitle(false);
return true;
}
}
return false;
}
void OverlayTabWidget::setOverlayMode(bool enable)
{
overlaid = enable;
if(!isVisible() || !count())
return;
touched = false;
if (_state <= State::Normal) {
titleBar->setVisible(!enable || OverlayManager::instance()->isMouseTransparent());
for (int i=0, c=splitter->count(); i<c; ++i) {
auto handle = qobject_cast<OverlaySplitterHandle*>(splitter->handle(i));
if (handle)
handle->showTitle(!enable);
}
}
QString stylesheet;
stylesheet = OverlayManager::instance()->getStyleSheet();
currentTransparent = isTransparent();
OverlayOption option;
if(!enable && isTransparent()) {
option = OverlayOption::ShowTab;
} else if (enable
&& !isTransparent()
&& (autoMode == AutoMode::EditShow || autoMode == AutoMode::AutoHide)) {
option = OverlayOption::Disable;
} else {
option = enable?OverlayOption::Enable:OverlayOption::Disable;
}
setProperty("transparent", option != OverlayOption::Disable);
proxyWidget->setStyleSheet(stylesheet);
this->setStyleSheet(stylesheet);
setOverlayMode(this, option);
_graphicsEffect->setEnabled(effectEnabled() && (enable || isTransparent()));
if (_state == State::Hint && OverlayParams::getDockOverlayHintTabBar()) {
tabBar()->setToolTip(proxyWidget->toolTip());
tabBar()->show();
} else if (OverlayParams::getDockOverlayHideTabBar() || count()==1) {
tabBar()->hide();
} else {
tabBar()->setToolTip(QString());
tabBar()->setVisible(!enable || !OverlayManager::instance()->getHideTab());
}
setRect(rectOverlay);
}
const QRect &OverlayTabWidget::getRect()
{
return rectOverlay;
}
bool OverlayTabWidget::getAutoHideRect(QRect &rect) const
{
rect = rectOverlay;
int hintWidth = OverlayParams::getDockOverlayHintSize();
switch(dockArea) {
case Qt::LeftDockWidgetArea:
case Qt::RightDockWidgetArea:
if (_TopOverlay->isVisible() && _TopOverlay->_state <= State::Normal)
rect.setTop(std::max(rect.top(), _TopOverlay->rectOverlay.bottom()));
if (dockArea == Qt::RightDockWidgetArea)
rect.setLeft(rect.left() + std::max(rect.width()-hintWidth,0));
else
rect.setRight(rect.right() - std::max(rect.width()-hintWidth,0));
break;
case Qt::TopDockWidgetArea:
case Qt::BottomDockWidgetArea:
if (_LeftOverlay->isVisible() && _LeftOverlay->_state <= State::Normal)
rect.setLeft(std::max(rect.left(),_LeftOverlay->rectOverlay.right()));
if (dockArea == Qt::TopDockWidgetArea)
rect.setBottom(rect.bottom() - std::max(rect.height()-hintWidth,0));
else {
rect.setTop(rect.top() + std::max(rect.height()-hintWidth,0));
if (_RightOverlay->isVisible() && _RightOverlay->_state <= State::Normal) {
QPoint offset = getMainWindow()->getMdiArea()->pos();
rect.setRight(std::min(rect.right(), _RightOverlay->x()-offset.x()));
}
}
break;
default:
break;
}
return _state != State::Showing && overlaid && checkAutoHide();
}
void OverlayTabWidget::setOffset(const QSize &ofs)
{
if(offset != ofs) {
offset = ofs;
if(hGrp) {
Base::StateLocker lock(_saving);
hGrp->SetInt("Offset1", ofs.width());
hGrp->SetInt("Offset3", ofs.height());
}
}
}
void OverlayTabWidget::setSizeDelta(int delta)
{
if(sizeDelta != delta) {
if(hGrp) {
Base::StateLocker lock(_saving);
hGrp->SetInt("Offset2", delta);
}
sizeDelta = delta;
}
}
void OverlayTabWidget::setRect(QRect rect)
{
if(busy || !parentWidget() || !getMainWindow() || !getMainWindow()->getMdiArea())
return;
if (rect.width() == 0)
rect.setWidth(OverlayParams::getDockOverlayMinimumSize()*3);
if (rect.height() == 0)
rect.setHeight(OverlayParams::getDockOverlayMinimumSize()*3);
switch(dockArea) {
case Qt::LeftDockWidgetArea:
rect.moveLeft(0);
if (rect.width() < OverlayParams::getDockOverlayMinimumSize())
rect.setWidth(OverlayParams::getDockOverlayMinimumSize());
break;
case Qt::RightDockWidgetArea:
if (rect.width() < OverlayParams::getDockOverlayMinimumSize())
rect.setLeft(rect.right()-OverlayParams::getDockOverlayMinimumSize());
break;
case Qt::TopDockWidgetArea:
rect.moveTop(0);
if (rect.height() < OverlayParams::getDockOverlayMinimumSize())
rect.setHeight(OverlayParams::getDockOverlayMinimumSize());
break;
case Qt::BottomDockWidgetArea:
if (rect.height() < OverlayParams::getDockOverlayMinimumSize())
rect.setTop(rect.bottom()-OverlayParams::getDockOverlayMinimumSize());
break;
default:
break;
}
if (hGrp && rect.size() != rectOverlay.size()) {
Base::StateLocker lock(_saving);
hGrp->SetInt("Width", rect.width());
hGrp->SetInt("Height", rect.height());
}
rectOverlay = rect;
QPoint offset = getMainWindow()->getMdiArea()->pos();
if (getAutoHideRect(rect) || _state == State::Hint || _state == State::Hidden) {
QRect rectHint = rect;
if (_state != State::Hint && _state != State::Hidden)
startHide();
else if (count() && OverlayParams::getDockOverlayHintTabBar()) {
switch(dockArea) {
case Qt::LeftDockWidgetArea:
case Qt::RightDockWidgetArea:
if (dockArea == Qt::LeftDockWidgetArea)
rect.setWidth(tabBar()->width());
else
rect.setLeft(rect.left() + rect.width() - tabBar()->width());
rect.setHeight(std::max(rect.height(),
tabBar()->y() + tabBar()->sizeHint().height() + 5));
break;
case Qt::BottomDockWidgetArea:
case Qt::TopDockWidgetArea:
if (dockArea == Qt::TopDockWidgetArea)
rect.setHeight(tabBar()->height());
else
rect.setTop(rect.top() + rect.height() - tabBar()->height());
rect.setWidth(std::max(rect.width(),
tabBar()->x() + tabBar()->sizeHint().width() + 5));
break;
default:
break;
}
setGeometry(rect.translated(offset));
}
proxyWidget->setGeometry(rectHint.translated(offset));
if (count()) {
proxyWidget->show();
proxyWidget->raise();
} else
proxyWidget->hide();
} else {
setGeometry(rectOverlay.translated(offset));
for (int i = 0, count = splitter->count(); i < count; ++i) {
splitter->widget(i)->show();
}
if (!isVisible() && count()) {
proxyWidget->hide();
startShow();
}
}
}
void OverlayTabWidget::addWidget(QDockWidget *dock, const QString &title)
{
if (!getMainWindow() || !getMainWindow()->getMdiArea())
return;
OverlayManager::instance()->registerDockWidget(dock->objectName(), this);
OverlayManager::setFocusView();
getMainWindow()->removeDockWidget(dock);
auto titleWidget = dock->titleBarWidget();
if(titleWidget && titleWidget->objectName()==QStringLiteral("OverlayTitle")) {
// replace the title bar with an invisible widget to hide it. The
// OverlayTabWidget uses its own title bar for all docks.
auto w = new QWidget();
w->setObjectName(QStringLiteral("OverlayTitle"));
dock->setTitleBarWidget(w);
w->hide();
titleWidget->deleteLater();
}
dock->show();
splitter->addWidget(dock);
auto dummyWidget = new QWidget(this);
addTab(dummyWidget, title);
connect(dock, &QObject::destroyed, dummyWidget, &QObject::deleteLater);
dock->setFeatures(dock->features() & ~QDockWidget::DockWidgetFloatable);
if(count() == 1) {
QRect rect = dock->geometry();
QSize sizeMain = getMainWindow()->getMdiArea()->size();
switch(dockArea) {
case Qt::LeftDockWidgetArea:
case Qt::RightDockWidgetArea:
if (rect.width() > sizeMain.width()/3)
rect.setWidth(sizeMain.width()/3);
break;
case Qt::TopDockWidgetArea:
case Qt::BottomDockWidgetArea:
if (rect.height() > sizeMain.height()/3)
rect.setHeight(sizeMain.height()/3);
break;
default:
break;
}
setRect(rect);
}
saveTabs();
}
int OverlayTabWidget::dockWidgetIndex(QDockWidget *dock) const
{
return splitter->indexOf(dock);
}
void OverlayTabWidget::removeWidget(QDockWidget *dock, QDockWidget *lastDock)
{
int index = dockWidgetIndex(dock);
if(index < 0)
return;
OverlayManager::instance()->unregisterDockWidget(dock->objectName(), this);
OverlayManager::setFocusView();
dock->show();
if(lastDock)
getMainWindow()->tabifyDockWidget(lastDock, dock);
else
getMainWindow()->addDockWidget(dockArea, dock);
auto w = this->widget(index);
removeTab(index);
w->deleteLater();
if(!count())
hide();
w = dock->titleBarWidget();
if(w && w->objectName() == QStringLiteral("OverlayTitle")) {
dock->setTitleBarWidget(nullptr);
w->deleteLater();
}
OverlayManager::instance()->setupTitleBar(dock);
dock->setFeatures(dock->features() | QDockWidget::DockWidgetFloatable);
setOverlayMode(dock, OverlayOption::Disable);
saveTabs();
}
void OverlayTabWidget::resizeEvent(QResizeEvent *ev)
{
QTabWidget::resizeEvent(ev);
if (_state <= State::Normal)
timer.start(10);
}
void OverlayTabWidget::setupLayout()
{
if (_state > State::Normal)
return;
if(count() == 1)
tabSize = 0;
else {
int tsize;
if(dockArea==Qt::LeftDockWidgetArea || dockArea==Qt::RightDockWidgetArea)
tsize = tabBar()->width();
else
tsize = tabBar()->height();
tabSize = tsize;
}
int titleBarSize = widgetMinSize(this, true);
QRect rect, rectTitle;
switch(tabPosition()) {
case West:
rectTitle = QRect(tabSize, 0, this->width()-tabSize, titleBarSize);
rect = QRect(rectTitle.left(), rectTitle.bottom(),
rectTitle.width(), this->height()-rectTitle.height());
break;
case East:
rectTitle = QRect(0, 0, this->width()-tabSize, titleBarSize);
rect = QRect(rectTitle.left(), rectTitle.bottom(),
rectTitle.width(), this->height()-rectTitle.height());
break;
case North:
rectTitle = QRect(0, tabSize, titleBarSize, this->height()-tabSize);
rect = QRect(rectTitle.right(), rectTitle.top(),
this->width()-rectTitle.width(), rectTitle.height());
break;
case South:
rectTitle = QRect(0, 0, titleBarSize, this->height()-tabSize);
rect = QRect(rectTitle.right(), rectTitle.top(),
this->width()-rectTitle.width(), rectTitle.height());
break;
}
if (_animation != 0.0) {
switch(dockArea) {
case Qt::LeftDockWidgetArea:
rect.moveLeft(rect.left() - _animation * rect.width());
break;
case Qt::RightDockWidgetArea:
rect.moveLeft(rect.left() + _animation * rect.width());
break;
case Qt::TopDockWidgetArea:
rect.moveTop(rect.top() - _animation * rect.height());
break;
case Qt::BottomDockWidgetArea:
rect.moveTop(rect.top() + _animation * rect.height());
break;
default:
break;
}
}
splitter->setGeometry(rect);
titleBar->setGeometry(rectTitle);
}
void OverlayTabWidget::setCurrent(QDockWidget *widget)
{
int index = dockWidgetIndex(widget);
if(index >= 0)
setCurrentIndex(index);
}
void OverlayTabWidget::onSplitterResize(int index)
{
const auto &sizes = splitter->sizes();
if (index >= 0 && index < sizes.count()) {
if (sizes[index] == 0) {
if (currentIndex() == index) {
bool done = false;
for (int i=index+1; i<sizes.count(); ++i) {
if (sizes[i] > 0) {
setCurrentIndex(i);
done = true;
break;
}
}
if (!done) {
for (int i=index-1; i>=0 ;--i) {
if (sizes[i] > 0) {
setCurrentIndex(i);
break;
}
}
}
}
} else
setCurrentIndex(index);
}
saveTabs();
}
void OverlayTabWidget::onCurrentChanged(int index)
{
setState(State::Showing);
auto sizes = splitter->sizes();
int i=0;
int size = splitter->orientation()==Qt::Vertical ?
height()-tabBar()->height() : width()-tabBar()->width();
for(auto &s : sizes) {
if(i++ == index)
s = size;
else
s = 0;
}
splitter->setSizes(sizes);
onSplitterResize(index);
saveTabs();
}
void OverlayTabWidget::onSizeGripMove(const QPoint &p)
{
if (!getMainWindow() || !getMainWindow()->getMdiArea())
return;
QPoint pos = mapFromGlobal(p) + this->pos();
QPoint offset = getMainWindow()->getMdiArea()->pos();
QRect rect = this->rectOverlay.translated(offset);
switch(dockArea) {
case Qt::LeftDockWidgetArea:
if (pos.x() - rect.left() < OverlayParams::getDockOverlayMinimumSize())
return;
rect.setRight(pos.x());
break;
case Qt::RightDockWidgetArea:
if (rect.right() - pos.x() < OverlayParams::getDockOverlayMinimumSize())
return;
rect.setLeft(pos.x());
break;
case Qt::TopDockWidgetArea:
if (pos.y() - rect.top() < OverlayParams::getDockOverlayMinimumSize())
return;
rect.setBottom(pos.y());
break;
default:
if (rect.bottom() - pos.y() < OverlayParams::getDockOverlayMinimumSize())
return;
rect.setTop(pos.y());
break;
}
this->setRect(rect.translated(-offset));
OverlayManager::instance()->refresh();
}
QLayoutItem *OverlayTabWidget::prepareTitleWidget(QWidget *widget, const QList<QAction*> &actions)
{
bool vertical = false;
QBoxLayout *layout = nullptr;
auto tabWidget = qobject_cast<OverlayTabWidget*>(widget->parentWidget());
if(!tabWidget) {
layout = new QBoxLayout(QBoxLayout::LeftToRight, widget);
} else {
switch(tabWidget->getDockArea()) {
case Qt::LeftDockWidgetArea:
layout = new QBoxLayout(QBoxLayout::LeftToRight, widget);
break;
case Qt::RightDockWidgetArea:
layout = new QBoxLayout(QBoxLayout::RightToLeft, widget);
break;
case Qt::TopDockWidgetArea:
layout = new QBoxLayout(QBoxLayout::TopToBottom, widget);
vertical = true;
break;
case Qt::BottomDockWidgetArea:
layout = new QBoxLayout(QBoxLayout::BottomToTop, widget);
vertical = true;
break;
default:
break;
}
}
layout->addSpacing(5);
layout->setContentsMargins(1,1,1,1);
int buttonSize = widgetMinSize(widget);
auto spacer = new QSpacerItem(buttonSize,buttonSize,
vertical?QSizePolicy::Minimum:QSizePolicy::Expanding,
vertical?QSizePolicy::Expanding:QSizePolicy::Minimum);
layout->addSpacerItem(spacer);
for(auto action : actions)
layout->addWidget(OverlayTabWidget::createTitleButton(action, buttonSize));
if (tabWidget) {
auto grip = new OverlaySizeGrip(tabWidget, vertical);
QObject::connect(grip, &OverlaySizeGrip::dragMove,
tabWidget, &OverlayTabWidget::onSizeGripMove);
layout->addWidget(grip);
grip->raise();
}
return spacer;
}
bool OverlayTabWidget::isStyleSheetDark(std::string curStyleSheet)
{
if (curStyleSheet.find("dark") != std::string::npos
|| curStyleSheet.find("Dark") != std::string::npos) {
return true;
}
return false;
}
QPixmap OverlayTabWidget::rotateAutoHideIcon(QPixmap pxAutoHide, Qt::DockWidgetArea dockArea)
{
switch (dockArea) {
case Qt::LeftDockWidgetArea:
return pxAutoHide;
break;
case Qt::RightDockWidgetArea:
return pxAutoHide.transformed(QTransform().scale(-1, 1));
break;
case Qt::TopDockWidgetArea:
return pxAutoHide.transformed(QTransform().rotate(90));
break;
case Qt::BottomDockWidgetArea:
return pxAutoHide.transformed(QTransform().rotate(-90));
break;
default:
return pxAutoHide;
break;
}
}
// -----------------------------------------------------------
OverlayTitleBar::OverlayTitleBar(QWidget * parent)
:QWidget(parent)
{
setFocusPolicy(Qt::ClickFocus);
setMouseTracking(true);
setCursor(Qt::OpenHandCursor);
}
void OverlayTitleBar::setTitleItem(QLayoutItem *item)
{
titleItem = item;
}
void OverlayTitleBar::paintEvent(QPaintEvent *)
{
if (!titleItem)
return;
QDockWidget *dock = qobject_cast<QDockWidget*>(parentWidget());
int vertical = false;
int flags = Qt::AlignCenter;
if (!dock) {
OverlayTabWidget *tabWidget = qobject_cast<OverlayTabWidget*>(parentWidget());
if (tabWidget) {
switch(tabWidget->getDockArea()) {
case Qt::TopDockWidgetArea:
vertical = true;
// fallthrough
case Qt::RightDockWidgetArea:
flags = Qt::AlignRight;
break;
case Qt::BottomDockWidgetArea:
vertical = true;
// fallthrough
case Qt::LeftDockWidgetArea:
flags = Qt::AlignLeft;
break;
default:
break;
}
dock = tabWidget->dockWidget(0);
}
}
if (!dock)
return;
QPainter painter(this);
if (qobject_cast<OverlayTabWidget*>(parentWidget()))
painter.fillRect(this->rect(), painter.background());
QRect r = titleItem->geometry();
if (vertical) {
r = r.transposed();
painter.translate(r.left(), r.top() + r.width());
painter.rotate(-90);
painter.translate(-r.left(), -r.top());
}
QString title;
if (OverlayManager::instance()->isMouseTransparent()) {
if (timerId == 0)
timerId = startTimer(500);
title = blink ? tr("Mouse pass through, Esc to stop") : dock->windowTitle();
} else {
if (timerId != 0) {
killTimer(timerId);
timerId = 0;
}
title = dock->windowTitle();
}
QString text = painter.fontMetrics().elidedText(
title, Qt::ElideRight, r.width());
painter.drawText(r, flags, text);
}
void OverlayTitleBar::timerEvent(QTimerEvent *ev)
{
if (timerId == ev->timerId()) {
update();
blink = !blink;
}
}
static inline bool
isNear(const QPoint &a, const QPoint &b, int tol = 16)
{
QPoint d = a - b;
return d.x()*d.x() + d.y()*d.y() < tol;
}
void OverlayTitleBar::endDrag()
{
if (OverlayTabWidget::_Dragging == this) {
OverlayTabWidget::_Dragging = nullptr;
setCursor(Qt::OpenHandCursor);
if (OverlayTabWidget::_DragFrame)
OverlayTabWidget::_DragFrame->hide();
if (OverlayTabWidget::_DragFloating)
OverlayTabWidget::_DragFrame->hide();
}
}
void OverlayTitleBar::mouseMoveEvent(QMouseEvent *me)
{
if (ignoreMouse) {
if (!(me->buttons() & Qt::LeftButton))
ignoreMouse = false;
else {
me->ignore();
return;
}
}
if (OverlayTabWidget::_Dragging != this && mouseMovePending && (me->buttons() & Qt::LeftButton)) {
if (isNear(dragOffset, me->pos()))
return;
mouseMovePending = false;
OverlayTabWidget::_Dragging = this;
}
if (OverlayTabWidget::_Dragging != this)
return;
if (!(me->buttons() & Qt::LeftButton)) {
endDrag();
return;
}
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
QPoint point = me->globalPos();
#else
QPoint point = me->globalPosition().toPoint();
#endif
OverlayManager::instance()->dragDockWidget(point,
parentWidget(),
dragOffset,
dragSize);
}
void OverlayTitleBar::mousePressEvent(QMouseEvent *me)
{
mouseMovePending = false;
QWidget *parent = parentWidget();
if (OverlayTabWidget::_Dragging || !parent || !getMainWindow() || me->button() != Qt::LeftButton)
return;
dragSize = parent->size();
OverlayTabWidget *tabWidget = qobject_cast<OverlayTabWidget*>(parent);
if (!tabWidget) {
if(QApplication::queryKeyboardModifiers() == Qt::ShiftModifier) {
ignoreMouse = true;
me->ignore();
return;
}
}
else {
for (int s : tabWidget->getSplitter()->sizes()) {
if (!s)
continue;
if (tabWidget == OverlayTabWidget::_TopOverlay
|| tabWidget == OverlayTabWidget::_BottomOverlay) {
dragSize.setWidth(s + this->width());
dragSize.setHeight(tabWidget->height());
}
else {
dragSize.setHeight(s + this->height());
dragSize.setWidth(tabWidget->width());
}
}
}
ignoreMouse = false;
QSize mwSize = getMainWindow()->size();
dragSize.setWidth(std::max(OverlayParams::getDockOverlayMinimumSize(),
static_cast<long>(std::min(mwSize.width()/2, dragSize.width()))));
dragSize.setHeight(std::max(OverlayParams::getDockOverlayMinimumSize(),
static_cast<long>(std::min(mwSize.height()/2, dragSize.height()))));
dragOffset = me->pos();
setCursor(Qt::ClosedHandCursor);
mouseMovePending = true;
}
void OverlayTitleBar::mouseReleaseEvent(QMouseEvent *me)
{
if (ignoreMouse) {
me->ignore();
return;
}
setCursor(Qt::OpenHandCursor);
mouseMovePending = false;
if (OverlayTabWidget::_Dragging != this)
return;
if (me->button() != Qt::LeftButton)
return;
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
QPoint point = me->globalPos();
#else
QPoint point = me->globalPosition().toPoint();
#endif
OverlayTabWidget::_Dragging = nullptr;
OverlayManager::instance()->dragDockWidget(point,
parentWidget(),
dragOffset,
dragSize,
true);
if (OverlayTabWidget::_DragFrame)
OverlayTabWidget::_DragFrame->hide();
if (OverlayTabWidget::_DragFloating)
OverlayTabWidget::_DragFloating->hide();
}
void OverlayTitleBar::keyPressEvent(QKeyEvent *ke)
{
if (OverlayTabWidget::_Dragging == this && ke->key() == Qt::Key_Escape)
endDrag();
}
// -----------------------------------------------------------
OverlayDragFrame::OverlayDragFrame(QWidget * parent)
:QWidget(parent)
{
}
void OverlayDragFrame::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawRect(0, 0, this->width()-1, this->height()-1);
painter.setOpacity(0.3);
painter.setBrush(QBrush(Qt::blue));
painter.drawRect(0, 0, this->width()-1, this->height()-1);
}
QSize OverlayDragFrame::sizeHint() const
{
return size();
}
QSize OverlayDragFrame::minimumSizeHint() const
{
return minimumSize();
}
// -----------------------------------------------------------
OverlaySizeGrip::OverlaySizeGrip(QWidget * parent, bool vertical)
:QWidget(parent), vertical(vertical)
{
if (vertical) {
this->setFixedHeight(6);
this->setMinimumWidth(widgetMinSize(this,true));
this->setCursor(Qt::SizeVerCursor);
}
else {
this->setFixedWidth(6);
this->setMinimumHeight(widgetMinSize(this,true));
this->setCursor(Qt::SizeHorCursor);
}
setMouseTracking(true);
}
void OverlaySizeGrip::paintEvent(QPaintEvent*)
{
QPainter painter(this);
painter.setPen(Qt::transparent);
painter.setOpacity(0.5);
painter.setBrush(QBrush(Qt::black, Qt::Dense6Pattern));
QRect rect(this->rect());
painter.drawRect(rect);
}
void OverlaySizeGrip::mouseMoveEvent(QMouseEvent *me)
{
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
QPoint point = me->globalPos();
#else
QPoint point = me->globalPosition().toPoint();
#endif
if ((me->buttons() & Qt::LeftButton)) {
Q_EMIT dragMove(point);
}
}
void OverlaySizeGrip::mousePressEvent(QMouseEvent *)
{
}
void OverlaySizeGrip::mouseReleaseEvent(QMouseEvent *)
{
}
// -----------------------------------------------------------
OverlaySplitter::OverlaySplitter(QWidget *parent)
: QSplitter(parent)
{
}
QSplitterHandle * OverlaySplitter::createHandle()
{
auto widget = new OverlaySplitterHandle(this->orientation(), this);
widget->setObjectName(QStringLiteral("OverlaySplitHandle"));
QList<QAction*> actions;
actions.append(&widget->actFloat);
widget->setTitleItem(OverlayTabWidget::prepareTitleWidget(widget, actions));
return widget;
}
// -----------------------------------------------------------
OverlaySplitterHandle::OverlaySplitterHandle(Qt::Orientation orientation, QSplitter *parent)
: QSplitterHandle(orientation, parent)
{
setMouseTracking(true);
setFocusPolicy(Qt::ClickFocus);
retranslate();
refreshIcons();
QObject::connect(&actFloat, &QAction::triggered, this, &OverlaySplitterHandle::onAction);
timer.setSingleShot(true);
QObject::connect(&timer, &QTimer::timeout, this, &OverlaySplitterHandle::onTimer);
}
void OverlaySplitterHandle::refreshIcons()
{
actFloat.setIcon(BitmapFactory().pixmap("qss:overlay/icons/float.svg"));
}
void OverlaySplitterHandle::onTimer()
{
if (isVisible() && qApp->widgetAt(QCursor::pos()) != this)
showTitle(false);
}
void OverlaySplitterHandle::showEvent(QShowEvent *ev)
{
if (OverlayParams::getDockOverlaySplitterHandleTimeout() > 0
&& qApp->widgetAt(QCursor::pos()) != this)
timer.start(OverlayParams::getDockOverlaySplitterHandleTimeout());
QSplitterHandle::showEvent(ev);
}
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
void OverlaySplitterHandle::enterEvent(QEvent *ev)
#else
void OverlaySplitterHandle::enterEvent(QEnterEvent *ev)
#endif
{
timer.stop();
QSplitterHandle::enterEvent(ev);
}
void OverlaySplitterHandle::leaveEvent(QEvent *ev)
{
if (OverlayParams::getDockOverlaySplitterHandleTimeout() > 0)
timer.start(OverlayParams::getDockOverlaySplitterHandleTimeout());
QSplitterHandle::leaveEvent(ev);
}
QSize OverlaySplitterHandle::sizeHint() const
{
QSize size = QSplitterHandle::sizeHint();
int minSize = widgetMinSize(this,true);
if (this->orientation() == Qt::Vertical)
size.setHeight(std::max(minSize, size.height()));
else
size.setWidth(std::max(minSize, size.width()));
return size;
}
void OverlaySplitterHandle::onAction()
{
auto action = qobject_cast<QAction*>(sender());
if(action == &actFloat) {
QDockWidget *dock = dockWidget();
if (dock)
OverlayManager::instance()->floatDockWidget(dock);
}
}
QDockWidget *OverlaySplitterHandle::dockWidget()
{
QSplitter *parent = splitter();
if (!parent)
return nullptr;
if (parent->handle(this->idx) != this) {
this->idx = -1;
for (int i=0, c=parent->count(); i<c; ++i) {
if (parent->handle(i) == this) {
this->idx = i;
break;
}
}
}
return qobject_cast<QDockWidget*>(parent->widget(this->idx));
}
void OverlaySplitterHandle::retranslate()
{
actFloat.setToolTip(QObject::tr("Toggle floating window"));
}
void OverlaySplitterHandle::changeEvent(QEvent *e)
{
QSplitterHandle::changeEvent(e);
if (e->type() == QEvent::LanguageChange)
retranslate();
}
void OverlaySplitterHandle::setTitleItem(QLayoutItem *item)
{
titleItem = item;
}
void OverlaySplitterHandle::showTitle(bool enable)
{
if (_showTitle == enable)
return;
if (!enable)
unsetCursor();
else {
setCursor(this->orientation() == Qt::Horizontal
? Qt::SizeHorCursor : Qt::SizeVerCursor);
if (OverlayParams::getDockOverlaySplitterHandleTimeout() > 0
&& qApp->widgetAt(QCursor::pos()) != this)
timer.start(OverlayParams::getDockOverlaySplitterHandleTimeout());
}
_showTitle = enable;
for (auto child : findChildren<QWidget*>(QString(), Qt::FindDirectChildrenOnly))
child->setVisible(enable);
update();
}
void OverlaySplitterHandle::paintEvent(QPaintEvent *e)
{
if (!_showTitle)
return;
if (!titleItem) {
QSplitterHandle::paintEvent(e);
return;
}
int flags = Qt::AlignCenter;
auto tabWidget = qobject_cast<OverlayTabWidget*>(
splitter() ? splitter()->parentWidget() : nullptr);
if (tabWidget) {
switch(tabWidget->getDockArea()) {
case Qt::TopDockWidgetArea:
case Qt::RightDockWidgetArea:
flags = Qt::AlignRight;
break;
case Qt::BottomDockWidgetArea:
case Qt::LeftDockWidgetArea:
flags = Qt::AlignLeft;
break;
default:
break;
}
}
QDockWidget *dock = dockWidget();
if (!dock) {
QSplitterHandle::paintEvent(e);
return;
}
QPainter painter(this);
painter.fillRect(this->rect(), painter.background());
QRect r = titleItem->geometry();
if (this->orientation() != Qt::Vertical) {
r = r.transposed();
painter.translate(r.left(), r.top() + r.width());
painter.rotate(-90);
painter.translate(-r.left(), -r.top());
}
QString text = painter.fontMetrics().elidedText(
dock->windowTitle(), Qt::ElideRight, r.width());
painter.drawText(r, flags, text);
}
void OverlaySplitterHandle::endDrag()
{
auto tabWidget = qobject_cast<OverlayTabWidget*>(splitter()->parentWidget());
if (tabWidget) {
dockWidget();
tabWidget->onSplitterResize(this->idx);
}
OverlayTabWidget::_Dragging = nullptr;
setCursor(this->orientation() == Qt::Horizontal
? Qt::SizeHorCursor : Qt::SizeVerCursor);
if (OverlayTabWidget::_DragFrame)
OverlayTabWidget::_DragFrame->hide();
if (OverlayTabWidget::_DragFloating)
OverlayTabWidget::_DragFloating->hide();
}
void OverlaySplitterHandle::keyPressEvent(QKeyEvent *ke)
{
if (OverlayTabWidget::_Dragging == this && ke->key() == Qt::Key_Escape)
endDrag();
}
void OverlaySplitterHandle::mouseMoveEvent(QMouseEvent *me)
{
if (OverlayTabWidget::_Dragging != this)
return;
if (!(me->buttons() & Qt::LeftButton)) {
endDrag();
return;
}
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
QPoint point = me->globalPos();
#else
QPoint point = me->globalPosition().toPoint();
#endif
if (dragging == 1) {
OverlayTabWidget *overlay = qobject_cast<OverlayTabWidget*>(
splitter()->parentWidget());
QPoint pos = me->pos();
if (overlay) {
switch(overlay->getDockArea()) {
case Qt::LeftDockWidgetArea:
case Qt::RightDockWidgetArea:
if (pos.x() < 0 || pos.x() > overlay->width())
dragging = 2;
break;
case Qt::TopDockWidgetArea:
case Qt::BottomDockWidgetArea:
if (pos.y() < 0 || pos.y() > overlay->height())
dragging = 2;
break;
default:
break;
}
}
if (dragging == 1) {
QPoint offset = parentWidget()->mapFromGlobal(point) - dragOffset;
moveSplitter(this->orientation() == Qt::Horizontal ? offset.x() : offset.y());
return;
}
setCursor(Qt::ClosedHandCursor);
}
OverlayManager::instance()->dragDockWidget(point,
dockWidget(),
dragOffset,
dragSize);
}
void OverlaySplitterHandle::mousePressEvent(QMouseEvent *me)
{
if (OverlayTabWidget::_Dragging || !getMainWindow() || me->button() != Qt::LeftButton)
return;
OverlayTabWidget::_Dragging = this;
dragging = 1;
dragOffset = me->pos();
auto dock = dockWidget();
if (dock) {
dragSize = dock->size();
dock->show();
} else
dragSize = QSize();
QSize mwSize = getMainWindow()->size();
dragSize.setWidth(std::max(OverlayParams::getDockOverlayMinimumSize(),
static_cast<long>(std::min(mwSize.width()/2, dragSize.width()))));
dragSize.setHeight(std::max(OverlayParams::getDockOverlayMinimumSize(),
static_cast<long>(std::min(mwSize.height()/2, dragSize.height()))));
}
void OverlaySplitterHandle::mouseReleaseEvent(QMouseEvent *me)
{
if (OverlayTabWidget::_Dragging != this || me->button() != Qt::LeftButton)
return;
if (dragging == 1) {
endDrag();
return;
}
endDrag();
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
QPoint point = me->globalPos();
#else
QPoint point = me->globalPosition().toPoint();
#endif
OverlayManager::instance()->dragDockWidget(point,
dockWidget(),
dragOffset,
dragSize,
true);
// Warning! the handle itself maybe deleted after return from
// dragDockWidget().
}
// -----------------------------------------------------------
OverlayGraphicsEffect::OverlayGraphicsEffect(QObject *parent) :
QGraphicsEffect(parent),
_enabled(false),
_size(1,1),
_blurRadius(2.0f),
_color(0, 0, 0, 80)
{
}
QT_BEGIN_NAMESPACE
extern Q_WIDGETS_EXPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0 );
QT_END_NAMESPACE
void OverlayGraphicsEffect::draw(QPainter* painter)
{
// if nothing to show outside the item, just draw source
if (!_enabled || _blurRadius + _size.height() <= 0 || _blurRadius + _size.width() <= 0) {
drawSource(painter);
return;
}
PixmapPadMode mode = QGraphicsEffect::PadToEffectiveBoundingRect;
QPoint offset;
QPixmap px = sourcePixmap(Qt::DeviceCoordinates, &offset, mode);
// return if no source
if (px.isNull())
return;
QTransform restoreTransform = painter->worldTransform();
painter->setWorldTransform(QTransform());
// Calculate size for the background image
QImage tmp(px.size(), QImage::Format_ARGB32_Premultiplied);
tmp.setDevicePixelRatio(px.devicePixelRatioF());
tmp.fill(0);
QPainter tmpPainter(&tmp);
QPainterPath clip;
tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
if(_size.width() == 0 && _size.height() == 0)
tmpPainter.drawPixmap(QPoint(0, 0), px);
else {
// exclude splitter handles
auto splitter = qobject_cast<QSplitter*>(parent());
if (splitter) {
int i = -1;
for (int size : splitter->sizes()) {
++i;
if (!size)
continue;
QWidget *w = splitter->widget(i);
if (w->findChild<TaskView::TaskView*>())
continue;
QRect rect = w->geometry();
if (splitter->orientation() == Qt::Vertical)
clip.addRect(rect.x(), rect.y()+4,
rect.width(), rect.height()-4);
else
clip.addRect(rect.x()+4, rect.y(),
rect.width()-4, rect.height());
}
if (clip.isEmpty()) {
drawSource(painter);
return;
}
tmpPainter.setClipPath(clip);
}
for (int x=-_size.width();x<=_size.width();++x) {
for (int y=-_size.height();y<=_size.height();++y) {
if (x || y) {
tmpPainter.drawPixmap(QPoint(x, y), px);
tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
}
}
}
}
tmpPainter.end();
// blur the alpha channel
QImage blurred(tmp.size(), QImage::Format_ARGB32_Premultiplied);
blurred.setDevicePixelRatio(px.devicePixelRatioF());
blurred.fill(0);
QPainter blurPainter(&blurred);
qt_blurImage(&blurPainter, tmp, blurRadius(), false, true);
blurPainter.end();
tmp = blurred;
// blacken the image...
tmpPainter.begin(&tmp);
tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
tmpPainter.fillRect(tmp.rect(), color());
tmpPainter.end();
// draw the blurred shadow...
painter->drawImage(QPointF(offset.x()+_offset.x(), offset.y()+_offset.y()), tmp);
// draw the actual pixmap...
painter->drawPixmap(offset, px, QRectF());
// restore world transform
painter->setWorldTransform(restoreTransform);
}
QRectF OverlayGraphicsEffect::boundingRectFor(const QRectF& rect) const
{
if (!_enabled)
return rect;
return rect.united(rect.adjusted(-_blurRadius - _size.width() + _offset.x(),
-_blurRadius - _size.height()+ _offset.y(),
_blurRadius + _size.width() + _offset.x(),
_blurRadius + _size.height() + _offset.y()));
}
#include "moc_OverlayWidgets.cpp"