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
34class DecorationStateData : public QSharedData
35{
36public:
37 QMarginsF borders;
38};
39
40DecorationState::DecorationState()
41 : d(new DecorationStateData)
42{
43}
44
45DecorationState::DecorationState(const DecorationState &other)
46 : d(other.d)
47{
48}
49
50DecorationState::~DecorationState()
51{
52}
53
54std::shared_ptr<DecorationState> DecorationState::clone() const
55{
56 return std::make_shared<DecorationState>(*this);
57}
58
59QMarginsF DecorationState::borders() const
60{
61 return d->borders;
62}
63
64void DecorationState::setBorders(const QMarginsF &borders)
65{
66 d->borders = borders;
67}
68
69Positioner::Positioner()
70 : d(new PositionerData)
71{
72}
73
74Positioner::Positioner(const Positioner &other)
75 : d(other.d)
76{
77}
78
79Positioner::~Positioner()
80{
81}
82
84{
85 return d->anchorRect;
86}
87
89{
90 d->anchorRect = rect;
91}
92
93Decoration::Private::Private(Decoration *deco, const QVariantList &args)
94 : sectionUnderMouse(Qt::NoSection)
95 , bridge(findBridge(args))
96 , client(std::shared_ptr<DecoratedWindow>(new DecoratedWindow(deco, bridge)))
97 , opaque(false)
98 , q(deco)
99{
100}
101
102void Decoration::Private::setSectionUnderMouse(Qt::WindowFrameSection section)
103{
104 if (sectionUnderMouse == section) {
105 return;
106 }
107 sectionUnderMouse = section;
108 Q_EMIT q->sectionUnderMouseChanged(sectionUnderMouse);
109}
110
111void Decoration::Private::updateSectionUnderMouse(const QPoint &mousePosition)
112{
113 if (titleBar.toRect().contains(mousePosition)) {
114 setSectionUnderMouse(Qt::TitleBarArea);
115 return;
116 }
117 const QSizeF size = q->size();
118 const QMarginsF borders = current->borders();
119 const int corner = 2 * settings->largeSpacing();
120 const bool left = mousePosition.x() < borders.left();
121 const bool top = mousePosition.y() < borders.top();
122 const bool bottom = mousePosition.y() >= size.height() - borders.bottom();
123 const bool right = mousePosition.x() >= size.width() - borders.right();
124 if (left) {
125 if (top && mousePosition.y() < titleBar.top() + corner) {
126 setSectionUnderMouse(Qt::TopLeftSection);
127 } else if (mousePosition.y() >= size.height() - borders.bottom() - corner && mousePosition.y() >= titleBar.bottom()) {
128 setSectionUnderMouse(Qt::BottomLeftSection);
129 } else {
130 setSectionUnderMouse(Qt::LeftSection);
131 }
132 return;
133 }
134 if (right) {
135 if (top && mousePosition.y() < titleBar.top() + corner) {
136 setSectionUnderMouse(Qt::TopRightSection);
137 } else if (mousePosition.y() >= size.height() - borders.bottom() - corner && mousePosition.y() >= titleBar.bottom()) {
138 setSectionUnderMouse(Qt::BottomRightSection);
139 } else {
140 setSectionUnderMouse(Qt::RightSection);
141 }
142 return;
143 }
144 if (bottom) {
145 if (mousePosition.y() >= titleBar.bottom()) {
146 if (mousePosition.x() < borders.left() + corner) {
147 setSectionUnderMouse(Qt::BottomLeftSection);
148 } else if (mousePosition.x() >= size.width() - borders.right() - corner) {
149 setSectionUnderMouse(Qt::BottomRightSection);
150 } else {
151 setSectionUnderMouse(Qt::BottomSection);
152 }
153 } else {
154 setSectionUnderMouse(Qt::TitleBarArea);
155 }
156 return;
157 }
158 if (top) {
159 if (mousePosition.y() < titleBar.top()) {
160 if (mousePosition.x() < borders.left() + corner) {
161 setSectionUnderMouse(Qt::TopLeftSection);
162 } else if (mousePosition.x() >= size.width() - borders.right() - corner) {
163 setSectionUnderMouse(Qt::TopRightSection);
164 } else {
165 setSectionUnderMouse(Qt::TopSection);
166 }
167 } else {
168 setSectionUnderMouse(Qt::TitleBarArea);
169 }
170 return;
171 }
172 setSectionUnderMouse(Qt::NoSection);
173}
174
175void Decoration::Private::addButton(DecorationButton *button)
176{
177 Q_ASSERT(!buttons.contains(button));
178 buttons << button;
179 QObject::connect(button, &QObject::destroyed, q, [this](QObject *o) {
180 auto it = buttons.begin();
181 while (it != buttons.end()) {
182 if (*it == static_cast<DecorationButton *>(o)) {
183 it = buttons.erase(it);
184 } else {
185 it++;
186 }
187 }
188 });
189}
190
191Decoration::Decoration(QObject *parent, const QVariantList &args)
192 : QObject(parent)
193 , d(new Private(this, args))
194{
195}
196
197Decoration::~Decoration() = default;
198
200{
201 return d->client.get();
202}
203
204void Decoration::requestClose()
205{
206 d->client->d->requestClose();
207}
208
209void Decoration::requestContextHelp()
210{
211 d->client->d->requestContextHelp();
212}
213
214void Decoration::requestMinimize()
215{
216 d->client->d->requestMinimize();
217}
218
219void Decoration::requestToggleOnAllDesktops()
220{
221 d->client->d->requestToggleOnAllDesktops();
222}
223
224void Decoration::requestToggleShade()
225{
226 d->client->d->requestToggleShade();
227}
228
229void Decoration::requestToggleKeepAbove()
230{
231 d->client->d->requestToggleKeepAbove();
232}
233
234void Decoration::requestToggleKeepBelow()
235{
236 d->client->d->requestToggleKeepBelow();
237}
238
239#if KDECORATIONS3_ENABLE_DEPRECATED_SINCE(5, 21)
241{
242 requestShowWindowMenu(QRect());
243}
244#endif
245
247{
248 d->client->d->requestShowWindowMenu(rect);
249}
250
251void Decoration::requestShowToolTip(const QString &text)
252{
253 d->client->d->requestShowToolTip(text);
254}
255
256void Decoration::requestHideToolTip()
257{
258 d->client->d->requestHideToolTip();
259}
260
261void Decoration::requestToggleMaximization(Qt::MouseButtons buttons)
262{
263 d->client->d->requestToggleMaximization(buttons);
264}
265
266void Decoration::showApplicationMenu(int actionId)
267{
268 const auto it = std::find_if(d->buttons.constBegin(), d->buttons.constEnd(), [](DecorationButton *button) {
269 return button->type() == DecorationButtonType::ApplicationMenu;
270 });
271 if (it != d->buttons.constEnd()) {
272 requestShowApplicationMenu((*it)->geometry().toRect(), actionId);
273 }
274}
275
276void Decoration::requestShowApplicationMenu(const QRect &rect, int actionId)
277{
278 d->client->d->requestShowApplicationMenu(rect, actionId);
279}
280
281void Decoration::setBlurRegion(const QRegion &region)
282{
283 if (d->blurRegion != region) {
284 d->blurRegion = region;
285 Q_EMIT blurRegionChanged();
286 }
287}
288
289void Decoration::setBorders(const QMarginsF &borders)
290{
291 if (d->next->borders() != borders) {
292 setState([borders](DecorationState *state) {
293 state->setBorders(borders);
294 });
295 }
296}
297
298void Decoration::setResizeOnlyBorders(const QMarginsF &borders)
299{
300 if (d->resizeOnlyBorders != borders) {
301 d->resizeOnlyBorders = borders;
302 Q_EMIT resizeOnlyBordersChanged();
303 }
304}
305
307{
308 if (d->titleBar != rect) {
309 d->titleBar = rect;
310 Q_EMIT titleBarChanged();
311 }
312}
313
314void Decoration::setOpaque(bool opaque)
315{
316 if (d->opaque != opaque) {
317 d->opaque = opaque;
318 Q_EMIT opaqueChanged(opaque);
319 }
320}
321
322void Decoration::setShadow(const std::shared_ptr<DecorationShadow> &shadow)
323{
324 if (d->shadow != shadow) {
325 d->shadow = shadow;
326 Q_EMIT shadowChanged(shadow);
327 }
328}
329
331{
332 return d->blurRegion;
333}
334
335QMarginsF Decoration::borders() const
336{
337 return d->current->borders();
338}
339
340QMarginsF Decoration::resizeOnlyBorders() const
341{
342 return d->resizeOnlyBorders;
343}
344
345QRectF Decoration::titleBar() const
346{
347 return d->titleBar;
348}
349
351{
352 return d->sectionUnderMouse;
353}
354
355std::shared_ptr<DecorationShadow> Decoration::shadow() const
356{
357 return d->shadow;
358}
359
360bool Decoration::isOpaque() const
361{
362 return d->opaque;
363}
364
365qreal Decoration::borderLeft() const
366{
367 return d->current->borders().left();
368}
369
370qreal Decoration::resizeOnlyBorderLeft() const
371{
372 return d->resizeOnlyBorders.left();
373}
374
375qreal Decoration::borderRight() const
376{
377 return d->current->borders().right();
378}
379
380qreal Decoration::resizeOnlyBorderRight() const
381{
382 return d->resizeOnlyBorders.right();
383}
384
385qreal Decoration::borderTop() const
386{
387 return d->current->borders().top();
388}
389
390qreal Decoration::resizeOnlyBorderTop() const
391{
392 return d->resizeOnlyBorders.top();
393}
394
395qreal Decoration::borderBottom() const
396{
397 return d->current->borders().bottom();
398}
399
400qreal Decoration::resizeOnlyBorderBottom() const
401{
402 return d->resizeOnlyBorders.bottom();
403}
404
405QSizeF Decoration::size() const
406{
407 const QMarginsF b = d->current->borders();
408 return QSizeF(d->client->width() + b.left() + b.right(), (d->client->isShaded() ? 0 : d->client->height()) + b.top() + b.bottom());
409}
410
412{
413 return QRectF(QPointF(0, 0), size());
414}
415
416bool Decoration::event(QEvent *event)
417{
418 switch (event->type()) {
420 hoverEnterEvent(static_cast<QHoverEvent *>(event));
421 return true;
423 hoverLeaveEvent(static_cast<QHoverEvent *>(event));
424 return true;
426 hoverMoveEvent(static_cast<QHoverEvent *>(event));
427 return true;
429 mousePressEvent(static_cast<QMouseEvent *>(event));
430 return true;
432 mouseReleaseEvent(static_cast<QMouseEvent *>(event));
433 return true;
435 mouseMoveEvent(static_cast<QMouseEvent *>(event));
436 return true;
437 case QEvent::Wheel:
438 wheelEvent(static_cast<QWheelEvent *>(event));
439 return true;
440 default:
441 return QObject::event(event);
442 }
443}
444
445void Decoration::hoverEnterEvent(QHoverEvent *event)
446{
447 for (DecorationButton *button : d->buttons) {
448 QCoreApplication::instance()->sendEvent(button, event);
449 }
450 auto flooredPos = QPoint(std::floor(event->position().x()), std::floor(event->position().y()));
451 d->updateSectionUnderMouse(flooredPos);
452}
453
454void Decoration::hoverLeaveEvent(QHoverEvent *event)
455{
456 for (DecorationButton *button : d->buttons) {
457 QCoreApplication::instance()->sendEvent(button, event);
458 }
459 d->setSectionUnderMouse(Qt::NoSection);
460}
461
462void Decoration::hoverMoveEvent(QHoverEvent *event)
463{
464 for (DecorationButton *button : d->buttons) {
465 if (!button->isEnabled() || !button->isVisible()) {
466 continue;
467 }
468 const bool hovered = button->isHovered();
469 const bool contains = button->contains(event->position());
470 if (!hovered && contains) {
471 QHoverEvent e(QEvent::HoverEnter, event->position(), event->oldPosF(), event->modifiers());
473 } else if (hovered && !contains) {
474 QHoverEvent e(QEvent::HoverLeave, event->position(), event->oldPosF(), event->modifiers());
476 } else if (hovered && contains) {
477 QCoreApplication::instance()->sendEvent(button, event);
478 }
479 }
480 auto flooredPos = QPoint(std::floor(event->position().x()), std::floor(event->position().y()));
481 d->updateSectionUnderMouse(flooredPos);
482}
483
484void Decoration::mouseMoveEvent(QMouseEvent *event)
485{
486 for (DecorationButton *button : d->buttons) {
487 if (button->isPressed()) {
488 QCoreApplication::instance()->sendEvent(button, event);
489 return;
490 }
491 }
492 // not handled, take care ourselves
493}
494
495void Decoration::mousePressEvent(QMouseEvent *event)
496{
497 for (DecorationButton *button : d->buttons) {
498 if (button->isHovered()) {
499 if (button->acceptedButtons().testFlag(event->button())) {
500 QCoreApplication::instance()->sendEvent(button, event);
501 }
502 event->setAccepted(true);
503 return;
504 }
505 }
506}
507
508void Decoration::mouseReleaseEvent(QMouseEvent *event)
509{
510 for (DecorationButton *button : d->buttons) {
511 if (button->isPressed() && button->acceptedButtons().testFlag(event->button())) {
512 QCoreApplication::instance()->sendEvent(button, event);
513 return;
514 }
515 }
516 // not handled, take care ourselves
517 d->updateSectionUnderMouse(event->pos());
518}
519
520void Decoration::wheelEvent(QWheelEvent *event)
521{
522 for (DecorationButton *button : d->buttons) {
523 if (button->contains(event->position())) {
524 QCoreApplication::instance()->sendEvent(button, event);
525 event->setAccepted(true);
526 }
527 }
528}
529
530void Decoration::update(const QRectF &r)
531{
532 Q_EMIT damaged(r.isNull() ? rect().toAlignedRect() : r.toAlignedRect());
533}
534
535void Decoration::update()
536{
537 update(QRect());
538}
539
540void Decoration::setSettings(const std::shared_ptr<DecorationSettings> &settings)
541{
542 d->settings = settings;
543}
544
545std::shared_ptr<DecorationSettings> Decoration::settings() const
546{
547 return d->settings;
548}
549
550std::shared_ptr<DecorationState> Decoration::createState()
551{
552 return std::make_shared<DecorationState>();
553}
554
555std::shared_ptr<DecorationState> Decoration::currentState() const
556{
557 return d->current;
558}
559
560std::shared_ptr<DecorationState> Decoration::nextState() const
561{
562 return d->next;
563}
564
566{
567 d->next = createState();
568 d->current = createState();
569}
570
571void Decoration::setState(std::function<void(DecorationState *state)> callback)
572{
573 callback(d->next.get());
574 Q_EMIT nextStateChanged(d->next);
575}
576
577void Decoration::apply(std::shared_ptr<DecorationState> state)
578{
579 if (d->current == state) {
580 return;
581 }
582
583 const auto previous = d->current;
584 d->current = state;
585 update();
586
587 if (previous->borders() != state->borders()) {
588 Q_EMIT bordersChanged();
589 }
590
591 Q_EMIT currentStateChanged(state);
592}
593
594void Decoration::popup(const Positioner &positioner, QMenu *menu)
595{
596 if (auto window = dynamic_cast<DecoratedWindowPrivateV3 *>(d->client->d.get())) {
597 window->popup(positioner, menu);
598 }
599}
600
601} // namespace
602
603#include "moc_decoration.cpp"
The Client which gets decorated.
A button to be used in a Decoration.
Base class for the Decoration.
Definition decoration.h:112
std::shared_ptr< DecorationState > currentState() const
Returns the currently applied state.
virtual std::shared_ptr< DecorationState > createState()
Create a state container.
DecoratedWindow * window() const
The DecoratedWindow for this Decoration.
QRectF titleBar
The titleBar is the area inside the Decoration containing all controls (e.g.
Definition decoration.h:134
std::shared_ptr< DecorationState > nextState() const
Returns the next state, i.e.
void requestShowWindowMenu(const QRect &rect)
Decoration(QObject *parent, const QVariantList &args)
Constructor for the Decoration.
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.
QRectF rect() const
The decoration's geometry in local coordinates.
void apply(std::shared_ptr< DecorationState > state)
Qt::WindowFrameSection sectionUnderMouse
This property denotes the part of the Decoration which is currently under the mouse pointer.
Definition decoration.h:128
void setTitleBar(const QRectF &rect)
An implementation has to invoke this method whenever the area containing the controls and caption cha...
std::shared_ptr< DecorationSettings > settings() const
void setState(std::function< void(DecorationState *state)> callback)
Notifies the framework that the decoration state has changed.
bool opaque
Whether the Decoration is fully opaque.
Definition decoration.h:141
void popup(const Positioner &positioner, QMenu *menu)
Shows the given menu at the position specified by the positioner.
std::shared_ptr< DecorationShadow > shadow() const
DecorationShadow for this Decoration.
Popup positioner.
Definition decoration.h:64
void setAnchorRect(const QRectF &rect)
Sets the anchor rectangle to rect.
QRectF anchorRect() const
Returns the anchor rectangle.
Framework for creating window decorations.
QCoreApplication * instance()
bool sendEvent(QObject *receiver, QEvent *event)
qreal bottom() const const
qreal left() const const
qreal right() const const
qreal top() const const
QObject(QObject *parent)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void destroyed(QObject *obj)
virtual bool event(QEvent *e)
QObject * parent() const const
int x() const const
int y() const const
bool isNull() const const
QRect toAlignedRect() const const
qreal height() const const
qreal width() const const
typedef MouseButtons
WindowFrameSection
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 4 2025 12:03:30 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.