File indexing completed on 2024-04-21 03:56:01

0001 /*
0002  *  SPDX-FileCopyrightText: 2023 Marco Martin <mart@kde.org>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "padding.h"
0008 
0009 #include <QMarginsF>
0010 #include <qnumeric.h>
0011 #include <qtypes.h>
0012 
0013 class PaddingPrivate
0014 {
0015     Padding *const q;
0016 
0017 public:
0018     enum Paddings {
0019         Left = 1 << 0,
0020         Top = 1 << 1,
0021         Right = 1 << 2,
0022         Bottom = 1 << 3,
0023         Horizontal = Left | Right,
0024         Vertical = Top | Bottom,
0025         All = Horizontal | Vertical
0026     };
0027 
0028     PaddingPrivate(Padding *qq)
0029         : q(qq)
0030     {
0031     }
0032 
0033     void calculateImplicitSize();
0034     void signalPaddings(const QMarginsF &oldPaddings, Paddings paddings);
0035     QMarginsF paddings() const;
0036     void disconnect();
0037 
0038     QPointer<QQuickItem> m_contentItem;
0039 
0040     qreal m_padding = 0;
0041 
0042     std::optional<qreal> m_horizontalPadding;
0043     std::optional<qreal> m_verticalPadding;
0044 
0045     std::optional<qreal> m_leftPadding;
0046     std::optional<qreal> m_topPadding;
0047     std::optional<qreal> m_rightPadding;
0048     std::optional<qreal> m_bottomPadding;
0049 };
0050 
0051 void PaddingPrivate::calculateImplicitSize()
0052 {
0053     qreal impWidth = 0;
0054     qreal impHeight = 0;
0055 
0056     if (m_contentItem) {
0057         impWidth += m_contentItem->implicitWidth();
0058         impHeight += m_contentItem->implicitHeight();
0059     }
0060 
0061     impWidth += q->leftPadding() + q->rightPadding();
0062     impHeight += q->topPadding() + q->bottomPadding();
0063 
0064     q->setImplicitSize(impWidth, impHeight);
0065 }
0066 
0067 QMarginsF PaddingPrivate::paddings() const
0068 {
0069     return {q->leftPadding(), q->topPadding(), q->rightPadding(), q->bottomPadding()};
0070 }
0071 
0072 void PaddingPrivate::signalPaddings(const QMarginsF &oldPaddings, Paddings which)
0073 {
0074     if ((which & Left) && !qFuzzyCompare(q->leftPadding(), oldPaddings.left())) {
0075         Q_EMIT q->leftPaddingChanged();
0076     }
0077     if ((which & Top) && !qFuzzyCompare(q->topPadding(), oldPaddings.top())) {
0078         Q_EMIT q->topPaddingChanged();
0079     }
0080     if ((which & Right) && !qFuzzyCompare(q->rightPadding(), oldPaddings.right())) {
0081         Q_EMIT q->rightPaddingChanged();
0082     }
0083     if ((which & Bottom) && !qFuzzyCompare(q->bottomPadding(), oldPaddings.bottom())) {
0084         Q_EMIT q->bottomPaddingChanged();
0085     }
0086     if ((which == Horizontal || which == All)
0087         && (!qFuzzyCompare(q->leftPadding(), oldPaddings.left()) || !qFuzzyCompare(q->rightPadding(), oldPaddings.right()))) {
0088         Q_EMIT q->horizontalPaddingChanged();
0089     }
0090     if ((which == Vertical || which == All)
0091         && (!qFuzzyCompare(q->topPadding(), oldPaddings.top()) || !qFuzzyCompare(q->bottomPadding(), oldPaddings.bottom()))) {
0092         Q_EMIT q->verticalPaddingChanged();
0093     }
0094     if (!qFuzzyCompare(q->leftPadding() + q->rightPadding(), oldPaddings.left() + oldPaddings.right())) {
0095         Q_EMIT q->availableWidthChanged();
0096     }
0097     if (!qFuzzyCompare(q->topPadding() + q->bottomPadding(), oldPaddings.top() + oldPaddings.bottom())) {
0098         Q_EMIT q->availableHeightChanged();
0099     }
0100 }
0101 
0102 void PaddingPrivate::disconnect()
0103 {
0104     if (m_contentItem) {
0105         QObject::disconnect(m_contentItem, &QQuickItem::implicitWidthChanged, q, &Padding::polish);
0106         QObject::disconnect(m_contentItem, &QQuickItem::implicitHeightChanged, q, &Padding::polish);
0107         QObject::disconnect(m_contentItem, &QQuickItem::visibleChanged, q, &Padding::polish);
0108         QObject::disconnect(m_contentItem, &QQuickItem::implicitWidthChanged, q, &Padding::implicitContentWidthChanged);
0109         QObject::disconnect(m_contentItem, &QQuickItem::implicitHeightChanged, q, &Padding::implicitContentHeightChanged);
0110     }
0111 }
0112 
0113 Padding::Padding(QQuickItem *parent)
0114     : QQuickItem(parent)
0115     , d(std::make_unique<PaddingPrivate>(this))
0116 {
0117 }
0118 
0119 Padding::~Padding()
0120 {
0121     d->disconnect();
0122 }
0123 
0124 void Padding::setContentItem(QQuickItem *item)
0125 {
0126     if (d->m_contentItem == item) {
0127         return;
0128     }
0129 
0130     // Not unsetting old contentItem's parent unlike Control
0131     d->disconnect();
0132 
0133     d->m_contentItem = item;
0134 
0135     if (d->m_contentItem) {
0136         d->m_contentItem->setParentItem(this);
0137         connect(d->m_contentItem, &QQuickItem::implicitWidthChanged, this, &Padding::polish);
0138         connect(d->m_contentItem, &QQuickItem::implicitHeightChanged, this, &Padding::polish);
0139         connect(d->m_contentItem, &QQuickItem::visibleChanged, this, &Padding::polish);
0140         connect(d->m_contentItem, &QQuickItem::implicitWidthChanged, this, &Padding::implicitContentWidthChanged);
0141         connect(d->m_contentItem, &QQuickItem::implicitHeightChanged, this, &Padding::implicitContentHeightChanged);
0142     }
0143 
0144     polish();
0145 
0146     Q_EMIT contentItemChanged();
0147     Q_EMIT implicitContentWidthChanged();
0148     Q_EMIT implicitContentWidthChanged();
0149 }
0150 
0151 QQuickItem *Padding::contentItem()
0152 {
0153     return d->m_contentItem;
0154 }
0155 
0156 void Padding::setPadding(qreal padding)
0157 {
0158     if (qFuzzyCompare(padding, d->m_padding)) {
0159         return;
0160     }
0161 
0162     const QMarginsF oldPadding = d->paddings();
0163     d->m_padding = padding;
0164 
0165     Q_EMIT paddingChanged();
0166 
0167     d->signalPaddings(oldPadding, PaddingPrivate::All);
0168 
0169     polish();
0170 }
0171 
0172 void Padding::resetPadding()
0173 {
0174     if (qFuzzyCompare(d->m_padding, 0)) {
0175         return;
0176     }
0177 
0178     const QMarginsF oldPadding = d->paddings();
0179     d->m_padding = 0;
0180 
0181     Q_EMIT paddingChanged();
0182 
0183     d->signalPaddings(oldPadding, PaddingPrivate::All);
0184 
0185     polish();
0186 }
0187 
0188 qreal Padding::padding() const
0189 {
0190     return d->m_padding;
0191 }
0192 
0193 void Padding::setHorizontalPadding(qreal padding)
0194 {
0195     if (qFuzzyCompare(padding, horizontalPadding()) && d->m_horizontalPadding.has_value()) {
0196         return;
0197     }
0198 
0199     const QMarginsF oldPadding = d->paddings();
0200     d->m_horizontalPadding = padding;
0201 
0202     d->signalPaddings(oldPadding, PaddingPrivate::Horizontal);
0203 
0204     polish();
0205 }
0206 
0207 void Padding::resetHorizontalPadding()
0208 {
0209     if (!d->m_horizontalPadding.has_value()) {
0210         return;
0211     }
0212 
0213     const QMarginsF oldPadding = d->paddings();
0214     d->m_horizontalPadding.reset();
0215 
0216     d->signalPaddings(oldPadding, PaddingPrivate::Horizontal);
0217 
0218     polish();
0219 }
0220 
0221 qreal Padding::horizontalPadding() const
0222 {
0223     return d->m_horizontalPadding.value_or(d->m_padding);
0224 }
0225 
0226 void Padding::setVerticalPadding(qreal padding)
0227 {
0228     if (qFuzzyCompare(padding, verticalPadding()) && d->m_verticalPadding.has_value()) {
0229         return;
0230     }
0231 
0232     const QMarginsF oldPadding = d->paddings();
0233     d->m_verticalPadding = padding;
0234 
0235     d->signalPaddings(oldPadding, PaddingPrivate::Vertical);
0236 
0237     polish();
0238 }
0239 
0240 void Padding::resetVerticalPadding()
0241 {
0242     if (!d->m_verticalPadding.has_value()) {
0243         return;
0244     }
0245 
0246     const QMarginsF oldPadding = d->paddings();
0247     d->m_verticalPadding.reset();
0248 
0249     d->signalPaddings(oldPadding, PaddingPrivate::Vertical);
0250 
0251     polish();
0252 }
0253 
0254 qreal Padding::verticalPadding() const
0255 {
0256     return d->m_verticalPadding.value_or(d->m_padding);
0257 }
0258 
0259 void Padding::setLeftPadding(qreal padding)
0260 {
0261     const QMarginsF oldPadding = d->paddings();
0262     if (qFuzzyCompare(padding, oldPadding.left()) && d->m_leftPadding.has_value()) {
0263         return;
0264     }
0265 
0266     d->m_leftPadding = padding;
0267 
0268     d->signalPaddings(oldPadding, PaddingPrivate::Left);
0269 
0270     polish();
0271 }
0272 
0273 void Padding::resetLeftPadding()
0274 {
0275     if (!d->m_leftPadding.has_value()) {
0276         return;
0277     }
0278 
0279     const QMarginsF oldPadding = d->paddings();
0280     d->m_leftPadding.reset();
0281 
0282     d->signalPaddings(oldPadding, PaddingPrivate::Left);
0283 
0284     polish();
0285 }
0286 
0287 qreal Padding::leftPadding() const
0288 {
0289     if (d->m_leftPadding.has_value()) {
0290         return d->m_leftPadding.value();
0291     } else {
0292         return horizontalPadding();
0293     }
0294 }
0295 
0296 void Padding::setTopPadding(qreal padding)
0297 {
0298     const QMarginsF oldPadding = d->paddings();
0299     if (qFuzzyCompare(padding, oldPadding.top()) && d->m_topPadding.has_value()) {
0300         return;
0301     }
0302 
0303     d->m_topPadding = padding;
0304 
0305     d->signalPaddings(oldPadding, PaddingPrivate::Top);
0306 
0307     polish();
0308 }
0309 
0310 void Padding::resetTopPadding()
0311 {
0312     if (!d->m_topPadding.has_value()) {
0313         return;
0314     }
0315 
0316     const QMarginsF oldPadding = d->paddings();
0317     d->m_topPadding.reset();
0318 
0319     d->signalPaddings(oldPadding, PaddingPrivate::Top);
0320 
0321     polish();
0322 }
0323 
0324 qreal Padding::topPadding() const
0325 {
0326     if (d->m_topPadding.has_value()) {
0327         return d->m_topPadding.value();
0328     } else {
0329         return verticalPadding();
0330     }
0331 }
0332 
0333 void Padding::setRightPadding(qreal padding)
0334 {
0335     const QMarginsF oldPadding = d->paddings();
0336     if (qFuzzyCompare(padding, oldPadding.right()) && d->m_rightPadding.has_value()) {
0337         return;
0338     }
0339 
0340     d->m_rightPadding = padding;
0341 
0342     d->signalPaddings(oldPadding, PaddingPrivate::Right);
0343 
0344     polish();
0345 }
0346 
0347 void Padding::resetRightPadding()
0348 {
0349     if (!d->m_rightPadding.has_value()) {
0350         return;
0351     }
0352 
0353     const QMarginsF oldPadding = d->paddings();
0354     d->m_rightPadding.reset();
0355 
0356     d->signalPaddings(oldPadding, PaddingPrivate::Right);
0357 
0358     polish();
0359 }
0360 
0361 qreal Padding::rightPadding() const
0362 {
0363     if (d->m_rightPadding.has_value()) {
0364         return d->m_rightPadding.value();
0365     } else {
0366         return horizontalPadding();
0367     }
0368 }
0369 
0370 void Padding::setBottomPadding(qreal padding)
0371 {
0372     const QMarginsF oldPadding = d->paddings();
0373     if (qFuzzyCompare(padding, oldPadding.bottom()) && d->m_bottomPadding.has_value()) {
0374         return;
0375     }
0376 
0377     d->m_bottomPadding = padding;
0378 
0379     d->signalPaddings(oldPadding, PaddingPrivate::Bottom);
0380 
0381     polish();
0382 }
0383 
0384 void Padding::resetBottomPadding()
0385 {
0386     if (!d->m_bottomPadding.has_value()) {
0387         return;
0388     }
0389 
0390     const QMarginsF oldPadding = d->paddings();
0391     d->m_bottomPadding.reset();
0392 
0393     d->signalPaddings(oldPadding, PaddingPrivate::Bottom);
0394 
0395     polish();
0396 }
0397 
0398 qreal Padding::bottomPadding() const
0399 {
0400     if (d->m_bottomPadding.has_value()) {
0401         return d->m_bottomPadding.value();
0402     } else {
0403         return verticalPadding();
0404     }
0405 }
0406 
0407 qreal Padding::availableWidth() const
0408 {
0409     return width() - leftPadding() - rightPadding();
0410 }
0411 
0412 qreal Padding::availableHeight() const
0413 {
0414     return height() - topPadding() - bottomPadding();
0415 }
0416 
0417 qreal Padding::implicitContentWidth() const
0418 {
0419     if (d->m_contentItem) {
0420         return d->m_contentItem->implicitWidth();
0421     } else {
0422         return 0.0;
0423     }
0424 }
0425 
0426 qreal Padding::implicitContentHeight() const
0427 {
0428     if (d->m_contentItem) {
0429         return d->m_contentItem->implicitHeight();
0430     } else {
0431         return 0.0;
0432     }
0433 }
0434 
0435 void Padding::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
0436 {
0437     if (newGeometry != oldGeometry) {
0438         Q_EMIT availableWidthChanged();
0439         Q_EMIT availableHeightChanged();
0440         polish();
0441     }
0442 
0443     QQuickItem::geometryChange(newGeometry, oldGeometry);
0444 }
0445 
0446 void Padding::updatePolish()
0447 {
0448     d->calculateImplicitSize();
0449     if (!d->m_contentItem) {
0450         return;
0451     }
0452 
0453     d->m_contentItem->setPosition(QPointF(leftPadding(), topPadding()));
0454     d->m_contentItem->setSize(QSizeF(availableWidth(), availableHeight()));
0455 }
0456 
0457 #include "moc_padding.cpp"