KDecoration2

decoration.cpp
1/*
2 * SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6#include "decoration.h"
7#include "decoratedclient.h"
8#include "decoration_p.h"
9#include "decorationbutton.h"
10#include "decorationsettings.h"
11#include "private/decoratedclientprivate.h"
12#include "private/decorationbridge.h"
13
14#include <QCoreApplication>
15#include <QHoverEvent>
16
17#include <cmath>
18
19namespace KDecoration2
20{
21namespace
22{
23DecorationBridge *findBridge(const QVariantList &args)
24{
25 for (const auto &arg : args) {
26 if (auto bridge = arg.toMap().value(QStringLiteral("bridge")).value<DecorationBridge *>()) {
27 return bridge;
28 }
29 }
30 Q_UNREACHABLE();
31}
32}
33
34Decoration::Private::Private(Decoration *deco, const QVariantList &args)
35 : sectionUnderMouse(Qt::NoSection)
36 , bridge(findBridge(args))
37 , client(std::shared_ptr<DecoratedClient>(new DecoratedClient(deco, bridge)))
38 , opaque(false)
39 , q(deco)
40{
41}
42
43void Decoration::Private::setSectionUnderMouse(Qt::WindowFrameSection section)
44{
45 if (sectionUnderMouse == section) {
46 return;
47 }
48 sectionUnderMouse = section;
49 Q_EMIT q->sectionUnderMouseChanged(sectionUnderMouse);
50}
51
52void Decoration::Private::updateSectionUnderMouse(const QPoint &mousePosition)
53{
54 if (titleBar.contains(mousePosition)) {
55 setSectionUnderMouse(Qt::TitleBarArea);
56 return;
57 }
58 const QSize size = q->size();
59 const int corner = 2 * settings->largeSpacing();
60 const bool left = mousePosition.x() < borders.left();
61 const bool top = mousePosition.y() < borders.top();
62 const bool bottom = size.height() - mousePosition.y() <= borders.bottom();
63 const bool right = size.width() - mousePosition.x() <= borders.right();
64 if (left) {
65 if (top && mousePosition.y() < titleBar.top() + corner) {
66 setSectionUnderMouse(Qt::TopLeftSection);
67 } else if (size.height() - mousePosition.y() <= borders.bottom() + corner && mousePosition.y() > titleBar.bottom()) {
68 setSectionUnderMouse(Qt::BottomLeftSection);
69 } else {
70 setSectionUnderMouse(Qt::LeftSection);
71 }
72 return;
73 }
74 if (right) {
75 if (top && mousePosition.y() < titleBar.top() + corner) {
76 setSectionUnderMouse(Qt::TopRightSection);
77 } else if (size.height() - mousePosition.y() <= borders.bottom() + corner && mousePosition.y() > titleBar.bottom()) {
78 setSectionUnderMouse(Qt::BottomRightSection);
79 } else {
80 setSectionUnderMouse(Qt::RightSection);
81 }
82 return;
83 }
84 if (bottom) {
85 if (mousePosition.y() > titleBar.bottom()) {
86 if (mousePosition.x() < borders.left() + corner) {
87 setSectionUnderMouse(Qt::BottomLeftSection);
88 } else if (size.width() - mousePosition.x() <= borders.right() + corner) {
89 setSectionUnderMouse(Qt::BottomRightSection);
90 } else {
91 setSectionUnderMouse(Qt::BottomSection);
92 }
93 } else {
94 setSectionUnderMouse(Qt::TitleBarArea);
95 }
96 return;
97 }
98 if (top) {
99 if (mousePosition.y() < titleBar.top()) {
100 if (mousePosition.x() < borders.left() + corner) {
101 setSectionUnderMouse(Qt::TopLeftSection);
102 } else if (size.width() - mousePosition.x() <= borders.right() + corner) {
103 setSectionUnderMouse(Qt::TopRightSection);
104 } else {
105 setSectionUnderMouse(Qt::TopSection);
106 }
107 } else {
108 setSectionUnderMouse(Qt::TitleBarArea);
109 }
110 return;
111 }
112 setSectionUnderMouse(Qt::NoSection);
113}
114
115void Decoration::Private::addButton(DecorationButton *button)
116{
117 Q_ASSERT(!buttons.contains(button));
118 buttons << button;
119 QObject::connect(button, &QObject::destroyed, q, [this](QObject *o) {
120 auto it = buttons.begin();
121 while (it != buttons.end()) {
122 if (*it == static_cast<DecorationButton *>(o)) {
123 it = buttons.erase(it);
124 } else {
125 it++;
126 }
127 }
128 });
129}
130
131Decoration::Decoration(QObject *parent, const QVariantList &args)
132 : QObject(parent)
133 , d(new Private(this, args))
134{
135 connect(this, &Decoration::bordersChanged, this, [this] {
136 update();
137 });
138}
139
140Decoration::~Decoration() = default;
141
143{
144 return d->client.get();
145}
146
147void Decoration::requestClose()
148{
149 d->client->d->requestClose();
150}
151
152void Decoration::requestContextHelp()
153{
154 d->client->d->requestContextHelp();
155}
156
157void Decoration::requestMinimize()
158{
159 d->client->d->requestMinimize();
160}
161
162void Decoration::requestToggleOnAllDesktops()
163{
164 d->client->d->requestToggleOnAllDesktops();
165}
166
167void Decoration::requestToggleShade()
168{
169 d->client->d->requestToggleShade();
170}
171
172void Decoration::requestToggleKeepAbove()
173{
174 d->client->d->requestToggleKeepAbove();
175}
176
177void Decoration::requestToggleKeepBelow()
178{
179 d->client->d->requestToggleKeepBelow();
180}
181
182#if KDECORATIONS2_ENABLE_DEPRECATED_SINCE(5, 21)
184{
186}
187#endif
188
190{
191 d->client->d->requestShowWindowMenu(rect);
192}
193
194void Decoration::requestShowToolTip(const QString &text)
195{
196 d->client->d->requestShowToolTip(text);
197}
198
199void Decoration::requestHideToolTip()
200{
201 d->client->d->requestHideToolTip();
202}
203
204void Decoration::requestToggleMaximization(Qt::MouseButtons buttons)
205{
206 d->client->d->requestToggleMaximization(buttons);
207}
208
209void Decoration::showApplicationMenu(int actionId)
210{
211 const auto it = std::find_if(d->buttons.constBegin(), d->buttons.constEnd(), [](DecorationButton *button) {
212 return button->type() == DecorationButtonType::ApplicationMenu;
213 });
214 if (it != d->buttons.constEnd()) {
215 requestShowApplicationMenu((*it)->geometry().toRect(), actionId);
216 }
217}
218
219void Decoration::requestShowApplicationMenu(const QRect &rect, int actionId)
220{
221 if (auto *appMenuEnabledPrivate = dynamic_cast<ApplicationMenuEnabledDecoratedClientPrivate *>(d->client->d.get())) {
222 appMenuEnabledPrivate->requestShowApplicationMenu(rect, actionId);
223 }
224}
225
226void Decoration::setBlurRegion(const QRegion &region)
227{
228 if (d->blurRegion != region) {
229 d->blurRegion = region;
230 Q_EMIT blurRegionChanged();
231 }
232}
233
234void Decoration::setBorders(const QMargins &borders)
235{
236 if (d->borders != borders) {
237 d->borders = borders;
238 Q_EMIT bordersChanged();
239 }
240}
241
242void Decoration::setResizeOnlyBorders(const QMargins &borders)
243{
244 if (d->resizeOnlyBorders != borders) {
245 d->resizeOnlyBorders = borders;
246 Q_EMIT resizeOnlyBordersChanged();
247 }
248}
249
251{
252 if (d->titleBar != rect) {
253 d->titleBar = rect;
254 Q_EMIT titleBarChanged();
255 }
256}
257
258void Decoration::setOpaque(bool opaque)
259{
260 if (d->opaque != opaque) {
261 d->opaque = opaque;
262 Q_EMIT opaqueChanged(opaque);
263 }
264}
265
266void Decoration::setShadow(const std::shared_ptr<DecorationShadow> &shadow)
267{
268 if (d->shadow != shadow) {
269 d->shadow = shadow;
270 Q_EMIT shadowChanged(shadow);
271 }
272}
273
275{
276 return d->blurRegion;
277}
278
279QMargins Decoration::borders() const
280{
281 return d->borders;
282}
283
284QMargins Decoration::resizeOnlyBorders() const
285{
286 return d->resizeOnlyBorders;
287}
288
290{
291 return d->titleBar;
292}
293
295{
296 return d->sectionUnderMouse;
297}
298
299std::shared_ptr<DecorationShadow> Decoration::shadow() const
300{
301 return d->shadow;
302}
303
304bool Decoration::isOpaque() const
305{
306 return d->opaque;
307}
308
309int Decoration::borderLeft() const
310{
311 return d->borders.left();
312}
313
314int Decoration::resizeOnlyBorderLeft() const
315{
316 return d->resizeOnlyBorders.left();
317}
318
319int Decoration::borderRight() const
320{
321 return d->borders.right();
322}
323
324int Decoration::resizeOnlyBorderRight() const
325{
326 return d->resizeOnlyBorders.right();
327}
328
329int Decoration::borderTop() const
330{
331 return d->borders.top();
332}
333
334int Decoration::resizeOnlyBorderTop() const
335{
336 return d->resizeOnlyBorders.top();
337}
338
339int Decoration::borderBottom() const
340{
341 return d->borders.bottom();
342}
343
344int Decoration::resizeOnlyBorderBottom() const
345{
346 return d->resizeOnlyBorders.bottom();
347}
348
349QSize Decoration::size() const
350{
351 const QMargins &b = d->borders;
352 return QSize(d->client->width() + b.left() + b.right(), //
353 (d->client->isShaded() ? 0 : d->client->height()) + b.top() + b.bottom());
354}
355
357{
358 return QRect(QPoint(0, 0), size());
359}
360
361bool Decoration::event(QEvent *event)
362{
363 switch (event->type()) {
365 hoverEnterEvent(static_cast<QHoverEvent *>(event));
366 return true;
368 hoverLeaveEvent(static_cast<QHoverEvent *>(event));
369 return true;
371 hoverMoveEvent(static_cast<QHoverEvent *>(event));
372 return true;
374 mousePressEvent(static_cast<QMouseEvent *>(event));
375 return true;
377 mouseReleaseEvent(static_cast<QMouseEvent *>(event));
378 return true;
380 mouseMoveEvent(static_cast<QMouseEvent *>(event));
381 return true;
382 case QEvent::Wheel:
383 wheelEvent(static_cast<QWheelEvent *>(event));
384 return true;
385 default:
386 return QObject::event(event);
387 }
388}
389
390void Decoration::hoverEnterEvent(QHoverEvent *event)
391{
392 for (DecorationButton *button : d->buttons) {
393 QCoreApplication::instance()->sendEvent(button, event);
394 }
395 auto flooredPos = QPoint(std::floor(event->position().x()), std::floor(event->position().y()));
396 d->updateSectionUnderMouse(flooredPos);
397}
398
399void Decoration::hoverLeaveEvent(QHoverEvent *event)
400{
401 for (DecorationButton *button : d->buttons) {
402 QCoreApplication::instance()->sendEvent(button, event);
403 }
404 d->setSectionUnderMouse(Qt::NoSection);
405}
406
407void Decoration::hoverMoveEvent(QHoverEvent *event)
408{
409 for (DecorationButton *button : d->buttons) {
410 if (!button->isEnabled() || !button->isVisible()) {
411 continue;
412 }
413 const bool hovered = button->isHovered();
414 const bool contains = button->contains(event->position());
415 if (!hovered && contains) {
416 QHoverEvent e(QEvent::HoverEnter, event->position(), event->oldPosF(), event->modifiers());
418 } else if (hovered && !contains) {
419 QHoverEvent e(QEvent::HoverLeave, event->position(), event->oldPosF(), event->modifiers());
421 } else if (hovered && contains) {
422 QCoreApplication::instance()->sendEvent(button, event);
423 }
424 }
425 auto flooredPos = QPoint(std::floor(event->position().x()), std::floor(event->position().y()));
426 d->updateSectionUnderMouse(flooredPos);
427}
428
429void Decoration::mouseMoveEvent(QMouseEvent *event)
430{
431 for (DecorationButton *button : d->buttons) {
432 if (button->isPressed()) {
433 QCoreApplication::instance()->sendEvent(button, event);
434 return;
435 }
436 }
437 // not handled, take care ourselves
438}
439
440void Decoration::mousePressEvent(QMouseEvent *event)
441{
442 for (DecorationButton *button : d->buttons) {
443 if (button->isHovered()) {
444 if (button->acceptedButtons().testFlag(event->button())) {
445 QCoreApplication::instance()->sendEvent(button, event);
446 }
447 event->setAccepted(true);
448 return;
449 }
450 }
451}
452
453void Decoration::mouseReleaseEvent(QMouseEvent *event)
454{
455 for (DecorationButton *button : d->buttons) {
456 if (button->isPressed() && button->acceptedButtons().testFlag(event->button())) {
457 QCoreApplication::instance()->sendEvent(button, event);
458 return;
459 }
460 }
461 // not handled, take care ourselves
462 d->updateSectionUnderMouse(event->pos());
463}
464
465void Decoration::wheelEvent(QWheelEvent *event)
466{
467 for (DecorationButton *button : d->buttons) {
468 if (button->contains(event->position())) {
469 QCoreApplication::instance()->sendEvent(button, event);
470 event->setAccepted(true);
471 }
472 }
473}
474
475void Decoration::update(const QRect &r)
476{
477 Q_EMIT damaged(r.isNull() ? rect() : r);
478}
479
480void Decoration::update()
481{
482 update(QRect());
483}
484
485void Decoration::setSettings(const std::shared_ptr<DecorationSettings> &settings)
486{
487 d->settings = settings;
488}
489
490std::shared_ptr<DecorationSettings> Decoration::settings() const
491{
492 return d->settings;
493}
494
495} // namespace
496
497#include "moc_decoration.cpp"
The Client which gets decorated.
void setSettings(const std::shared_ptr< DecorationSettings > &settings)
Invoked by the framework to set the Settings for this Decoration before init is invoked.
void requestShowWindowMenu(const QRect &rect)
bool opaque
Whether the Decoration is fully opaque.
Definition decoration.h:87
DecoratedClient * client() const
The DecoratedClient for this Decoration.
std::shared_ptr< DecorationShadow > shadow() const
DecorationShadow for this Decoration.
void setTitleBar(const QRect &rect)
An implementation has to invoke this method whenever the area containing the controls and caption cha...
QRegion blurRegion() const
The decoration's blur region in local coordinates.
QRect titleBar
The titleBar is the area inside the Decoration containing all controls (e.g.
Definition decoration.h:80
QRect rect() const
The decoration's geometry in local coordinates.
std::shared_ptr< DecorationSettings > settings() const
Qt::WindowFrameSection sectionUnderMouse
This property denotes the part of the Decoration which is currently under the mouse pointer.
Definition decoration.h:74
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
Framework for creating window decorations.
QCoreApplication * instance()
bool sendEvent(QObject *receiver, QEvent *event)
int left() const const
int right() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void destroyed(QObject *obj)
virtual bool event(QEvent *e)
int x() const const
int y() const const
bool isNull() const const
int height() const const
int width() const const
typedef MouseButtons
NoSection
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:17:09 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.