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