KDecoration2

decorationbuttongroup.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 "decorationbuttongroup.h"
7 #include "decoration.h"
8 #include "decorationbuttongroup_p.h"
9 #include "decorationsettings.h"
10 
11 #include <QDebug>
12 #include <QGuiApplication>
13 
14 namespace KDecoration2
15 {
16 DecorationButtonGroup::Private::Private(Decoration *decoration, DecorationButtonGroup *parent)
17  : decoration(decoration)
18  , spacing(0.0)
19  , q(parent)
20 {
21 }
22 
23 DecorationButtonGroup::Private::~Private() = default;
24 
25 void DecorationButtonGroup::Private::setGeometry(const QRectF &geo)
26 {
27  if (geometry == geo) {
28  return;
29  }
30  geometry = geo;
31  Q_EMIT q->geometryChanged(geometry);
32 }
33 
34 namespace
35 {
36 static bool s_layoutRecursion = false;
37 }
38 
39 void DecorationButtonGroup::Private::updateLayout()
40 {
41  if (s_layoutRecursion) {
42  return;
43  }
44  s_layoutRecursion = true;
45  const QPointF &pos = geometry.topLeft();
46  // first calculate new size
47  qreal height = 0;
48  qreal width = 0;
49  for (auto it = buttons.constBegin(); it != buttons.constEnd(); ++it) {
50  if (!(*it)->isVisible()) {
51  continue;
52  }
53  height = qMax(height, qreal((*it)->size().height()));
54  width += (*it)->size().width();
55  if (it + 1 != buttons.constEnd()) {
56  width += spacing;
57  }
58  }
59  setGeometry(QRectF(pos, QSizeF(width, height)));
60 
61  QGuiApplication* app = qobject_cast<QGuiApplication*>(QCoreApplication::instance());
62  const auto layoutDirection = app ? app->layoutDirection() : Qt::LeftToRight;
63 
64  qreal leftPosition = pos.x();
65  qreal rightPosition = pos.x() + width;
66 
67  if (layoutDirection == Qt::LeftToRight) for (auto button : qAsConst(buttons)) {
68  if (!button->isVisible()) {
69  continue;
70  }
71  const auto size = button->size();
72  const auto buttonPos = QPointF(leftPosition, pos.y());
73  button->setGeometry(QRectF(buttonPos, size));
74  leftPosition += size.width() + spacing;
75  } else if (layoutDirection == Qt::RightToLeft) for (auto button : qAsConst(buttons)) {
76  if (!button->isVisible()) {
77  continue;
78  }
79  const auto size = button->size();
80  const auto buttonPos = QPointF(rightPosition - size.width(), pos.y());
81  button->setGeometry(QRectF(buttonPos, size));
82  rightPosition -= size.width() + spacing;
83  } else {
84  qCritical() << "There's an unhandled layout direction! This is likely an issue of KDecoration2 not being updated to handle it\n"
85  << "or the application having an invalid layout direction set. Either way, this is a critical bug.";
86  }
87 
88  s_layoutRecursion = false;
89 }
90 
91 DecorationButtonGroup::DecorationButtonGroup(Decoration *parent)
92  : QObject(parent)
93  , d(new Private(parent, this))
94 {
95 }
96 
97 DecorationButtonGroup::DecorationButtonGroup(DecorationButtonGroup::Position type,
98  Decoration *parent,
99  std::function<DecorationButton *(DecorationButtonType, Decoration *, QObject *)> buttonCreator)
100  : QObject(parent)
101  , d(new Private(parent, this))
102 {
103  QGuiApplication* app = qobject_cast<QGuiApplication*>(QCoreApplication::instance());
104  const auto layoutDirection = app ? app->layoutDirection() : Qt::LeftToRight;
105  auto settings = parent->settings();
106  auto createButtons = [=] {
107  const auto &buttons =
108  (type == Position::Left) ?
109  (layoutDirection == Qt::LeftToRight ? settings->decorationButtonsLeft() : settings->decorationButtonsRight()) :
110  (layoutDirection == Qt::LeftToRight ? settings->decorationButtonsRight() : settings->decorationButtonsLeft());
111  for (DecorationButtonType type : buttons) {
112  if (DecorationButton *b = buttonCreator(type, parent, this)) {
113  addButton(QPointer<DecorationButton>(b));
114  }
115  }
116  };
117  createButtons();
118  auto changed = type == Position::Left ? &DecorationSettings::decorationButtonsLeftChanged : &DecorationSettings::decorationButtonsRightChanged;
119  connect(settings.data(), changed, this, [this, createButtons] {
120  qDeleteAll(d->buttons);
121  d->buttons.clear();
122  createButtons();
123  });
124 }
125 
126 DecorationButtonGroup::~DecorationButtonGroup() = default;
127 
128 QPointer<Decoration> DecorationButtonGroup::decoration() const
129 {
130  return QPointer<Decoration>(d->decoration);
131 }
132 
134 {
135  return d->geometry;
136 }
137 
139 {
140  // TODO: check for deletion of button
141  auto it = std::find_if(d->buttons.begin(), d->buttons.end(), [type](const QPointer<DecorationButton> &button) {
142  return button->type() == type;
143  });
144  return it != d->buttons.end();
145 }
146 
147 qreal DecorationButtonGroup::spacing() const
148 {
149  return d->spacing;
150 }
151 
153 {
154  return d->geometry.topLeft();
155 }
156 
157 void DecorationButtonGroup::setPos(const QPointF &pos)
158 {
159  if (d->geometry.topLeft() == pos) {
160  return;
161  }
162  d->setGeometry(QRectF(pos, d->geometry.size()));
163  d->updateLayout();
164 }
165 
166 void DecorationButtonGroup::setSpacing(qreal spacing)
167 {
168  if (d->spacing == spacing) {
169  return;
170  }
171  d->spacing = spacing;
172  Q_EMIT spacingChanged(d->spacing);
173  d->updateLayout();
174 }
175 
177 {
178  Q_ASSERT(!button.isNull());
179  connect(button.data(), &DecorationButton::visibilityChanged, this, [this]() {
180  d->updateLayout();
181  });
182  connect(button.data(), &DecorationButton::geometryChanged, this, [this]() {
183  d->updateLayout();
184  });
185  d->buttons.append(button);
186  d->updateLayout();
187 }
188 
190 {
191  return d->buttons;
192 }
193 
195 {
196  bool needUpdate = false;
197  auto it = d->buttons.begin();
198  while (it != d->buttons.end()) {
199  if ((*it)->type() == type) {
200  it = d->buttons.erase(it);
201  needUpdate = true;
202  } else {
203  it++;
204  }
205  }
206  if (needUpdate) {
207  d->updateLayout();
208  }
209 }
210 
212 {
213  bool needUpdate = false;
214  auto it = d->buttons.begin();
215  while (it != d->buttons.end()) {
216  if (*it == button) {
217  it = d->buttons.erase(it);
218  needUpdate = true;
219  } else {
220  it++;
221  }
222  }
223  if (needUpdate) {
224  d->updateLayout();
225  }
226 }
227 
228 void DecorationButtonGroup::paint(QPainter *painter, const QRect &repaintArea)
229 {
230  const auto &buttons = d->buttons;
231  for (auto button : buttons) {
232  if (!button->isVisible()) {
233  continue;
234  }
235  button->paint(painter, repaintArea);
236  }
237 }
238 
239 } // namespace
QPointF pos
The top left Position of the DecorationButtonGroup.
void removeButton(const QPointer< DecorationButton > &button)
Removes button from the DecorationButtonGroup and triggers a re-layout of all DecorationButtons.
void addButton(const QPointer< DecorationButton > &button)
Adds button to the DecorationButtonGroup and triggers a re-layout of all DecorationButtons.
bool hasButton(DecorationButtonType type) const
DecorationButtonType
The DecorationButtonType is a helper type for the DecorationButton.
Q_EMITQ_EMIT
Type type(const QSqlDatabase &db)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool isNull() const const
QVector< QPointer< DecorationButton > > buttons() const
qreal spacing
The spacing to use between the DecorationButtons.
QCoreApplication * instance()
QRectF geometry
The geometry of the DecorationButtonGroup in Decoration-local coordinates.
GeoCoordinates geo(const QVariant &location)
virtual void paint(QPainter *painter, const QRect &repaintArea)
Paints the DecorationButtonGroup.
qreal x() const const
qreal y() const const
LeftToRight
T * data() const const
QObject * parent() 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 Sep 28 2022 04:07:26 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.