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

0001 /*
0002  * SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005  */
0006 
0007 #include "toolbarlayout.h"
0008 
0009 #include <cmath>
0010 #include <unordered_map>
0011 
0012 #include <QDeadlineTimer>
0013 #include <QQmlComponent>
0014 #include <QTimer>
0015 
0016 #include "loggingcategory.h"
0017 #include "toolbarlayoutdelegate.h"
0018 
0019 ToolBarLayoutAttached::ToolBarLayoutAttached(QObject *parent)
0020     : QObject(parent)
0021 {
0022 }
0023 
0024 QObject *ToolBarLayoutAttached::action() const
0025 {
0026     return m_action;
0027 }
0028 
0029 void ToolBarLayoutAttached::setAction(QObject *action)
0030 {
0031     m_action = action;
0032 }
0033 
0034 class ToolBarLayoutPrivate
0035 {
0036     ToolBarLayout *const q;
0037 
0038 public:
0039     ToolBarLayoutPrivate(ToolBarLayout *qq)
0040         : q(qq)
0041     {
0042     }
0043     ~ToolBarLayoutPrivate()
0044     {
0045         if (moreButtonIncubator) {
0046             moreButtonIncubator->clear();
0047             delete moreButtonIncubator;
0048         }
0049     }
0050 
0051     void calculateImplicitSize();
0052     void performLayout();
0053     QList<ToolBarLayoutDelegate *> createDelegates();
0054     ToolBarLayoutDelegate *createDelegate(QObject *action);
0055     qreal layoutStart(qreal layoutWidth);
0056     void maybeHideDelegate(int index, qreal &currentWidth, qreal totalWidth);
0057 
0058     QList<QObject *> actions;
0059     ToolBarLayout::ActionsProperty actionsProperty;
0060     QList<QObject *> hiddenActions;
0061     QQmlComponent *fullDelegate = nullptr;
0062     QQmlComponent *iconDelegate = nullptr;
0063     QQmlComponent *moreButton = nullptr;
0064     qreal spacing = 0.0;
0065     Qt::Alignment alignment = Qt::AlignLeft;
0066     qreal visibleActionsWidth = 0.0;
0067     qreal visibleWidth = 0.0;
0068     Qt::LayoutDirection layoutDirection = Qt::LeftToRight;
0069     ToolBarLayout::HeightMode heightMode = ToolBarLayout::ConstrainIfLarger;
0070 
0071     bool completed = false;
0072     bool actionsChanged = false;
0073     bool implicitSizeValid = false;
0074 
0075     std::unordered_map<QObject *, std::unique_ptr<ToolBarLayoutDelegate>> delegates;
0076     QList<ToolBarLayoutDelegate *> sortedDelegates;
0077     QQuickItem *moreButtonInstance = nullptr;
0078     ToolBarDelegateIncubator *moreButtonIncubator = nullptr;
0079     bool shouldShowMoreButton = false;
0080     int firstHiddenIndex = -1;
0081 
0082     QList<QObject *> removedActions;
0083     QTimer *removalTimer = nullptr;
0084 
0085     QElapsedTimer performanceTimer;
0086 
0087     static void appendAction(ToolBarLayout::ActionsProperty *list, QObject *action);
0088     static qsizetype actionCount(ToolBarLayout::ActionsProperty *list);
0089     static QObject *action(ToolBarLayout::ActionsProperty *list, qsizetype index);
0090     static void clearActions(ToolBarLayout::ActionsProperty *list);
0091 };
0092 
0093 ToolBarLayout::ToolBarLayout(QQuickItem *parent)
0094     : QQuickItem(parent)
0095     , d(std::make_unique<ToolBarLayoutPrivate>(this))
0096 {
0097     d->actionsProperty = ActionsProperty(this,
0098                                          this,
0099                                          ToolBarLayoutPrivate::appendAction,
0100                                          ToolBarLayoutPrivate::actionCount,
0101                                          ToolBarLayoutPrivate::action,
0102                                          ToolBarLayoutPrivate::clearActions);
0103 
0104     // To prevent multiple assignments to actions from constantly recreating
0105     // delegates, we cache the delegates and only remove them once they are no
0106     // longer being used. This timer is responsible for triggering that removal.
0107     d->removalTimer = new QTimer{this};
0108     d->removalTimer->setInterval(1000);
0109     d->removalTimer->setSingleShot(true);
0110     connect(d->removalTimer, &QTimer::timeout, this, [this]() {
0111         for (auto action : std::as_const(d->removedActions)) {
0112             if (!d->actions.contains(action)) {
0113                 d->delegates.erase(action);
0114             }
0115         }
0116         d->removedActions.clear();
0117     });
0118 }
0119 
0120 ToolBarLayout::~ToolBarLayout()
0121 {
0122 }
0123 
0124 ToolBarLayout::ActionsProperty ToolBarLayout::actionsProperty() const
0125 {
0126     return d->actionsProperty;
0127 }
0128 
0129 void ToolBarLayout::addAction(QObject *action)
0130 {
0131     if (action == nullptr) {
0132         return;
0133     }
0134     d->actions.append(action);
0135     d->actionsChanged = true;
0136 
0137     connect(action, &QObject::destroyed, this, [this](QObject *action) {
0138         auto itr = d->delegates.find(action);
0139         if (itr != d->delegates.end()) {
0140             d->delegates.erase(itr);
0141         }
0142 
0143         d->actions.removeOne(action);
0144         d->actionsChanged = true;
0145 
0146         relayout();
0147     });
0148 
0149     relayout();
0150 }
0151 
0152 void ToolBarLayout::removeAction(QObject *action)
0153 {
0154     auto itr = d->delegates.find(action);
0155     if (itr != d->delegates.end()) {
0156         itr->second->hide();
0157     }
0158 
0159     d->actions.removeOne(action);
0160     d->removedActions.append(action);
0161     d->removalTimer->start();
0162     d->actionsChanged = true;
0163 
0164     relayout();
0165 }
0166 
0167 void ToolBarLayout::clearActions()
0168 {
0169     for (auto action : std::as_const(d->actions)) {
0170         auto itr = d->delegates.find(action);
0171         if (itr != d->delegates.end()) {
0172             itr->second->hide();
0173         }
0174     }
0175 
0176     d->removedActions.append(d->actions);
0177     d->actions.clear();
0178     d->actionsChanged = true;
0179 
0180     relayout();
0181 }
0182 
0183 QList<QObject *> ToolBarLayout::hiddenActions() const
0184 {
0185     return d->hiddenActions;
0186 }
0187 
0188 QQmlComponent *ToolBarLayout::fullDelegate() const
0189 {
0190     return d->fullDelegate;
0191 }
0192 
0193 void ToolBarLayout::setFullDelegate(QQmlComponent *newFullDelegate)
0194 {
0195     if (newFullDelegate == d->fullDelegate) {
0196         return;
0197     }
0198 
0199     d->fullDelegate = newFullDelegate;
0200     d->delegates.clear();
0201     relayout();
0202     Q_EMIT fullDelegateChanged();
0203 }
0204 
0205 QQmlComponent *ToolBarLayout::iconDelegate() const
0206 {
0207     return d->iconDelegate;
0208 }
0209 
0210 void ToolBarLayout::setIconDelegate(QQmlComponent *newIconDelegate)
0211 {
0212     if (newIconDelegate == d->iconDelegate) {
0213         return;
0214     }
0215 
0216     d->iconDelegate = newIconDelegate;
0217     d->delegates.clear();
0218     relayout();
0219     Q_EMIT iconDelegateChanged();
0220 }
0221 
0222 QQmlComponent *ToolBarLayout::moreButton() const
0223 {
0224     return d->moreButton;
0225 }
0226 
0227 void ToolBarLayout::setMoreButton(QQmlComponent *newMoreButton)
0228 {
0229     if (newMoreButton == d->moreButton) {
0230         return;
0231     }
0232 
0233     d->moreButton = newMoreButton;
0234     if (d->moreButtonInstance) {
0235         d->moreButtonInstance->deleteLater();
0236         d->moreButtonInstance = nullptr;
0237     }
0238     relayout();
0239     Q_EMIT moreButtonChanged();
0240 }
0241 
0242 qreal ToolBarLayout::spacing() const
0243 {
0244     return d->spacing;
0245 }
0246 
0247 void ToolBarLayout::setSpacing(qreal newSpacing)
0248 {
0249     if (newSpacing == d->spacing) {
0250         return;
0251     }
0252 
0253     d->spacing = newSpacing;
0254     relayout();
0255     Q_EMIT spacingChanged();
0256 }
0257 
0258 Qt::Alignment ToolBarLayout::alignment() const
0259 {
0260     return d->alignment;
0261 }
0262 
0263 void ToolBarLayout::setAlignment(Qt::Alignment newAlignment)
0264 {
0265     if (newAlignment == d->alignment) {
0266         return;
0267     }
0268 
0269     d->alignment = newAlignment;
0270     relayout();
0271     Q_EMIT alignmentChanged();
0272 }
0273 
0274 qreal ToolBarLayout::visibleWidth() const
0275 {
0276     return d->visibleWidth;
0277 }
0278 
0279 qreal ToolBarLayout::minimumWidth() const
0280 {
0281     return d->moreButtonInstance ? d->moreButtonInstance->width() : 0;
0282 }
0283 
0284 Qt::LayoutDirection ToolBarLayout::layoutDirection() const
0285 {
0286     return d->layoutDirection;
0287 }
0288 
0289 void ToolBarLayout::setLayoutDirection(Qt::LayoutDirection &newLayoutDirection)
0290 {
0291     if (newLayoutDirection == d->layoutDirection) {
0292         return;
0293     }
0294 
0295     d->layoutDirection = newLayoutDirection;
0296     relayout();
0297     Q_EMIT layoutDirectionChanged();
0298 }
0299 
0300 ToolBarLayout::HeightMode ToolBarLayout::heightMode() const
0301 {
0302     return d->heightMode;
0303 }
0304 
0305 void ToolBarLayout::setHeightMode(HeightMode newHeightMode)
0306 {
0307     if (newHeightMode == d->heightMode) {
0308         return;
0309     }
0310 
0311     d->heightMode = newHeightMode;
0312     relayout();
0313     Q_EMIT heightModeChanged();
0314 }
0315 
0316 void ToolBarLayout::relayout()
0317 {
0318     d->implicitSizeValid = false;
0319     polish();
0320 }
0321 
0322 void ToolBarLayout::componentComplete()
0323 {
0324     QQuickItem::componentComplete();
0325     d->completed = true;
0326     relayout();
0327 }
0328 
0329 void ToolBarLayout::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
0330 {
0331     if (newGeometry != oldGeometry) {
0332         if (newGeometry.size() != QSizeF{implicitWidth(), implicitHeight()}) {
0333             relayout();
0334         } else {
0335             polish();
0336         }
0337     }
0338     QQuickItem::geometryChange(newGeometry, oldGeometry);
0339 }
0340 
0341 void ToolBarLayout::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
0342 {
0343     if (change == ItemVisibleHasChanged || change == ItemSceneChange) {
0344         relayout();
0345     }
0346     QQuickItem::itemChange(change, data);
0347 }
0348 
0349 void ToolBarLayout::updatePolish()
0350 {
0351     d->performLayout();
0352 }
0353 
0354 /**
0355  * Calculate the implicit size for this layout.
0356  *
0357  * This is a separate step from performing the actual layout, because of a nasty
0358  * little issue with Control, where it will unconditionally set the height of
0359  * its contentItem, which means QQuickItem::heightValid() becomes useless. So
0360  * instead, we first calculate our implicit size, ignoring any explicitly set
0361  * item size. Then we follow that by performing the actual layouting, using the
0362  * width and height retrieved from the item, as those will return the explicitly
0363  * set width/height if set and the implicit size otherwise. Since control
0364  * watches for implicit size changes, we end up with correct behaviour both when
0365  * we get an explicit size set and when we're relying on implicit size
0366  * calculation.
0367  */
0368 void ToolBarLayoutPrivate::calculateImplicitSize()
0369 {
0370     if (!completed) {
0371         return;
0372     }
0373 
0374     if (!fullDelegate || !iconDelegate || !moreButton) {
0375         qCWarning(KirigamiLog) << "ToolBarLayout: Unable to layout, required properties are not set";
0376         return;
0377     }
0378 
0379     if (actions.isEmpty()) {
0380         q->setImplicitSize(0., 0.);
0381         return;
0382     }
0383 
0384     hiddenActions.clear();
0385     firstHiddenIndex = -1;
0386 
0387     sortedDelegates = createDelegates();
0388 
0389     bool ready = std::all_of(delegates.cbegin(), delegates.cend(), [](const std::pair<QObject *const, std::unique_ptr<ToolBarLayoutDelegate>> &entry) {
0390         return entry.second->isReady();
0391     });
0392     if (!ready || !moreButtonInstance) {
0393         return;
0394     }
0395 
0396     qreal maxHeight = 0.0;
0397     qreal maxWidth = 0.0;
0398 
0399     // First, calculate the total width and maximum height of all delegates.
0400     // This will be used to determine which actions to show, which ones to
0401     // collapse to icon-only etc.
0402     for (auto entry : std::as_const(sortedDelegates)) {
0403         if (!entry->isActionVisible()) {
0404             entry->hide();
0405             continue;
0406         }
0407 
0408         if (entry->isHidden()) {
0409             entry->hide();
0410             hiddenActions.append(entry->action());
0411             continue;
0412         }
0413 
0414         if (entry->isIconOnly()) {
0415             entry->showIcon();
0416         } else {
0417             entry->showFull();
0418         }
0419 
0420         maxWidth += entry->width() + spacing;
0421         maxHeight = std::max(maxHeight, entry->maxHeight());
0422     }
0423 
0424     // The last entry also gets spacing but shouldn't, so remove that.
0425     maxWidth -= spacing;
0426 
0427     visibleActionsWidth = 0.0;
0428 
0429     if (maxWidth > q->width() - (hiddenActions.isEmpty() ? 0.0 : moreButtonInstance->width() + spacing)) {
0430         // We have more items than fit into the view, so start hiding some.
0431 
0432         qreal layoutWidth = q->width() - (moreButtonInstance->width() + spacing);
0433         if (alignment & Qt::AlignHCenter) {
0434             // When centering, we need to reserve space on both sides to make sure
0435             // things are properly centered, otherwise we will be to the right of
0436             // the center.
0437             layoutWidth -= (moreButtonInstance->width() + spacing);
0438         }
0439 
0440         for (int i = 0; i < sortedDelegates.size(); ++i) {
0441             auto delegate = sortedDelegates.at(i);
0442 
0443             maybeHideDelegate(i, visibleActionsWidth, layoutWidth);
0444 
0445             if (delegate->isVisible()) {
0446                 visibleActionsWidth += delegate->width() + spacing;
0447             }
0448         }
0449         if (!qFuzzyIsNull(visibleActionsWidth)) {
0450             // Like above, remove spacing on the last element that incorrectly gets spacing added.
0451             visibleActionsWidth -= spacing;
0452         }
0453     } else {
0454         visibleActionsWidth = maxWidth;
0455     }
0456 
0457     if (!hiddenActions.isEmpty()) {
0458         maxHeight = std::max(maxHeight, moreButtonInstance->implicitHeight());
0459     };
0460 
0461     q->setImplicitSize(maxWidth, maxHeight);
0462     Q_EMIT q->hiddenActionsChanged();
0463 
0464     implicitSizeValid = true;
0465 
0466     q->polish();
0467 }
0468 
0469 void ToolBarLayoutPrivate::performLayout()
0470 {
0471     if (!completed || actions.isEmpty()) {
0472         return;
0473     }
0474 
0475     if (!implicitSizeValid) {
0476         calculateImplicitSize();
0477     }
0478 
0479     if (sortedDelegates.isEmpty()) {
0480         sortedDelegates = createDelegates();
0481     }
0482 
0483     bool ready = std::all_of(delegates.cbegin(), delegates.cend(), [](const std::pair<QObject *const, std::unique_ptr<ToolBarLayoutDelegate>> &entry) {
0484         return entry.second->isReady();
0485     });
0486     if (!ready || !moreButtonInstance) {
0487         return;
0488     }
0489 
0490     qreal width = q->width();
0491     qreal height = q->height();
0492 
0493     if (!hiddenActions.isEmpty()) {
0494         if (layoutDirection == Qt::LeftToRight) {
0495             moreButtonInstance->setX(width - moreButtonInstance->width());
0496         } else {
0497             moreButtonInstance->setX(0.0);
0498         }
0499 
0500         if (heightMode == ToolBarLayout::AlwaysFill) {
0501             moreButtonInstance->setHeight(height);
0502         } else if (heightMode == ToolBarLayout::ConstrainIfLarger) {
0503             if (moreButtonInstance->implicitHeight() > height) {
0504                 moreButtonInstance->setHeight(height);
0505             } else {
0506                 moreButtonInstance->resetHeight();
0507             }
0508         } else {
0509             moreButtonInstance->resetHeight();
0510         }
0511 
0512         moreButtonInstance->setY(qRound((height - moreButtonInstance->height()) / 2.0));
0513         shouldShowMoreButton = true;
0514         moreButtonInstance->setVisible(true);
0515     } else {
0516         shouldShowMoreButton = false;
0517         moreButtonInstance->setVisible(false);
0518     }
0519 
0520     qreal currentX = layoutStart(visibleActionsWidth);
0521     for (auto entry : std::as_const(sortedDelegates)) {
0522         if (!entry->isVisible()) {
0523             continue;
0524         }
0525 
0526         if (heightMode == ToolBarLayout::AlwaysFill) {
0527             entry->setHeight(height);
0528         } else if (heightMode == ToolBarLayout::ConstrainIfLarger) {
0529             if (entry->implicitHeight() > height) {
0530                 entry->setHeight(height);
0531             } else {
0532                 entry->resetHeight();
0533             }
0534         } else {
0535             entry->resetHeight();
0536         }
0537 
0538         qreal y = qRound((height - entry->height()) / 2.0);
0539 
0540         if (layoutDirection == Qt::LeftToRight) {
0541             entry->setPosition(currentX, y);
0542             currentX += entry->width() + spacing;
0543         } else {
0544             entry->setPosition(currentX - entry->width(), y);
0545             currentX -= entry->width() + spacing;
0546         }
0547 
0548         entry->show();
0549     }
0550 
0551     qreal newVisibleWidth = visibleActionsWidth;
0552     if (moreButtonInstance->isVisible()) {
0553         newVisibleWidth += moreButtonInstance->width() + (newVisibleWidth > 0.0 ? spacing : 0.0);
0554     }
0555     if (!qFuzzyCompare(newVisibleWidth, visibleWidth)) {
0556         visibleWidth = newVisibleWidth;
0557         Q_EMIT q->visibleWidthChanged();
0558     }
0559 
0560     if (actionsChanged) {
0561         // Due to the way QQmlListProperty works, if we emit changed every time
0562         // an action is added/removed, we end up emitting way too often. So
0563         // instead only do it after everything else is done.
0564         Q_EMIT q->actionsChanged();
0565         actionsChanged = false;
0566     }
0567 
0568     sortedDelegates.clear();
0569 }
0570 
0571 QList<ToolBarLayoutDelegate *> ToolBarLayoutPrivate::createDelegates()
0572 {
0573     QList<ToolBarLayoutDelegate *> result;
0574     for (auto action : std::as_const(actions)) {
0575         if (delegates.find(action) != delegates.end()) {
0576             result.append(delegates.at(action).get());
0577         } else if (action) {
0578             auto delegate = std::unique_ptr<ToolBarLayoutDelegate>(createDelegate(action));
0579             if (delegate) {
0580                 result.append(delegate.get());
0581                 delegates.emplace(action, std::move(delegate));
0582             }
0583         }
0584     }
0585 
0586     if (!moreButtonInstance && !moreButtonIncubator) {
0587         moreButtonIncubator = new ToolBarDelegateIncubator(moreButton, qmlContext(moreButton));
0588         moreButtonIncubator->setStateCallback([this](QQuickItem *item) {
0589             item->setParentItem(q);
0590         });
0591         moreButtonIncubator->setCompletedCallback([this](ToolBarDelegateIncubator *incubator) {
0592             moreButtonInstance = qobject_cast<QQuickItem *>(incubator->object());
0593             moreButtonInstance->setVisible(false);
0594 
0595             QObject::connect(moreButtonInstance, &QQuickItem::visibleChanged, q, [this]() {
0596                 moreButtonInstance->setVisible(shouldShowMoreButton);
0597             });
0598             QObject::connect(moreButtonInstance, &QQuickItem::widthChanged, q, &ToolBarLayout::minimumWidthChanged);
0599             q->relayout();
0600             Q_EMIT q->minimumWidthChanged();
0601 
0602             QTimer::singleShot(0, q, [this]() {
0603                 delete moreButtonIncubator;
0604                 moreButtonIncubator = nullptr;
0605             });
0606         });
0607         moreButtonIncubator->create();
0608     }
0609 
0610     return result;
0611 }
0612 
0613 ToolBarLayoutDelegate *ToolBarLayoutPrivate::createDelegate(QObject *action)
0614 {
0615     QQmlComponent *fullComponent = nullptr;
0616     auto displayComponent = action->property("displayComponent");
0617     if (displayComponent.isValid()) {
0618         fullComponent = displayComponent.value<QQmlComponent *>();
0619     }
0620 
0621     if (!fullComponent) {
0622         fullComponent = fullDelegate;
0623     }
0624 
0625     auto result = new ToolBarLayoutDelegate(q);
0626     result->setAction(action);
0627     result->createItems(fullComponent, iconDelegate, [this, action](QQuickItem *newItem) {
0628         newItem->setParentItem(q);
0629         auto attached = static_cast<ToolBarLayoutAttached *>(qmlAttachedPropertiesObject<ToolBarLayout>(newItem, true));
0630         attached->setAction(action);
0631     });
0632 
0633     return result;
0634 }
0635 
0636 qreal ToolBarLayoutPrivate::layoutStart(qreal layoutWidth)
0637 {
0638     qreal availableWidth = moreButtonInstance->isVisible() ? q->width() - (moreButtonInstance->width() + spacing) : q->width();
0639 
0640     if (alignment & Qt::AlignLeft) {
0641         return layoutDirection == Qt::LeftToRight ? 0.0 : q->width();
0642     } else if (alignment & Qt::AlignHCenter) {
0643         return (q->width() / 2) + (layoutDirection == Qt::LeftToRight ? -layoutWidth / 2.0 : layoutWidth / 2.0);
0644     } else if (alignment & Qt::AlignRight) {
0645         qreal offset = availableWidth - layoutWidth;
0646         return layoutDirection == Qt::LeftToRight ? offset : q->width() - offset;
0647     }
0648     return 0.0;
0649 }
0650 
0651 void ToolBarLayoutPrivate::maybeHideDelegate(int index, qreal &currentWidth, qreal totalWidth)
0652 {
0653     auto delegate = sortedDelegates.at(index);
0654 
0655     if (!delegate->isVisible()) {
0656         // If the delegate isn't visible anyway, do nothing.
0657         return;
0658     }
0659 
0660     if (currentWidth + delegate->width() < totalWidth && (firstHiddenIndex < 0 || index < firstHiddenIndex)) {
0661         // If the delegate is fully visible and we have not already hidden
0662         // actions, do nothing.
0663         return;
0664     }
0665 
0666     if (delegate->isKeepVisible()) {
0667         // If the action is marked as KeepVisible, we need to try our best to
0668         // keep it in view. If the full size delegate does not fit, we try the
0669         // icon-only delegate. If that also does not fit, try and find other
0670         // actions to hide. Finally, if that also fails, we will hide the
0671         // delegate.
0672         if (currentWidth + delegate->iconWidth() > totalWidth) {
0673             // First, hide any earlier actions that are not marked as KeepVisible.
0674             for (auto currentIndex = index - 1; currentIndex >= 0; --currentIndex) {
0675                 auto previousDelegate = sortedDelegates.at(currentIndex);
0676                 if (!previousDelegate->isVisible() || previousDelegate->isKeepVisible()) {
0677                     continue;
0678                 }
0679 
0680                 auto width = previousDelegate->width();
0681                 previousDelegate->hide();
0682                 hiddenActions.append(previousDelegate->action());
0683                 currentWidth -= (width + spacing);
0684 
0685                 if (currentWidth + delegate->fullWidth() <= totalWidth) {
0686                     delegate->showFull();
0687                     break;
0688                 } else if (currentWidth + delegate->iconWidth() <= totalWidth) {
0689                     delegate->showIcon();
0690                     break;
0691                 }
0692             }
0693 
0694             if (currentWidth + delegate->width() <= totalWidth) {
0695                 return;
0696             }
0697 
0698             // Hiding normal actions did not help enough, so go through actions
0699             // with KeepVisible set and try and collapse them to IconOnly.
0700             for (auto currentIndex = index - 1; currentIndex >= 0; --currentIndex) {
0701                 auto previousDelegate = sortedDelegates.at(currentIndex);
0702                 if (!previousDelegate->isVisible() || !previousDelegate->isKeepVisible()) {
0703                     continue;
0704                 }
0705 
0706                 auto extraSpace = previousDelegate->width() - previousDelegate->iconWidth();
0707                 previousDelegate->showIcon();
0708                 currentWidth -= extraSpace;
0709 
0710                 if (currentWidth + delegate->fullWidth() <= totalWidth) {
0711                     delegate->showFull();
0712                     break;
0713                 } else if (currentWidth + delegate->iconWidth() <= totalWidth) {
0714                     delegate->showIcon();
0715                     break;
0716                 }
0717             }
0718 
0719             // If that also did not work, then hide this action after all.
0720             if (currentWidth + delegate->width() > totalWidth) {
0721                 delegate->hide();
0722                 hiddenActions.append(delegate->action());
0723             }
0724         } else {
0725             delegate->showIcon();
0726         }
0727     } else {
0728         // The action is not marked as KeepVisible and it does not fit within
0729         // the current layout, so hide it.
0730         delegate->hide();
0731         hiddenActions.append(delegate->action());
0732 
0733         // If this is the first item to be hidden, mark it so we know we should
0734         // also hide the following items.
0735         if (firstHiddenIndex < 0) {
0736             firstHiddenIndex = index;
0737         }
0738     }
0739 }
0740 
0741 void ToolBarLayoutPrivate::appendAction(ToolBarLayout::ActionsProperty *list, QObject *action)
0742 {
0743     auto layout = reinterpret_cast<ToolBarLayout *>(list->data);
0744     layout->addAction(action);
0745 }
0746 
0747 qsizetype ToolBarLayoutPrivate::actionCount(ToolBarLayout::ActionsProperty *list)
0748 {
0749     return reinterpret_cast<ToolBarLayout *>(list->data)->d->actions.count();
0750 }
0751 
0752 QObject *ToolBarLayoutPrivate::action(ToolBarLayout::ActionsProperty *list, qsizetype index)
0753 {
0754     return reinterpret_cast<ToolBarLayout *>(list->data)->d->actions.at(index);
0755 }
0756 
0757 void ToolBarLayoutPrivate::clearActions(ToolBarLayout::ActionsProperty *list)
0758 {
0759     reinterpret_cast<ToolBarLayout *>(list->data)->clearActions();
0760 }
0761 
0762 #include "moc_toolbarlayout.cpp"