Files
create/src/Gui/OverlayWidgets.cpp

2702 lines
89 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 "PreCompiled.h"
#ifndef _PreComp_
# 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>
#endif
#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();
}
if(owner->getState() != OverlayTabWidget::State::Hidden
&& hit == HitTest::HitOuter
&& OverlayParams::getDockOverlayActivateOnHover()) {
if (owner->isVisible() && owner->tabBar()->isVisible()) {
QSize size = owner->tabBar()->size();
QPoint pt = owner->tabBar()->mapToGlobal(
QPoint(size.width(), size.height()));
QPoint pos = QCursor::pos();
switch(this->dockArea) {
case Qt::LeftDockWidgetArea:
case Qt::RightDockWidgetArea:
if (pos.y() < pt.y())
return HitTest::HitNone;
break;
case Qt::TopDockWidgetArea:
case Qt::BottomDockWidgetArea:
if (pos.x() < pt.x())
return HitTest::HitNone;
break;
default:
break;
}
}
owner->setState(OverlayTabWidget::State::Showing);
}
} else if (!drawLine) {
timer.stop();
} else if (delay) {
if (!timer.isActive())
timer.start(OverlayParams::getDockOverlayHintDelay());
} else {
timer.stop();
owner->setState(OverlayTabWidget::State::Normal);
drawLine = false;
ToolTip::hideText();
update();
}
return hit;
}
void OverlayProxyWidget::onTimer()
{
hitTest(QCursor::pos(), false);
}
#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("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 QT_VERSION>QT_VERSION_CHECK(5,12,2) && QT_VERSION < QT_VERSION_CHECK(5,12,6)
// Work around Qt bug https://bugreports.qt.io/browse/QTBUG-77006
widget->setStyleSheet(OverlayManager::instance()->getStyleSheet());
#endif
if (qobject_cast<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;
#if 0
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
static int count;
getMainWindow()->showMessage(
QStringLiteral("dock overlay redraw %1").arg(count++));
}
#endif
QTransform restoreTransform = painter->worldTransform();
painter->setWorldTransform(QTransform());
// Calculate size for the background image
QImage tmp(px.size(), QImage::Format_ARGB32_Premultiplied);
tmp.setDevicePixelRatio(px.devicePixelRatioF());
tmp.fill(0);
QPainter tmpPainter(&tmp);
QPainterPath clip;
tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
if(_size.width() == 0 && _size.height() == 0)
tmpPainter.drawPixmap(QPoint(0, 0), px);
else {
// exclude splitter handles
auto splitter = qobject_cast<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());
#if 0
QWidget *focus = qApp->focusWidget();
if (focus) {
QWidget *widget = qobject_cast<QWidget*>(this->parent());
if (auto *edit = qobject_cast<QPlainTextEdit*>(focus)) {
if (!edit->isReadOnly() && edit->isEnabled()) {
for(auto w=edit->parentWidget(); w; w=w->parentWidget()) {
if (w == widget) {
QRect r = edit->cursorRect();
QRect rect(edit->viewport()->mapTo(widget, r.topLeft()),
edit->viewport()->mapTo(widget, r.bottomRight()));
// painter->fillRect(rect, edit->textColor());
// painter->fillRect(rect, edit->currentCharFormat().foreground());
painter->fillRect(rect.translated(offset), Qt::white);
}
}
}
}
}
#endif
// restore world transform
painter->setWorldTransform(restoreTransform);
}
QRectF OverlayGraphicsEffect::boundingRectFor(const QRectF& rect) const
{
if (!_enabled)
return rect;
return rect.united(rect.adjusted(-_blurRadius - _size.width() + _offset.x(),
-_blurRadius - _size.height()+ _offset.y(),
_blurRadius + _size.width() + _offset.x(),
_blurRadius + _size.height() + _offset.y()));
}
#include "moc_OverlayWidgets.cpp"