/* * SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "breezemdiwindowshadow.h" #include "breezeboxshadowrenderer.h" #include "breezemetrics.h" #include "breezeshadowhelper.h" #include "breezestyleconfigdata.h" #include #include #include #include namespace Breeze { //____________________________________________________________________ MdiWindowShadow::MdiWindowShadow(QWidget *parent, const TileSet &shadowTiles) : QWidget(parent) , _shadowTiles(shadowTiles) { setAttribute(Qt::WA_OpaquePaintEvent, false); setAttribute(Qt::WA_TransparentForMouseEvents, true); setFocusPolicy(Qt::NoFocus); } //____________________________________________________________________ void MdiWindowShadow::updateGeometry() { if (!_widget) { return; } // metrics const CompositeShadowParams params = ShadowHelper::lookupShadowParams(StyleConfigData::shadowSize()); if (params.isNone()) { return; } const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius).expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius)); const QSize shadowSize = BoxShadowRenderer::calculateMinimumShadowTextureSize(boxSize, params.shadow1.radius, params.shadow1.offset) .expandedTo(BoxShadowRenderer::calculateMinimumShadowTextureSize(boxSize, params.shadow2.radius, params.shadow2.offset)); const QRect shadowRect(QPoint(0, 0), shadowSize); QRect boxRect(QPoint(0, 0), boxSize); boxRect.moveCenter(shadowRect.center()); const int topSize(boxRect.top() - shadowRect.top() - Metrics::Shadow_Overlap - params.offset.y()); const int bottomSize(shadowRect.bottom() - boxRect.bottom() - Metrics::Shadow_Overlap + params.offset.y()); const int leftSize(boxRect.left() - shadowRect.left() - Metrics::Shadow_Overlap - params.offset.x()); const int rightSize(shadowRect.right() - boxRect.right() - Metrics::Shadow_Overlap + params.offset.x()); // get tileSet rect auto hole = _widget->frameGeometry(); _shadowTilesRect = hole.adjusted(-leftSize, -topSize, rightSize, bottomSize); // get parent MDI area's viewport auto parent(parentWidget()); if (parent && !qobject_cast(parent) && qobject_cast(parent->parentWidget())) { parent = parent->parentWidget(); } if (qobject_cast(parent)) { parent = qobject_cast(parent)->viewport(); } // set geometry QRect geometry(_shadowTilesRect); if (parent) { geometry &= parent->rect(); hole &= parent->rect(); } // update geometry and mask const QRegion mask = QRegion(geometry) - hole.adjusted(2, 2, -2, -2); if (mask.isEmpty()) { hide(); } else { setGeometry(geometry); setMask(mask.translated(-geometry.topLeft())); show(); } // translate rendering rect _shadowTilesRect.translate(-geometry.topLeft()); } //____________________________________________________________________ void MdiWindowShadow::updateZOrder() { stackUnder(_widget); } //____________________________________________________________________ void MdiWindowShadow::paintEvent(QPaintEvent *event) { if (!_shadowTiles.isValid()) { return; } QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing); painter.setClipRegion(event->region()); _shadowTiles.render(_shadowTilesRect, &painter); } //____________________________________________________________________ MdiWindowShadowFactory::MdiWindowShadowFactory(QObject *parent) : QObject(parent) { } //____________________________________________________________________________________ bool MdiWindowShadowFactory::registerWidget(QWidget *widget) { // check widget type auto subwindow(qobject_cast(widget)); if (!subwindow) { return false; } if (subwindow->widget() && subwindow->widget()->inherits("KMainWindow")) { return false; } // make sure widget is not already registered if (isRegistered(widget)) { return false; } // store in set _registeredWidgets.insert(widget); // create shadow immediately if widget is already visible if (widget->isVisible()) { installShadow(widget); updateShadowGeometry(widget); updateShadowZOrder(widget); } widget->installEventFilter(this); // catch object destruction connect(widget, &QObject::destroyed, this, &MdiWindowShadowFactory::widgetDestroyed); return true; } //____________________________________________________________________________________ void MdiWindowShadowFactory::unregisterWidget(QWidget *widget) { if (!isRegistered(widget)) { return; } widget->removeEventFilter(this); _registeredWidgets.remove(widget); removeShadow(widget); } //____________________________________________________________________________________ bool MdiWindowShadowFactory::eventFilter(QObject *object, QEvent *event) { switch (event->type()) { // TODO: possibly implement ZOrderChange event, to make sure that // the shadow is always painted on top case QEvent::ZOrderChange: updateShadowZOrder(object); break; case QEvent::Hide: hideShadows(object); break; case QEvent::Show: installShadow(object); updateShadowGeometry(object); updateShadowZOrder(object); break; case QEvent::Move: case QEvent::Resize: updateShadowGeometry(object); break; default: break; } return QObject::eventFilter(object, event); } //____________________________________________________________________________________ MdiWindowShadow *MdiWindowShadowFactory::findShadow(QObject *object) const { // check object, if (!object->parent()) { return nullptr; } // find existing window shadows const auto children = object->parent()->children(); for (QObject *child : children) { if (MdiWindowShadow *shadow = qobject_cast(child)) { if (shadow->widget() == object) { return shadow; } } } return nullptr; } //____________________________________________________________________________________ void MdiWindowShadowFactory::installShadow(QObject *object) { // cast auto widget(static_cast(object)); if (!widget->parentWidget()) { return; } // make sure shadow is not already installed if (findShadow(object)) { return; } if (!_shadowHelper) { return; } // create new shadow auto windowShadow(new MdiWindowShadow(widget->parentWidget(), _shadowHelper->shadowTiles(widget))); windowShadow->setWidget(widget); } //____________________________________________________________________________________ void MdiWindowShadowFactory::removeShadow(QObject *object) { if (MdiWindowShadow *windowShadow = findShadow(object)) { windowShadow->hide(); windowShadow->deleteLater(); } } //____________________________________________________________________________________ void MdiWindowShadowFactory::widgetDestroyed(QObject *object) { _registeredWidgets.remove(object); removeShadow(object); } }