mirror of https://github.com/cutefishos/qt-plugins
refactor(qt-plugins): remove X11 integrations
parent
dccded7c80
commit
688d4d44d9
@ -1,136 +0,0 @@
|
|||||||
/* This file is part of the KDE libraries
|
|
||||||
* Copyright 2015 Martin Gräßlin <mgraesslin@kde.org>
|
|
||||||
* Copyright 2016 Marco Martin <mart@kde.org>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License or ( at
|
|
||||||
* your option ) version 3 or, at the discretion of KDE e.V. ( which shall
|
|
||||||
* act as a proxy as in section 14 of the GPLv3 ), 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 Lesser General Public License
|
|
||||||
* along with this library; see the file COPYING.LIB. If not, write to
|
|
||||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
#include "x11integration.h"
|
|
||||||
#include "x11utils.h"
|
|
||||||
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QPlatformSurfaceEvent>
|
|
||||||
#include <QGuiApplication>
|
|
||||||
#include <QtGui/qguiapplication_platform.h>
|
|
||||||
#include <QWindow>
|
|
||||||
#include <QWidget>
|
|
||||||
#include <QVariant>
|
|
||||||
#include <QRegion>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include <NETWM>
|
|
||||||
#include <KWindowEffects>
|
|
||||||
|
|
||||||
#include <xcb/xcb.h>
|
|
||||||
|
|
||||||
static const char s_schemePropertyName[] = "KDE_COLOR_SCHEME_PATH";
|
|
||||||
static const QByteArray s_blurBehindPropertyName = QByteArrayLiteral("ENABLE_BLUR_BEHIND_HINT");
|
|
||||||
static const QByteArray s_blurRegionPropertyName = QByteArrayLiteral("BLUR_REGION");
|
|
||||||
|
|
||||||
X11Integration::X11Integration()
|
|
||||||
: QObject()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
X11Integration::~X11Integration() = default;
|
|
||||||
|
|
||||||
void X11Integration::init()
|
|
||||||
{
|
|
||||||
QCoreApplication::instance()->installEventFilter(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool X11Integration::eventFilter(QObject *watched, QEvent *event)
|
|
||||||
{
|
|
||||||
//the drag and drop window should NOT be a tooltip
|
|
||||||
//https://bugreports.qt.io/browse/QTBUG-52560
|
|
||||||
if (event->type() == QEvent::Show && watched->inherits("QShapedPixmapWindow")) {
|
|
||||||
//static cast should be safe there
|
|
||||||
QWindow *w = static_cast<QWindow *>(watched);
|
|
||||||
NETWinInfo info(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection(), w->winId(), Cutefish::X11::rootWindow(), NET::WMWindowType, NET::Properties2());
|
|
||||||
info.setWindowType(NET::DNDIcon);
|
|
||||||
// TODO: does this flash the xcb connection?
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (event->type() == QEvent::PlatformSurface) {
|
|
||||||
// if (QWindow *w = qobject_cast<QWindow*>(watched)) {
|
|
||||||
// QPlatformSurfaceEvent *pe = static_cast<QPlatformSurfaceEvent*>(event);
|
|
||||||
// if (!w->flags().testFlag(Qt::ForeignWindow)) {
|
|
||||||
// if (pe->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) {
|
|
||||||
// const auto blurBehindProperty = w->property(s_blurBehindPropertyName.constData());
|
|
||||||
// if (blurBehindProperty.isValid()) {
|
|
||||||
// KWindowEffects::enableBlurBehind(w->winId(), blurBehindProperty.toBool());
|
|
||||||
// }
|
|
||||||
// installDesktopFileName(w);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (event->type() == QEvent::ApplicationPaletteChange) {
|
|
||||||
// const auto topLevelWindows = QGuiApplication::topLevelWindows();
|
|
||||||
// for (QWindow *w : topLevelWindows) {
|
|
||||||
// installColorScheme(w);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void X11Integration::installDesktopFileName(QWindow *w)
|
|
||||||
{
|
|
||||||
if (!w->isTopLevel()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString desktopFileName = QGuiApplication::desktopFileName();
|
|
||||||
if (desktopFileName.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// handle apps which set the desktopFileName property with filename suffix,
|
|
||||||
// due to unclear API dox (https://bugreports.qt.io/browse/QTBUG-75521)
|
|
||||||
if (desktopFileName.endsWith(QLatin1String(".desktop"))) {
|
|
||||||
desktopFileName.chop(8);
|
|
||||||
}
|
|
||||||
NETWinInfo info(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection(), w->winId(), Cutefish::X11::rootWindow(), NET::Properties(), NET::Properties2());
|
|
||||||
info.setDesktopFileName(desktopFileName.toUtf8().constData());
|
|
||||||
}
|
|
||||||
|
|
||||||
void X11Integration::setWindowProperty(QWindow *window, const QByteArray &name, const QByteArray &value)
|
|
||||||
{
|
|
||||||
auto *c = qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection();
|
|
||||||
|
|
||||||
xcb_atom_t atom;
|
|
||||||
auto it = m_atoms.find(name);
|
|
||||||
if (it == m_atoms.end()) {
|
|
||||||
const xcb_intern_atom_cookie_t cookie = xcb_intern_atom(c, false, name.length(), name.constData());
|
|
||||||
QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> reply(xcb_intern_atom_reply(c, cookie, nullptr));
|
|
||||||
if (!reply.isNull()) {
|
|
||||||
atom = reply->atom;
|
|
||||||
m_atoms[name] = atom;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
atom = *it;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.isEmpty()) {
|
|
||||||
xcb_delete_property(c, window->winId(), atom);
|
|
||||||
} else {
|
|
||||||
xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom, XCB_ATOM_STRING,
|
|
||||||
8, value.length(), value.constData());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
/* This file is part of the KDE libraries
|
|
||||||
* Copyright 2015 Martin Gräßlin <mgraesslin@kde.org>
|
|
||||||
* Copyright 2016 Marco Martin <mart@kde.org>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License or ( at
|
|
||||||
* your option ) version 3 or, at the discretion of KDE e.V. ( which shall
|
|
||||||
* act as a proxy as in section 14 of the GPLv3 ), 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 Lesser General Public License
|
|
||||||
* along with this library; see the file COPYING.LIB. If not, write to
|
|
||||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
#ifndef X11INTEGRATION_H
|
|
||||||
#define X11INTEGRATION_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QHash>
|
|
||||||
#include <xcb/xcb.h>
|
|
||||||
|
|
||||||
class QWindow;
|
|
||||||
|
|
||||||
class X11Integration : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit X11Integration();
|
|
||||||
~X11Integration() override;
|
|
||||||
void init();
|
|
||||||
|
|
||||||
void setWindowProperty(QWindow *window, const QByteArray &name, const QByteArray &value);
|
|
||||||
|
|
||||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void installDesktopFileName(QWindow *w);
|
|
||||||
QHash<QByteArray, xcb_atom_t> m_atoms;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,170 +0,0 @@
|
|||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
// breezeblurhelper.cpp
|
|
||||||
// handle regions passed to kwin for blurring
|
|
||||||
// -------------------
|
|
||||||
//
|
|
||||||
// Copyright (C) 2021 Reion Wong <reionwong@gmail.com>
|
|
||||||
// Copyright (C) 2018 Alex Nemeth <alex.nemeth329@gmail.com>
|
|
||||||
//
|
|
||||||
// Largely rewritten from Oxygen widget style
|
|
||||||
// Copyright (C) 2007 Thomas Luebking <thomas.luebking@web.de>
|
|
||||||
// Copyright (c) 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to
|
|
||||||
// deal in the Software without restriction, including without limitation the
|
|
||||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
// sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
// IN THE SOFTWARE.
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include "blurhelper.h"
|
|
||||||
#include "x11utils.h"
|
|
||||||
|
|
||||||
// KDE Frameworks
|
|
||||||
#include <KWindowEffects>
|
|
||||||
#include <KWindowSystem>
|
|
||||||
|
|
||||||
// Qt
|
|
||||||
#include <QWidget>
|
|
||||||
#include <QWindow>
|
|
||||||
#include <QScreen>
|
|
||||||
#include <QRect>
|
|
||||||
#include <QVariant>
|
|
||||||
#include <QEvent>
|
|
||||||
#include <QPainterPath>
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QtGui/qguiapplication_platform.h>
|
|
||||||
|
|
||||||
// XCB
|
|
||||||
#include <xcb/xcb.h>
|
|
||||||
#include <xcb/shape.h>
|
|
||||||
#include <xcb/xcb_icccm.h>
|
|
||||||
|
|
||||||
BlurHelper::BlurHelper(QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlurHelper::registerWidget(QWidget *widget)
|
|
||||||
{
|
|
||||||
// install event filter
|
|
||||||
addEventFilter(widget);
|
|
||||||
|
|
||||||
// schedule shadow area repaint
|
|
||||||
update(widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlurHelper::unregisterWidget(QWidget *widget)
|
|
||||||
{
|
|
||||||
// remove event filter
|
|
||||||
widget->removeEventFilter(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BlurHelper::eventFilter(QObject *object, QEvent *event)
|
|
||||||
{
|
|
||||||
switch (event->type()) {
|
|
||||||
case QEvent::Hide:
|
|
||||||
case QEvent::Show:
|
|
||||||
case QEvent::Resize: {
|
|
||||||
// cast to widget and check
|
|
||||||
QWidget *widget(qobject_cast<QWidget*>(object));
|
|
||||||
|
|
||||||
if (!widget)
|
|
||||||
break;
|
|
||||||
|
|
||||||
update(widget);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// never eat events
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void BlurHelper::update(QWidget *widget) const
|
|
||||||
{
|
|
||||||
// KWindowEffects is an X11/EWMH integration. On Wayland there is no X11
|
|
||||||
// native interface to pass the blur region to, and calling it can crash
|
|
||||||
// while a QMenu is being painted.
|
|
||||||
if (!KWindowSystem::isPlatformX11())
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
directly from bespin code. Supposedly prevent playing with some 'pseudo-widgets'
|
|
||||||
that have winId matching some other -random- window
|
|
||||||
*/
|
|
||||||
if (!(widget->testAttribute(Qt::WA_WState_Created) || widget->internalWinId()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
QWindow *window = widget->windowHandle();
|
|
||||||
if (!window)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (widget->mask().isEmpty()) {
|
|
||||||
KWindowEffects::enableBlurBehind(window, true);
|
|
||||||
} else {
|
|
||||||
KWindowEffects::enableBlurBehind(window, true, widget->mask());
|
|
||||||
}
|
|
||||||
|
|
||||||
// force update
|
|
||||||
if (widget->isVisible()) {
|
|
||||||
widget->update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlurHelper::enableBlurBehind(QWidget *widget, bool enable, qreal windowRadius)
|
|
||||||
{
|
|
||||||
if (!widget || !KWindowSystem::isPlatformX11())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto native = qGuiApp->nativeInterface<QNativeInterface::QX11Application>();
|
|
||||||
if (!native)
|
|
||||||
return;
|
|
||||||
|
|
||||||
xcb_connection_t *c = native->connection();
|
|
||||||
if (!c)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION");
|
|
||||||
xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
|
|
||||||
QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
|
|
||||||
if (!atom)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (enable) {
|
|
||||||
qreal devicePixelRatio = qApp->devicePixelRatio();
|
|
||||||
QPainterPath path;
|
|
||||||
path.addRoundedRect(QRectF(QPoint(0, 0), widget->size() * devicePixelRatio),
|
|
||||||
windowRadius * devicePixelRatio,
|
|
||||||
windowRadius * devicePixelRatio);
|
|
||||||
QVector<uint32_t> data;
|
|
||||||
for (const QPolygonF &polygon : path.toFillPolygons()) {
|
|
||||||
QRegion region = polygon.toPolygon();
|
|
||||||
for (auto i = region.begin(); i != region.end(); ++i) {
|
|
||||||
data << i->x() << i->y() << i->width() << i->height();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xcb_change_property(c, XCB_PROP_MODE_REPLACE, widget->winId(),
|
|
||||||
atom->atom, XCB_ATOM_CARDINAL,
|
|
||||||
32, data.size(), data.constData());
|
|
||||||
|
|
||||||
} else {
|
|
||||||
xcb_delete_property(c, widget->winId(), atom->atom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
// breezeblurhelper.h
|
|
||||||
// handle regions passed to kwin for blurring
|
|
||||||
// -------------------
|
|
||||||
//
|
|
||||||
// Copyright (C) 2018 Alex Nemeth <alex.nemeth329@gmail.com>
|
|
||||||
//
|
|
||||||
// Largely rewritten from Oxygen widget style
|
|
||||||
// Copyright (C) 2007 Thomas Luebking <thomas.luebking@web.de>
|
|
||||||
// Copyright (c) 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to
|
|
||||||
// deal in the Software without restriction, including without limitation the
|
|
||||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
// sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
// IN THE SOFTWARE.
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#ifndef BLURHELPER_H
|
|
||||||
#define BLURHELPER_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
class BlurHelper : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit BlurHelper(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
void registerWidget(QWidget *);
|
|
||||||
void unregisterWidget(QWidget *);
|
|
||||||
bool eventFilter(QObject *, QEvent *) override;
|
|
||||||
|
|
||||||
void update(QWidget *) const;
|
|
||||||
|
|
||||||
void enableBlurBehind(QWidget *widget, bool enable = true, qreal windowRadius = 0.0);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void addEventFilter(QObject *object) {
|
|
||||||
object->removeEventFilter(this);
|
|
||||||
object->installEventFilter(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // BLURHELPER_H
|
|
||||||
@ -1,348 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
|
||||||
*
|
|
||||||
* The box blur implementation is based on AlphaBoxBlur from Firefox.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
// own
|
|
||||||
#include "boxshadowrenderer.h"
|
|
||||||
|
|
||||||
// Qt
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QtMath>
|
|
||||||
|
|
||||||
static inline int calculateBlurRadius(qreal stdDev)
|
|
||||||
{
|
|
||||||
// See https://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
|
|
||||||
const qreal gaussianScaleFactor = (3.0 * qSqrt(2.0 * M_PI) / 4.0) * 1.5;
|
|
||||||
return qMax(2, qFloor(stdDev * gaussianScaleFactor + 0.5));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline qreal calculateBlurStdDev(int radius)
|
|
||||||
{
|
|
||||||
// See https://www.w3.org/TR/css-backgrounds-3/#shadow-blur
|
|
||||||
return radius * 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QSize calculateBlurExtent(int radius)
|
|
||||||
{
|
|
||||||
const int blurRadius = calculateBlurRadius(calculateBlurStdDev(radius));
|
|
||||||
return QSize(blurRadius, blurRadius);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BoxLobes
|
|
||||||
{
|
|
||||||
int left; ///< how many pixels sample to the left
|
|
||||||
int right; ///< how many pixels sample to the right
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute box filter parameters.
|
|
||||||
*
|
|
||||||
* @param radius The blur radius.
|
|
||||||
* @returns Parameters for three box filters.
|
|
||||||
**/
|
|
||||||
static QVector<BoxLobes> computeLobes(int radius)
|
|
||||||
{
|
|
||||||
const int blurRadius = calculateBlurRadius(calculateBlurStdDev(radius));
|
|
||||||
const int z = blurRadius / 3;
|
|
||||||
|
|
||||||
int major;
|
|
||||||
int minor;
|
|
||||||
int final;
|
|
||||||
|
|
||||||
switch (blurRadius % 3) {
|
|
||||||
case 0:
|
|
||||||
major = z;
|
|
||||||
minor = z;
|
|
||||||
final = z;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
major = z + 1;
|
|
||||||
minor = z;
|
|
||||||
final = z;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
major = z + 1;
|
|
||||||
minor = z;
|
|
||||||
final = z + 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_ASSERT(major + minor + final == blurRadius);
|
|
||||||
|
|
||||||
return {
|
|
||||||
{major, minor},
|
|
||||||
{minor, major},
|
|
||||||
{final, final}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process a row with a box filter.
|
|
||||||
*
|
|
||||||
* @param src The start of the row.
|
|
||||||
* @param dst The destination.
|
|
||||||
* @param width The width of the row, in pixels.
|
|
||||||
* @param horizontalStride The number of bytes from one alpha value to the
|
|
||||||
* next alpha value.
|
|
||||||
* @param verticalStride The number of bytes from one row to the next row.
|
|
||||||
* @param lobes Params of the box filter.
|
|
||||||
* @param transposeInput Whether the input is transposed.
|
|
||||||
* @param transposeOutput Whether the output should be transposed.
|
|
||||||
**/
|
|
||||||
static inline void boxBlurRowAlpha(const uint8_t *src, uint8_t *dst, int width, int horizontalStride,
|
|
||||||
int verticalStride, const BoxLobes &lobes, bool transposeInput,
|
|
||||||
bool transposeOutput)
|
|
||||||
{
|
|
||||||
const int inputStep = transposeInput ? verticalStride : horizontalStride;
|
|
||||||
const int outputStep = transposeOutput ? verticalStride : horizontalStride;
|
|
||||||
|
|
||||||
const int boxSize = lobes.left + 1 + lobes.right;
|
|
||||||
const int reciprocal = (1 << 24) / boxSize;
|
|
||||||
|
|
||||||
uint32_t alphaSum = (boxSize + 1) / 2;
|
|
||||||
|
|
||||||
const uint8_t *left = src;
|
|
||||||
const uint8_t *right = src;
|
|
||||||
uint8_t *out = dst;
|
|
||||||
|
|
||||||
const uint8_t firstValue = src[0];
|
|
||||||
const uint8_t lastValue = src[(width - 1) * inputStep];
|
|
||||||
|
|
||||||
alphaSum += firstValue * lobes.left;
|
|
||||||
|
|
||||||
const uint8_t *initEnd = src + (boxSize - lobes.left) * inputStep;
|
|
||||||
while (right < initEnd) {
|
|
||||||
alphaSum += *right;
|
|
||||||
right += inputStep;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t *leftEnd = src + boxSize * inputStep;
|
|
||||||
while (right < leftEnd) {
|
|
||||||
*out = (alphaSum * reciprocal) >> 24;
|
|
||||||
alphaSum += *right - firstValue;
|
|
||||||
right += inputStep;
|
|
||||||
out += outputStep;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t *centerEnd = src + width * inputStep;
|
|
||||||
while (right < centerEnd) {
|
|
||||||
*out = (alphaSum * reciprocal) >> 24;
|
|
||||||
alphaSum += *right - *left;
|
|
||||||
left += inputStep;
|
|
||||||
right += inputStep;
|
|
||||||
out += outputStep;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t *rightEnd = dst + width * outputStep;
|
|
||||||
while (out < rightEnd) {
|
|
||||||
*out = (alphaSum * reciprocal) >> 24;
|
|
||||||
alphaSum += lastValue - *left;
|
|
||||||
left += inputStep;
|
|
||||||
out += outputStep;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Blur the alpha channel of a given image.
|
|
||||||
*
|
|
||||||
* @param image The input image.
|
|
||||||
* @param radius The blur radius.
|
|
||||||
* @param rect Specifies what part of the image to blur. If nothing is provided, then
|
|
||||||
* the whole alpha channel of the input image will be blurred.
|
|
||||||
**/
|
|
||||||
static inline void boxBlurAlpha(QImage &image, int radius, const QRect &rect = {})
|
|
||||||
{
|
|
||||||
if (radius < 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QVector<BoxLobes> lobes = computeLobes(radius);
|
|
||||||
|
|
||||||
const QRect blurRect = rect.isNull() ? image.rect() : rect;
|
|
||||||
|
|
||||||
const int alphaOffset = QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3;
|
|
||||||
const int width = blurRect.width();
|
|
||||||
const int height = blurRect.height();
|
|
||||||
const int rowStride = image.bytesPerLine();
|
|
||||||
const int pixelStride = image.depth() >> 3;
|
|
||||||
|
|
||||||
const int bufferStride = qMax(width, height) * pixelStride;
|
|
||||||
QScopedPointer<uint8_t, QScopedPointerArrayDeleter<uint8_t> > buf(new uint8_t[2 * bufferStride]);
|
|
||||||
uint8_t *buf1 = buf.data();
|
|
||||||
uint8_t *buf2 = buf1 + bufferStride;
|
|
||||||
|
|
||||||
// Blur the image in horizontal direction.
|
|
||||||
for (int i = 0; i < height; ++i) {
|
|
||||||
uint8_t *row = image.scanLine(blurRect.y() + i) + blurRect.x() * pixelStride + alphaOffset;
|
|
||||||
boxBlurRowAlpha(row, buf1, width, pixelStride, rowStride, lobes[0], false, false);
|
|
||||||
boxBlurRowAlpha(buf1, buf2, width, pixelStride, rowStride, lobes[1], false, false);
|
|
||||||
boxBlurRowAlpha(buf2, row, width, pixelStride, rowStride, lobes[2], false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blur the image in vertical direction.
|
|
||||||
for (int i = 0; i < width; ++i) {
|
|
||||||
uint8_t *column = image.scanLine(blurRect.y()) + (blurRect.x() + i) * pixelStride + alphaOffset;
|
|
||||||
boxBlurRowAlpha(column, buf1, height, pixelStride, rowStride, lobes[0], true, false);
|
|
||||||
boxBlurRowAlpha(buf1, buf2, height, pixelStride, rowStride, lobes[1], false, false);
|
|
||||||
boxBlurRowAlpha(buf2, column, height, pixelStride, rowStride, lobes[2], false, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void mirrorTopLeftQuadrant(QImage &image)
|
|
||||||
{
|
|
||||||
const int width = image.width();
|
|
||||||
const int height = image.height();
|
|
||||||
|
|
||||||
const int centerX = qCeil(width * 0.5);
|
|
||||||
const int centerY = qCeil(height * 0.5);
|
|
||||||
|
|
||||||
const int alphaOffset = QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3;
|
|
||||||
const int stride = image.depth() >> 3;
|
|
||||||
|
|
||||||
for (int y = 0; y < centerY; ++y) {
|
|
||||||
uint8_t *in = image.scanLine(y) + alphaOffset;
|
|
||||||
uint8_t *out = in + (width - 1) * stride;
|
|
||||||
|
|
||||||
for (int x = 0; x < centerX; ++x, in += stride, out -= stride) {
|
|
||||||
*out = *in;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int y = 0; y < centerY; ++y) {
|
|
||||||
const uint8_t *in = image.scanLine(y) + alphaOffset;
|
|
||||||
uint8_t *out = image.scanLine(width - y - 1) + alphaOffset;
|
|
||||||
|
|
||||||
for (int x = 0; x < width; ++x, in += stride, out += stride) {
|
|
||||||
*out = *in;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void renderShadow(QPainter *painter, const QRect &rect, qreal borderRadius, const QPoint &offset, int radius, const QColor &color)
|
|
||||||
{
|
|
||||||
const QSize inflation = calculateBlurExtent(radius);
|
|
||||||
const QSize size = rect.size() + 2 * inflation;
|
|
||||||
|
|
||||||
const qreal dpr = painter->device()->devicePixelRatioF();
|
|
||||||
|
|
||||||
QImage shadow(size * dpr, QImage::Format_ARGB32_Premultiplied);
|
|
||||||
shadow.setDevicePixelRatio(dpr);
|
|
||||||
shadow.fill(Qt::transparent);
|
|
||||||
|
|
||||||
QRect boxRect(QPoint(0, 0), rect.size());
|
|
||||||
boxRect.moveCenter(QRect(QPoint(0, 0), size).center());
|
|
||||||
|
|
||||||
const qreal xRadius = 2.0 * borderRadius / boxRect.width();
|
|
||||||
const qreal yRadius = 2.0 * borderRadius / boxRect.height();
|
|
||||||
|
|
||||||
QPainter shadowPainter;
|
|
||||||
shadowPainter.begin(&shadow);
|
|
||||||
shadowPainter.setRenderHint(QPainter::Antialiasing);
|
|
||||||
shadowPainter.setPen(Qt::NoPen);
|
|
||||||
shadowPainter.setBrush(Qt::black);
|
|
||||||
shadowPainter.drawRoundedRect(boxRect, xRadius, yRadius);
|
|
||||||
shadowPainter.end();
|
|
||||||
|
|
||||||
// Because the shadow texture is symmetrical, that's enough to blur
|
|
||||||
// only the top-left quadrant and then mirror it.
|
|
||||||
const QRect blurRect(0, 0, qCeil(shadow.width() * 0.5), qCeil(shadow.height() * 0.5));
|
|
||||||
const int scaledRadius = qRound(radius * dpr);
|
|
||||||
boxBlurAlpha(shadow, scaledRadius, blurRect);
|
|
||||||
mirrorTopLeftQuadrant(shadow);
|
|
||||||
|
|
||||||
// Give the shadow a tint of the desired color.
|
|
||||||
shadowPainter.begin(&shadow);
|
|
||||||
shadowPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
|
||||||
shadowPainter.fillRect(shadow.rect(), color);
|
|
||||||
shadowPainter.end();
|
|
||||||
|
|
||||||
// Actually, present the shadow.
|
|
||||||
QRect shadowRect = shadow.rect();
|
|
||||||
shadowRect.setSize(shadowRect.size() / dpr);
|
|
||||||
shadowRect.moveCenter(rect.center() + offset);
|
|
||||||
painter->drawImage(shadowRect, shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BoxShadowRenderer::setBoxSize(const QSize &size)
|
|
||||||
{
|
|
||||||
m_boxSize = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BoxShadowRenderer::setBorderRadius(qreal radius)
|
|
||||||
{
|
|
||||||
m_borderRadius = radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BoxShadowRenderer::setDevicePixelRatio(qreal dpr)
|
|
||||||
{
|
|
||||||
m_dpr = dpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BoxShadowRenderer::addShadow(const QPoint &offset, int radius, const QColor &color)
|
|
||||||
{
|
|
||||||
Shadow shadow = {};
|
|
||||||
shadow.offset = offset;
|
|
||||||
shadow.radius = radius;
|
|
||||||
shadow.color = color;
|
|
||||||
m_shadows.append(shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
QImage BoxShadowRenderer::render() const
|
|
||||||
{
|
|
||||||
if (m_shadows.isEmpty()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize canvasSize;
|
|
||||||
for (const Shadow &shadow : qAsConst(m_shadows)) {
|
|
||||||
canvasSize = canvasSize.expandedTo(
|
|
||||||
calculateMinimumShadowTextureSize(m_boxSize, shadow.radius, shadow.offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
QImage canvas(canvasSize * m_dpr, QImage::Format_ARGB32_Premultiplied);
|
|
||||||
canvas.setDevicePixelRatio(m_dpr);
|
|
||||||
canvas.fill(Qt::transparent);
|
|
||||||
|
|
||||||
QRect boxRect(QPoint(0, 0), m_boxSize);
|
|
||||||
boxRect.moveCenter(QRect(QPoint(0, 0), canvasSize).center());
|
|
||||||
|
|
||||||
QPainter painter(&canvas);
|
|
||||||
for (const Shadow &shadow : qAsConst(m_shadows)) {
|
|
||||||
renderShadow(&painter, boxRect, m_borderRadius, shadow.offset, shadow.radius, shadow.color);
|
|
||||||
}
|
|
||||||
painter.end();
|
|
||||||
|
|
||||||
return canvas;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize BoxShadowRenderer::calculateMinimumBoxSize(int radius)
|
|
||||||
{
|
|
||||||
const QSize blurExtent = calculateBlurExtent(radius);
|
|
||||||
return 2 * blurExtent + QSize(1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize BoxShadowRenderer::calculateMinimumShadowTextureSize(const QSize &boxSize, int radius, const QPoint &offset)
|
|
||||||
{
|
|
||||||
return boxSize + 2 * calculateBlurExtent(radius) + QSize(qAbs(offset.x()), qAbs(offset.y()));
|
|
||||||
}
|
|
||||||
@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// Qt
|
|
||||||
#include <QColor>
|
|
||||||
#include <QImage>
|
|
||||||
#include <QPoint>
|
|
||||||
#include <QSize>
|
|
||||||
|
|
||||||
class BoxShadowRenderer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Compiler generated constructors & destructor are fine.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the size of the box.
|
|
||||||
* @param size The size of the box.
|
|
||||||
**/
|
|
||||||
void setBoxSize(const QSize &size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the radius of box' corners.
|
|
||||||
* @param radius The border radius, in pixels.
|
|
||||||
**/
|
|
||||||
void setBorderRadius(qreal radius);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the device pixel ratio of the resulting shadow texture.
|
|
||||||
* @param dpr The device pixel ratio.
|
|
||||||
**/
|
|
||||||
void setDevicePixelRatio(qreal dpr);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a shadow.
|
|
||||||
* @param offset The offset of the shadow.
|
|
||||||
* @param radius The blur radius.
|
|
||||||
* @param color The color of the shadow.
|
|
||||||
**/
|
|
||||||
void addShadow(const QPoint &offset, int radius, const QColor &color);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the shadow.
|
|
||||||
**/
|
|
||||||
QImage render() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the minimum size of the box.
|
|
||||||
*
|
|
||||||
* This helper computes the minimum size of the box so the shadow behind it has
|
|
||||||
* full its strength.
|
|
||||||
*
|
|
||||||
* @param radius The blur radius of the shadow.
|
|
||||||
**/
|
|
||||||
static QSize calculateMinimumBoxSize(int radius);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the minimum size of the shadow texture.
|
|
||||||
*
|
|
||||||
* This helper computes the minimum size of the resulting texture so the shadow
|
|
||||||
* is not clipped.
|
|
||||||
*
|
|
||||||
* @param boxSize The size of the box.
|
|
||||||
* @param radius The blur radius.
|
|
||||||
* @param offset The offset of the shadow.
|
|
||||||
**/
|
|
||||||
static QSize calculateMinimumShadowTextureSize(const QSize &boxSize, int radius, const QPoint &offset);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QSize m_boxSize;
|
|
||||||
qreal m_borderRadius = 0.0;
|
|
||||||
qreal m_dpr = 1.0;
|
|
||||||
|
|
||||||
struct Shadow {
|
|
||||||
QPoint offset;
|
|
||||||
int radius;
|
|
||||||
QColor color;
|
|
||||||
};
|
|
||||||
|
|
||||||
QVector<Shadow> m_shadows;
|
|
||||||
};
|
|
||||||
@ -1,414 +0,0 @@
|
|||||||
/*************************************************************************
|
|
||||||
* Copyright (C) 2014 by Hugo Pereira Da Costa <hugo.pereira@free.fr> *
|
|
||||||
* Copyright (C) 2018, 2020 by Vlad Zahorodnii <vlad.zahorodnii@kde.org> *
|
|
||||||
* Copyright (C) 2020, 2020 by Reven Martin <revenmartin@gmail.com> *
|
|
||||||
* *
|
|
||||||
* This program is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU General Public License as published by *
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or *
|
|
||||||
* (at your option) any later version. *
|
|
||||||
* *
|
|
||||||
* This program 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 General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU General Public License *
|
|
||||||
* along with this program; if not, write to the *
|
|
||||||
* Free Software Foundation, Inc., *
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
|
|
||||||
*************************************************************************/
|
|
||||||
|
|
||||||
#include "shadowhelper.h"
|
|
||||||
#include "boxshadowrenderer.h"
|
|
||||||
|
|
||||||
#include <QDockWidget>
|
|
||||||
#include <QEvent>
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QMenu>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QPixmap>
|
|
||||||
#include <QPlatformSurfaceEvent>
|
|
||||||
#include <QToolBar>
|
|
||||||
#include <QTextStream>
|
|
||||||
|
|
||||||
#include <KWindowSystem>
|
|
||||||
|
|
||||||
const char netWMSkipShadow[] = "_CUTEFISH_NET_WM_SKIP_SHADOW";
|
|
||||||
const char netWMForceShadow[] = "_CUTEFISH_NET_WM_FORCE_SHADOW";
|
|
||||||
const char netWMFrameRadius[] = "_CUTEFISH_NET_WM_FRAME_RADIUS";
|
|
||||||
|
|
||||||
enum {
|
|
||||||
ShadowNone,
|
|
||||||
ShadowSmall,
|
|
||||||
ShadowMedium,
|
|
||||||
ShadowLarge,
|
|
||||||
ShadowVeryLarge
|
|
||||||
};
|
|
||||||
|
|
||||||
const CompositeShadowParams s_shadowParams[] = {
|
|
||||||
// None
|
|
||||||
CompositeShadowParams(),
|
|
||||||
// Small
|
|
||||||
CompositeShadowParams(
|
|
||||||
QPoint(0, 3),
|
|
||||||
ShadowParams(QPoint(0, 0), 16, 0.26),
|
|
||||||
ShadowParams(QPoint(0, -2), 8, 0.16)),
|
|
||||||
// Medium
|
|
||||||
CompositeShadowParams(
|
|
||||||
QPoint(0, 4),
|
|
||||||
ShadowParams(QPoint(0, 0), 20, 0.24),
|
|
||||||
ShadowParams(QPoint(0, -2), 10, 0.14)),
|
|
||||||
// Large
|
|
||||||
CompositeShadowParams(
|
|
||||||
QPoint(0, 5),
|
|
||||||
ShadowParams(QPoint(0, 0), 24, 0.22),
|
|
||||||
ShadowParams(QPoint(0, -3), 12, 0.12)),
|
|
||||||
// Very Large
|
|
||||||
CompositeShadowParams(
|
|
||||||
QPoint(0, 6),
|
|
||||||
ShadowParams(QPoint(0, 0), 32, 0.2),
|
|
||||||
ShadowParams(QPoint(0, -3), 16, 0.1))
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
ShadowHelper::ShadowHelper(QObject * parent)
|
|
||||||
: QObject(parent),
|
|
||||||
m_frameRadius(12)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ShadowHelper::~ShadowHelper()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CompositeShadowParams ShadowHelper::lookupShadowParams(int shadowSizeEnum)
|
|
||||||
{
|
|
||||||
switch (shadowSizeEnum) {
|
|
||||||
case ShadowNone:
|
|
||||||
return s_shadowParams[0];
|
|
||||||
case ShadowSmall:
|
|
||||||
return s_shadowParams[1];
|
|
||||||
case ShadowMedium:
|
|
||||||
return s_shadowParams[2];
|
|
||||||
case ShadowLarge:
|
|
||||||
return s_shadowParams[3];
|
|
||||||
case ShadowVeryLarge:
|
|
||||||
return s_shadowParams[4];
|
|
||||||
default:
|
|
||||||
// Fallback to the Large size.
|
|
||||||
return s_shadowParams[3];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShadowHelper::registerWidget(QWidget *widget, bool force)
|
|
||||||
{
|
|
||||||
// make sure widget is not already registered
|
|
||||||
if (m_widgets.contains(widget))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// check if widget qualifies
|
|
||||||
if (!(force || acceptWidget(widget)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
qreal frameRadius = m_frameRadius;
|
|
||||||
const auto frameRadiusProperty = widget->property(netWMFrameRadius);
|
|
||||||
if (frameRadiusProperty.isValid())
|
|
||||||
frameRadius = frameRadiusProperty.toReal();
|
|
||||||
|
|
||||||
installShadows(widget, shadowTiles(frameRadius));
|
|
||||||
m_widgets.insert(widget);
|
|
||||||
|
|
||||||
// install event filter
|
|
||||||
widget->removeEventFilter(this);
|
|
||||||
widget->installEventFilter(this);
|
|
||||||
|
|
||||||
// connect destroy signal
|
|
||||||
connect(widget, &QObject::destroyed, this, &ShadowHelper::objectDeleted);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowHelper::unregisterWidget(QWidget *widget)
|
|
||||||
{
|
|
||||||
if (m_widgets.remove(widget)) {
|
|
||||||
// uninstall the event filter
|
|
||||||
widget->removeEventFilter(this);
|
|
||||||
|
|
||||||
// disconnect all signals
|
|
||||||
disconnect(widget, nullptr, this, nullptr);
|
|
||||||
|
|
||||||
// uninstall the shadow
|
|
||||||
uninstallShadows(widget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShadowHelper::eventFilter(QObject *object, QEvent *event)
|
|
||||||
{
|
|
||||||
if (KWindowSystem::isPlatformX11()) {
|
|
||||||
// check event type
|
|
||||||
if (event->type() == QEvent::WinIdChange) {
|
|
||||||
QWidget *widget = static_cast<QWidget *>(object);
|
|
||||||
|
|
||||||
qreal frameRadius = m_frameRadius;
|
|
||||||
const auto frameRadiusProperty = widget->property(netWMFrameRadius);
|
|
||||||
if (frameRadiusProperty.isValid())
|
|
||||||
frameRadius = frameRadiusProperty.toReal();
|
|
||||||
TileSet shadowTileSet = shadowTiles(frameRadius);
|
|
||||||
installShadows(widget, shadowTileSet);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (event->type() != QEvent::PlatformSurface)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// QWidget *widget(static_cast<QWidget *>(object));
|
|
||||||
QPlatformSurfaceEvent* surfaceEvent(static_cast<QPlatformSurfaceEvent*>(event));
|
|
||||||
|
|
||||||
switch (surfaceEvent->surfaceEventType()) {
|
|
||||||
case QPlatformSurfaceEvent::SurfaceCreated:
|
|
||||||
//installShadows(widget);
|
|
||||||
break;
|
|
||||||
case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed:
|
|
||||||
// Don't care.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TileSet ShadowHelper::shadowTiles(const qreal frameRadius)
|
|
||||||
{
|
|
||||||
const CompositeShadowParams params = lookupShadowParams(ShadowVeryLarge);
|
|
||||||
|
|
||||||
if (params.isNone())
|
|
||||||
return TileSet();
|
|
||||||
// } else if (_shadowTiles.isValid()) {
|
|
||||||
// return _shadowTiles;
|
|
||||||
// }
|
|
||||||
|
|
||||||
auto withOpacity = [](const QColor &color, qreal opacity) -> QColor {
|
|
||||||
QColor c(color);
|
|
||||||
c.setAlphaF(opacity);
|
|
||||||
return c;
|
|
||||||
};
|
|
||||||
|
|
||||||
const QColor color = Qt::black;
|
|
||||||
// const qreal strength = static_cast<qreal>(255) / 255.0;
|
|
||||||
const qreal strength = 1.5;
|
|
||||||
|
|
||||||
const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius)
|
|
||||||
.expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius));
|
|
||||||
|
|
||||||
const qreal dpr = qApp->devicePixelRatio();
|
|
||||||
|
|
||||||
BoxShadowRenderer shadowRenderer;
|
|
||||||
shadowRenderer.setBorderRadius(frameRadius);
|
|
||||||
shadowRenderer.setBoxSize(boxSize);
|
|
||||||
shadowRenderer.setDevicePixelRatio(dpr);
|
|
||||||
|
|
||||||
shadowRenderer.addShadow(params.shadow1.offset, params.shadow1.radius,
|
|
||||||
withOpacity(color, params.shadow1.opacity * strength));
|
|
||||||
shadowRenderer.addShadow(params.shadow2.offset, params.shadow2.radius,
|
|
||||||
withOpacity(color, params.shadow2.opacity * strength));
|
|
||||||
|
|
||||||
QImage shadowTexture = shadowRenderer.render();
|
|
||||||
|
|
||||||
const QRect outerRect(QPoint(0, 0), shadowTexture.size() / dpr);
|
|
||||||
|
|
||||||
QRect boxRect(QPoint(0, 0), boxSize);
|
|
||||||
boxRect.moveCenter(outerRect.center());
|
|
||||||
|
|
||||||
// Mask out inner rect.
|
|
||||||
QPainter painter(&shadowTexture);
|
|
||||||
painter.setRenderHint(QPainter::Antialiasing);
|
|
||||||
|
|
||||||
int Shadow_Overlap = 3;
|
|
||||||
const QMargins margins = QMargins(
|
|
||||||
boxRect.left() - outerRect.left() - Shadow_Overlap - params.offset.x(),
|
|
||||||
boxRect.top() - outerRect.top() - Shadow_Overlap - params.offset.y(),
|
|
||||||
outerRect.right() - boxRect.right() - Shadow_Overlap + params.offset.x(),
|
|
||||||
outerRect.bottom() - boxRect.bottom() - Shadow_Overlap + params.offset.y());
|
|
||||||
|
|
||||||
painter.setPen(Qt::NoPen);
|
|
||||||
painter.setBrush(Qt::black);
|
|
||||||
painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
|
|
||||||
painter.drawRoundedRect(
|
|
||||||
outerRect - margins,
|
|
||||||
frameRadius,
|
|
||||||
frameRadius);
|
|
||||||
|
|
||||||
// We're done.
|
|
||||||
painter.end();
|
|
||||||
|
|
||||||
const QPoint innerRectTopLeft = outerRect.center();
|
|
||||||
TileSet tiles = TileSet(
|
|
||||||
QPixmap::fromImage(shadowTexture),
|
|
||||||
innerRectTopLeft.x(),
|
|
||||||
innerRectTopLeft.y(),
|
|
||||||
1, 1);
|
|
||||||
|
|
||||||
return tiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowHelper::objectDeleted(QObject *object)
|
|
||||||
{
|
|
||||||
QWidget *widget(static_cast<QWidget *>(object));
|
|
||||||
m_widgets.remove(widget);
|
|
||||||
m_shadows.remove(widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShadowHelper::isMenu(QWidget *widget) const
|
|
||||||
{
|
|
||||||
return qobject_cast<QMenu*>(widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShadowHelper::isToolTip(QWidget *widget) const
|
|
||||||
{
|
|
||||||
return widget->inherits("QTipLabel") || (widget->windowFlags() & Qt::WindowType_Mask) == Qt::ToolTip;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShadowHelper::isDockWidget(QWidget *widget) const
|
|
||||||
{
|
|
||||||
return qobject_cast<QDockWidget*>(widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShadowHelper::isToolBar(QWidget *widget) const
|
|
||||||
{
|
|
||||||
return qobject_cast<QToolBar*>(widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShadowHelper::acceptWidget(QWidget *widget) const
|
|
||||||
{
|
|
||||||
// flags
|
|
||||||
if (widget->property(netWMSkipShadow).toBool())
|
|
||||||
return false;
|
|
||||||
if (widget->property(netWMForceShadow).toBool())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// menus
|
|
||||||
if (isMenu(widget))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// combobox dropdown lists
|
|
||||||
if (widget->inherits("QComboBoxPrivateContainer"))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// tooltips
|
|
||||||
if (isToolTip(widget) && !widget->inherits("Plasma::ToolTip"))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// detached widgets
|
|
||||||
if (isDockWidget(widget) || isToolBar(widget))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// reject
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
KWindowShadowTile::Ptr ShadowHelper::createTile(const QPixmap& source)
|
|
||||||
{
|
|
||||||
KWindowShadowTile::Ptr tile = KWindowShadowTile::Ptr::create();
|
|
||||||
tile->setImage(source.toImage());
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowHelper::installShadows(QWidget *widget, TileSet shadowTiles)
|
|
||||||
{
|
|
||||||
if (!widget)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// only toplevel widgets can cast drop-shadows
|
|
||||||
if (!widget->isWindow())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// widget must have valid native window
|
|
||||||
if (!widget->testAttribute(Qt::WA_WState_Created))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// create platform shadow tiles
|
|
||||||
QVector<KWindowShadowTile::Ptr> tiles = {
|
|
||||||
createTile(shadowTiles.pixmap(1)),
|
|
||||||
createTile(shadowTiles.pixmap(2)),
|
|
||||||
createTile(shadowTiles.pixmap(5)),
|
|
||||||
createTile(shadowTiles.pixmap(8)),
|
|
||||||
createTile(shadowTiles.pixmap(7)),
|
|
||||||
createTile(shadowTiles.pixmap(6)),
|
|
||||||
createTile(shadowTiles.pixmap(3)),
|
|
||||||
createTile(shadowTiles.pixmap(0))
|
|
||||||
};
|
|
||||||
if (tiles.count() != numTiles)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// find a shadow associated with the widget
|
|
||||||
KWindowShadow*& shadow = m_shadows[ widget ];
|
|
||||||
|
|
||||||
if (!shadow)
|
|
||||||
shadow = new KWindowShadow(widget);
|
|
||||||
|
|
||||||
if (shadow->isCreated())
|
|
||||||
shadow->destroy();
|
|
||||||
|
|
||||||
shadow->setTopTile(tiles[ 0 ]);
|
|
||||||
shadow->setTopRightTile(tiles[ 1 ]);
|
|
||||||
shadow->setRightTile(tiles[ 2 ]);
|
|
||||||
shadow->setBottomRightTile(tiles[ 3 ]);
|
|
||||||
shadow->setBottomTile(tiles[ 4 ]);
|
|
||||||
shadow->setBottomLeftTile(tiles[ 5 ]);
|
|
||||||
shadow->setLeftTile(tiles[ 6 ]);
|
|
||||||
shadow->setTopLeftTile(tiles[ 7 ]);
|
|
||||||
shadow->setPadding(shadowMargins(widget, shadowTiles));
|
|
||||||
shadow->setWindow(widget->windowHandle());
|
|
||||||
shadow->create();
|
|
||||||
}
|
|
||||||
|
|
||||||
QMargins ShadowHelper::shadowMargins(QWidget *widget, TileSet shadowTiles) const
|
|
||||||
{
|
|
||||||
const CompositeShadowParams params = lookupShadowParams(ShadowVeryLarge);
|
|
||||||
if (params.isNone())
|
|
||||||
return QMargins();
|
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
int Shadow_Overlap = 4;
|
|
||||||
QMargins margins(
|
|
||||||
boxRect.left() - shadowRect.left() - Shadow_Overlap - params.offset.x(),
|
|
||||||
boxRect.top() - shadowRect.top() - Shadow_Overlap - params.offset.y(),
|
|
||||||
shadowRect.right() - boxRect.right() - Shadow_Overlap + params.offset.x(),
|
|
||||||
shadowRect.bottom() - boxRect.bottom() - Shadow_Overlap + params.offset.y());
|
|
||||||
|
|
||||||
if (widget->inherits("QBalloonTip")) {
|
|
||||||
// Balloon tip needs special margins to deal with the arrow.
|
|
||||||
int top = widget->contentsMargins().top();
|
|
||||||
int bottom = widget->contentsMargins().bottom();
|
|
||||||
|
|
||||||
// Need to decrement default size further due to extra hard coded round corner.
|
|
||||||
margins -= 1;
|
|
||||||
|
|
||||||
// Arrow can be either to the top or the bottom. Adjust margins accordingly.
|
|
||||||
const int diff = qAbs(top - bottom);
|
|
||||||
if (top > bottom) {
|
|
||||||
margins.setTop(margins.top() - diff);
|
|
||||||
} else {
|
|
||||||
margins.setBottom(margins.bottom() - diff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
margins *= shadowTiles.pixmap(0).devicePixelRatio();
|
|
||||||
|
|
||||||
return margins;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowHelper::uninstallShadows(QWidget *widget)
|
|
||||||
{
|
|
||||||
delete m_shadows.take(widget);
|
|
||||||
}
|
|
||||||
@ -1,148 +0,0 @@
|
|||||||
/*************************************************************************
|
|
||||||
* Copyright (C) 2014 by Hugo Pereira Da Costa <hugo.pereira@free.fr> *
|
|
||||||
* Copyright (C) 2020 by Vlad Zahorodnii <vlad.zahorodnii@kde.org> *
|
|
||||||
* Copyright (C) 2020, 2020 by Reven Martin <revenmartin@gmail.com> *
|
|
||||||
* *
|
|
||||||
* This program is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU General Public License as published by *
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or *
|
|
||||||
* (at your option) any later version. *
|
|
||||||
* *
|
|
||||||
* This program 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 General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU General Public License *
|
|
||||||
* along with this program; if not, write to the *
|
|
||||||
* Free Software Foundation, Inc., *
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
|
|
||||||
*************************************************************************/
|
|
||||||
|
|
||||||
#ifndef SHADOWHELPER_H
|
|
||||||
#define SHADOWHELPER_H
|
|
||||||
|
|
||||||
#include "tileset.h"
|
|
||||||
#include <KWindowShadow>
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QPointer>
|
|
||||||
#include <QMap>
|
|
||||||
#include <QMargins>
|
|
||||||
#include <QSet>
|
|
||||||
|
|
||||||
struct ShadowParams
|
|
||||||
{
|
|
||||||
ShadowParams() = default;
|
|
||||||
|
|
||||||
ShadowParams(const QPoint &offset, int radius, qreal opacity):
|
|
||||||
offset(offset),
|
|
||||||
radius(radius),
|
|
||||||
opacity(opacity)
|
|
||||||
{}
|
|
||||||
|
|
||||||
QPoint offset;
|
|
||||||
int radius = 0;
|
|
||||||
qreal opacity = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CompositeShadowParams
|
|
||||||
{
|
|
||||||
CompositeShadowParams() = default;
|
|
||||||
|
|
||||||
CompositeShadowParams(
|
|
||||||
const QPoint &offset,
|
|
||||||
const ShadowParams &shadow1,
|
|
||||||
const ShadowParams &shadow2)
|
|
||||||
: offset(offset)
|
|
||||||
, shadow1(shadow1)
|
|
||||||
, shadow2(shadow2) {}
|
|
||||||
|
|
||||||
bool isNone() const
|
|
||||||
{ return qMax(shadow1.radius, shadow2.radius) == 0; }
|
|
||||||
|
|
||||||
QPoint offset;
|
|
||||||
ShadowParams shadow1;
|
|
||||||
ShadowParams shadow2;
|
|
||||||
};
|
|
||||||
|
|
||||||
//* handle shadow pixmaps passed to window manager via X property
|
|
||||||
class ShadowHelper: public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
//* constructor
|
|
||||||
ShadowHelper(QObject *);
|
|
||||||
|
|
||||||
//* destructor
|
|
||||||
~ShadowHelper() override;
|
|
||||||
|
|
||||||
//* shadow params from size enum
|
|
||||||
static CompositeShadowParams lookupShadowParams(int shadowSizeEnum);
|
|
||||||
|
|
||||||
//* register widget
|
|
||||||
bool registerWidget(QWidget *, bool force = false);
|
|
||||||
|
|
||||||
//* unregister widget
|
|
||||||
void unregisterWidget(QWidget *);
|
|
||||||
|
|
||||||
//* event filter
|
|
||||||
bool eventFilter(QObject *, QEvent *) override;
|
|
||||||
|
|
||||||
void setFrameRadius(qreal radius) { m_frameRadius = radius; }
|
|
||||||
|
|
||||||
//* shadow tiles
|
|
||||||
/** is public because it is also needed for mdi windows */
|
|
||||||
// TileSet shadowTiles();
|
|
||||||
|
|
||||||
TileSet shadowTiles(const qreal frameRadius);
|
|
||||||
|
|
||||||
protected Q_SLOTS:
|
|
||||||
//* unregister widget
|
|
||||||
void objectDeleted(QObject *);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
//* true if widget is a menu
|
|
||||||
bool isMenu(QWidget *) const;
|
|
||||||
|
|
||||||
//* true if widget is a tooltip
|
|
||||||
bool isToolTip(QWidget *) const;
|
|
||||||
|
|
||||||
//* dock widget
|
|
||||||
bool isDockWidget(QWidget *) const;
|
|
||||||
|
|
||||||
//* toolbar
|
|
||||||
bool isToolBar(QWidget *) const;
|
|
||||||
|
|
||||||
//* accept widget
|
|
||||||
bool acceptWidget(QWidget *) const;
|
|
||||||
|
|
||||||
// create shadow tile from pixmap
|
|
||||||
KWindowShadowTile::Ptr createTile(const QPixmap &);
|
|
||||||
|
|
||||||
//* installs shadow on given widget in a platform independent way
|
|
||||||
// void installShadows( QWidget * );
|
|
||||||
|
|
||||||
void installShadows(QWidget *widget, TileSet shadowTiles);
|
|
||||||
|
|
||||||
//* uninstalls shadow on given widget in a platform independent way
|
|
||||||
void uninstallShadows(QWidget *);
|
|
||||||
|
|
||||||
//* gets the shadow margins for the given widget
|
|
||||||
QMargins shadowMargins(QWidget*, TileSet) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
//* registered widgets
|
|
||||||
QSet<QWidget *> m_widgets;
|
|
||||||
|
|
||||||
//* managed shadows
|
|
||||||
QMap<QWidget *, KWindowShadow *> m_shadows;
|
|
||||||
|
|
||||||
qreal m_frameRadius;
|
|
||||||
|
|
||||||
//* number of tiles
|
|
||||||
enum { numTiles = 8 };
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,183 +0,0 @@
|
|||||||
/*************************************************************************
|
|
||||||
* Copyright (C) 2014 by Hugo Pereira Da Costa <hugo.pereira@free.fr> *
|
|
||||||
* *
|
|
||||||
* This program is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU General Public License as published by *
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or *
|
|
||||||
* (at your option) any later version. *
|
|
||||||
* *
|
|
||||||
* This program 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 General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU General Public License *
|
|
||||||
* along with this program; if not, write to the *
|
|
||||||
* Free Software Foundation, Inc., *
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
|
|
||||||
*************************************************************************/
|
|
||||||
|
|
||||||
#include "tileset.h"
|
|
||||||
|
|
||||||
#include <QPainter>
|
|
||||||
|
|
||||||
//___________________________________________________________
|
|
||||||
inline bool bits(TileSet::Tiles flags, TileSet::Tiles testFlags)
|
|
||||||
{ return (flags & testFlags) == testFlags; }
|
|
||||||
|
|
||||||
//______________________________________________________________________________________
|
|
||||||
inline qreal devicePixelRatio( const QPixmap& pixmap )
|
|
||||||
{
|
|
||||||
return pixmap.devicePixelRatio();
|
|
||||||
}
|
|
||||||
|
|
||||||
//______________________________________________________________________________________
|
|
||||||
inline void setDevicePixelRatio( QPixmap& pixmap, qreal value )
|
|
||||||
{
|
|
||||||
return pixmap.setDevicePixelRatio( value );
|
|
||||||
}
|
|
||||||
|
|
||||||
//______________________________________________________________
|
|
||||||
void TileSet::initPixmap( PixmapList& pixmaps, const QPixmap &source, int width, int height, const QRect &rect)
|
|
||||||
{
|
|
||||||
QSize size( width, height );
|
|
||||||
if( !( size.isValid() && rect.isValid() ) )
|
|
||||||
{
|
|
||||||
pixmaps.append( QPixmap() );
|
|
||||||
|
|
||||||
} else if( size != rect.size() ) {
|
|
||||||
|
|
||||||
const qreal dpiRatio( devicePixelRatio( source ) );
|
|
||||||
const QRect scaledRect( rect.topLeft()*dpiRatio, rect.size()*dpiRatio );
|
|
||||||
const QSize scaledSize( size*dpiRatio );
|
|
||||||
const QPixmap tile( source.copy(scaledRect) );
|
|
||||||
QPixmap pixmap( scaledSize );
|
|
||||||
|
|
||||||
pixmap.fill(Qt::transparent);
|
|
||||||
QPainter painter(&pixmap);
|
|
||||||
painter.drawTiledPixmap(0, 0, scaledSize.width(), scaledSize.height(), tile);
|
|
||||||
setDevicePixelRatio( pixmap, dpiRatio );
|
|
||||||
pixmaps.append( pixmap );
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
const qreal dpiRatio( devicePixelRatio( source ) );
|
|
||||||
const QRect scaledRect( rect.topLeft()*dpiRatio, rect.size()*dpiRatio );
|
|
||||||
QPixmap pixmap( source.copy( scaledRect ) );
|
|
||||||
setDevicePixelRatio( pixmap, dpiRatio );
|
|
||||||
pixmaps.append( pixmap );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//______________________________________________________________
|
|
||||||
TileSet::TileSet():
|
|
||||||
_w1(0),
|
|
||||||
_h1(0),
|
|
||||||
_w3(0),
|
|
||||||
_h3(0)
|
|
||||||
{ _pixmaps.reserve(9); }
|
|
||||||
|
|
||||||
//______________________________________________________________
|
|
||||||
TileSet::TileSet(const QPixmap &source, int w1, int h1, int w2, int h2 ):
|
|
||||||
_w1(w1),
|
|
||||||
_h1(h1),
|
|
||||||
_w3(0),
|
|
||||||
_h3(0)
|
|
||||||
{
|
|
||||||
_pixmaps.reserve(9);
|
|
||||||
if( source.isNull() ) return;
|
|
||||||
|
|
||||||
_w3 = source.width()/devicePixelRatio( source ) - (w1 + w2);
|
|
||||||
_h3 = source.height()/devicePixelRatio( source ) - (h1 + h2);
|
|
||||||
int w = w2;
|
|
||||||
int h = h2;
|
|
||||||
|
|
||||||
// initialise pixmap array
|
|
||||||
initPixmap( _pixmaps, source, _w1, _h1, QRect(0, 0, _w1, _h1) );
|
|
||||||
initPixmap( _pixmaps, source, w, _h1, QRect(_w1, 0, w2, _h1) );
|
|
||||||
initPixmap( _pixmaps, source, _w3, _h1, QRect(_w1+w2, 0, _w3, _h1) );
|
|
||||||
initPixmap( _pixmaps, source, _w1, h, QRect(0, _h1, _w1, h2) );
|
|
||||||
initPixmap( _pixmaps, source, w, h, QRect(_w1, _h1, w2, h2) );
|
|
||||||
initPixmap( _pixmaps, source, _w3, h, QRect(_w1+w2, _h1, _w3, h2) );
|
|
||||||
initPixmap( _pixmaps, source, _w1, _h3, QRect(0, _h1+h2, _w1, _h3) );
|
|
||||||
initPixmap( _pixmaps, source, w, _h3, QRect(_w1, _h1+h2, w2, _h3) );
|
|
||||||
initPixmap( _pixmaps, source, _w3, _h3, QRect(_w1+w2, _h1+h2, _w3, _h3) );
|
|
||||||
}
|
|
||||||
|
|
||||||
//___________________________________________________________
|
|
||||||
void TileSet::render(const QRect &constRect, QPainter *painter, Tiles tiles) const
|
|
||||||
{
|
|
||||||
|
|
||||||
const bool oldHint( painter->testRenderHint( QPainter::SmoothPixmapTransform ) );
|
|
||||||
painter->setRenderHint( QPainter::SmoothPixmapTransform, true );
|
|
||||||
|
|
||||||
// check initialization
|
|
||||||
if( _pixmaps.size() < 9 ) return;
|
|
||||||
|
|
||||||
// copy source rect
|
|
||||||
QRect rect( constRect );
|
|
||||||
|
|
||||||
// get rect dimensions
|
|
||||||
int x0, y0, w, h;
|
|
||||||
rect.getRect(&x0, &y0, &w, &h);
|
|
||||||
|
|
||||||
// calculate pixmaps widths
|
|
||||||
int wLeft(0);
|
|
||||||
int wRight(0);
|
|
||||||
if( _w1+_w3 > 0 )
|
|
||||||
{
|
|
||||||
qreal wRatio( qreal( _w1 )/qreal( _w1 + _w3 ) );
|
|
||||||
wLeft = (tiles&Right) ? qMin( _w1, int(w*wRatio) ):_w1;
|
|
||||||
wRight = (tiles&Left) ? qMin( _w3, int(w*(1.0-wRatio)) ):_w3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate pixmap heights
|
|
||||||
int hTop(0);
|
|
||||||
int hBottom(0);
|
|
||||||
if( _h1+_h3 > 0 )
|
|
||||||
{
|
|
||||||
qreal hRatio( qreal( _h1 )/qreal( _h1 + _h3 ) );
|
|
||||||
hTop = (tiles&Bottom) ? qMin( _h1, int(h*hRatio) ):_h1;
|
|
||||||
hBottom = (tiles&Top) ? qMin( _h3, int(h*(1.0-hRatio)) ):_h3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate corner locations
|
|
||||||
w -= wLeft + wRight;
|
|
||||||
h -= hTop + hBottom;
|
|
||||||
const int x1 = x0 + wLeft;
|
|
||||||
const int x2 = x1 + w;
|
|
||||||
const int y1 = y0 + hTop;
|
|
||||||
const int y2 = y1 + h;
|
|
||||||
|
|
||||||
const int w2 = _pixmaps.at(7).width()/devicePixelRatio( _pixmaps.at(7) );
|
|
||||||
const int h2 = _pixmaps.at(5).height()/devicePixelRatio( _pixmaps.at(5) );
|
|
||||||
|
|
||||||
// corner
|
|
||||||
if( bits( tiles, Top|Left) ) painter->drawPixmap(x0, y0, _pixmaps.at(0), 0, 0, wLeft*devicePixelRatio( _pixmaps.at(0) ), hTop*devicePixelRatio( _pixmaps.at(0) ));
|
|
||||||
if( bits( tiles, Top|Right) ) painter->drawPixmap(x2, y0, _pixmaps.at(2), (_w3-wRight)*devicePixelRatio( _pixmaps.at(2) ), 0, wRight*devicePixelRatio( _pixmaps.at(2) ), hTop*devicePixelRatio( _pixmaps.at(2) ) );
|
|
||||||
if( bits( tiles, Bottom|Left) ) painter->drawPixmap(x0, y2, _pixmaps.at(6), 0, (_h3-hBottom)*devicePixelRatio( _pixmaps.at(6) ), wLeft*devicePixelRatio( _pixmaps.at(6) ), hBottom*devicePixelRatio( _pixmaps.at(6) ));
|
|
||||||
if( bits( tiles, Bottom|Right) ) painter->drawPixmap(x2, y2, _pixmaps.at(8), (_w3-wRight)*devicePixelRatio( _pixmaps.at(8) ), (_h3-hBottom)*devicePixelRatio( _pixmaps.at(8) ), wRight*devicePixelRatio( _pixmaps.at(8) ), hBottom*devicePixelRatio( _pixmaps.at(8) ) );
|
|
||||||
|
|
||||||
// top and bottom
|
|
||||||
if( w > 0 )
|
|
||||||
{
|
|
||||||
if( tiles&Top ) painter->drawPixmap(x1, y0, w, hTop, _pixmaps.at(1), 0, 0, w2*devicePixelRatio( _pixmaps.at(1) ), hTop*devicePixelRatio( _pixmaps.at(1) ) );
|
|
||||||
if( tiles&Bottom ) painter->drawPixmap(x1, y2, w, hBottom, _pixmaps.at(7), 0, (_h3-hBottom)*devicePixelRatio( _pixmaps.at(7) ), w2*devicePixelRatio( _pixmaps.at(7) ), hBottom*devicePixelRatio( _pixmaps.at(7) ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// left and right
|
|
||||||
if( h > 0 )
|
|
||||||
{
|
|
||||||
if( tiles&Left ) painter->drawPixmap(x0, y1, wLeft, h, _pixmaps.at(3), 0, 0, wLeft*devicePixelRatio( _pixmaps.at(3) ), h2*devicePixelRatio( _pixmaps.at(3) ) );
|
|
||||||
if( tiles&Right ) painter->drawPixmap(x2, y1, wRight, h, _pixmaps.at(5), (_w3-wRight)*devicePixelRatio( _pixmaps.at(5) ), 0, wRight*devicePixelRatio( _pixmaps.at(5) ), h2*devicePixelRatio( _pixmaps.at(5) ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// center
|
|
||||||
if( (tiles&Center) && h > 0 && w > 0 ) painter->drawPixmap(x1, y1, w, h, _pixmaps.at(4));
|
|
||||||
|
|
||||||
// restore
|
|
||||||
painter->setRenderHint( QPainter::SmoothPixmapTransform, oldHint );
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,121 +0,0 @@
|
|||||||
#ifndef TILESET_H
|
|
||||||
#define TILESET_H
|
|
||||||
|
|
||||||
/*************************************************************************
|
|
||||||
* Copyright (C) 2014 by Hugo Pereira Da Costa <hugo.pereira@free.fr> *
|
|
||||||
* *
|
|
||||||
* This program is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU General Public License as published by *
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or *
|
|
||||||
* (at your option) any later version. *
|
|
||||||
* *
|
|
||||||
* This program 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 General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU General Public License *
|
|
||||||
* along with this program; if not, write to the *
|
|
||||||
* Free Software Foundation, Inc., *
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
|
|
||||||
*************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
#include <QPixmap>
|
|
||||||
#include <QRect>
|
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
//* handles proper scaling of pixmap to match widget rect.
|
|
||||||
/**
|
|
||||||
tilesets are collections of stretchable pixmaps corresponding to a given widget corners, sides, and center.
|
|
||||||
corner pixmaps are never stretched. center pixmaps are
|
|
||||||
*/
|
|
||||||
class TileSet
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
Create a TileSet from a pixmap. The size of the bottom/right chunks is
|
|
||||||
whatever is left over from the other chunks, whose size is specified
|
|
||||||
in the required parameters.
|
|
||||||
|
|
||||||
@param w1 width of the left chunks
|
|
||||||
@param h1 height of the top chunks
|
|
||||||
@param w2 width of the not-left-or-right chunks
|
|
||||||
@param h2 height of the not-top-or-bottom chunks
|
|
||||||
*/
|
|
||||||
TileSet(const QPixmap&, int w1, int h1, int w2, int h2 );
|
|
||||||
|
|
||||||
//* empty constructor
|
|
||||||
TileSet();
|
|
||||||
|
|
||||||
//* destructor
|
|
||||||
virtual ~TileSet()
|
|
||||||
{}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Flags specifying what sides to draw in ::render. Corners are drawn when
|
|
||||||
the sides forming that corner are drawn, e.g. Top|Left draws the
|
|
||||||
top-center, center-left, and top-left chunks. The center-center chunk is
|
|
||||||
only drawn when Center is requested.
|
|
||||||
*/
|
|
||||||
enum Tile {
|
|
||||||
Top = 0x1,
|
|
||||||
Left = 0x2,
|
|
||||||
Bottom = 0x4,
|
|
||||||
Right = 0x8,
|
|
||||||
Center = 0x10,
|
|
||||||
TopLeft = Top|Left,
|
|
||||||
TopRight = Top|Right,
|
|
||||||
BottomLeft = Bottom|Left,
|
|
||||||
BottomRight = Bottom|Right,
|
|
||||||
Ring = Top|Left|Bottom|Right,
|
|
||||||
Horizontal = Left|Right|Center,
|
|
||||||
Vertical = Top|Bottom|Center,
|
|
||||||
Full = Ring|Center
|
|
||||||
};
|
|
||||||
Q_DECLARE_FLAGS(Tiles, Tile)
|
|
||||||
|
|
||||||
/**
|
|
||||||
Fills the specified rect with tiled chunks. Corners are never tiled,
|
|
||||||
edges are tiled in one direction, and the center chunk is tiled in both
|
|
||||||
directions. Partial tiles are used as needed so that the entire rect is
|
|
||||||
perfectly filled. Filling is performed as if all chunks are being drawn.
|
|
||||||
*/
|
|
||||||
void render(const QRect&, QPainter*, Tiles = Ring) const;
|
|
||||||
|
|
||||||
//* return size associated to this tileset
|
|
||||||
QSize size() const
|
|
||||||
{ return QSize( _w1 + _w3, _h1 + _h3 ); }
|
|
||||||
|
|
||||||
//* is valid
|
|
||||||
bool isValid() const
|
|
||||||
{ return _pixmaps.size() == 9; }
|
|
||||||
|
|
||||||
//* returns pixmap for given index
|
|
||||||
QPixmap pixmap( int index ) const
|
|
||||||
{ return _pixmaps[index]; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
//* shortcut to pixmap list
|
|
||||||
using PixmapList = QVector<QPixmap>;
|
|
||||||
|
|
||||||
//* initialize pixmap
|
|
||||||
void initPixmap( PixmapList&, const QPixmap&, int w, int h, const QRect& );
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
//* pixmap arry
|
|
||||||
PixmapList _pixmaps;
|
|
||||||
|
|
||||||
// dimensions
|
|
||||||
int _w1;
|
|
||||||
int _h1;
|
|
||||||
int _w3;
|
|
||||||
int _h3;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(TileSet::Tiles)
|
|
||||||
|
|
||||||
#endif //TILESET_H
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QGuiApplication>
|
|
||||||
#include <QtGui/qguiapplication_platform.h>
|
|
||||||
|
|
||||||
#include <xcb/xcb.h>
|
|
||||||
|
|
||||||
namespace Cutefish::X11
|
|
||||||
{
|
|
||||||
inline xcb_window_t rootWindow()
|
|
||||||
{
|
|
||||||
const auto native = qGuiApp ? qGuiApp->nativeInterface<QNativeInterface::QX11Application>() : nullptr;
|
|
||||||
const auto c = native ? native->connection() : nullptr;
|
|
||||||
if (!c)
|
|
||||||
return XCB_WINDOW_NONE;
|
|
||||||
|
|
||||||
const auto *setup = xcb_get_setup(c);
|
|
||||||
const auto iterator = xcb_setup_roots_iterator(setup);
|
|
||||||
return iterator.data ? iterator.data->root : XCB_WINDOW_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue