File indexing completed on 2024-04-28 16:44:34
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) for (auto button : qAsConst(buttons)) { 0068 if (!button->isVisible()) { 0069 continue; 0070 } 0071 const auto size = button->size(); 0072 const auto buttonPos = QPointF(leftPosition, pos.y()); 0073 button->setGeometry(QRectF(buttonPos, size)); 0074 leftPosition += size.width() + spacing; 0075 } else if (layoutDirection == Qt::RightToLeft) for (auto button : qAsConst(buttons)) { 0076 if (!button->isVisible()) { 0077 continue; 0078 } 0079 const auto size = button->size(); 0080 const auto buttonPos = QPointF(rightPosition - size.width(), pos.y()); 0081 button->setGeometry(QRectF(buttonPos, size)); 0082 rightPosition -= size.width() + spacing; 0083 } else { 0084 qCritical() << "There's an unhandled layout direction! This is likely an issue of KDecoration2 not being updated to handle it\n" 0085 << "or the application having an invalid layout direction set. Either way, this is a critical bug."; 0086 } 0087 0088 s_layoutRecursion = false; 0089 } 0090 0091 DecorationButtonGroup::DecorationButtonGroup(Decoration *parent) 0092 : QObject(parent) 0093 , d(new Private(parent, this)) 0094 { 0095 } 0096 0097 DecorationButtonGroup::DecorationButtonGroup(DecorationButtonGroup::Position type, 0098 Decoration *parent, 0099 std::function<DecorationButton *(DecorationButtonType, Decoration *, QObject *)> buttonCreator) 0100 : QObject(parent) 0101 , d(new Private(parent, this)) 0102 { 0103 QGuiApplication* app = qobject_cast<QGuiApplication*>(QCoreApplication::instance()); 0104 const auto layoutDirection = app ? app->layoutDirection() : Qt::LeftToRight; 0105 auto settings = parent->settings(); 0106 auto createButtons = [=] { 0107 const auto &buttons = 0108 (type == Position::Left) ? 0109 (layoutDirection == Qt::LeftToRight ? settings->decorationButtonsLeft() : settings->decorationButtonsRight()) : 0110 (layoutDirection == Qt::LeftToRight ? settings->decorationButtonsRight() : settings->decorationButtonsLeft()); 0111 for (DecorationButtonType type : buttons) { 0112 if (DecorationButton *b = buttonCreator(type, parent, this)) { 0113 addButton(QPointer<DecorationButton>(b)); 0114 } 0115 } 0116 }; 0117 createButtons(); 0118 auto changed = type == Position::Left ? &DecorationSettings::decorationButtonsLeftChanged : &DecorationSettings::decorationButtonsRightChanged; 0119 connect(settings.data(), changed, this, [this, createButtons] { 0120 qDeleteAll(d->buttons); 0121 d->buttons.clear(); 0122 createButtons(); 0123 }); 0124 } 0125 0126 DecorationButtonGroup::~DecorationButtonGroup() = default; 0127 0128 QPointer<Decoration> DecorationButtonGroup::decoration() const 0129 { 0130 return QPointer<Decoration>(d->decoration); 0131 } 0132 0133 QRectF DecorationButtonGroup::geometry() const 0134 { 0135 return d->geometry; 0136 } 0137 0138 bool DecorationButtonGroup::hasButton(DecorationButtonType type) const 0139 { 0140 // TODO: check for deletion of button 0141 auto it = std::find_if(d->buttons.begin(), d->buttons.end(), [type](const QPointer<DecorationButton> &button) { 0142 return button->type() == type; 0143 }); 0144 return it != d->buttons.end(); 0145 } 0146 0147 qreal DecorationButtonGroup::spacing() const 0148 { 0149 return d->spacing; 0150 } 0151 0152 QPointF DecorationButtonGroup::pos() const 0153 { 0154 return d->geometry.topLeft(); 0155 } 0156 0157 void DecorationButtonGroup::setPos(const QPointF &pos) 0158 { 0159 if (d->geometry.topLeft() == pos) { 0160 return; 0161 } 0162 d->setGeometry(QRectF(pos, d->geometry.size())); 0163 d->updateLayout(); 0164 } 0165 0166 void DecorationButtonGroup::setSpacing(qreal spacing) 0167 { 0168 if (d->spacing == spacing) { 0169 return; 0170 } 0171 d->spacing = spacing; 0172 Q_EMIT spacingChanged(d->spacing); 0173 d->updateLayout(); 0174 } 0175 0176 void DecorationButtonGroup::addButton(const QPointer<DecorationButton> &button) 0177 { 0178 Q_ASSERT(!button.isNull()); 0179 connect(button.data(), &DecorationButton::visibilityChanged, this, [this]() { 0180 d->updateLayout(); 0181 }); 0182 connect(button.data(), &DecorationButton::geometryChanged, this, [this]() { 0183 d->updateLayout(); 0184 }); 0185 d->buttons.append(button); 0186 d->updateLayout(); 0187 } 0188 0189 QVector<QPointer<DecorationButton>> DecorationButtonGroup::buttons() const 0190 { 0191 return d->buttons; 0192 } 0193 0194 void DecorationButtonGroup::removeButton(DecorationButtonType type) 0195 { 0196 bool needUpdate = false; 0197 auto it = d->buttons.begin(); 0198 while (it != d->buttons.end()) { 0199 if ((*it)->type() == type) { 0200 it = d->buttons.erase(it); 0201 needUpdate = true; 0202 } else { 0203 it++; 0204 } 0205 } 0206 if (needUpdate) { 0207 d->updateLayout(); 0208 } 0209 } 0210 0211 void DecorationButtonGroup::removeButton(const QPointer<DecorationButton> &button) 0212 { 0213 bool needUpdate = false; 0214 auto it = d->buttons.begin(); 0215 while (it != d->buttons.end()) { 0216 if (*it == button) { 0217 it = d->buttons.erase(it); 0218 needUpdate = true; 0219 } else { 0220 it++; 0221 } 0222 } 0223 if (needUpdate) { 0224 d->updateLayout(); 0225 } 0226 } 0227 0228 void DecorationButtonGroup::paint(QPainter *painter, const QRect &repaintArea) 0229 { 0230 const auto &buttons = d->buttons; 0231 for (auto button : buttons) { 0232 if (!button->isVisible()) { 0233 continue; 0234 } 0235 button->paint(painter, repaintArea); 0236 } 0237 } 0238 0239 } // namespace