MauiKit Controls

android/shadowhelper/windowshadow.cpp
1/*
2 * Copyright (C) 2021 CutefishOS Team.
3 *
4 * Author: revenmartin <revenmartin@gmail.com>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "windowshadow.h"
21#include "boxshadowrenderer.h"
22#include <QDebug>
23
24enum {
25 ShadowNone,
26 ShadowSmall,
27 ShadowMedium,
28 ShadowLarge,
29 ShadowVeryLarge
30};
31
32const CompositeShadowParams s_shadowParams[] = {
33 // None
34 CompositeShadowParams(),
35 // Small
36 CompositeShadowParams(
37 QPoint(0, 3),
38 ShadowParams(QPoint(0, 0), 16, 0.26),
39 ShadowParams(QPoint(0, -2), 8, 0.16)),
40 // Medium
41 CompositeShadowParams(
42 QPoint(0, 4),
43 ShadowParams(QPoint(0, 0), 20, 0.24),
44 ShadowParams(QPoint(0, -2), 10, 0.14)),
45 // Large
46 CompositeShadowParams(
47 QPoint(0, 5),
48 ShadowParams(QPoint(0, 0), 24, 0.22),
49 ShadowParams(QPoint(0, -3), 12, 0.12)),
50 // Very Large
51 CompositeShadowParams(
52 QPoint(0, 6),
53 ShadowParams(QPoint(0, 0), 32, 0.1),
54 ShadowParams(QPoint(0, -3), 16, 0.05))
55};
56
57WindowShadow::WindowShadow(QObject *parent) noexcept
58 : QObject(parent)
59 , m_view(nullptr)
60{
61
62}
63
64WindowShadow::~WindowShadow()
65{
66}
67
68CompositeShadowParams WindowShadow::lookupShadowParams(int shadowSizeEnum)
69{
70 switch (shadowSizeEnum) {
71 case ShadowNone:
72 return s_shadowParams[0];
73 case ShadowSmall:
74 return s_shadowParams[1];
75 case ShadowMedium:
76 return s_shadowParams[2];
77 case ShadowLarge:
78 return s_shadowParams[3];
79 case ShadowVeryLarge:
80 return s_shadowParams[4];
81 default:
82 // Fallback to the Large size.
83 return s_shadowParams[3];
84 }
85}
86
87void WindowShadow::classBegin()
88{
89 m_shadowTiles = this->shadowTiles();
90}
91
92void WindowShadow::componentComplete()
93{
94 configureTiles();
95}
96
97void WindowShadow::setView(QWindow *view)
98{
99 if (view != m_view) {
100 m_view = view;
101 emit viewChanged();
102 configureTiles();
103
104 connect(m_view, &QWindow::visibleChanged, this, &WindowShadow::onViewVisibleChanged);
105 }
106}
107
108QWindow *WindowShadow::view() const
109{
110 return m_view;
111}
112
113void WindowShadow::setGeometry(const QRect &rect)
114{
115 if (rect != m_rect) {
116 m_rect = rect;
117 emit geometryChanged();
118 configureTiles();
119 }
120}
121
122QRect WindowShadow::geometry() const
123{
124 return m_rect;
125}
126
127void WindowShadow::setRadius(qreal value)
128{
129 if (m_radius != value) {
130 m_radius = value;
131 emit radiusChanged();
132
133 this->classBegin();
134
135 configureTiles();
136 }
137}
138
139qreal WindowShadow::strength() const
140{
141 return m_strength;
142}
143
144void WindowShadow::setStrength(qreal strength)
145{
146 if (m_strength != strength) {
147 m_strength = strength;
148
149 this->classBegin();
150 configureTiles();
151
152 emit strengthChanged();
153 }
154}
155
156void WindowShadow::onViewVisibleChanged(bool visible)
157{
158 if (visible && m_view) {
159 configureTiles();
160 }
161}
162
163void WindowShadow::configureTiles()
164{
165}
166
167TileSet WindowShadow::shadowTiles()
168{
169 const qreal frameRadius = m_radius;
170 const CompositeShadowParams params = lookupShadowParams(ShadowVeryLarge);
171
172 if (params.isNone())
173 return TileSet();
174
175 auto withOpacity = [](const QColor &color, qreal opacity) -> QColor {
176 QColor c(color);
177 c.setAlphaF(opacity);
178 return c;
179 };
180
181 const QColor color = Qt::black;
182 const qreal strength = m_strength;
183
184 const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius)
185 .expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius));
186
187 const qreal dpr = qApp->devicePixelRatio();
188
189 BoxShadowRenderer shadowRenderer;
190 shadowRenderer.setBorderRadius(frameRadius);
191 shadowRenderer.setBoxSize(boxSize);
192 shadowRenderer.setDevicePixelRatio(dpr);
193
194 shadowRenderer.addShadow(params.shadow1.offset, params.shadow1.radius,
195 withOpacity(color, params.shadow1.opacity * strength));
196 shadowRenderer.addShadow(params.shadow2.offset, params.shadow2.radius,
197 withOpacity(color, params.shadow2.opacity * strength));
198
199 QImage shadowTexture = shadowRenderer.render();
200
201 const QRect outerRect(QPoint(0, 0), shadowTexture.size() / dpr);
202
203 QRect boxRect(QPoint(0, 0), boxSize);
204 boxRect.moveCenter(outerRect.center());
205
206 // Mask out inner rect.
207 QPainter painter(&shadowTexture);
208 painter.setRenderHint(QPainter::Antialiasing);
209
210 int Shadow_Overlap = 3;
211 const QMargins margins = QMargins(
212 boxRect.left() - outerRect.left() - Shadow_Overlap - params.offset.x(),
213 boxRect.top() - outerRect.top() - Shadow_Overlap - params.offset.y(),
214 outerRect.right() - boxRect.right() - Shadow_Overlap + params.offset.x(),
215 outerRect.bottom() - boxRect.bottom() - Shadow_Overlap + params.offset.y());
216
217 painter.setPen(Qt::NoPen);
218 painter.setBrush(Qt::black);
219 painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
220 painter.drawRoundedRect(
221 outerRect - margins,
222 frameRadius,
223 frameRadius);
224
225 // We're done.
226 painter.end();
227
228 const QPoint innerRectTopLeft = outerRect.center();
229 TileSet tiles = TileSet(
230 QPixmap::fromImage(shadowTexture),
231 innerRectTopLeft.x(),
232 innerRectTopLeft.y(),
233 1, 1);
234
235 return tiles;
236}
237
238QMargins WindowShadow::shadowMargins(TileSet shadowTiles) const
239{
240 const CompositeShadowParams params = lookupShadowParams(ShadowVeryLarge);
241 if (params.isNone())
242 return QMargins();
243
244 const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius)
245 .expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius));
246
247 const QSize shadowSize = BoxShadowRenderer::calculateMinimumShadowTextureSize(boxSize, params.shadow1.radius, params.shadow1.offset)
248 .expandedTo(BoxShadowRenderer::calculateMinimumShadowTextureSize(boxSize, params.shadow2.radius, params.shadow2.offset));
249
250 const QRect shadowRect(QPoint(0, 0), shadowSize);
251
252 QRect boxRect(QPoint(0, 0), boxSize);
253 boxRect.moveCenter(shadowRect.center());
254
255 int Shadow_Overlap = 4;
256 QMargins margins(
257 boxRect.left() - shadowRect.left() - Shadow_Overlap - params.offset.x(),
258 boxRect.top() - shadowRect.top() - Shadow_Overlap - params.offset.y(),
259 shadowRect.right() - boxRect.right() - Shadow_Overlap + params.offset.x(),
260 shadowRect.bottom() - boxRect.bottom() - Shadow_Overlap + params.offset.y());
261
262 margins *= shadowTiles.pixmap(0).devicePixelRatio();
263
264 return margins;
265}
tilesets are collections of stretchable pixmaps corresponding to a given widget corners,...
QSize size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
CompositionMode_DestinationOut
qreal devicePixelRatio() const const
QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
int x() const const
int y() const const
QSize expandedTo(const QSize &otherSize) const const
void visibleChanged(bool arg)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 17 2024 11:56:16 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.