/**************************************************************************** * Copyright (c) 2020 Zheng Lei (realthunder) * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ****************************************************************************/ # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include #include #include #include #include #include "OverlayWidgets.h" #include #include #include #include "Application.h" #include "BitmapFactory.h" #include "Clipping.h" #include "ComboView.h" #include "Command.h" #include "Control.h" #include "MainWindow.h" #include "MDIView.h" #include "NaviCube.h" #include "OverlayManager.h" #include "OverlayParams.h" #include "TaskView/TaskView.h" #include "Tree.h" #include "TreeParams.h" #include "propertyeditor/PropertyEditor.h" FC_LOG_LEVEL_INIT("Dock", true, true); using namespace Gui; OverlayDragFrame *OverlayTabWidget::_DragFrame; QDockWidget *OverlayTabWidget::_DragFloating; QWidget *OverlayTabWidget::_Dragging; OverlayTabWidget *OverlayTabWidget::_LeftOverlay; OverlayTabWidget *OverlayTabWidget::_RightOverlay; OverlayTabWidget *OverlayTabWidget::_TopOverlay; OverlayTabWidget *OverlayTabWidget::_BottomOverlay; static inline int widgetMinSize(const QWidget *widget, bool margin=false) { return widget->fontMetrics().ascent() + widget->fontMetrics().descent() + (margin?4:0); } // ----------------------------------------------------------- OverlayProxyWidget::OverlayProxyWidget(OverlayTabWidget *tabOverlay) :QWidget(tabOverlay->parentWidget()), owner(tabOverlay), _hintColor(QColor(50,50,50,150)) { dockArea = owner->getDockArea(); timer.setSingleShot(true); QObject::connect(&timer, &QTimer::timeout, this, &OverlayProxyWidget::onTimer); setAttribute(Qt::WA_TransparentForMouseEvents, true); } bool OverlayProxyWidget::isActivated() const { return drawLine && isVisible(); } OverlayProxyWidget::HitTest OverlayProxyWidget::hitTest(const QPoint &globalPt, bool delay) { if (!isVisible() || !owner->count()) return HitTest::HitNone; auto pt = mapFromGlobal(globalPt); QTabBar *tabbar = owner->tabBar(); if (tabbar->isVisible() && tabbar->tabAt(pt)>=0) { ToolTip::showText(globalPt, QObject::tr("Press Esc to hide hint"), this); return HitTest::HitOuter; } HitTest hit = HitTest::HitNone; QRect rect = this->getRect(); QSize s = this->size(); int hintSize = OverlayParams::getDockOverlayHintTriggerSize(); // if (owner->getState() == OverlayTabWidget::State::HintHidden) // hintSize *= 2; switch(dockArea) { case Qt::LeftDockWidgetArea: if (pt.y() >= 0 && pt.y() <= s.height() && pt.x() > 0 && pt.x() < hintSize) hit = HitTest::HitOuter; break; case Qt::RightDockWidgetArea: if (pt.y() >= 0 && pt.y() <= s.height() && pt.x() < s.width() && pt.x() > -hintSize) hit = HitTest::HitOuter; break; case Qt::TopDockWidgetArea: if (pt.x() >= 0 && pt.x() <= s.width() && pt.y() > 0 && pt.y() < hintSize) hit = HitTest::HitOuter; break; case Qt::BottomDockWidgetArea: if (pt.x() >= 0 && pt.x() <= s.width() && pt.y() < s.height() && pt.y() > -hintSize) hit = HitTest::HitOuter; break; } if (rect.contains(pt)) { hit = HitTest::HitInner; ToolTip::showText(globalPt, QObject::tr("Press Esc to hide hint"), this); } else if (drawLine) ToolTip::hideText(); if (owner->getState() == OverlayTabWidget::State::HintHidden) { if (hit == HitTest::HitNone) owner->setState(OverlayTabWidget::State::Normal); else { hit = HitTest::HitNone; ToolTip::hideText(); } } if (hit != HitTest::HitNone) { if (drawLine) timer.stop(); else if (delay) { if (!timer.isActive()) timer.start(OverlayParams::getDockOverlayHintDelay()); return hit; } else { timer.stop(); owner->setState(OverlayTabWidget::State::Hint); drawLine = true; update(); } 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= size.width() || pos.y()+j >= size.height()) continue; res = qAlpha(_image.pixel(pos*_imageScale + QPoint(i,j))); if (res) return res; } } return 0; } void OverlayTabWidget::paintEvent(QPaintEvent *ev) { Base::StateLocker guard(repainting); repaintTimer.stop(); if (!_image.isNull()) _image = QImage(); QTabWidget::paintEvent(ev); } void OverlayTabWidget::onRepaint() { Base::StateLocker guard(repainting); repaintTimer.stop(); if (!_image.isNull()) _image = QImage(); splitter->repaint(); } void OverlayTabWidget::scheduleRepaint() { if(!repainting && isVisible() && _graphicsEffect) { repaintTimer.start(100); } } QColor OverlayTabWidget::effectColor() const { return _graphicsEffect->color(); } void OverlayTabWidget::setEffectColor(const QColor &color) { _graphicsEffect->setColor(color); _graphicsEffectTab->setColor(color); } int OverlayTabWidget::effectWidth() const { return _graphicsEffect->size().width(); } void OverlayTabWidget::setEffectWidth(int s) { auto size = _graphicsEffect->size(); size.setWidth(s); _graphicsEffect->setSize(size); _graphicsEffectTab->setSize(size); } int OverlayTabWidget::effectHeight() const { return _graphicsEffect->size().height(); } void OverlayTabWidget::setEffectHeight(int s) { auto size = _graphicsEffect->size(); size.setHeight(s); _graphicsEffect->setSize(size); _graphicsEffectTab->setSize(size); } qreal OverlayTabWidget::effectOffsetX() const { return _graphicsEffect->offset().x(); } void OverlayTabWidget::setEffectOffsetX(qreal d) { auto offset = _graphicsEffect->offset(); offset.setX(d); _graphicsEffect->setOffset(offset); _graphicsEffectTab->setOffset(offset); } qreal OverlayTabWidget::effectOffsetY() const { return _graphicsEffect->offset().y(); } void OverlayTabWidget::setEffectOffsetY(qreal d) { auto offset = _graphicsEffect->offset(); offset.setY(d); _graphicsEffect->setOffset(offset); _graphicsEffectTab->setOffset(offset); } qreal OverlayTabWidget::effectBlurRadius() const { return _graphicsEffect->blurRadius(); } void OverlayTabWidget::setEffectBlurRadius(qreal r) { _graphicsEffect->setBlurRadius(r); _graphicsEffectTab->setBlurRadius(r); } bool OverlayTabWidget::effectEnabled() const { return _effectEnabled; } void OverlayTabWidget::setEffectEnabled(bool enable) { _effectEnabled = enable; } bool OverlayTabWidget::eventFilter(QObject *o, QEvent *ev) { if(ev->type() == QEvent::Resize && o == tabBar()) { if (_state <= State::Normal) timer.start(10); } return QTabWidget::eventFilter(o, ev); } void OverlayTabWidget::restore(ParameterGrp::handle handle) { if (!handle) { hGrp = handle; return; } if (!parentWidget()) return; std::string widgets = handle->GetASCII("Widgets",""); for(auto &name : QString::fromUtf8(widgets.c_str()).split(QLatin1Char(','))) { if(name.isEmpty()) continue; OverlayManager::instance()->registerDockWidget(name, this); auto dock = getMainWindow()->findChild(name); if(dock) addWidget(dock, dock->windowTitle()); } int width = handle->GetInt("Width", 0); int height = handle->GetInt("Height", 0); int offset1 = handle->GetInt("Offset1", 0); int offset2 = handle->GetInt("Offset3", 0); setOffset(QSize(offset1,offset2)); setSizeDelta(handle->GetInt("Offset2", 0)); if(width && height) { QRect rect(0, 0, width, height); switch(dockArea) { case Qt::RightDockWidgetArea: rect.moveRight(parentWidget()->size().width()); break; case Qt::BottomDockWidgetArea: rect.moveBottom(parentWidget()->size().height()); break; default: break; } setRect(rect); } if (handle->GetBool("AutoHide", false)) setAutoMode(AutoMode::AutoHide); else if (handle->GetBool("EditHide", false)) setAutoMode(AutoMode::EditHide); else if (handle->GetBool("EditShow", false)) setAutoMode(AutoMode::EditShow); else if (handle->GetBool("TaskShow", false)) setAutoMode(AutoMode::TaskShow); else setAutoMode(AutoMode::NoAutoMode); setTransparent(handle->GetBool("Transparent", false)); _sizemap.clear(); std::string savedSizes = handle->GetASCII("Sizes",""); QList sizes; int idx = 0; for(auto &size : QString::fromUtf8(savedSizes.c_str()).split(QLatin1Char(','))) { sizes.append(size.toInt()); _sizemap[dockWidget(idx++)] = sizes.back(); } FC_LOG("restore " << objectName().toUtf8().constData() << " " << savedSizes); getSplitter()->setSizes(sizes); hGrp = handle; } void OverlayTabWidget::saveTabs() { if(!hGrp) return; std::ostringstream os, os2; _sizemap.clear(); auto sizes = splitter->sizes(); bool first = true; for(int i=0,c=splitter->count(); iobjectName().size()) { os << dock->objectName().toUtf8().constData() << ","; if (first) first = false; else os2 << ","; os2 << sizes[i]; } _sizemap[dock] = sizes[i]; } Base::StateLocker lock(_saving); hGrp->SetASCII("Widgets", os.str().c_str()); hGrp->SetASCII("Sizes", os2.str().c_str()); FC_LOG("save " << objectName().toUtf8().constData() << " " << os2.str()); } void OverlayTabWidget::onTabMoved(int from, int to) { QWidget *w = splitter->widget(from); splitter->insertWidget(to,w); saveTabs(); } void OverlayTabWidget::setTitleBar(QWidget *w) { titleBar = w; } void OverlayTabWidget::changeEvent(QEvent *e) { QTabWidget::changeEvent(e); if (e->type() == QEvent::LanguageChange) retranslate(); } void OverlayTabWidget::retranslate() { actTransparent.setToolTip(tr("Toggle transparent mode")); actNoAutoMode.setText(tr("None")); actNoAutoMode.setToolTip(tr("Turn off auto hide/show")); actAutoHide.setText(tr("Auto hide")); actAutoHide.setToolTip(tr("Auto hide docked widgets on leave")); actEditHide.setText(tr("Hide on edit")); actEditHide.setToolTip(tr("Auto hide docked widgets on editing")); actEditShow.setText(tr("Show on edit")); actEditShow.setToolTip(tr("Auto show docked widgets on editing")); actTaskShow.setText(tr("Auto task")); actTaskShow.setToolTip(tr("Auto show task view for any current task, and hide the view when there is no task.")); actOverlay.setToolTip(tr("Toggle overlay")); syncAutoMode(); } void OverlayTabWidget::syncAutoMode() { 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(widget)) { auto parent = widget->parentWidget(); if (parent) { parent = parent->parentWidget(); if (qobject_cast(parent)) { auto scrollArea = static_cast(parent); if (scrollArea->verticalScrollBar() == widget) { if (!OverlayParams::getDockOverlayHidePropertyViewScrollBar() || option == OverlayOption::Disable) widget->setStyleSheet(QString()); else { static QString _style = QStringLiteral("*{width:0}"); widget->setStyleSheet(_style); } } } auto treeView = qobject_cast(parent); if (treeView) { auto scrollArea = static_cast(parent); if (scrollArea->verticalScrollBar() == widget) { if (!TreeParams::getHideScrollBar() || option == OverlayOption::Disable) widget->setStyleSheet(QString()); else { static QString _style = QStringLiteral("*{width:0}"); widget->setStyleSheet(_style); } } } if (treeView) { auto header = treeView->header(); if (!TreeParams::getHideHeaderView() || option==OverlayOption::Disable) header->setStyleSheet(QString()); else { static QString _style = QStringLiteral( "QHeaderView:section {" "height: 0px;" "background-color: transparent;" "padding: 0px;" "border: transparent;}"); header->setStyleSheet(_style); } } } } auto tabbar = qobject_cast(widget); if(tabbar) { if(!tabbar->autoHide() || tabbar->count()>1) { if(!OverlayManager::instance()->getHideTab()) tabbar->setVisible(true); else tabbar->setVisible(option == OverlayOption::Disable || (option == OverlayOption::ShowTab && tabbar->count()>1)); return; } } if (!qobject_cast(widget) || !qobject_cast(widget->parentWidget())) { if(option != OverlayOption::Disable) { widget->setWindowFlags(widget->windowFlags() | Qt::FramelessWindowHint); } else { widget->setWindowFlags(widget->windowFlags() & ~Qt::FramelessWindowHint); } widget->setAttribute(Qt::WA_NoSystemBackground, option != OverlayOption::Disable); widget->setAttribute(Qt::WA_TranslucentBackground, option != OverlayOption::Disable); } } void OverlayTabWidget::setOverlayMode(QWidget *widget, OverlayOption option) { if(!widget || (qobject_cast(widget) && !qobject_cast(widget)) || qobject_cast(widget)) return; if(widget != tabBar()) { if(OverlayParams::getDockOverlayAutoMouseThrough() && option == OverlayOption::ShowTab) { widget->setMouseTracking(true); } } _setOverlayMode(widget, option); if(qobject_cast(widget)) { // do not set child QAbstractItemView of QComboBox, otherwise the drop down box // won't be shown return; } for(auto child : widget->children()) setOverlayMode(qobject_cast(child), option); } void OverlayTabWidget::setTransparent(bool enable) { if(actTransparent.isChecked() == enable) return; if(hGrp) { Base::StateLocker lock(_saving); hGrp->SetBool("Transparent", enable); } actTransparent.setChecked(enable); OverlayManager::instance()->refresh(this); } bool OverlayTabWidget::isTransparent() const { if (!actTransparent.isChecked()) return false; if(OverlayParams::getDockOverlayAutoView()) { auto view = getMainWindow()->activeWindow(); if (!view) return false; if(!view->onHasMsg("CanPan") && view->parentWidget() && view->parentWidget()->isMaximized()) return false; } return true; } bool OverlayTabWidget::isOverlaid(QueryOption option) const { if (option != QueryOption::QueryOverlay && currentTransparent != isTransparent()) return option == QueryOption::TransparencyChanged; return overlaid; } void OverlayTabWidget::setAutoMode(AutoMode mode) { if (autoMode == mode) return; autoMode = mode; if (hGrp) { bool autohide = false, editshow = false, edithide = false, taskshow = false; switch (mode) { case AutoMode::AutoHide: autohide = true; break; case AutoMode::EditShow: editshow = true; break; case AutoMode::EditHide: edithide = true; break; case AutoMode::TaskShow: taskshow = true; break; default: break; } Base::StateLocker lock(_saving); hGrp->SetBool("AutoHide", autohide); hGrp->SetBool("EditShow", editshow); hGrp->SetBool("EditHide", edithide); hGrp->SetBool("TaskShow", taskshow); } syncAutoMode(); OverlayManager::instance()->refresh(this); } QDockWidget *OverlayTabWidget::currentDockWidget() const { int index = -1; for(int size : splitter->sizes()) { ++index; if(size>0) return dockWidget(index); } return dockWidget(currentIndex()); } QDockWidget *OverlayTabWidget::dockWidget(int index) const { if(index < 0 || index >= splitter->count()) return nullptr; return qobject_cast(splitter->widget(index)); } void OverlayTabWidget::updateSplitterHandles() { if (overlaid || _state > State::Normal) return; for (int i=0, c=splitter->count(); i(splitter->handle(i)); if (handle) handle->showTitle(true); } } bool OverlayTabWidget::onEscape() { if (getState() == OverlayTabWidget::State::Hint || getState() == OverlayTabWidget::State::Hidden) { setState(OverlayTabWidget::State::HintHidden); return true; } if (!isVisible()) return false; if (titleBar->isVisible() && titleBar->underMouse()) { titleBar->hide(); return true; } for (int i=0, c=splitter->count(); i(splitter->handle(i)); if (handle->isVisible() && handle->underMouse()) { handle->showTitle(false); return true; } } return false; } void OverlayTabWidget::setOverlayMode(bool enable) { overlaid = enable; if(!isVisible() || !count()) return; touched = false; if (_state <= State::Normal) { titleBar->setVisible(!enable || OverlayManager::instance()->isMouseTransparent()); for (int i=0, c=splitter->count(); i(splitter->handle(i)); if (handle) handle->showTitle(!enable); } } QString stylesheet; stylesheet = OverlayManager::instance()->getStyleSheet(); currentTransparent = isTransparent(); OverlayOption option; if(!enable && isTransparent()) { option = OverlayOption::ShowTab; } else if (enable && !isTransparent() && (autoMode == AutoMode::EditShow || autoMode == AutoMode::AutoHide)) { option = OverlayOption::Disable; } else { option = enable?OverlayOption::Enable:OverlayOption::Disable; } 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 0) { setCurrentIndex(i); done = true; break; } } if (!done) { for (int i=index-1; i>=0 ;--i) { if (sizes[i] > 0) { setCurrentIndex(i); break; } } } } } else setCurrentIndex(index); } saveTabs(); } void OverlayTabWidget::onCurrentChanged(int index) { setState(State::Showing); auto sizes = splitter->sizes(); int i=0; int size = splitter->orientation()==Qt::Vertical ? height()-tabBar()->height() : width()-tabBar()->width(); for(auto &s : sizes) { if(i++ == index) s = size; else s = 0; } splitter->setSizes(sizes); onSplitterResize(index); saveTabs(); } void OverlayTabWidget::onSizeGripMove(const QPoint &p) { if (!getMainWindow() || !getMainWindow()->getMdiArea()) return; QPoint pos = mapFromGlobal(p) + this->pos(); QPoint offset = getMainWindow()->getMdiArea()->pos(); QRect rect = this->rectOverlay.translated(offset); switch(dockArea) { case Qt::LeftDockWidgetArea: if (pos.x() - rect.left() < OverlayParams::getDockOverlayMinimumSize()) return; rect.setRight(pos.x()); break; case Qt::RightDockWidgetArea: if (rect.right() - pos.x() < OverlayParams::getDockOverlayMinimumSize()) return; rect.setLeft(pos.x()); break; case Qt::TopDockWidgetArea: if (pos.y() - rect.top() < OverlayParams::getDockOverlayMinimumSize()) return; rect.setBottom(pos.y()); break; default: if (rect.bottom() - pos.y() < OverlayParams::getDockOverlayMinimumSize()) return; rect.setTop(pos.y()); break; } this->setRect(rect.translated(-offset)); OverlayManager::instance()->refresh(); } QLayoutItem *OverlayTabWidget::prepareTitleWidget(QWidget *widget, const QList &actions) { bool vertical = false; QBoxLayout *layout = nullptr; auto tabWidget = qobject_cast(widget->parentWidget()); if(!tabWidget) { layout = new QBoxLayout(QBoxLayout::LeftToRight, widget); } else { switch(tabWidget->getDockArea()) { case Qt::LeftDockWidgetArea: layout = new QBoxLayout(QBoxLayout::LeftToRight, widget); break; case Qt::RightDockWidgetArea: layout = new QBoxLayout(QBoxLayout::RightToLeft, widget); break; case Qt::TopDockWidgetArea: layout = new QBoxLayout(QBoxLayout::TopToBottom, widget); vertical = true; break; case Qt::BottomDockWidgetArea: layout = new QBoxLayout(QBoxLayout::BottomToTop, widget); vertical = true; break; default: break; } } layout->addSpacing(5); layout->setContentsMargins(1,1,1,1); int buttonSize = widgetMinSize(widget); auto spacer = new QSpacerItem(buttonSize,buttonSize, vertical?QSizePolicy::Minimum:QSizePolicy::Expanding, vertical?QSizePolicy::Expanding:QSizePolicy::Minimum); layout->addSpacerItem(spacer); for(auto action : actions) layout->addWidget(OverlayTabWidget::createTitleButton(action, buttonSize)); if (tabWidget) { auto grip = new OverlaySizeGrip(tabWidget, vertical); QObject::connect(grip, &OverlaySizeGrip::dragMove, tabWidget, &OverlayTabWidget::onSizeGripMove); layout->addWidget(grip); grip->raise(); } return spacer; } 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(parentWidget()); int vertical = false; int flags = Qt::AlignCenter; if (!dock) { OverlayTabWidget *tabWidget = qobject_cast(parentWidget()); if (tabWidget) { switch(tabWidget->getDockArea()) { case Qt::TopDockWidgetArea: vertical = true; // fallthrough case Qt::RightDockWidgetArea: flags = Qt::AlignRight; break; case Qt::BottomDockWidgetArea: vertical = true; // fallthrough case Qt::LeftDockWidgetArea: flags = Qt::AlignLeft; break; default: break; } dock = tabWidget->dockWidget(0); } } if (!dock) return; QPainter painter(this); if (qobject_cast(parentWidget())) painter.fillRect(this->rect(), painter.background()); QRect r = titleItem->geometry(); if (vertical) { r = r.transposed(); painter.translate(r.left(), r.top() + r.width()); painter.rotate(-90); painter.translate(-r.left(), -r.top()); } QString title; if (OverlayManager::instance()->isMouseTransparent()) { if (timerId == 0) timerId = startTimer(500); title = blink ? tr("Mouse pass through, Esc to stop") : dock->windowTitle(); } else { if (timerId != 0) { killTimer(timerId); timerId = 0; } title = dock->windowTitle(); } QString text = painter.fontMetrics().elidedText( title, Qt::ElideRight, r.width()); painter.drawText(r, flags, text); } void OverlayTitleBar::timerEvent(QTimerEvent *ev) { if (timerId == ev->timerId()) { update(); blink = !blink; } } static inline bool isNear(const QPoint &a, const QPoint &b, int tol = 16) { QPoint d = a - b; return d.x()*d.x() + d.y()*d.y() < tol; } void OverlayTitleBar::endDrag() { if (OverlayTabWidget::_Dragging == this) { OverlayTabWidget::_Dragging = nullptr; setCursor(Qt::OpenHandCursor); if (OverlayTabWidget::_DragFrame) OverlayTabWidget::_DragFrame->hide(); if (OverlayTabWidget::_DragFloating) OverlayTabWidget::_DragFrame->hide(); } } void OverlayTitleBar::mouseMoveEvent(QMouseEvent *me) { if (ignoreMouse) { if (!(me->buttons() & Qt::LeftButton)) ignoreMouse = false; else { me->ignore(); return; } } if (OverlayTabWidget::_Dragging != this && mouseMovePending && (me->buttons() & Qt::LeftButton)) { if (isNear(dragOffset, me->pos())) return; mouseMovePending = false; OverlayTabWidget::_Dragging = this; } if (OverlayTabWidget::_Dragging != this) return; if (!(me->buttons() & Qt::LeftButton)) { endDrag(); return; } #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(parent); if (!tabWidget) { if(QApplication::queryKeyboardModifiers() == Qt::ShiftModifier) { ignoreMouse = true; me->ignore(); return; } } else { for (int s : tabWidget->getSplitter()->sizes()) { if (!s) continue; if (tabWidget == OverlayTabWidget::_TopOverlay || tabWidget == OverlayTabWidget::_BottomOverlay) { dragSize.setWidth(s + this->width()); dragSize.setHeight(tabWidget->height()); } else { dragSize.setHeight(s + this->height()); dragSize.setWidth(tabWidget->width()); } } } ignoreMouse = false; QSize mwSize = getMainWindow()->size(); dragSize.setWidth(std::max(OverlayParams::getDockOverlayMinimumSize(), static_cast(std::min(mwSize.width()/2, dragSize.width())))); dragSize.setHeight(std::max(OverlayParams::getDockOverlayMinimumSize(), static_cast(std::min(mwSize.height()/2, dragSize.height())))); dragOffset = me->pos(); setCursor(Qt::ClosedHandCursor); mouseMovePending = true; } void OverlayTitleBar::mouseReleaseEvent(QMouseEvent *me) { if (ignoreMouse) { me->ignore(); return; } setCursor(Qt::OpenHandCursor); mouseMovePending = false; if (OverlayTabWidget::_Dragging != this) return; if (me->button() != Qt::LeftButton) return; #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 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(sender()); if(action == &actFloat) { QDockWidget *dock = dockWidget(); if (dock) OverlayManager::instance()->floatDockWidget(dock); } } QDockWidget *OverlaySplitterHandle::dockWidget() { QSplitter *parent = splitter(); if (!parent) return nullptr; if (parent->handle(this->idx) != this) { this->idx = -1; for (int i=0, c=parent->count(); ihandle(i) == this) { this->idx = i; break; } } } return qobject_cast(parent->widget(this->idx)); } void OverlaySplitterHandle::retranslate() { actFloat.setToolTip(QObject::tr("Toggle floating window")); } void OverlaySplitterHandle::changeEvent(QEvent *e) { QSplitterHandle::changeEvent(e); if (e->type() == QEvent::LanguageChange) retranslate(); } void OverlaySplitterHandle::setTitleItem(QLayoutItem *item) { titleItem = item; } void OverlaySplitterHandle::showTitle(bool enable) { if (_showTitle == enable) return; if (!enable) unsetCursor(); else { setCursor(this->orientation() == Qt::Horizontal ? Qt::SizeHorCursor : Qt::SizeVerCursor); if (OverlayParams::getDockOverlaySplitterHandleTimeout() > 0 && qApp->widgetAt(QCursor::pos()) != this) timer.start(OverlayParams::getDockOverlaySplitterHandleTimeout()); } _showTitle = enable; for (auto child : findChildren(QString(), Qt::FindDirectChildrenOnly)) child->setVisible(enable); update(); } void OverlaySplitterHandle::paintEvent(QPaintEvent *e) { if (!_showTitle) return; if (!titleItem) { QSplitterHandle::paintEvent(e); return; } int flags = Qt::AlignCenter; auto tabWidget = qobject_cast( splitter() ? splitter()->parentWidget() : nullptr); if (tabWidget) { switch(tabWidget->getDockArea()) { case Qt::TopDockWidgetArea: case Qt::RightDockWidgetArea: flags = Qt::AlignRight; break; case Qt::BottomDockWidgetArea: case Qt::LeftDockWidgetArea: flags = Qt::AlignLeft; break; default: break; } } QDockWidget *dock = dockWidget(); if (!dock) { QSplitterHandle::paintEvent(e); return; } QPainter painter(this); painter.fillRect(this->rect(), painter.background()); QRect r = titleItem->geometry(); if (this->orientation() != Qt::Vertical) { r = r.transposed(); painter.translate(r.left(), r.top() + r.width()); painter.rotate(-90); painter.translate(-r.left(), -r.top()); } QString text = painter.fontMetrics().elidedText( dock->windowTitle(), Qt::ElideRight, r.width()); painter.drawText(r, flags, text); } void OverlaySplitterHandle::endDrag() { auto tabWidget = qobject_cast(splitter()->parentWidget()); if (tabWidget) { dockWidget(); tabWidget->onSplitterResize(this->idx); } OverlayTabWidget::_Dragging = nullptr; setCursor(this->orientation() == Qt::Horizontal ? Qt::SizeHorCursor : Qt::SizeVerCursor); if (OverlayTabWidget::_DragFrame) OverlayTabWidget::_DragFrame->hide(); if (OverlayTabWidget::_DragFloating) OverlayTabWidget::_DragFloating->hide(); } void OverlaySplitterHandle::keyPressEvent(QKeyEvent *ke) { if (OverlayTabWidget::_Dragging == this && ke->key() == Qt::Key_Escape) endDrag(); } void OverlaySplitterHandle::mouseMoveEvent(QMouseEvent *me) { if (OverlayTabWidget::_Dragging != this) return; if (!(me->buttons() & Qt::LeftButton)) { endDrag(); return; } #if 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( 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(std::min(mwSize.width()/2, dragSize.width())))); dragSize.setHeight(std::max(OverlayParams::getDockOverlayMinimumSize(), static_cast(std::min(mwSize.height()/2, dragSize.height())))); } void OverlaySplitterHandle::mouseReleaseEvent(QMouseEvent *me) { if (OverlayTabWidget::_Dragging != this || me->button() != Qt::LeftButton) return; if (dragging == 1) { endDrag(); return; } endDrag(); #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(parent()); if (splitter) { int i = -1; for (int size : splitter->sizes()) { ++i; if (!size) continue; QWidget *w = splitter->widget(i); if (w->findChild()) continue; QRect rect = w->geometry(); if (splitter->orientation() == Qt::Vertical) clip.addRect(rect.x(), rect.y()+4, rect.width(), rect.height()-4); else clip.addRect(rect.x()+4, rect.y(), rect.width()-4, rect.height()); } if (clip.isEmpty()) { drawSource(painter); return; } tmpPainter.setClipPath(clip); } for (int x=-_size.width();x<=_size.width();++x) { for (int y=-_size.height();y<=_size.height();++y) { if (x || y) { tmpPainter.drawPixmap(QPoint(x, y), px); tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceOver); } } } } tmpPainter.end(); // blur the alpha channel QImage blurred(tmp.size(), QImage::Format_ARGB32_Premultiplied); blurred.setDevicePixelRatio(px.devicePixelRatioF()); blurred.fill(0); QPainter blurPainter(&blurred); qt_blurImage(&blurPainter, tmp, blurRadius(), false, true); blurPainter.end(); tmp = blurred; // blacken the image... tmpPainter.begin(&tmp); tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); tmpPainter.fillRect(tmp.rect(), color()); tmpPainter.end(); // draw the blurred shadow... painter->drawImage(QPointF(offset.x()+_offset.x(), offset.y()+_offset.y()), tmp); // draw the actual pixmap... painter->drawPixmap(offset, px, QRectF()); // 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"