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
69Decoration::Private::Private(Decoration *deco, const QVariantList &args)
70 : sectionUnderMouse(Qt::NoSection)
71 , bridge(findBridge(args))
72 , client(std::shared_ptr<DecoratedWindow>(new DecoratedWindow(deco, bridge)))
73 , opaque(false)
74 , q(deco)
75{
76}
77
78void Decoration::Private::setSectionUnderMouse(Qt::WindowFrameSection section)
79{
80 if (sectionUnderMouse == section) {
81 return;
82 }
83 sectionUnderMouse = section;
84 Q_EMIT q->sectionUnderMouseChanged(sectionUnderMouse);
85}
86
87void Decoration::Private::updateSectionUnderMouse(const QPoint &mousePosition)
88{
89 if (titleBar.toRect().contains(mousePosition)) {
90 setSectionUnderMouse(Qt::TitleBarArea);
91 return;
92 }
93 const QSizeF size = q->size();
94 const QMarginsF borders = current->borders();
95 const int corner = 2 * settings->largeSpacing();
96 const bool left = mousePosition.x() < borders.left();
97 const bool top = mousePosition.y() < borders.top();
98 const bool bottom = mousePosition.y() >= size.height() - borders.bottom();
99 const bool right = mousePosition.x() >= size.width() - borders.right();
100 if (left) {
101 if (top && mousePosition.y() < titleBar.top() + corner) {
102 setSectionUnderMouse(Qt::TopLeftSection);
103 } else if (mousePosition.y() >= size.height() - borders.bottom() - corner && mousePosition.y() >= titleBar.bottom()) {
104 setSectionUnderMouse(Qt::BottomLeftSection);
105 } else {
106 setSectionUnderMouse(Qt::LeftSection);
107 }
108 return;
109 }
110 if (right) {
111 if (top && mousePosition.y() < titleBar.top() + corner) {
112 setSectionUnderMouse(Qt::TopRightSection);
113 } else if (mousePosition.y() >= size.height() - borders.bottom() - corner && mousePosition.y() >= titleBar.bottom()) {
114 setSectionUnderMouse(Qt::BottomRightSection);
115 } else {
116 setSectionUnderMouse(Qt::RightSection);
117 }
118 return;
119 }
120 if (bottom) {
121 if (mousePosition.y() >= titleBar.bottom()) {
122 if (mousePosition.x() < borders.left() + corner) {
123 setSectionUnderMouse(Qt::BottomLeftSection);
124 } else if (mousePosition.x() >= size.width() - borders.right() - corner) {
125 setSectionUnderMouse(Qt::BottomRightSection);
126 } else {
127 setSectionUnderMouse(Qt::BottomSection);
128 }
129 } else {
130 setSectionUnderMouse(Qt::TitleBarArea);
131 }
132 return;
133 }
134 if (top) {
135 if (mousePosition.y() < titleBar.top()) {
136 if (mousePosition.x() < borders.left() + corner) {
137 setSectionUnderMouse(Qt::TopLeftSection);
138 } else if (mousePosition.x() >= size.width() - borders.right() - corner) {
139 setSectionUnderMouse(Qt::TopRightSection);
140 } else {
141 setSectionUnderMouse(Qt::TopSection);
142 }
143 } else {
144 setSectionUnderMouse(Qt::TitleBarArea);
145 }
146 return;
147 }
148 setSectionUnderMouse(Qt::NoSection);
149}
150
151void Decoration::Private::addButton(DecorationButton *button)
152{
153 Q_ASSERT(!buttons.contains(button));
154 buttons << button;
155 QObject::connect(button, &QObject::destroyed, q, [this](QObject *o) {
156 auto it = buttons.begin();
157 while (it != buttons.end()) {
158 if (*it == static_cast<DecorationButton *>(o)) {
159 it = buttons.erase(it);
160 } else {
161 it++;
162 }
163 }
164 });
165}
166
167Decoration::Decoration(QObject *parent, const QVariantList &args)
168 : QObject(parent)
169 , d(new Private(this, args))
170{
171}
172
173Decoration::~Decoration() = default;
174
176{
177 return d->client.get();
178}
179
180void Decoration::requestClose()
181{
182 d->client->d->requestClose();
183}
184
185void Decoration::requestContextHelp()
186{
187 d->client->d->requestContextHelp();
188}
189
190void Decoration::requestMinimize()
191{
192 d->client->d->requestMinimize();
193}
194
195void Decoration::requestToggleOnAllDesktops()
196{
197 d->client->d->requestToggleOnAllDesktops();
198}
199
200void Decoration::requestToggleShade()
201{
202 d->client->d->requestToggleShade();
203}
204
205void Decoration::requestToggleKeepAbove()
206{
207 d->client->d->requestToggleKeepAbove();
208}
209
210void Decoration::requestToggleKeepBelow()
211{
212 d->client->d->requestToggleKeepBelow();
213}
214
215#if KDECORATIONS3_ENABLE_DEPRECATED_SINCE(5, 21)
217{
219}
220#endif
221
223{
224 d->client->d->requestShowWindowMenu(rect);
225}
226
227void Decoration::requestShowToolTip(const QString &text)
228{
229 d->client->d->requestShowToolTip(text);
230}
231
232void Decoration::requestHideToolTip()
233{
234 d->client->d->requestHideToolTip();
235}
236
237void Decoration::requestToggleMaximization(Qt::MouseButtons buttons)
238{
239 d->client->d->requestToggleMaximization(buttons);
240}
241
242void Decoration::showApplicationMenu(int actionId)
243{
244 const auto it = std::find_if(d->buttons.constBegin(), d->buttons.constEnd(), [](DecorationButton *button) {
245 return button->type() == DecorationButtonType::ApplicationMenu;
246 });
247 if (it != d->buttons.constEnd()) {
248 requestShowApplicationMenu((*it)->geometry().toRect(), actionId);
249 }
250}
251
252void Decoration::requestShowApplicationMenu(const QRect &rect, int actionId)
253{
254 d->client->d->requestShowApplicationMenu(rect, actionId);
255}
256
257void Decoration::setBlurRegion(const QRegion &region)
258{
259 if (d->blurRegion != region) {
260 d->blurRegion = region;
261 Q_EMIT blurRegionChanged();
262 }
263}
264
265void Decoration::setBorders(const QMarginsF &borders)
266{
267 if (d->next->borders() != borders) {
268 setState([borders](DecorationState *state) {
269 state->setBorders(borders);
270 });
271 }
272}
273
274void Decoration::setResizeOnlyBorders(const QMarginsF &borders)
275{
276 if (d->resizeOnlyBorders != borders) {
277 d->resizeOnlyBorders = borders;
278 Q_EMIT resizeOnlyBordersChanged();
279 }
280}
281
283{
284 if (d->titleBar != rect) {
285 d->titleBar = rect;
286 Q_EMIT titleBarChanged();
287 }
288}
289
290void Decoration::setOpaque(bool opaque)
291{
292 if (d->opaque != opaque) {
293 d->opaque = opaque;
294 Q_EMIT opaqueChanged(opaque);
295 }
296}
297
298void Decoration::setShadow(const std::shared_ptr<DecorationShadow> &shadow)
299{
300 if (d->shadow != shadow) {
301 d->shadow = shadow;
302 Q_EMIT shadowChanged(shadow);
303 }
304}
305
307{
308 return d->blurRegion;
309}
310
311QMarginsF Decoration::borders() const
312{
313 return d->current->borders();
314}
315
316QMarginsF Decoration::resizeOnlyBorders() const
317{
318 return d->resizeOnlyBorders;
319}
320
322{
323 return d->titleBar;
324}
325
327{
328 return d->sectionUnderMouse;
329}
330
331std::shared_ptr<DecorationShadow> Decoration::shadow() const
332{
333 return d->shadow;
334}
335
336bool Decoration::isOpaque() const
337{
338 return d->opaque;
339}
340
341qreal Decoration::borderLeft() const
342{
343 return d->current->borders().left();
344}
345
346qreal Decoration::resizeOnlyBorderLeft() const
347{
348 return d->resizeOnlyBorders.left();
349}
350
351qreal Decoration::borderRight() const
352{
353 return d->current->borders().right();
354}
355
356qreal Decoration::resizeOnlyBorderRight() const
357{
358 return d->resizeOnlyBorders.right();
359}
360
361qreal Decoration::borderTop() const
362{
363 return d->current->borders().top();
364}
365
366qreal Decoration::resizeOnlyBorderTop() const
367{
368 return d->resizeOnlyBorders.top();
369}
370
371qreal Decoration::borderBottom() const
372{
373 return d->current->borders().bottom();
374}
375
376qreal Decoration::resizeOnlyBorderBottom() const
377{
378 return d->resizeOnlyBorders.bottom();
379}
380
381QSizeF Decoration::size() const
382{
383 const QMarginsF b = d->current->borders();
384 return QSizeF(d->client->width() + b.left() + b.right(), (d->client->isShaded() ? 0 : d->client->height()) + b.top() + b.bottom());
385}
386
388{
389 return QRectF(QPointF(0, 0), size());
390}
391
392bool Decoration::event(QEvent *event)
393{
394 switch (event->type()) {
396 hoverEnterEvent(static_cast<QHoverEvent *>(event));
397 return true;
399 hoverLeaveEvent(static_cast<QHoverEvent *>(event));
400 return true;
402 hoverMoveEvent(static_cast<QHoverEvent *>(event));
403 return true;
405 mousePressEvent(static_cast<QMouseEvent *>(event));
406 return true;
408 mouseReleaseEvent(static_cast<QMouseEvent *>(event));
409 return true;
411 mouseMoveEvent(static_cast<QMouseEvent *>(event));
412 return true;
413 case QEvent::Wheel:
414 wheelEvent(static_cast<QWheelEvent *>(event));
415 return true;
416 default:
417 return QObject::event(event);
418 }
419}
420
421void Decoration::hoverEnterEvent(QHoverEvent *event)
422{
423 for (DecorationButton *button : d->buttons) {
424 QCoreApplication::instance()->sendEvent(button, event);
425 }
426 auto flooredPos = QPoint(std::floor(event->position().x()), std::floor(event->position().y()));
427 d->updateSectionUnderMouse(flooredPos);
428}
429
430void Decoration::hoverLeaveEvent(QHoverEvent *event)
431{
432 for (DecorationButton *button : d->buttons) {
433 QCoreApplication::instance()->sendEvent(button, event);
434 }
435 d->setSectionUnderMouse(Qt::NoSection);
436}
437
438void Decoration::hoverMoveEvent(QHoverEvent *event)
439{
440 for (DecorationButton *button : d->buttons) {
441 if (!button->isEnabled() || !button->isVisible()) {
442 continue;
443 }
444 const bool hovered = button->isHovered();
445 const bool contains = button->contains(event->position());
446 if (!hovered && contains) {
447 QHoverEvent e(QEvent::HoverEnter, event->position(), event->oldPosF(), event->modifiers());
449 } else if (hovered && !contains) {
450 QHoverEvent e(QEvent::HoverLeave, event->position(), event->oldPosF(), event->modifiers());
452 } else if (hovered && contains) {
453 QCoreApplication::instance()->sendEvent(button, event);
454 }
455 }
456 auto flooredPos = QPoint(std::floor(event->position().x()), std::floor(event->position().y()));
457 d->updateSectionUnderMouse(flooredPos);
458}
459
460void Decoration::mouseMoveEvent(QMouseEvent *event)
461{
462 for (DecorationButton *button : d->buttons) {
463 if (button->isPressed()) {
464 QCoreApplication::instance()->sendEvent(button, event);
465 return;
466 }
467 }
468 // not handled, take care ourselves
469}
470
471void Decoration::mousePressEvent(QMouseEvent *event)
472{
473 for (DecorationButton *button : d->buttons) {
474 if (button->isHovered()) {
475 if (button->acceptedButtons().testFlag(event->button())) {
476 QCoreApplication::instance()->sendEvent(button, event);
477 }
478 event->setAccepted(true);
479 return;
480 }
481 }
482}
483
484void Decoration::mouseReleaseEvent(QMouseEvent *event)
485{
486 for (DecorationButton *button : d->buttons) {
487 if (button->isPressed() && button->acceptedButtons().testFlag(event->button())) {
488 QCoreApplication::instance()->sendEvent(button, event);
489 return;
490 }
491 }
492 // not handled, take care ourselves
493 d->updateSectionUnderMouse(event->pos());
494}
495
496void Decoration::wheelEvent(QWheelEvent *event)
497{
498 for (DecorationButton *button : d->buttons) {
499 if (button->contains(event->position())) {
500 QCoreApplication::instance()->sendEvent(button, event);
501 event->setAccepted(true);
502 }
503 }
504}
505
506void Decoration::update(const QRectF &r)
507{
508 Q_EMIT damaged(r.isNull() ? rect().toAlignedRect() : r.toAlignedRect());
509}
510
511void Decoration::update()
512{
513 update(QRect());
514}
515
516void Decoration::setSettings(const std::shared_ptr<DecorationSettings> &settings)
517{
518 d->settings = settings;
519}
520
521std::shared_ptr<DecorationSettings> Decoration::settings() const
522{
523 return d->settings;
524}
525
526std::shared_ptr<DecorationState> Decoration::createState()
527{
528 return std::make_shared<DecorationState>();
529}
530
531std::shared_ptr<DecorationState> Decoration::currentState() const
532{
533 return d->current;
534}
535
536std::shared_ptr<DecorationState> Decoration::nextState() const
537{
538 return d->next;
539}
540
542{
543 d->next = createState();
544 d->current = createState();
545}
546
547void Decoration::setState(std::function<void(DecorationState *state)> callback)
548{
549 callback(d->next.get());
550 Q_EMIT nextStateChanged(d->next);
551}
552
553void Decoration::apply(std::shared_ptr<DecorationState> state)
554{
555 if (d->current == state) {
556 return;
557 }
558
559 const auto previous = d->current;
560 d->current = state;
561 update();
562
563 if (previous->borders() != state->borders()) {
564 Q_EMIT bordersChanged();
565 }
566
567 Q_EMIT currentStateChanged(state);
568}
569
570} // namespace
571
572#include "moc_decoration.cpp"
The Client which gets decorated.
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:105
std::shared_ptr< DecorationState > nextState() const
Returns the next state, i.e.
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.
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:99
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:112
std::shared_ptr< DecorationShadow > shadow() const
DecorationShadow for this Decoration.
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
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
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
QRect toAlignedRect() const const
qreal height() const const
qreal 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 Sat Dec 21 2024 16:55:48 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.