aerothemeplasma/kwin/decoration/breeze-v5.93.0/kstyle/breezeframeshadow.cpp
2024-08-09 03:20:25 +02:00

331 lines
9.6 KiB
C++

/*
* SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "breezeframeshadow.h"
#include "breezemetrics.h"
#include <QAbstractScrollArea>
#include <QApplication>
#include <QDebug>
#include <QFrame>
#include <QMouseEvent>
#include <QPainter>
#include <QSplitter>
#include <KColorUtils>
namespace Breeze
{
//____________________________________________________________________________________
bool FrameShadowFactory::registerWidget(QWidget *widget, Helper &helper)
{
if (!widget) {
return false;
}
if (isRegistered(widget)) {
return false;
}
// check whether widget is a frame, and has the proper shape
bool accepted = false;
// cast to frame and check
QFrame *frame(qobject_cast<QFrame *>(widget));
if (frame) {
// also do not install on QSplitter
/*
due to Qt, splitters are set with a frame style that matches the condition below,
though no shadow should be installed, obviously
*/
if (qobject_cast<QSplitter *>(widget)) {
return false;
}
// further checks on frame shape, and parent
if (frame->frameStyle() == (QFrame::StyledPanel | QFrame::Sunken)) {
accepted = true;
}
} else if (widget->inherits("KTextEditor::View")) {
accepted = true;
}
if (!accepted) {
return false;
}
// make sure that the widget is not embedded into a KHTMLView
QWidget *parent(widget->parentWidget());
while (parent && !parent->isWindow()) {
if (parent->inherits("KHTMLView")) {
return false;
}
parent = parent->parentWidget();
}
// store in set
_registeredWidgets.insert(widget);
// catch object destruction
connect(widget, &QObject::destroyed, this, &FrameShadowFactory::widgetDestroyed);
// install shadow
installShadows(widget, helper);
return true;
}
//____________________________________________________________________________________
void FrameShadowFactory::unregisterWidget(QWidget *widget)
{
if (!isRegistered(widget)) {
return;
}
_registeredWidgets.remove(widget);
removeShadows(widget);
}
//____________________________________________________________________________________
bool FrameShadowFactory::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: {
raiseShadows(object);
break;
}
default:
break;
}
return QObject::eventFilter(object, event);
}
//____________________________________________________________________________________
void FrameShadowFactory::installShadows(QWidget *widget, Helper &helper)
{
removeShadows(widget);
widget->installEventFilter(this);
widget->installEventFilter(&_addEventFilter);
installShadow(widget, helper, SideTop);
installShadow(widget, helper, SideBottom);
widget->removeEventFilter(&_addEventFilter);
}
//____________________________________________________________________________________
void FrameShadowFactory::removeShadows(QWidget *widget)
{
widget->removeEventFilter(this);
const QList<QObject *> children = widget->children();
for (QObject *child : children) {
if (FrameShadow *shadow = qobject_cast<FrameShadow *>(child)) {
shadow->hide();
shadow->setParent(nullptr);
shadow->deleteLater();
}
}
}
//____________________________________________________________________________________
void FrameShadowFactory::updateShadowsGeometry(const QObject *object, QRect rect) const
{
const QList<QObject *> &children = object->children();
for (QObject *child : children) {
if (FrameShadow *shadow = qobject_cast<FrameShadow *>(child)) {
shadow->updateGeometry(rect);
}
}
}
//____________________________________________________________________________________
void FrameShadowFactory::raiseShadows(QObject *object) const
{
const QList<QObject *> &children = object->children();
for (QObject *child : children) {
if (FrameShadow *shadow = qobject_cast<FrameShadow *>(child)) {
shadow->raise();
}
}
}
//____________________________________________________________________________________
void FrameShadowFactory::update(QObject *object) const
{
const QList<QObject *> &children = object->children();
for (QObject *child : children) {
if (FrameShadow *shadow = qobject_cast<FrameShadow *>(child)) {
shadow->update();
}
}
}
//____________________________________________________________________________________
void FrameShadowFactory::updateState(const QWidget *widget, bool focus, bool hover, qreal opacity, AnimationMode mode) const
{
const QList<QObject *> &children = widget->children();
for (QObject *child : children) {
if (FrameShadow *shadow = qobject_cast<FrameShadow *>(child)) {
shadow->updateState(focus, hover, opacity, mode);
}
}
}
//____________________________________________________________________________________
void FrameShadowFactory::installShadow(QWidget *widget, Helper &helper, Side area) const
{
FrameShadow *shadow(nullptr);
shadow = new FrameShadow(area, helper);
shadow->setParent(widget);
shadow->hide();
}
//____________________________________________________________________________________
void FrameShadowFactory::widgetDestroyed(QObject *object)
{
_registeredWidgets.remove(object);
}
//____________________________________________________________________________________
FrameShadow::FrameShadow(Side area, Helper &helper)
: _helper(helper)
, _area(area)
{
setAttribute(Qt::WA_OpaquePaintEvent, false);
setFocusPolicy(Qt::NoFocus);
setAttribute(Qt::WA_TransparentForMouseEvents, true);
setContextMenuPolicy(Qt::NoContextMenu);
// grab viewport widget
QWidget *viewport(this->viewport());
// set cursor from viewport
if (viewport) {
setCursor(viewport->cursor());
}
}
//____________________________________________________________________________________
void FrameShadow::updateGeometry(QRect rect)
{
// show on first call
if (isHidden()) {
show();
}
// store offsets between passed rect and parent widget rect
QRect parentRect(parentWidget()->contentsRect());
_margins = QMargins(rect.left() - parentRect.left(), rect.top() - parentRect.top(), rect.right() - parentRect.right(), rect.bottom() - parentRect.bottom());
// for efficiency, take out the part for which nothing is rendered
rect.adjust(1, 1, -1, -1);
// adjust geometry
const int shadowSize(Metrics::Frame_FrameRadius);
switch (_area) {
case SideTop:
rect.setHeight(shadowSize);
break;
case SideBottom:
rect.setTop(rect.bottom() - shadowSize + 1);
break;
case SideLeft:
rect.setWidth(shadowSize);
rect.adjust(0, shadowSize, 0, -shadowSize);
break;
case SideRight:
rect.setLeft(rect.right() - shadowSize + 1);
rect.adjust(0, shadowSize, 0, -shadowSize);
break;
default:
return;
}
setGeometry(rect);
}
//____________________________________________________________________________________
void FrameShadow::updateState(bool focus, bool hover, qreal opacity, AnimationMode mode)
{
bool changed(false);
if (_hasFocus != focus) {
_hasFocus = focus;
changed |= true;
}
if (_mouseOver != hover) {
_mouseOver = hover;
changed |= !_hasFocus;
}
if (_mode != mode) {
_mode = mode;
changed |= (_mode == AnimationNone) || (_mode == AnimationFocus) || (_mode == AnimationHover && !_hasFocus);
}
if (_opacity != opacity) {
_opacity = opacity;
changed |= (_mode != AnimationNone);
}
if (changed) {
if (QWidget *viewport = this->viewport()) {
// need to disable viewport updates to avoid some redundant painting
// besides it fixes one visual glitch (from Qt) in QTableViews
viewport->setUpdatesEnabled(false);
update();
viewport->setUpdatesEnabled(true);
} else {
update();
}
}
}
//____________________________________________________________________________________
void FrameShadow::paintEvent(QPaintEvent *event)
{
// this fixes shadows in frames that change frameStyle() after polish()
if (QFrame *frame = qobject_cast<QFrame *>(parentWidget())) {
if (frame->frameStyle() != (QFrame::StyledPanel | QFrame::Sunken)) {
return;
}
}
const QRect parentRect(parentWidget()->contentsRect().translated(mapFromParent(QPoint(0, 0))));
const QRect rect(parentRect.adjusted(_margins.left(), _margins.top(), _margins.right(), _margins.bottom()));
// render
QPainter painter(this);
painter.setClipRegion(event->region());
painter.setRenderHint(QPainter::Antialiasing);
const QColor outline(_helper.frameOutlineColor(palette(), _mouseOver, _hasFocus, _opacity, _mode));
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
_helper.renderFrame(&painter, rect, QColor(), outline);
}
//____________________________________________________________________________________
QWidget *FrameShadow::viewport() const
{
if (!parentWidget()) {
return nullptr;
} else if (QAbstractScrollArea *widget = qobject_cast<QAbstractScrollArea *>(parentWidget())) {
return widget->viewport();
} else {
return nullptr;
}
}
}