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(setBlurRegion, blurRegion, const QRegion &, )
223 DELEGATE(setBorders, borders, const QMargins &, )
224 DELEGATE(setResizeOnlyBorders, resizeOnlyBorders, const QMargins &, )
225 DELEGATE(setTitleBar, titleBar, const QRect &, )
226 DELEGATE(setOpaque, opaque, bool, d->opaque)
227 DELEGATE(setShadow, shadow, const QSharedPointer<DecorationShadow> &, d->shadow)
228 
229 #undef DELEGATE
230 
231 #define DELEGATE(name, type) \
232  type Decoration::name() const \
233  { \
234  return d->name; \
235  }
236 
237 DELEGATE(blurRegion, QRegion)
238 DELEGATE(borders, QMargins)
239 DELEGATE(resizeOnlyBorders, QMargins)
240 DELEGATE(titleBar, QRect)
241 DELEGATE(sectionUnderMouse, Qt::WindowFrameSection)
242 DELEGATE(shadow, QSharedPointer<DecorationShadow>)
243 
244 #undef DELEGATE
245 
246 bool Decoration::isOpaque() const
247 {
248  return d->opaque;
249 }
250 
251 #define BORDER(name, Name) \
252  int Decoration::border##Name() const \
253  { \
254  return d->borders.name(); \
255  } \
256  int Decoration::resizeOnlyBorder##Name() const \
257  { \
258  return d->resizeOnlyBorders.name(); \
259  }
260 
261 BORDER(left, Left)
262 BORDER(right, Right)
263 BORDER(top, Top)
264 BORDER(bottom, Bottom)
265 #undef BORDER
266 
267 QSize Decoration::size() const
268 {
269  const QMargins &b = d->borders;
270  return QSize(d->client->width() + b.left() + b.right(), //
271  (d->client->isShaded() ? 0 : d->client->height()) + b.top() + b.bottom());
272 }
273 
275 {
276  return QRect(QPoint(0, 0), size());
277 }
278 
279 bool Decoration::event(QEvent *event)
280 {
281  switch (event->type()) {
282  case QEvent::HoverEnter:
283  hoverEnterEvent(static_cast<QHoverEvent *>(event));
284  return true;
285  case QEvent::HoverLeave:
286  hoverLeaveEvent(static_cast<QHoverEvent *>(event));
287  return true;
288  case QEvent::HoverMove:
289  hoverMoveEvent(static_cast<QHoverEvent *>(event));
290  return true;
292  mousePressEvent(static_cast<QMouseEvent *>(event));
293  return true;
295  mouseReleaseEvent(static_cast<QMouseEvent *>(event));
296  return true;
297  case QEvent::MouseMove:
298  mouseMoveEvent(static_cast<QMouseEvent *>(event));
299  return true;
300  case QEvent::Wheel:
301  wheelEvent(static_cast<QWheelEvent *>(event));
302  return true;
303  default:
304  return QObject::event(event);
305  }
306 }
307 
308 void Decoration::hoverEnterEvent(QHoverEvent *event)
309 {
310  for (DecorationButton *button : d->buttons) {
311  QCoreApplication::instance()->sendEvent(button, event);
312  }
313  d->updateSectionUnderMouse(event->pos());
314 }
315 
316 void Decoration::hoverLeaveEvent(QHoverEvent *event)
317 {
318  for (DecorationButton *button : d->buttons) {
319  QCoreApplication::instance()->sendEvent(button, event);
320  }
321  d->setSectionUnderMouse(Qt::NoSection);
322 }
323 
324 void Decoration::hoverMoveEvent(QHoverEvent *event)
325 {
326  for (DecorationButton *button : d->buttons) {
327  if (!button->isEnabled() || !button->isVisible()) {
328  continue;
329  }
330  const bool hovered = button->isHovered();
331  const bool contains = button->contains(event->posF());
332  if (!hovered && contains) {
333  QHoverEvent e(QEvent::HoverEnter, event->posF(), event->oldPosF(), event->modifiers());
334  QCoreApplication::instance()->sendEvent(button, &e);
335  } else if (hovered && !contains) {
336  QHoverEvent e(QEvent::HoverLeave, event->posF(), event->oldPosF(), event->modifiers());
337  QCoreApplication::instance()->sendEvent(button, &e);
338  } else if (hovered && contains) {
339  QCoreApplication::instance()->sendEvent(button, event);
340  }
341  }
342  d->updateSectionUnderMouse(event->pos());
343 }
344 
345 void Decoration::mouseMoveEvent(QMouseEvent *event)
346 {
347  for (DecorationButton *button : d->buttons) {
348  if (button->isPressed()) {
349  QCoreApplication::instance()->sendEvent(button, event);
350  return;
351  }
352  }
353  // not handled, take care ourselves
354 }
355 
356 void Decoration::mousePressEvent(QMouseEvent *event)
357 {
358  for (DecorationButton *button : d->buttons) {
359  if (button->isHovered()) {
360  if (button->acceptedButtons().testFlag(event->button())) {
361  QCoreApplication::instance()->sendEvent(button, event);
362  }
363  event->setAccepted(true);
364  return;
365  }
366  }
367 }
368 
369 void Decoration::mouseReleaseEvent(QMouseEvent *event)
370 {
371  for (DecorationButton *button : d->buttons) {
372  if (button->isPressed() && button->acceptedButtons().testFlag(event->button())) {
373  QCoreApplication::instance()->sendEvent(button, event);
374  return;
375  }
376  }
377  // not handled, take care ourselves
378  d->updateSectionUnderMouse(event->pos());
379 }
380 
381 void Decoration::wheelEvent(QWheelEvent *event)
382 {
383  for (DecorationButton *button : d->buttons) {
384  if (button->contains(event->position())) {
385  QCoreApplication::instance()->sendEvent(button, event);
386  event->setAccepted(true);
387  }
388  }
389 }
390 
391 void Decoration::update(const QRect &r)
392 {
393  Q_EMIT damaged(r.isNull() ? rect() : r);
394 }
395 
396 void Decoration::update()
397 {
398  update(QRect());
399 }
400 
402 {
403  d->settings = settings;
404 }
405 
407 {
408  return d->settings;
409 }
410 
411 } // namespace
QTextStream & right(QTextStream &stream)
int bottom() const const
Q_EMITQ_EMIT
QTextStream & left(QTextStream &stream)
void requestShowWindowMenu(const QRect &rect)
Definition: decoration.cpp:174
QRect rect() const
The decoration's geometry in local coordinates.
Definition: decoration.cpp:274
NoSection
int x() const const
int y() const const
int width() const const
typedef MouseButtons
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void destroyed(QObject *obj)
QWeakPointer< DecoratedClient > client() const
The DecoratedClient for this Decoration.
Definition: decoration.cpp:146
int right() const const
virtual bool event(QEvent *e)
int height() const const
bool sendEvent(QObject *receiver, QEvent *event)
QCoreApplication * instance()
virtual void init()
This method gets invoked from the framework once the Decoration is created and completely setup.
Definition: decoration.cpp:141
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
bool isNull() const const
T * get() const const
QSharedPointer< DecorationSettings > settings() const
Definition: decoration.cpp:406
bool isNull() const const
int left() const const
void setSettings(const QSharedPointer< DecorationSettings > &settings)
Invoked by the framework to set the Settings for this Decoration before init is invoked.
Definition: decoration.cpp:401
int top() const const
Framework for creating window decorations.
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Wed Aug 17 2022 04:02:05 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.