KDecoration2

decoration.cpp
1 /*
2  * SPDX-FileCopyrightText: 2014 Martin Gräßlin <[email protected]>
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 namespace KDecoration2
18 {
19 namespace
20 {
21 DecorationBridge *findBridge(const QVariantList &args)
22 {
23  for (const auto &arg : args) {
24  if (auto bridge = arg.toMap().value(QStringLiteral("bridge")).value<DecorationBridge *>()) {
25  return bridge;
26  }
27  }
28  Q_UNREACHABLE();
29 }
30 }
31 
32 Decoration::Private::Private(Decoration *deco, const QVariantList &args)
33  : sectionUnderMouse(Qt::NoSection)
34  , bridge(findBridge(args))
35  , client(QSharedPointer<DecoratedClient>(new DecoratedClient(deco, bridge)))
36  , opaque(false)
37  , q(deco)
38 {
39  Q_UNUSED(args)
40 }
41 
42 void Decoration::Private::setSectionUnderMouse(Qt::WindowFrameSection section)
43 {
44  if (sectionUnderMouse == section) {
45  return;
46  }
47  sectionUnderMouse = section;
48  Q_EMIT q->sectionUnderMouseChanged(sectionUnderMouse);
49 }
50 
51 void Decoration::Private::updateSectionUnderMouse(const QPoint &mousePosition)
52 {
53  if (titleBar.contains(mousePosition)) {
54  setSectionUnderMouse(Qt::TitleBarArea);
55  return;
56  }
57  const QSize size = q->size();
58  const int corner = 2 * settings->largeSpacing();
59  const bool left = mousePosition.x() < borders.left();
60  const bool top = mousePosition.y() < borders.top();
61  const bool bottom = size.height() - mousePosition.y() <= borders.bottom();
62  const bool right = size.width() - mousePosition.x() <= borders.right();
63  if (left) {
64  if (top && mousePosition.y() < titleBar.top() + corner) {
65  setSectionUnderMouse(Qt::TopLeftSection);
66  } else if (size.height() - mousePosition.y() <= borders.bottom() + corner && mousePosition.y() > titleBar.bottom()) {
67  setSectionUnderMouse(Qt::BottomLeftSection);
68  } else {
69  setSectionUnderMouse(Qt::LeftSection);
70  }
71  return;
72  }
73  if (right) {
74  if (top && mousePosition.y() < titleBar.top() + corner) {
75  setSectionUnderMouse(Qt::TopRightSection);
76  } else if (size.height() - mousePosition.y() <= borders.bottom() + corner && mousePosition.y() > titleBar.bottom()) {
77  setSectionUnderMouse(Qt::BottomRightSection);
78  } else {
79  setSectionUnderMouse(Qt::RightSection);
80  }
81  return;
82  }
83  if (bottom) {
84  if (mousePosition.y() > titleBar.bottom()) {
85  if (mousePosition.x() < borders.left() + corner) {
86  setSectionUnderMouse(Qt::BottomLeftSection);
87  } else if (size.width() - mousePosition.x() <= borders.right() + corner) {
88  setSectionUnderMouse(Qt::BottomRightSection);
89  } else {
90  setSectionUnderMouse(Qt::BottomSection);
91  }
92  } else {
93  setSectionUnderMouse(Qt::TitleBarArea);
94  }
95  return;
96  }
97  if (top) {
98  if (mousePosition.y() < titleBar.top()) {
99  if (mousePosition.x() < borders.left() + corner) {
100  setSectionUnderMouse(Qt::TopLeftSection);
101  } else if (size.width() - mousePosition.x() <= borders.right() + corner) {
102  setSectionUnderMouse(Qt::TopRightSection);
103  } else {
104  setSectionUnderMouse(Qt::TopSection);
105  }
106  } else {
107  setSectionUnderMouse(Qt::TitleBarArea);
108  }
109  return;
110  }
111  setSectionUnderMouse(Qt::NoSection);
112 }
113 
114 void Decoration::Private::addButton(DecorationButton *button)
115 {
116  Q_ASSERT(!buttons.contains(button));
117  buttons << button;
118  QObject::connect(button, &QObject::destroyed, q, [this](QObject *o) {
119  auto it = buttons.begin();
120  while (it != buttons.end()) {
121  if (*it == static_cast<DecorationButton *>(o)) {
122  it = buttons.erase(it);
123  } else {
124  it++;
125  }
126  }
127  });
128 }
129 
130 Decoration::Decoration(QObject *parent, const QVariantList &args)
131  : QObject(parent)
132  , d(new Private(this, args))
133 {
134  connect(this, &Decoration::bordersChanged, this, [this] {
135  update();
136  });
137 }
138 
139 Decoration::~Decoration() = default;
140 
142 {
143  Q_ASSERT(!d->settings.isNull());
144 }
145 
147 {
148  return d->client.toWeakRef();
149 }
150 
151 #define DELEGATE(name) \
152  void Decoration::name() \
153  { \
154  d->client->d->name(); \
155  }
156 
157 DELEGATE(requestClose)
158 DELEGATE(requestContextHelp)
159 DELEGATE(requestMinimize)
160 DELEGATE(requestToggleOnAllDesktops)
161 DELEGATE(requestToggleShade)
162 DELEGATE(requestToggleKeepAbove)
163 DELEGATE(requestToggleKeepBelow)
164 
165 #undef DELEGATE
166 
167 #if KDECORATIONS2_ENABLE_DEPRECATED_SINCE(5, 21)
169 {
171 }
172 #endif
173 
175 {
176  d->client->d->requestShowWindowMenu(rect);
177 }
178 
179 void Decoration::requestShowToolTip(const QString &text)
180 {
181  d->client->d->requestShowToolTip(text);
182 }
183 
184 void Decoration::requestHideToolTip()
185 {
186  d->client->d->requestHideToolTip();
187 }
188 
189 void Decoration::requestToggleMaximization(Qt::MouseButtons buttons)
190 {
191  d->client->d->requestToggleMaximization(buttons);
192 }
193 
194 void Decoration::showApplicationMenu(int actionId)
195 {
196  auto it = std::find_if(d->buttons.constBegin(), d->buttons.constEnd(), [](DecorationButton *button) {
197  return button->type() == DecorationButtonType::ApplicationMenu;
198  });
199 
200  if (it != d->buttons.constEnd()) {
201  requestShowApplicationMenu((*it)->geometry().toRect(), actionId);
202  }
203 }
204 
205 void Decoration::requestShowApplicationMenu(const QRect &rect, int actionId)
206 {
207  if (auto *appMenuEnabledPrivate = dynamic_cast<ApplicationMenuEnabledDecoratedClientPrivate *>(d->client->d.get())) {
208  appMenuEnabledPrivate->requestShowApplicationMenu(rect, actionId);
209  }
210 }
211 
212 #define DELEGATE(name, variableName, type, emitValue) \
213  void Decoration::name(type a) \
214  { \
215  if (d->variableName == a) { \
216  return; \
217  } \
218  d->variableName = a; \
219  Q_EMIT variableName##Changed(emitValue); \
220  }
221 
222 DELEGATE(setBorders, borders, const QMargins &, )
223 DELEGATE(setResizeOnlyBorders, resizeOnlyBorders, const QMargins &, )
224 DELEGATE(setTitleBar, titleBar, const QRect &, )
225 DELEGATE(setOpaque, opaque, bool, d->opaque)
226 DELEGATE(setShadow, shadow, const QSharedPointer<DecorationShadow> &, d->shadow)
227 
228 #undef DELEGATE
229 
230 #define DELEGATE(name, type) \
231  type Decoration::name() const \
232  { \
233  return d->name; \
234  }
235 
236 DELEGATE(borders, QMargins)
237 DELEGATE(resizeOnlyBorders, QMargins)
238 DELEGATE(titleBar, QRect)
241 
242 #undef DELEGATE
243 
244 bool Decoration::isOpaque() const
245 {
246  return d->opaque;
247 }
248 
249 #define BORDER(name, Name) \
250  int Decoration::border##Name() const \
251  { \
252  return d->borders.name(); \
253  } \
254  int Decoration::resizeOnlyBorder##Name() const \
255  { \
256  return d->resizeOnlyBorders.name(); \
257  }
258 
259 BORDER(left, Left)
260 BORDER(right, Right)
261 BORDER(top, Top)
262 BORDER(bottom, Bottom)
263 #undef BORDER
264 
265 QSize Decoration::size() const
266 {
267  const QMargins &b = d->borders;
268  return QSize(d->client->width() + b.left() + b.right(), //
269  (d->client->isShaded() ? 0 : d->client->height()) + b.top() + b.bottom());
270 }
271 
273 {
274  return QRect(QPoint(0, 0), size());
275 }
276 
277 bool Decoration::event(QEvent *event)
278 {
279  switch (event->type()) {
280  case QEvent::HoverEnter:
281  hoverEnterEvent(static_cast<QHoverEvent *>(event));
282  return true;
283  case QEvent::HoverLeave:
284  hoverLeaveEvent(static_cast<QHoverEvent *>(event));
285  return true;
286  case QEvent::HoverMove:
287  hoverMoveEvent(static_cast<QHoverEvent *>(event));
288  return true;
290  mousePressEvent(static_cast<QMouseEvent *>(event));
291  return true;
293  mouseReleaseEvent(static_cast<QMouseEvent *>(event));
294  return true;
295  case QEvent::MouseMove:
296  mouseMoveEvent(static_cast<QMouseEvent *>(event));
297  return true;
298  case QEvent::Wheel:
299  wheelEvent(static_cast<QWheelEvent *>(event));
300  return true;
301  default:
302  return QObject::event(event);
303  }
304 }
305 
306 void Decoration::hoverEnterEvent(QHoverEvent *event)
307 {
308  for (DecorationButton *button : d->buttons) {
309  QCoreApplication::instance()->sendEvent(button, event);
310  }
311  d->updateSectionUnderMouse(event->pos());
312 }
313 
314 void Decoration::hoverLeaveEvent(QHoverEvent *event)
315 {
316  for (DecorationButton *button : d->buttons) {
317  QCoreApplication::instance()->sendEvent(button, event);
318  }
319  d->setSectionUnderMouse(Qt::NoSection);
320 }
321 
322 void Decoration::hoverMoveEvent(QHoverEvent *event)
323 {
324  for (DecorationButton *button : d->buttons) {
325  if (!button->isEnabled() || !button->isVisible()) {
326  continue;
327  }
328  const bool hovered = button->isHovered();
329  const bool contains = button->contains(event->posF());
330  if (!hovered && contains) {
331  QHoverEvent e(QEvent::HoverEnter, event->posF(), event->oldPosF(), event->modifiers());
332  QCoreApplication::instance()->sendEvent(button, &e);
333  } else if (hovered && !contains) {
334  QHoverEvent e(QEvent::HoverLeave, event->posF(), event->oldPosF(), event->modifiers());
335  QCoreApplication::instance()->sendEvent(button, &e);
336  } else if (hovered && contains) {
337  QCoreApplication::instance()->sendEvent(button, event);
338  }
339  }
340  d->updateSectionUnderMouse(event->pos());
341 }
342 
343 void Decoration::mouseMoveEvent(QMouseEvent *event)
344 {
345  for (DecorationButton *button : d->buttons) {
346  if (button->isPressed()) {
347  QCoreApplication::instance()->sendEvent(button, event);
348  return;
349  }
350  }
351  // not handled, take care ourselves
352 }
353 
354 void Decoration::mousePressEvent(QMouseEvent *event)
355 {
356  for (DecorationButton *button : d->buttons) {
357  if (button->isHovered()) {
358  if (button->acceptedButtons().testFlag(event->button())) {
359  QCoreApplication::instance()->sendEvent(button, event);
360  }
361  event->setAccepted(true);
362  return;
363  }
364  }
365 }
366 
367 void Decoration::mouseReleaseEvent(QMouseEvent *event)
368 {
369  for (DecorationButton *button : d->buttons) {
370  if (button->isPressed() && button->acceptedButtons().testFlag(event->button())) {
371  QCoreApplication::instance()->sendEvent(button, event);
372  return;
373  }
374  }
375  // not handled, take care ourselves
376  d->updateSectionUnderMouse(event->pos());
377 }
378 
379 void Decoration::wheelEvent(QWheelEvent *event)
380 {
381  for (DecorationButton *button : d->buttons) {
382  if (button->contains(event->posF())) {
383  QCoreApplication::instance()->sendEvent(button, event);
384  event->setAccepted(true);
385  }
386  }
387 }
388 
389 void Decoration::update(const QRect &r)
390 {
391  Q_EMIT damaged(r.isNull() ? rect() : r);
392 }
393 
394 void Decoration::update()
395 {
396  update(QRect());
397 }
398 
400 {
401  d->settings = settings;
402 }
403 
405 {
406  return d->settings;
407 }
408 
409 } // namespace
A button to be used in a Decoration.
QEvent::Type type() const const
Decoration(QObject *parent, const QVariantList &args)
Constructor for the Decoration.
Definition: decoration.cpp:130
int width() const const
bool opaque
Whether the Decoration is fully opaque.
Definition: decoration.h:89
Framework for creating window decorations.
int right() const const
const QPointF & posF() const const
int left() const const
Left
T * get() const const
QTextStream & right(QTextStream &stream)
typedef MouseButtons
QRect rect() const
The decoration&#39;s geometry in local coordinates.
Definition: decoration.cpp:272
int x() const const
int y() const const
virtual bool event(QEvent *e)
QTextStream & left(QTextStream &stream)
QWeakPointer< DecoratedClient > client() const
The DecoratedClient for this Decoration.
Definition: decoration.cpp:146
const QPointF & posF() const const
Qt::WindowFrameSection sectionUnderMouse
This property denotes the part of the Decoration which is currently under the mouse pointer...
Definition: decoration.h:76
int top() const const
Qt::MouseButton button() const const
int bottom() const const
virtual void init()
This method gets invoked from the framework once the Decoration is created and completely setup...
Definition: decoration.cpp:141
bool sendEvent(QObject *receiver, QEvent *event)
QRect titleBar
The titleBar is the area inside the Decoration containing all controls (e.g.
Definition: decoration.h:82
The ApplicationMenu button requests showing the application&#39;s menu on left or right click...
QCoreApplication * instance()
void setSettings(const QSharedPointer< DecorationSettings > &settings)
Invoked by the framework to set the Settings for this Decoration before init is invoked.
Definition: decoration.cpp:399
QSize size
The size of the DecoratedClient.
NoSection
Qt::MouseButtons acceptedButtons
The mouse buttons the DecorationButton accepts.
void setTitleBar(const QRect &rect)
An implementation has to invoke this method whenever the area containing the controls and caption cha...
bool isNull() const const
bool isNull() const const
bool contains(const QPointF &pos) const
Returns true if pos is inside of the button, otherwise returns false.
Top
QPoint pos() const const
QSharedPointer< DecorationSettings > settings() const
Definition: decoration.cpp:404
int height() const const
void requestShowWindowMenu(const QRect &rect)
Definition: decoration.cpp:174
QPoint pos() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QSharedPointer< DecorationShadow > shadow() const
DecorationShadow for this Decoration.
QObject * parent() const const
void destroyed(QObject *obj)
Q_EMITQ_EMIT
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Jun 20 2021 23:07:39 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.