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 ¤tWidth, 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 ¤tWidth, 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"