build: remove KF5WindowSystem when build with MSVC

pull/2/head
PikachuHy 4 years ago
parent fc7d3f5c44
commit 435ef1b8ee

@ -27,7 +27,9 @@ if (UNIX)
set(QT ${QT} X11Extras)
endif ()
find_package(Qt5 REQUIRED ${QT})
find_package(KF5WindowSystem REQUIRED)
if (UNIX)
find_package(KF5WindowSystem REQUIRED)
endif ()
include(GenerateExportHeader)
include(GNUInstallDirs)

@ -6,9 +6,6 @@ target_sources(${TARGET} PRIVATE
fishui.cpp
iconthemeprovider.cpp
thememanager.cpp
shadowhelper/windowshadow.cpp
shadowhelper/boxshadowrenderer.cpp
shadowhelper/tileset.cpp
iconitem.cpp
newiconitem.cpp
@ -24,12 +21,18 @@ if (MSVC)
target_sources(${TARGET} PRIVATE
platforms/windows/windowhelper.cpp platforms/windows/windowhelper.h
platforms/windows/blurhelper/windowblur.cpp platforms/windows/blurhelper/windowblur.h
platforms/windows/shadowhelper/boxshadowrenderer.cpp platforms/windows/shadowhelper/boxshadowrenderer.h
platforms/windows/shadowhelper/tileset.cpp platforms/windows/shadowhelper/tileset.h
platforms/windows/shadowhelper/windowshadow.cpp platforms/windows/shadowhelper/windowshadow.h
)
target_include_directories(${TARGET} PRIVATE platforms/windows)
else ()
target_sources(${TARGET} PRIVATE
platforms/linux/windowhelper.cpp platforms/linux/windowhelper.h
platforms/linux/blurhelper/windowblur.cpp platforms/linux/blurhelper/windowblur.h
platforms/linux/shadowhelper/boxshadowrenderer.cpp platforms/linux/shadowhelper/boxshadowrenderer.h
platforms/linux/shadowhelper/tileset.cpp platforms/linux/shadowhelper/tileset.h
platforms/linux/shadowhelper/windowshadow.cpp platforms/linux/shadowhelper/windowshadow.h
)
target_include_directories(${TARGET} PRIVATE platforms/linux)
endif ()
@ -43,10 +46,9 @@ target_link_libraries(${TARGET}
Qt5::Quick
Qt5::QuickControls2
Qt5::GuiPrivate
KF5::WindowSystem
)
if (UNIX)
target_link_libraries(${TARGET} PRIVATE Qt5::X11Extras)
target_link_libraries(${TARGET} PRIVATE Qt5::X11Extras KF5::WindowSystem)
endif ()
generate_export_header(${TARGET} BASE_NAME ${TARGET})

@ -0,0 +1,348 @@
/*
* 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()));
}

@ -0,0 +1,97 @@
/*
* 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;
};

@ -0,0 +1,183 @@
/*************************************************************************
* 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 );
}

@ -0,0 +1,121 @@
#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

@ -0,0 +1,265 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <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 3 of the License, or
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "windowshadow.h"
#include "boxshadowrenderer.h"
#include <QDebug>
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.1),
ShadowParams(QPoint(0, -3), 16, 0.05))
};
WindowShadow::WindowShadow(QObject *parent) noexcept
: QObject(parent)
, m_view(nullptr)
{
}
WindowShadow::~WindowShadow()
{
}
CompositeShadowParams WindowShadow::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];
}
}
void WindowShadow::classBegin()
{
m_shadowTiles = this->shadowTiles();
}
void WindowShadow::componentComplete()
{
configureTiles();
}
void WindowShadow::setView(QWindow *view)
{
if (view != m_view) {
m_view = view;
emit viewChanged();
configureTiles();
connect(m_view, &QWindow::visibleChanged, this, &WindowShadow::onViewVisibleChanged);
}
}
QWindow *WindowShadow::view() const
{
return m_view;
}
void WindowShadow::setGeometry(const QRect &rect)
{
if (rect != m_rect) {
m_rect = rect;
emit geometryChanged();
configureTiles();
}
}
QRect WindowShadow::geometry() const
{
return m_rect;
}
void WindowShadow::setRadius(qreal value)
{
if (m_radius != value) {
m_radius = value;
emit radiusChanged();
this->classBegin();
configureTiles();
}
}
qreal WindowShadow::strength() const
{
return m_strength;
}
void WindowShadow::setStrength(qreal strength)
{
if (m_strength != strength) {
m_strength = strength;
this->classBegin();
configureTiles();
emit strengthChanged();
}
}
void WindowShadow::onViewVisibleChanged(bool visible)
{
if (visible && m_view) {
configureTiles();
}
}
void WindowShadow::configureTiles()
{
}
TileSet WindowShadow::shadowTiles()
{
const qreal frameRadius = m_radius;
const CompositeShadowParams params = lookupShadowParams(ShadowVeryLarge);
if (params.isNone())
return TileSet();
auto withOpacity = [](const QColor &color, qreal opacity) -> QColor {
QColor c(color);
c.setAlphaF(opacity);
return c;
};
const QColor color = Qt::black;
const qreal strength = m_strength;
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;
}
QMargins WindowShadow::shadowMargins(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());
margins *= shadowTiles.pixmap(0).devicePixelRatio();
return margins;
}

@ -0,0 +1,127 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <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 3 of the License, or
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef WINDOWSHADOW_H
#define WINDOWSHADOW_H
#include "tileset.h"
#include <QApplication>
#include <QMap>
#include <QObject>
#include <QPainter>
#include <QPixmap>
#include <QQmlEngine>
#include <QQmlParserStatus>
#include <QRect>
#include <QWindow>
#include <QVector>
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;
};
class WindowShadow : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(QWindow *view READ view WRITE setView NOTIFY viewChanged)
Q_PROPERTY(QRect geometry READ geometry WRITE setGeometry NOTIFY geometryChanged)
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged)
public:
WindowShadow(QObject *parent = nullptr) noexcept;
~WindowShadow() override;
static CompositeShadowParams lookupShadowParams(int shadowSizeEnum);
void classBegin() override;
void componentComplete() override;
void setView(QWindow *view);
QWindow *view() const;
void setGeometry(const QRect &rect);
QRect geometry() const;
void setRadius(qreal value);
qreal radius() { return m_radius; }
qreal strength() const;
void setStrength(qreal strength);
private slots:
void onViewVisibleChanged(bool);
private:
void configureTiles();
TileSet shadowTiles();
QMargins shadowMargins(TileSet) const;
signals:
void geometryChanged();
void enabledChanged();
void viewChanged();
void edgesChanged();
void radiusChanged();
void strengthChanged();
private:
QWindow *m_view;
QRect m_rect;
TileSet m_shadowTiles;
qreal m_radius = 10;
qreal m_strength = 1.2;
};
#endif

@ -23,7 +23,6 @@
#include <QCursor>
#include <QDebug>
#include <KWindowSystem>
WindowHelper::WindowHelper(QObject *parent)
: QObject(parent)
@ -42,7 +41,7 @@ void WindowHelper::startSystemResize(QWindow *w, Qt::Edges edges)
void WindowHelper::minimizeWindow(QWindow *w)
{
KWindowSystem::minimizeWindow(w->winId());
qWarning() << "not implement";
}
void WindowHelper::doStartSystemMoveResize(QWindow *w, int edges)

Loading…
Cancel
Save