File indexing completed on 2024-05-05 05:29:54
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 #include "decorationbuttongroup.h" 0007 #include "decoration.h" 0008 #include "decorationbuttongroup_p.h" 0009 #include "decorationsettings.h" 0010 0011 #include <QDebug> 0012 #include <QGuiApplication> 0013 0014 namespace KDecoration2 0015 { 0016 DecorationButtonGroup::Private::Private(Decoration *decoration, DecorationButtonGroup *parent) 0017 : decoration(decoration) 0018 , spacing(0.0) 0019 , q(parent) 0020 { 0021 } 0022 0023 DecorationButtonGroup::Private::~Private() = default; 0024 0025 void DecorationButtonGroup::Private::setGeometry(const QRectF &geo) 0026 { 0027 if (geometry == geo) { 0028 return; 0029 } 0030 geometry = geo; 0031 Q_EMIT q->geometryChanged(geometry); 0032 } 0033 0034 namespace 0035 { 0036 static bool s_layoutRecursion = false; 0037 } 0038 0039 void DecorationButtonGroup::Private::updateLayout() 0040 { 0041 if (s_layoutRecursion) { 0042 return; 0043 } 0044 s_layoutRecursion = true; 0045 const QPointF &pos = geometry.topLeft(); 0046 // first calculate new size 0047 qreal height = 0; 0048 qreal width = 0; 0049 for (auto it = buttons.constBegin(); it != buttons.constEnd(); ++it) { 0050 if (!(*it)->isVisible()) { 0051 continue; 0052 } 0053 height = qMax(height, qreal((*it)->size().height())); 0054 width += (*it)->size().width(); 0055 if (it + 1 != buttons.constEnd()) { 0056 width += spacing; 0057 } 0058 } 0059 setGeometry(QRectF(pos, QSizeF(width, height))); 0060 0061 QGuiApplication* app = qobject_cast<QGuiApplication*>(QCoreApplication::instance()); 0062 const auto layoutDirection = app ? app->layoutDirection() : Qt::LeftToRight; 0063 0064 qreal leftPosition = pos.x(); 0065 qreal rightPosition = pos.x() + width; 0066 0067 if (layoutDirection == Qt::LeftToRight) 0068 for (auto button : std::as_const(buttons)) { 0069 if (!button->isVisible()) { 0070 continue; 0071 } 0072 const auto size = button->size(); 0073 const auto buttonPos = QPointF(leftPosition, pos.y()); 0074 button->setGeometry(QRectF(buttonPos, size)); 0075 leftPosition += size.width() + spacing; 0076 } 0077 else if (layoutDirection == Qt::RightToLeft) 0078 for (auto button : std::as_const(buttons)) { 0079 if (!button->isVisible()) { 0080 continue; 0081 } 0082 const auto size = button->size(); 0083 const auto buttonPos = QPointF(rightPosition - size.width(), pos.y()); 0084 button->setGeometry(QRectF(buttonPos, size)); 0085 rightPosition -= size.width() + spacing; 0086 } 0087 else { 0088 qCritical() << "There's an unhandled layout direction! This is likely an issue of KDecoration2 not being updated to handle it\n" 0089 << "or the application having an invalid layout direction set. Either way, this is a critical bug."; 0090 } 0091 0092 s_layoutRecursion = false; 0093 } 0094 0095 DecorationButtonGroup::DecorationButtonGroup(Decoration *parent) 0096 : QObject(parent) 0097 , d(new Private(parent, this)) 0098 { 0099 } 0100 0101 DecorationButtonGroup::DecorationButtonGroup(DecorationButtonGroup::Position type, 0102 Decoration *parent, 0103 std::function<DecorationButton *(DecorationButtonType, Decoration *, QObject *)> buttonCreator) 0104 : QObject(parent) 0105 , d(new Private(parent, this)) 0106 { 0107 QGuiApplication* app = qobject_cast<QGuiApplication*>(QCoreApplication::instance()); 0108 const auto layoutDirection = app ? app->layoutDirection() : Qt::LeftToRight; 0109 auto settings = parent->settings(); 0110 auto createButtons = [&] { 0111 const auto &buttons = 0112 (type == Position::Left) ? 0113 (layoutDirection == Qt::LeftToRight ? settings->decorationButtonsLeft() : settings->decorationButtonsRight()) : 0114 (layoutDirection == Qt::LeftToRight ? settings->decorationButtonsRight() : settings->decorationButtonsLeft()); 0115 for (DecorationButtonType type : buttons) { 0116 if (DecorationButton *b = buttonCreator(type, parent, this)) { 0117 addButton(b); 0118 } 0119 } 0120 }; 0121 createButtons(); 0122 auto changed = type == Position::Left ? &DecorationSettings::decorationButtonsLeftChanged : &DecorationSettings::decorationButtonsRightChanged; 0123 connect(settings.get(), changed, this, [this, createButtons] { 0124 qDeleteAll(d->buttons); 0125 d->buttons.clear(); 0126 createButtons(); 0127 }); 0128 } 0129 0130 DecorationButtonGroup::~DecorationButtonGroup() = default; 0131 0132 Decoration *DecorationButtonGroup::decoration() const 0133 { 0134 return d->decoration; 0135 } 0136 0137 QRectF DecorationButtonGroup::geometry() const 0138 { 0139 return d->geometry; 0140 } 0141 0142 bool DecorationButtonGroup::hasButton(DecorationButtonType type) const 0143 { 0144 // TODO: check for deletion of button 0145 auto it = std::find_if(d->buttons.begin(), d->buttons.end(), [type](DecorationButton *button) { 0146 return button->type() == type; 0147 }); 0148 return it != d->buttons.end(); 0149 } 0150 0151 qreal DecorationButtonGroup::spacing() const 0152 { 0153 return d->spacing; 0154 } 0155 0156 QPointF DecorationButtonGroup::pos() const 0157 { 0158 return d->geometry.topLeft(); 0159 } 0160 0161 void DecorationButtonGroup::setPos(const QPointF &pos) 0162 { 0163 if (d->geometry.topLeft() == pos) { 0164 return; 0165 } 0166 d->setGeometry(QRectF(pos, d->geometry.size())); 0167 d->updateLayout(); 0168 } 0169 0170 void DecorationButtonGroup::setSpacing(qreal spacing) 0171 { 0172 if (d->spacing == spacing) { 0173 return; 0174 } 0175 d->spacing = spacing; 0176 Q_EMIT spacingChanged(d->spacing); 0177 d->updateLayout(); 0178 } 0179 0180 void DecorationButtonGroup::addButton(DecorationButton *button) 0181 { 0182 Q_ASSERT(button); 0183 connect(button, &DecorationButton::visibilityChanged, this, [this]() { 0184 d->updateLayout(); 0185 }); 0186 connect(button, &DecorationButton::geometryChanged, this, [this]() { 0187 d->updateLayout(); 0188 }); 0189 d->buttons.append(button); 0190 d->updateLayout(); 0191 } 0192 0193 QList<DecorationButton *> DecorationButtonGroup::buttons() const 0194 { 0195 return d->buttons; 0196 } 0197 0198 void DecorationButtonGroup::removeButton(DecorationButtonType type) 0199 { 0200 bool needUpdate = false; 0201 auto it = d->buttons.begin(); 0202 while (it != d->buttons.end()) { 0203 if ((*it)->type() == type) { 0204 it = d->buttons.erase(it); 0205 needUpdate = true; 0206 } else { 0207 it++; 0208 } 0209 } 0210 if (needUpdate) { 0211 d->updateLayout(); 0212 } 0213 } 0214 0215 void DecorationButtonGroup::removeButton(DecorationButton *button) 0216 { 0217 bool needUpdate = false; 0218 auto it = d->buttons.begin(); 0219 while (it != d->buttons.end()) { 0220 if (*it == button) { 0221 it = d->buttons.erase(it); 0222 needUpdate = true; 0223 } else { 0224 it++; 0225 } 0226 } 0227 if (needUpdate) { 0228 d->updateLayout(); 0229 } 0230 } 0231 0232 void DecorationButtonGroup::paint(QPainter *painter, const QRect &repaintArea) 0233 { 0234 const auto &buttons = d->buttons; 0235 for (auto button : buttons) { 0236 if (!button->isVisible()) { 0237 continue; 0238 } 0239 button->paint(painter, repaintArea); 0240 } 0241 } 0242 0243 } // namespace 0244 0245 #include "moc_decorationbuttongroup.cpp"