KDecoration2

decorationbuttongroup.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 "decorationbuttongroup.h"
7#include "decoration.h"
8#include "decorationbuttongroup_p.h"
9#include "decorationsettings.h"
10
11#include <QDebug>
12#include <QGuiApplication>
13
14namespace KDecoration2
15{
16DecorationButtonGroup::Private::Private(Decoration *decoration, DecorationButtonGroup *parent)
17 : decoration(decoration)
18 , spacing(0.0)
19 , q(parent)
20{
21}
22
23DecorationButtonGroup::Private::~Private() = default;
24
25void 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
34namespace
35{
36static bool s_layoutRecursion = false;
37}
38
39void 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)
68 for (auto button : std::as_const(buttons)) {
69 if (!button->isVisible()) {
70 continue;
71 }
72 const auto size = button->size();
73 const auto buttonPos = QPointF(leftPosition, pos.y());
74 button->setGeometry(QRectF(buttonPos, size));
75 leftPosition += size.width() + spacing;
76 }
77 else if (layoutDirection == Qt::RightToLeft)
78 for (auto button : std::as_const(buttons)) {
79 if (!button->isVisible()) {
80 continue;
81 }
82 const auto size = button->size();
83 const auto buttonPos = QPointF(rightPosition - size.width(), pos.y());
84 button->setGeometry(QRectF(buttonPos, size));
85 rightPosition -= size.width() + spacing;
86 }
87 else {
88 qCritical() << "There's an unhandled layout direction! This is likely an issue of KDecoration2 not being updated to handle it\n"
89 << "or the application having an invalid layout direction set. Either way, this is a critical bug.";
90 }
91
92 s_layoutRecursion = false;
93}
94
95DecorationButtonGroup::DecorationButtonGroup(Decoration *parent)
96 : QObject(parent)
97 , d(new Private(parent, this))
98{
99}
100
101DecorationButtonGroup::DecorationButtonGroup(DecorationButtonGroup::Position type,
102 Decoration *parent,
103 std::function<DecorationButton *(DecorationButtonType, Decoration *, QObject *)> buttonCreator)
104 : QObject(parent)
105 , d(new Private(parent, this))
106{
107 auto createButtons = [this, buttonCreator, type] {
109 const DecorationSettings *settings = d->decoration->settings().get();
110 const auto &buttons =
111 (type == Position::Left) ?
112 (layoutDirection == Qt::LeftToRight ? settings->decorationButtonsLeft() : settings->decorationButtonsRight()) :
113 (layoutDirection == Qt::LeftToRight ? settings->decorationButtonsRight() : settings->decorationButtonsLeft());
114 for (DecorationButtonType type : buttons) {
115 if (DecorationButton *b = buttonCreator(type, d->decoration, this)) {
116 addButton(b);
117 }
118 }
119 };
120 createButtons();
121 auto changed = type == Position::Left ? &DecorationSettings::decorationButtonsLeftChanged : &DecorationSettings::decorationButtonsRightChanged;
122 connect(parent->settings().get(), changed, this, [this, createButtons] {
123 qDeleteAll(d->buttons);
124 d->buttons.clear();
125 createButtons();
126 });
127}
128
129DecorationButtonGroup::~DecorationButtonGroup() = default;
130
131Decoration *DecorationButtonGroup::decoration() const
132{
133 return d->decoration;
134}
135
136QRectF DecorationButtonGroup::geometry() const
137{
138 return d->geometry;
139}
140
141bool DecorationButtonGroup::hasButton(DecorationButtonType type) const
142{
143 // TODO: check for deletion of button
144 auto it = std::find_if(d->buttons.begin(), d->buttons.end(), [type](DecorationButton *button) {
145 return button->type() == type;
146 });
147 return it != d->buttons.end();
148}
149
150qreal DecorationButtonGroup::spacing() const
151{
152 return d->spacing;
153}
154
155QPointF DecorationButtonGroup::pos() const
156{
157 return d->geometry.topLeft();
158}
159
160void DecorationButtonGroup::setPos(const QPointF &pos)
161{
162 if (d->geometry.topLeft() == pos) {
163 return;
164 }
165 d->setGeometry(QRectF(pos, d->geometry.size()));
166 d->updateLayout();
167}
168
169void DecorationButtonGroup::setSpacing(qreal spacing)
170{
171 if (d->spacing == spacing) {
172 return;
173 }
174 d->spacing = spacing;
175 Q_EMIT spacingChanged(d->spacing);
176 d->updateLayout();
177}
178
179void DecorationButtonGroup::addButton(DecorationButton *button)
180{
181 Q_ASSERT(button);
182 connect(button, &DecorationButton::visibilityChanged, this, [this]() {
183 d->updateLayout();
184 });
185 connect(button, &DecorationButton::geometryChanged, this, [this]() {
186 d->updateLayout();
187 });
188 d->buttons.append(button);
189 d->updateLayout();
190}
191
192QList<DecorationButton *> DecorationButtonGroup::buttons() const
193{
194 return d->buttons;
195}
196
197void DecorationButtonGroup::removeButton(DecorationButtonType type)
198{
199 bool needUpdate = false;
200 auto it = d->buttons.begin();
201 while (it != d->buttons.end()) {
202 if ((*it)->type() == type) {
203 it = d->buttons.erase(it);
204 needUpdate = true;
205 } else {
206 it++;
207 }
208 }
209 if (needUpdate) {
210 d->updateLayout();
211 }
212}
213
214void DecorationButtonGroup::removeButton(DecorationButton *button)
215{
216 bool needUpdate = false;
217 auto it = d->buttons.begin();
218 while (it != d->buttons.end()) {
219 if (*it == button) {
220 it = d->buttons.erase(it);
221 needUpdate = true;
222 } else {
223 it++;
224 }
225 }
226 if (needUpdate) {
227 d->updateLayout();
228 }
229}
230
231void DecorationButtonGroup::paint(QPainter *painter, const QRect &repaintArea)
232{
233 const auto &buttons = d->buttons;
234 for (auto button : buttons) {
235 if (!button->isVisible()) {
236 continue;
237 }
238 button->paint(painter, repaintArea);
239 }
240}
241
242} // namespace
243
244#include "moc_decorationbuttongroup.cpp"
A button to be used in a Decoration.
Type type(const QSqlDatabase &db)
Framework for creating window decorations.
DecorationButtonType
The DecorationButtonType is a helper type for the DecorationButton.
GeoCoordinates geo(const QVariant &location)
QCoreApplication * instance()
iterator begin()
qreal x() const const
qreal y() const const
LeftToRight
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:17:09 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.