KWin
Loading...
Searching...
No Matches
previewitem.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5*/
6#include "previewitem.h"
7#include "previewbridge.h"
8#include "previewclient.h"
9#include "previewsettings.h"
10#include <KDecoration2/DecoratedClient>
11#include <KDecoration2/Decoration>
12#include <KDecoration2/DecorationSettings>
13#include <KDecoration2/DecorationShadow>
14#include <QCoreApplication>
15#include <QCursor>
16#include <QPainter>
17#include <QQmlContext>
18#include <QQmlEngine>
19
20#include <cmath>
21
22#include <QDebug>
23
24namespace KDecoration2
25{
26namespace Preview
27{
28
29PreviewItem::PreviewItem(QQuickItem *parent)
30 : QQuickPaintedItem(parent)
31 , m_decoration(nullptr)
32 , m_windowColor(QPalette().window().color())
33{
34 setAcceptHoverEvents(true);
35 setAcceptedMouseButtons(Qt::AllButtons);
36 connect(this, &PreviewItem::widthChanged, this, &PreviewItem::syncSize);
37 connect(this, &PreviewItem::heightChanged, this, &PreviewItem::syncSize);
38 connect(this, &PreviewItem::bridgeChanged, this, &PreviewItem::createDecoration);
39 connect(this, &PreviewItem::settingsChanged, this, &PreviewItem::createDecoration);
40}
41
43{
44 m_decoration->deleteLater();
45 if (m_bridge) {
46 m_bridge->unregisterPreviewItem(this);
47 }
48}
49
51{
52 QQuickPaintedItem::componentComplete();
53 createDecoration();
54 if (m_decoration) {
55 m_decoration->setSettings(m_settings->settings());
56 m_decoration->init();
57 syncSize();
58 }
59}
60
61void PreviewItem::createDecoration()
62{
63 if (m_bridge.isNull() || m_settings.isNull() || m_decoration) {
64 return;
65 }
66 Decoration *decoration = m_bridge->createDecoration(nullptr);
67 m_client = m_bridge->lastCreatedClient();
69}
70
71Decoration *PreviewItem::decoration() const
72{
73 return m_decoration;
74}
75
76void PreviewItem::setDecoration(Decoration *deco)
77{
78 if (!deco || m_decoration == deco) {
79 return;
80 }
81
82 m_decoration = deco;
83 m_decoration->setProperty("visualParent", QVariant::fromValue(this));
84 connect(m_decoration, &Decoration::bordersChanged, this, &PreviewItem::syncSize);
85 connect(m_decoration, &Decoration::shadowChanged, this, &PreviewItem::syncSize);
86 connect(m_decoration, &Decoration::shadowChanged, this, &PreviewItem::shadowChanged);
87 connect(m_decoration, &Decoration::damaged, this, [this]() {
88 update();
89 });
90 Q_EMIT decorationChanged(m_decoration);
91}
92
94{
95 return m_windowColor;
96}
97
98void PreviewItem::setWindowColor(const QColor &color)
99{
100 if (m_windowColor == color) {
101 return;
102 }
103 m_windowColor = color;
104 Q_EMIT windowColorChanged(m_windowColor);
105 update();
106}
107
108void PreviewItem::paint(QPainter *painter)
109{
110 if (!m_decoration) {
111 return;
112 }
113 int paddingLeft = 0;
114 int paddingTop = 0;
115 int paddingRight = 0;
116 int paddingBottom = 0;
117 paintShadow(painter, paddingLeft, paddingRight, paddingTop, paddingBottom);
118 m_decoration->paint(painter, QRect(0, 0, width(), height()));
119 if (m_drawBackground) {
120 painter->fillRect(m_decoration->borderLeft(), m_decoration->borderTop(),
121 width() - m_decoration->borderLeft() - m_decoration->borderRight() - paddingLeft - paddingRight,
122 height() - m_decoration->borderTop() - m_decoration->borderBottom() - paddingTop - paddingBottom,
123 m_windowColor);
124 }
125}
126
127void PreviewItem::paintShadow(QPainter *painter, int &paddingLeft, int &paddingRight, int &paddingTop, int &paddingBottom)
128{
129 const auto &shadow = m_decoration->shadow();
130 if (!shadow) {
131 return;
132 }
133
134 paddingLeft = shadow->paddingLeft();
135 paddingTop = shadow->paddingTop();
136 paddingRight = shadow->paddingRight();
137 paddingBottom = shadow->paddingBottom();
138
139 const QImage shadowPixmap = shadow->shadow();
140 if (shadowPixmap.isNull()) {
141 return;
142 }
143
144 const QRect outerRect(-paddingLeft, -paddingTop, width(), height());
145 const QRect shadowRect(shadowPixmap.rect());
146
147 const QSize topLeftSize(shadow->topLeftGeometry().size());
148 QRect topLeftTarget(
149 QPoint(outerRect.x(), outerRect.y()),
150 topLeftSize);
151
152 const QSize topRightSize(shadow->topRightGeometry().size());
153 QRect topRightTarget(
154 QPoint(outerRect.x() + outerRect.width() - topRightSize.width(),
155 outerRect.y()),
156 topRightSize);
157
158 const QSize bottomRightSize(shadow->bottomRightGeometry().size());
159 QRect bottomRightTarget(
160 QPoint(outerRect.x() + outerRect.width() - bottomRightSize.width(),
161 outerRect.y() + outerRect.height() - bottomRightSize.height()),
162 bottomRightSize);
163
164 const QSize bottomLeftSize(shadow->bottomLeftGeometry().size());
165 QRect bottomLeftTarget(
166 QPoint(outerRect.x(),
167 outerRect.y() + outerRect.height() - bottomLeftSize.height()),
168 bottomLeftSize);
169
170 // Re-distribute the corner tiles so no one of them is overlapping with others.
171 // By doing this, we assume that shadow's corner tiles are symmetric
172 // and it is OK to not draw top/right/bottom/left tile between corners.
173 // For example, let's say top-left and top-right tiles are overlapping.
174 // In that case, the right side of the top-left tile will be shifted to left,
175 // the left side of the top-right tile will shifted to right, and the top
176 // tile won't be rendered.
177 bool drawTop = true;
178 if (topLeftTarget.x() + topLeftTarget.width() >= topRightTarget.x()) {
179 const float halfOverlap = std::abs(topLeftTarget.x() + topLeftTarget.width() - topRightTarget.x()) / 2.0f;
180 topLeftTarget.setRight(topLeftTarget.right() - std::floor(halfOverlap));
181 topRightTarget.setLeft(topRightTarget.left() + std::ceil(halfOverlap));
182 drawTop = false;
183 }
184
185 bool drawRight = true;
186 if (topRightTarget.y() + topRightTarget.height() >= bottomRightTarget.y()) {
187 const float halfOverlap = std::abs(topRightTarget.y() + topRightTarget.height() - bottomRightTarget.y()) / 2.0f;
188 topRightTarget.setBottom(topRightTarget.bottom() - std::floor(halfOverlap));
189 bottomRightTarget.setTop(bottomRightTarget.top() + std::ceil(halfOverlap));
190 drawRight = false;
191 }
192
193 bool drawBottom = true;
194 if (bottomLeftTarget.x() + bottomLeftTarget.width() >= bottomRightTarget.x()) {
195 const float halfOverlap = std::abs(bottomLeftTarget.x() + bottomLeftTarget.width() - bottomRightTarget.x()) / 2.0f;
196 bottomLeftTarget.setRight(bottomLeftTarget.right() - std::floor(halfOverlap));
197 bottomRightTarget.setLeft(bottomRightTarget.left() + std::ceil(halfOverlap));
198 drawBottom = false;
199 }
200
201 bool drawLeft = true;
202 if (topLeftTarget.y() + topLeftTarget.height() >= bottomLeftTarget.y()) {
203 const float halfOverlap = std::abs(topLeftTarget.y() + topLeftTarget.height() - bottomLeftTarget.y()) / 2.0f;
204 topLeftTarget.setBottom(topLeftTarget.bottom() - std::floor(halfOverlap));
205 bottomLeftTarget.setTop(bottomLeftTarget.top() + std::ceil(halfOverlap));
206 drawLeft = false;
207 }
208
209 painter->translate(paddingLeft, paddingTop);
210
211 painter->drawImage(topLeftTarget, shadowPixmap,
212 QRect(QPoint(0, 0), topLeftTarget.size()));
213
214 painter->drawImage(topRightTarget, shadowPixmap,
215 QRect(QPoint(shadowRect.width() - topRightTarget.width(), 0),
216 topRightTarget.size()));
217
218 painter->drawImage(bottomRightTarget, shadowPixmap,
219 QRect(QPoint(shadowRect.width() - bottomRightTarget.width(),
220 shadowRect.height() - bottomRightTarget.height()),
221 bottomRightTarget.size()));
222
223 painter->drawImage(bottomLeftTarget, shadowPixmap,
224 QRect(QPoint(0, shadowRect.height() - bottomLeftTarget.height()),
225 bottomLeftTarget.size()));
226
227 if (drawTop) {
228 QRect topTarget(topLeftTarget.x() + topLeftTarget.width(),
229 topLeftTarget.y(),
230 topRightTarget.x() - topLeftTarget.x() - topLeftTarget.width(),
231 topRightTarget.height());
232 QRect topSource(shadow->topGeometry());
233 topSource.setHeight(topTarget.height());
234 topSource.moveTop(shadowRect.top());
235 painter->drawImage(topTarget, shadowPixmap, topSource);
236 }
237
238 if (drawRight) {
239 QRect rightTarget(topRightTarget.x(),
240 topRightTarget.y() + topRightTarget.height(),
241 topRightTarget.width(),
242 bottomRightTarget.y() - topRightTarget.y() - topRightTarget.height());
243 QRect rightSource(shadow->rightGeometry());
244 rightSource.setWidth(rightTarget.width());
245 rightSource.moveRight(shadowRect.right());
246 painter->drawImage(rightTarget, shadowPixmap, rightSource);
247 }
248
249 if (drawBottom) {
250 QRect bottomTarget(bottomLeftTarget.x() + bottomLeftTarget.width(),
251 bottomLeftTarget.y(),
252 bottomRightTarget.x() - bottomLeftTarget.x() - bottomLeftTarget.width(),
253 bottomRightTarget.height());
254 QRect bottomSource(shadow->bottomGeometry());
255 bottomSource.setHeight(bottomTarget.height());
256 bottomSource.moveBottom(shadowRect.bottom());
257 painter->drawImage(bottomTarget, shadowPixmap, bottomSource);
258 }
259
260 if (drawLeft) {
261 QRect leftTarget(topLeftTarget.x(),
262 topLeftTarget.y() + topLeftTarget.height(),
263 topLeftTarget.width(),
264 bottomLeftTarget.y() - topLeftTarget.y() - topLeftTarget.height());
265 QRect leftSource(shadow->leftGeometry());
266 leftSource.setWidth(leftTarget.width());
267 leftSource.moveLeft(shadowRect.left());
268 painter->drawImage(leftTarget, shadowPixmap, leftSource);
269 }
270}
271
272static QMouseEvent cloneEventWithPadding(QMouseEvent *event, int paddingLeft, int paddingTop)
273{
274 return QMouseEvent(
275 event->type(),
276 event->localPos() - QPointF(paddingLeft, paddingTop),
277 event->button(),
278 event->buttons(),
279 event->modifiers());
280}
281
282static QHoverEvent cloneEventWithPadding(QHoverEvent *event, int paddingLeft, int paddingTop)
283{
284 return QHoverEvent(
285 event->type(),
286 event->posF() - QPointF(paddingLeft, paddingTop),
287 event->oldPosF() - QPointF(paddingLeft, paddingTop),
288 event->modifiers());
289}
290
291template <typename E>
292void PreviewItem::proxyPassEvent(E *event) const
293{
294 const auto &shadow = m_decoration->shadow();
295 if (shadow) {
296 E e = cloneEventWithPadding(event, shadow->paddingLeft(), shadow->paddingTop());
297 QCoreApplication::instance()->sendEvent(decoration(), &e);
298 } else {
299 QCoreApplication::instance()->sendEvent(decoration(), event);
300 }
301 // Propagate events to parent
302 event->ignore();
303}
304
306{
307 proxyPassEvent(event);
308}
309
310void PreviewItem::mousePressEvent(QMouseEvent *event)
311{
312 proxyPassEvent(event);
313}
314
315void PreviewItem::mouseReleaseEvent(QMouseEvent *event)
316{
317 proxyPassEvent(event);
318}
319
320void PreviewItem::mouseMoveEvent(QMouseEvent *event)
321{
322 proxyPassEvent(event);
323}
324
325void PreviewItem::hoverEnterEvent(QHoverEvent *event)
326{
327 proxyPassEvent(event);
328}
329
330void PreviewItem::hoverLeaveEvent(QHoverEvent *event)
331{
332 proxyPassEvent(event);
333}
334
335void PreviewItem::hoverMoveEvent(QHoverEvent *event)
336{
337 proxyPassEvent(event);
338}
339
341{
342 return m_drawBackground;
343}
344
346{
347 if (m_drawBackground == set) {
348 return;
349 }
350 m_drawBackground = set;
351 Q_EMIT drawingBackgroundChanged(set);
352}
353
355{
356 return m_bridge.data();
357}
358
360{
361 if (m_bridge == bridge) {
362 return;
363 }
364 if (m_bridge) {
365 m_bridge->unregisterPreviewItem(this);
366 }
367 m_bridge = bridge;
368 if (m_bridge) {
369 m_bridge->registerPreviewItem(this);
370 }
371 Q_EMIT bridgeChanged();
372}
373
375{
376 return m_settings.data();
377}
378
380{
381 if (m_settings == settings) {
382 return;
383 }
384 m_settings = settings;
385 Q_EMIT settingsChanged();
386}
387
389{
390 return m_client.data();
391}
392
393void PreviewItem::syncSize()
394{
395 if (!m_client) {
396 return;
397 }
398 int widthOffset = 0;
399 int heightOffset = 0;
400 auto shadow = m_decoration->shadow();
401 if (shadow) {
402 widthOffset = shadow->paddingLeft() + shadow->paddingRight();
403 heightOffset = shadow->paddingTop() + shadow->paddingBottom();
404 }
405 m_client->setWidth(width() - m_decoration->borderLeft() - m_decoration->borderRight() - widthOffset);
406 m_client->setHeight(height() - m_decoration->borderTop() - m_decoration->borderBottom() - heightOffset);
407}
408
409DecorationShadow *PreviewItem::shadow() const
410{
411 if (!m_decoration) {
412 return nullptr;
413 }
414 return m_decoration->shadow().get();
415}
416
417}
418}
419
420#include "moc_previewitem.cpp"
KDecoration2::DecorationShadow * shadow
Definition previewitem.h:30
void mousePressEvent(QMouseEvent *event) override
void hoverMoveEvent(QHoverEvent *event) override
void paint(QPainter *painter) override
void hoverEnterEvent(QHoverEvent *event) override
void setWindowColor(const QColor &color)
void setSettings(Settings *settings)
void decorationChanged(KDecoration2::Decoration *deco)
void mouseDoubleClickEvent(QMouseEvent *event) override
KDecoration2::Preview::PreviewClient * client
Definition previewitem.h:29
KDecoration2::Decoration * decoration
Definition previewitem.h:26
void hoverLeaveEvent(QHoverEvent *event) override
PreviewItem(QQuickItem *parent=nullptr)
void windowColorChanged(const QColor &color)
KDecoration2::Preview::PreviewBridge * bridge
Definition previewitem.h:27
KDecoration2::Preview::Settings * settings
Definition previewitem.h:28
void mouseMoveEvent(QMouseEvent *event) override
void mouseReleaseEvent(QMouseEvent *event) override
void setDecoration(KDecoration2::Decoration *deco)
void setBridge(PreviewBridge *bridge)