You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
246 lines
7.3 KiB
246 lines
7.3 KiB
/* |
|
* SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr> |
|
* |
|
* SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
|
|
#include "breezemdiwindowshadow.h" |
|
|
|
#include "breezeboxshadowrenderer.h" |
|
#include "breezemetrics.h" |
|
#include "breezeshadowhelper.h" |
|
#include "breezestyleconfigdata.h" |
|
|
|
#include <QMdiArea> |
|
#include <QMdiSubWindow> |
|
#include <QPainter> |
|
#include <QTextStream> |
|
|
|
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<QMdiArea *>(parent) && qobject_cast<QMdiArea *>(parent->parentWidget())) { |
|
parent = parent->parentWidget(); |
|
} |
|
|
|
if (qobject_cast<QAbstractScrollArea *>(parent)) { |
|
parent = qobject_cast<QAbstractScrollArea *>(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<QMdiSubWindow *>(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 |
|
auto children = object->parent()->children(); |
|
foreach (QObject *child, children) { |
|
if (MdiWindowShadow *shadow = qobject_cast<MdiWindowShadow *>(child)) { |
|
if (shadow->widget() == object) |
|
return shadow; |
|
} |
|
} |
|
|
|
return nullptr; |
|
} |
|
|
|
//____________________________________________________________________________________ |
|
void MdiWindowShadowFactory::installShadow(QObject *object) |
|
{ |
|
// cast |
|
auto widget(static_cast<QWidget *>(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); |
|
} |
|
|
|
}
|
|
|