KDecoration3

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 "decoratedwindow.h"
8#include "decoration_p.h"
9#include "decorationbutton.h"
10#include "decorationsettings.h"
11#include "private/decoratedwindowprivate.h"
12#include "private/decorationbridge.h"
13
14#include <QCoreApplication>
15#include <QHoverEvent>
16
17#include <cmath>
18
19namespace KDecoration3
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<DecoratedWindow>(new DecoratedWindow(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 d->client->d->requestShowApplicationMenu(rect, actionId);
222}
223
224void Decoration::setBlurRegion(const QRegion &region)
225{
226 if (d->blurRegion != region) {
227 d->blurRegion = region;
228 Q_EMIT blurRegionChanged();
229 }
230}
231
232void Decoration::setBorders(const QMargins &borders)
233{
234 if (d->borders != borders) {
235 d->borders = borders;
236 Q_EMIT bordersChanged();
237 }
238}
239
240void Decoration::setResizeOnlyBorders(const QMargins &borders)
241{
242 if (d->resizeOnlyBorders != borders) {
243 d->resizeOnlyBorders = borders;
244 Q_EMIT resizeOnlyBordersChanged();
245 }
246}
247
249{
250 if (d->titleBar != rect) {
251 d->titleBar = rect;
252 Q_EMIT titleBarChanged();
253 }
254}
255
256void Decoration::setOpaque(bool opaque)
257{
258 if (d->opaque != opaque) {
259 d->opaque = opaque;
260 Q_EMIT opaqueChanged(opaque);
261 }
262}
263
264void Decoration::setShadow(const std::shared_ptr<DecorationShadow> &shadow)
265{
266 if (d->shadow != shadow) {
267 d->shadow = shadow;
268 Q_EMIT shadowChanged(shadow);
269 }
270}
271
273{
274 return d->blurRegion;
275}
276
277QMargins Decoration::borders() const
278{
279 return d->borders;
280}
281
282QMargins Decoration::resizeOnlyBorders() const
283{
284 return d->resizeOnlyBorders;
285}
286
288{
289 return d->titleBar;
290}
291
293{
294 return d->sectionUnderMouse;
295}
296
297std::shared_ptr<DecorationShadow> Decoration::shadow() const
298{
299 return d->shadow;
300}
301
302bool Decoration::isOpaque() const
303{
304 return d->opaque;
305}
306
307int Decoration::borderLeft() const
308{
309 return d->borders.left();
310}
311
312int Decoration::resizeOnlyBorderLeft() const
313{
314 return d->resizeOnlyBorders.left();
315}
316
317int Decoration::borderRight() const
318{
319 return d->borders.right();
320}
321
322int Decoration::resizeOnlyBorderRight() const
323{
324 return d->resizeOnlyBorders.right();
325}
326
327int Decoration::borderTop() const
328{
329 return d->borders.top();
330}
331
332int Decoration::resizeOnlyBorderTop() const
333{
334 return d->resizeOnlyBorders.top();
335}
336
337int Decoration::borderBottom() const
338{
339 return d->borders.bottom();
340}
341
342int Decoration::resizeOnlyBorderBottom() const
343{
344 return d->resizeOnlyBorders.bottom();
345}
346
347QSize Decoration::size() const
348{
349 const QMargins &b = d->borders;
350 return QSize(d->client->width() + b.left() + b.right(), //
351 (d->client->isShaded() ? 0 : d->client->height()) + b.top() + b.bottom());
352}
353
355{
356 return QRect(QPoint(0, 0), size());
357}
358
359bool Decoration::event(QEvent *event)
360{
361 switch (event->type()) {
363 hoverEnterEvent(static_cast<QHoverEvent *>(event));
364 return true;
366 hoverLeaveEvent(static_cast<QHoverEvent *>(event));
367 return true;
369 hoverMoveEvent(static_cast<QHoverEvent *>(event));
370 return true;
372 mousePressEvent(static_cast<QMouseEvent *>(event));
373 return true;
375 mouseReleaseEvent(static_cast<QMouseEvent *>(event));
376 return true;
378 mouseMoveEvent(static_cast<QMouseEvent *>(event));
379 return true;
380 case QEvent::Wheel:
381 wheelEvent(static_cast<QWheelEvent *>(event));
382 return true;
383 default:
384 return QObject::event(event);
385 }
386}
387
388void Decoration::hoverEnterEvent(QHoverEvent *event)
389{
390 for (DecorationButton *button : d->buttons) {
391 QCoreApplication::instance()->sendEvent(button, event);
392 }
393 auto flooredPos = QPoint(std::floor(event->position().x()), std::floor(event->position().y()));
394 d->updateSectionUnderMouse(flooredPos);
395}
396
397void Decoration::hoverLeaveEvent(QHoverEvent *event)
398{
399 for (DecorationButton *button : d->buttons) {
400 QCoreApplication::instance()->sendEvent(button, event);
401 }
402 d->setSectionUnderMouse(Qt::NoSection);
403}
404
405void Decoration::hoverMoveEvent(QHoverEvent *event)
406{
407 for (DecorationButton *button : d->buttons) {
408 if (!button->isEnabled() || !button->isVisible()) {
409 continue;
410 }
411 const bool hovered = button->isHovered();
412 const bool contains = button->contains(event->position());
413 if (!hovered && contains) {
414 QHoverEvent e(QEvent::HoverEnter, event->position(), event->oldPosF(), event->modifiers());
416 } else if (hovered && !contains) {
417 QHoverEvent e(QEvent::HoverLeave, event->position(), event->oldPosF(), event->modifiers());
419 } else if (hovered && contains) {
420 QCoreApplication::instance()->sendEvent(button, event);
421 }
422 }
423 auto flooredPos = QPoint(std::floor(event->position().x()), std::floor(event->position().y()));
424 d->updateSectionUnderMouse(flooredPos);
425}
426
427void Decoration::mouseMoveEvent(QMouseEvent *event)
428{
429 for (DecorationButton *button : d->buttons) {
430 if (button->isPressed()) {
431 QCoreApplication::instance()->sendEvent(button, event);
432 return;
433 }
434 }
435 // not handled, take care ourselves
436}
437
438void Decoration::mousePressEvent(QMouseEvent *event)
439{
440 for (DecorationButton *button : d->buttons) {
441 if (button->isHovered()) {
442 if (button->acceptedButtons().testFlag(event->button())) {
443 QCoreApplication::instance()->sendEvent(button, event);
444 }
445 event->setAccepted(true);
446 return;
447 }
448 }
449}
450
451void Decoration::mouseReleaseEvent(QMouseEvent *event)
452{
453 for (DecorationButton *button : d->buttons) {
454 if (button->isPressed() && button->acceptedButtons().testFlag(event->button())) {
455 QCoreApplication::instance()->sendEvent(button, event);
456 return;
457 }
458 }
459 // not handled, take care ourselves
460 d->updateSectionUnderMouse(event->pos());
461}
462
463void Decoration::wheelEvent(QWheelEvent *event)
464{
465 for (DecorationButton *button : d->buttons) {
466 if (button->contains(event->position())) {
467 QCoreApplication::instance()->sendEvent(button, event);
468 event->setAccepted(true);
469 }
470 }
471}
472
473void Decoration::update(const QRect &r)
474{
475 Q_EMIT damaged(r.isNull() ? rect() : r);
476}
477
478void Decoration::update()
479{
480 update(QRect());
481}
482
483void Decoration::setSettings(const std::shared_ptr<DecorationSettings> &settings)
484{
485 d->settings = settings;
486}
487
488std::shared_ptr<DecorationSettings> Decoration::settings() const
489{
490 return d->settings;
491}
492
493} // namespace
494
495#include "moc_decoration.cpp"
The Client which gets decorated.
DecoratedWindow * window() const
The DecoratedWindow for this Decoration.
QRect titleBar
The titleBar is the area inside the Decoration containing all controls (e.g.
Definition decoration.h:80
void requestShowWindowMenu(const QRect &rect)
void setSettings(const std::shared_ptr< DecorationSettings > &settings)
Invoked by the framework to set the Settings for this Decoration before init is invoked.
QRegion blurRegion() const
The decoration's blur region in local coordinates.
Qt::WindowFrameSection sectionUnderMouse
This property denotes the part of the Decoration which is currently under the mouse pointer.
Definition decoration.h:74
std::shared_ptr< DecorationSettings > settings() const
void setTitleBar(const QRect &rect)
An implementation has to invoke this method whenever the area containing the controls and caption cha...
bool opaque
Whether the Decoration is fully opaque.
Definition decoration.h:87
std::shared_ptr< DecorationShadow > shadow() const
DecorationShadow for this Decoration.
QRect rect() const
The decoration's geometry in local coordinates.
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 bottom() const const
int left() const const
int right() const const
int top() 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 Fri Nov 22 2024 12:02:45 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.