aerothemeplasma/KWin/smod_kwin_theme/breeze/kstyle/breezemdiwindowshadow.cpp

259 lines
7.4 KiB
C++

/*
* 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);
}
}