1/*
2 * SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6
7#include "toolbarlayout.h"
8
9#include <cmath>
10#include <unordered_map>
11
12#include <QDeadlineTimer>
13#include <QElapsedTimer>
14#include <QQmlComponent>
15#include <QTimer>
16
17#include "loggingcategory.h"
18#include "toolbarlayoutdelegate.h"
19
20ToolBarLayoutAttached::ToolBarLayoutAttached(QObject *parent)
21 : QObject(parent)
22{
23}
24
25QObject *ToolBarLayoutAttached::action() const
26{
27 return m_action;
28}
29
30void ToolBarLayoutAttached::setAction(QObject *action)
31{
32 m_action = action;
33}
34
35class ToolBarLayoutPrivate
36{
37 ToolBarLayout *const q;
38
39public:
40 ToolBarLayoutPrivate(ToolBarLayout *qq)
41 : q(qq)
42 {
43 }
44 ~ToolBarLayoutPrivate()
45 {
46 if (moreButtonIncubator) {
47 moreButtonIncubator->clear();
48 delete moreButtonIncubator;
49 }
50 }
51
52 void calculateImplicitSize();
53 void performLayout();
54 QList<ToolBarLayoutDelegate *> createDelegates();
55 ToolBarLayoutDelegate *createDelegate(QObject *action);
56 qreal layoutStart(qreal layoutWidth);
57 void maybeHideDelegate(int index, qreal &currentWidth, qreal totalWidth);
58
59 QList<QObject *> actions;
60 ToolBarLayout::ActionsProperty actionsProperty;
61 QList<QObject *> hiddenActions;
62 QQmlComponent *fullDelegate = nullptr;
63 QQmlComponent *iconDelegate = nullptr;
64 QQmlComponent *separatorDelegate = nullptr;
65 QQmlComponent *moreButton = nullptr;
66 qreal spacing = 0.0;
67 Qt::Alignment alignment = Qt::AlignLeft;
68 qreal visibleActionsWidth = 0.0;
69 qreal visibleWidth = 0.0;
70 Qt::LayoutDirection layoutDirection = Qt::LeftToRight;
71 ToolBarLayout::HeightMode heightMode = ToolBarLayout::ConstrainIfLarger;
72
73 bool completed = false;
74 bool actionsChanged = false;
75 bool implicitSizeValid = false;
76
77 std::unordered_map<QObject *, std::unique_ptr<ToolBarLayoutDelegate>> delegates;
78 QList<ToolBarLayoutDelegate *> sortedDelegates;
79 QQuickItem *moreButtonInstance = nullptr;
80 ToolBarDelegateIncubator *moreButtonIncubator = nullptr;
81 bool shouldShowMoreButton = false;
82 int firstHiddenIndex = -1;
83
84 QList<QObject *> removedActions;
85 QTimer *removalTimer = nullptr;
86
87 QElapsedTimer performanceTimer;
88
89 static void appendAction(ToolBarLayout::ActionsProperty *list, QObject *action);
90 static qsizetype actionCount(ToolBarLayout::ActionsProperty *list);
91 static QObject *action(ToolBarLayout::ActionsProperty *list, qsizetype index);
92 static void clearActions(ToolBarLayout::ActionsProperty *list);
93};
94
95ToolBarLayout::ToolBarLayout(QQuickItem *parent)
96 : QQuickItem(parent)
97 , d(std::make_unique<ToolBarLayoutPrivate>(args: this))
98{
99 d->actionsProperty = ActionsProperty(this,
100 this,
101 ToolBarLayoutPrivate::appendAction,
102 ToolBarLayoutPrivate::actionCount,
103 ToolBarLayoutPrivate::action,
104 ToolBarLayoutPrivate::clearActions);
105
106 // To prevent multiple assignments to actions from constantly recreating
107 // delegates, we cache the delegates and only remove them once they are no
108 // longer being used. This timer is responsible for triggering that removal.
109 d->removalTimer = new QTimer{this};
110 d->removalTimer->setInterval(1000);
111 d->removalTimer->setSingleShot(true);
112 connect(sender: d->removalTimer, signal: &QTimer::timeout, context: this, slot: [this]() {
113 for (auto action : std::as_const(t&: d->removedActions)) {
114 if (!d->actions.contains(t: action)) {
115 d->delegates.erase(x: action);
116 }
117 }
118 d->removedActions.clear();
119 });
120}
121
122ToolBarLayout::~ToolBarLayout()
123{
124}
125
126ToolBarLayout::ActionsProperty ToolBarLayout::actionsProperty() const
127{
128 return d->actionsProperty;
129}
130
131void ToolBarLayout::addAction(QObject *action)
132{
133 if (action == nullptr) {
134 return;
135 }
136 d->actions.append(t: action);
137 d->actionsChanged = true;
138
139 connect(sender: action, signal: &QObject::destroyed, context: this, slot: [this](QObject *action) {
140 auto itr = d->delegates.find(x: action);
141 if (itr != d->delegates.end()) {
142 d->delegates.erase(position: itr);
143 }
144
145 d->actions.removeOne(t: action);
146 d->actionsChanged = true;
147
148 relayout();
149 });
150
151 relayout();
152}
153
154void ToolBarLayout::removeAction(QObject *action)
155{
156 auto itr = d->delegates.find(x: action);
157 if (itr != d->delegates.end()) {
158 itr->second->hide();
159 }
160
161 d->actions.removeOne(t: action);
162 d->removedActions.append(t: action);
163 d->removalTimer->start();
164 d->actionsChanged = true;
165
166 relayout();
167}
168
169void ToolBarLayout::clearActions()
170{
171 for (auto action : std::as_const(t&: d->actions)) {
172 auto itr = d->delegates.find(x: action);
173 if (itr != d->delegates.end()) {
174 itr->second->hide();
175 }
176 }
177
178 d->removedActions.append(l: d->actions);
179 d->actions.clear();
180 d->actionsChanged = true;
181
182 relayout();
183}
184
185QList<QObject *> ToolBarLayout::hiddenActions() const
186{
187 return d->hiddenActions;
188}
189
190QQmlComponent *ToolBarLayout::fullDelegate() const
191{
192 return d->fullDelegate;
193}
194
195void ToolBarLayout::setFullDelegate(QQmlComponent *newFullDelegate)
196{
197 if (newFullDelegate == d->fullDelegate) {
198 return;
199 }
200
201 d->fullDelegate = newFullDelegate;
202 d->delegates.clear();
203 relayout();
204 Q_EMIT fullDelegateChanged();
205}
206
207QQmlComponent *ToolBarLayout::iconDelegate() const
208{
209 return d->iconDelegate;
210}
211
212void ToolBarLayout::setIconDelegate(QQmlComponent *newIconDelegate)
213{
214 if (newIconDelegate == d->iconDelegate) {
215 return;
216 }
217
218 d->iconDelegate = newIconDelegate;
219 d->delegates.clear();
220 relayout();
221 Q_EMIT iconDelegateChanged();
222}
223
224QQmlComponent *ToolBarLayout::separatorDelegate() const
225{
226 return d->separatorDelegate;
227}
228
229void ToolBarLayout::setSeparatorDelegate(QQmlComponent *newSeparatorDelegate)
230{
231 if (newSeparatorDelegate == d->separatorDelegate) {
232 return;
233 }
234
235 d->separatorDelegate = newSeparatorDelegate;
236 d->delegates.clear();
237 relayout();
238 Q_EMIT separatorDelegateChanged();
239}
240
241QQmlComponent *ToolBarLayout::moreButton() const
242{
243 return d->moreButton;
244}
245
246void ToolBarLayout::setMoreButton(QQmlComponent *newMoreButton)
247{
248 if (newMoreButton == d->moreButton) {
249 return;
250 }
251
252 d->moreButton = newMoreButton;
253 if (d->moreButtonInstance) {
254 d->moreButtonInstance->deleteLater();
255 d->moreButtonInstance = nullptr;
256 }
257 relayout();
258 Q_EMIT moreButtonChanged();
259}
260
261qreal ToolBarLayout::spacing() const
262{
263 return d->spacing;
264}
265
266void ToolBarLayout::setSpacing(qreal newSpacing)
267{
268 if (newSpacing == d->spacing) {
269 return;
270 }
271
272 d->spacing = newSpacing;
273 relayout();
274 Q_EMIT spacingChanged();
275}
276
277Qt::Alignment ToolBarLayout::alignment() const
278{
279 return d->alignment;
280}
281
282void ToolBarLayout::setAlignment(Qt::Alignment newAlignment)
283{
284 if (newAlignment == d->alignment) {
285 return;
286 }
287
288 d->alignment = newAlignment;
289 relayout();
290 Q_EMIT alignmentChanged();
291}
292
293qreal ToolBarLayout::visibleWidth() const
294{
295 return d->visibleWidth;
296}
297
298qreal ToolBarLayout::minimumWidth() const
299{
300 return d->moreButtonInstance ? d->moreButtonInstance->width() : 0;
301}
302
303Qt::LayoutDirection ToolBarLayout::layoutDirection() const
304{
305 return d->layoutDirection;
306}
307
308void ToolBarLayout::setLayoutDirection(Qt::LayoutDirection &newLayoutDirection)
309{
310 if (newLayoutDirection == d->layoutDirection) {
311 return;
312 }
313
314 d->layoutDirection = newLayoutDirection;
315 relayout();
316 Q_EMIT layoutDirectionChanged();
317}
318
319ToolBarLayout::HeightMode ToolBarLayout::heightMode() const
320{
321 return d->heightMode;
322}
323
324void ToolBarLayout::setHeightMode(HeightMode newHeightMode)
325{
326 if (newHeightMode == d->heightMode) {
327 return;
328 }
329
330 d->heightMode = newHeightMode;
331 relayout();
332 Q_EMIT heightModeChanged();
333}
334
335void ToolBarLayout::relayout()
336{
337 d->implicitSizeValid = false;
338 polish();
339}
340
341void ToolBarLayout::componentComplete()
342{
343 QQuickItem::componentComplete();
344 d->completed = true;
345 relayout();
346}
347
348void ToolBarLayout::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
349{
350 if (newGeometry != oldGeometry) {
351 relayout();
352 }
353 QQuickItem::geometryChange(newGeometry, oldGeometry);
354}
355
356void ToolBarLayout::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
357{
358 if (change == ItemVisibleHasChanged || change == ItemSceneChange) {
359 relayout();
360 }
361 QQuickItem::itemChange(change, data);
362}
363
364void ToolBarLayout::updatePolish()
365{
366 d->performLayout();
367}
368
369/*
370 * Calculate the implicit size for this layout.
371 *
372 * This is a separate step from performing the actual layout, because of a nasty
373 * little issue with Control, where it will unconditionally set the height of
374 * its contentItem, which means QQuickItem::heightValid() becomes useless. So
375 * instead, we first calculate our implicit size, ignoring any explicitly set
376 * item size. Then we follow that by performing the actual layouting, using the
377 * width and height retrieved from the item, as those will return the explicitly
378 * set width/height if set and the implicit size otherwise. Since control
379 * watches for implicit size changes, we end up with correct behaviour both when
380 * we get an explicit size set and when we're relying on implicit size
381 * calculation.
382 */
383void ToolBarLayoutPrivate::calculateImplicitSize()
384{
385 if (!completed) {
386 return;
387 }
388
389 if (!fullDelegate || !iconDelegate || !separatorDelegate || !moreButton) {
390 qCWarning(KirigamiLayoutsLog) << "ToolBarLayout: Unable to layout, required properties are not set";
391 return;
392 }
393
394 if (actions.isEmpty()) {
395 q->setImplicitSize(0., 0.);
396 return;
397 }
398
399 hiddenActions.clear();
400 firstHiddenIndex = -1;
401
402 sortedDelegates = createDelegates();
403
404 bool ready = std::all_of(first: delegates.cbegin(), last: delegates.cend(), pred: [](const std::pair<QObject *const, std::unique_ptr<ToolBarLayoutDelegate>> &entry) {
405 return entry.second->isReady();
406 });
407 if (!ready || !moreButtonInstance) {
408 return;
409 }
410
411 qreal maxHeight = 0.0;
412 qreal maxWidth = 0.0;
413
414 // First, calculate the total width and maximum height of all delegates.
415 // This will be used to determine which actions to show, which ones to
416 // collapse to icon-only etc.
417 for (auto entry : std::as_const(t&: sortedDelegates)) {
418 if (!entry->isActionVisible()) {
419 entry->hide();
420 continue;
421 }
422
423 if (entry->isHidden()) {
424 entry->hide();
425 hiddenActions.append(t: entry->action());
426 continue;
427 }
428
429 if (entry->isIconOnly()) {
430 entry->showIcon();
431 } else {
432 entry->showFull();
433 }
434
435 maxWidth += entry->width() + spacing;
436 maxHeight = std::max(a: maxHeight, b: entry->maxHeight());
437 }
438
439 // The last entry also gets spacing but shouldn't, so remove that.
440 maxWidth -= spacing;
441
442 visibleActionsWidth = 0.0;
443
444 q->setImplicitWidth(maxWidth);
445
446 if (maxWidth > q->width() - (hiddenActions.isEmpty() ? 0.0 : moreButtonInstance->width() + spacing)) {
447 // We have more items than fit into the view, so start hiding some.
448
449 qreal layoutWidth = q->width() - (moreButtonInstance->width() + spacing);
450 if (alignment & Qt::AlignHCenter) {
451 // When centering, we need to reserve space on both sides to make sure
452 // things are properly centered, otherwise we will be to the right of
453 // the center.
454 layoutWidth -= (moreButtonInstance->width() + spacing);
455 }
456
457 for (int i = 0; i < sortedDelegates.size(); ++i) {
458 auto delegate = sortedDelegates.at(i);
459
460 maybeHideDelegate(index: i, currentWidth&: visibleActionsWidth, totalWidth: layoutWidth);
461
462 if (delegate->isVisible()) {
463 visibleActionsWidth += delegate->width() + spacing;
464 }
465 }
466 if (!qFuzzyIsNull(d: visibleActionsWidth)) {
467 // Like above, remove spacing on the last element that incorrectly gets spacing added.
468 visibleActionsWidth -= spacing;
469 }
470 } else {
471 visibleActionsWidth = maxWidth;
472 }
473
474 if (!hiddenActions.isEmpty()) {
475 maxHeight = std::max(a: maxHeight, b: moreButtonInstance->implicitHeight());
476 };
477
478 q->setImplicitHeight(maxHeight);
479
480 Q_EMIT q->hiddenActionsChanged();
481
482 implicitSizeValid = true;
483
484 q->polish();
485}
486
487void ToolBarLayoutPrivate::performLayout()
488{
489 if (!completed || actions.isEmpty()) {
490 return;
491 }
492
493 if (!implicitSizeValid) {
494 calculateImplicitSize();
495 }
496
497 if (sortedDelegates.isEmpty()) {
498 sortedDelegates = createDelegates();
499 }
500
501 bool ready = std::all_of(first: delegates.cbegin(), last: delegates.cend(), pred: [](const std::pair<QObject *const, std::unique_ptr<ToolBarLayoutDelegate>> &entry) {
502 return entry.second->isReady();
503 });
504 if (!ready || !moreButtonInstance) {
505 // Hide toolbar actions if delegates are still being
506 // loaded (otherwise they visibly glitch in one-by-one as they load)
507 q->setVisible(false);
508 return;
509 }
510 // Set visibility to true once all delegates have loaded
511 q->setVisible(true);
512
513 qreal width = q->width();
514 qreal height = q->height();
515
516 if (!hiddenActions.isEmpty()) {
517 if (layoutDirection == Qt::LeftToRight) {
518 moreButtonInstance->setX(width - moreButtonInstance->width());
519 } else {
520 moreButtonInstance->setX(0.0);
521 }
522
523 if (heightMode == ToolBarLayout::AlwaysFill) {
524 moreButtonInstance->setHeight(height);
525 } else if (heightMode == ToolBarLayout::ConstrainIfLarger) {
526 if (moreButtonInstance->implicitHeight() > height) {
527 moreButtonInstance->setHeight(height);
528 } else {
529 moreButtonInstance->resetHeight();
530 }
531 } else {
532 moreButtonInstance->resetHeight();
533 }
534
535 moreButtonInstance->setY(qRound(d: (height - moreButtonInstance->height()) / 2.0));
536 shouldShowMoreButton = true;
537 moreButtonInstance->setVisible(true);
538 } else {
539 shouldShowMoreButton = false;
540 moreButtonInstance->setVisible(false);
541 }
542
543 qreal currentX = layoutStart(layoutWidth: visibleActionsWidth);
544 for (auto entry : std::as_const(t&: sortedDelegates)) {
545 if (!entry->isVisible()) {
546 continue;
547 }
548
549 if (heightMode == ToolBarLayout::AlwaysFill) {
550 entry->setHeight(height);
551 } else if (heightMode == ToolBarLayout::ConstrainIfLarger) {
552 if (entry->implicitHeight() > height) {
553 entry->setHeight(height);
554 } else {
555 entry->resetHeight();
556 }
557 } else {
558 entry->resetHeight();
559 }
560
561 qreal y = qRound(d: (height - entry->height()) / 2.0);
562
563 if (layoutDirection == Qt::LeftToRight) {
564 entry->setPosition(x: currentX, y);
565 currentX += entry->width() + spacing;
566 } else {
567 entry->setPosition(x: currentX - entry->width(), y);
568 currentX -= entry->width() + spacing;
569 }
570
571 entry->show();
572 }
573
574 qreal newVisibleWidth = visibleActionsWidth;
575 if (moreButtonInstance->isVisible()) {
576 newVisibleWidth += moreButtonInstance->width() + (newVisibleWidth > 0.0 ? spacing : 0.0);
577 }
578 if (!qFuzzyCompare(p1: newVisibleWidth, p2: visibleWidth)) {
579 visibleWidth = newVisibleWidth;
580 Q_EMIT q->visibleWidthChanged();
581 }
582
583 if (actionsChanged) {
584 // Due to the way QQmlListProperty works, if we emit changed every time
585 // an action is added/removed, we end up emitting way too often. So
586 // instead only do it after everything else is done.
587 Q_EMIT q->actionsChanged();
588 actionsChanged = false;
589 }
590
591 sortedDelegates.clear();
592}
593
594QList<ToolBarLayoutDelegate *> ToolBarLayoutPrivate::createDelegates()
595{
596 QList<ToolBarLayoutDelegate *> result;
597 for (auto action : std::as_const(t&: actions)) {
598 if (delegates.find(x: action) != delegates.end()) {
599 result.append(t: delegates.at(k: action).get());
600 } else if (action) {
601 auto delegate = std::unique_ptr<ToolBarLayoutDelegate>(createDelegate(action));
602 if (delegate) {
603 result.append(t: delegate.get());
604 delegates.emplace(args&: action, args: std::move(delegate));
605 }
606 }
607 }
608
609 if (!moreButtonInstance && !moreButtonIncubator) {
610 moreButtonIncubator = new ToolBarDelegateIncubator(moreButton, qmlContext(moreButton));
611 moreButtonIncubator->setStateCallback([this](QQuickItem *item) {
612 item->setParentItem(q);
613 });
614 moreButtonIncubator->setCompletedCallback([this](ToolBarDelegateIncubator *incubator) {
615 moreButtonInstance = qobject_cast<QQuickItem *>(o: incubator->object());
616 moreButtonInstance->setVisible(false);
617
618 QObject::connect(sender: moreButtonInstance, signal: &QQuickItem::visibleChanged, context: q, slot: [this]() {
619 moreButtonInstance->setVisible(shouldShowMoreButton);
620 });
621 QObject::connect(sender: moreButtonInstance, signal: &QQuickItem::widthChanged, context: q, slot: &ToolBarLayout::minimumWidthChanged);
622 q->relayout();
623 Q_EMIT q->minimumWidthChanged();
624
625 QTimer::singleShot(interval: 0, receiver: q, slot: [this]() {
626 delete moreButtonIncubator;
627 moreButtonIncubator = nullptr;
628 });
629 });
630 moreButtonIncubator->create();
631 }
632
633 return result;
634}
635
636ToolBarLayoutDelegate *ToolBarLayoutPrivate::createDelegate(QObject *action)
637{
638 QQmlComponent *fullComponent = nullptr;
639 auto displayComponent = action->property(name: "displayComponent");
640 if (displayComponent.isValid()) {
641 fullComponent = displayComponent.value<QQmlComponent *>();
642 }
643
644 if (!fullComponent) {
645 fullComponent = fullDelegate;
646 }
647
648 auto separator = action->property(name: "separator");
649 if (separator.isValid() && separator.toBool()) {
650 fullComponent = separatorDelegate;
651 }
652
653 auto result = new ToolBarLayoutDelegate(q);
654 result->setAction(action);
655 result->createItems(fullComponent, iconComponent: iconDelegate, callback: [this, action](QQuickItem *newItem) {
656 newItem->setParentItem(q);
657 auto attached = static_cast<ToolBarLayoutAttached *>(qmlAttachedPropertiesObject<ToolBarLayout>(obj: newItem, create: true));
658 attached->setAction(action);
659
660 if (!q->childItems().isEmpty() && q->childItems().first() != newItem) {
661 // Due to asynchronous item creation, we end up creating the last item
662 // first. So move items before previously inserted items to ensure
663 // we have a more sensible tab order.
664 // Note that this will be incorrect if we end up completing in random
665 // order.
666 newItem->stackBefore(q->childItems().first());
667 }
668 });
669
670 return result;
671}
672
673qreal ToolBarLayoutPrivate::layoutStart(qreal layoutWidth)
674{
675 qreal availableWidth = moreButtonInstance->isVisible() ? q->width() - (moreButtonInstance->width() + spacing) : q->width();
676
677 if (alignment & Qt::AlignLeft) {
678 return layoutDirection == Qt::LeftToRight ? 0.0 : q->width();
679 } else if (alignment & Qt::AlignHCenter) {
680 return (q->width() / 2) + (layoutDirection == Qt::LeftToRight ? -layoutWidth / 2.0 : layoutWidth / 2.0);
681 } else if (alignment & Qt::AlignRight) {
682 qreal offset = availableWidth - layoutWidth;
683 return layoutDirection == Qt::LeftToRight ? offset : q->width() - offset;
684 }
685 return 0.0;
686}
687
688void ToolBarLayoutPrivate::maybeHideDelegate(int index, qreal &currentWidth, qreal totalWidth)
689{
690 auto delegate = sortedDelegates.at(i: index);
691
692 if (!delegate->isVisible()) {
693 // If the delegate isn't visible anyway, do nothing.
694 return;
695 }
696
697 if (currentWidth + delegate->width() < totalWidth && (firstHiddenIndex < 0 || index < firstHiddenIndex)) {
698 // If the delegate is fully visible and we have not already hidden
699 // actions, do nothing.
700 return;
701 }
702
703 if (delegate->isKeepVisible()) {
704 // If the action is marked as KeepVisible, we need to try our best to
705 // keep it in view. If the full size delegate does not fit, we try the
706 // icon-only delegate. If that also does not fit, try and find other
707 // actions to hide. Finally, if that also fails, we will hide the
708 // delegate.
709 if (currentWidth + delegate->iconWidth() > totalWidth) {
710 // First, hide any earlier actions that are not marked as KeepVisible.
711 for (auto currentIndex = index - 1; currentIndex >= 0; --currentIndex) {
712 auto previousDelegate = sortedDelegates.at(i: currentIndex);
713 if (!previousDelegate->isVisible() || previousDelegate->isKeepVisible()) {
714 continue;
715 }
716
717 auto width = previousDelegate->width();
718 previousDelegate->hide();
719 hiddenActions.append(t: previousDelegate->action());
720 currentWidth -= (width + spacing);
721
722 if (currentWidth + delegate->fullWidth() <= totalWidth) {
723 delegate->showFull();
724 break;
725 } else if (currentWidth + delegate->iconWidth() <= totalWidth) {
726 delegate->showIcon();
727 break;
728 }
729 }
730
731 if (currentWidth + delegate->width() <= totalWidth) {
732 return;
733 }
734
735 // Hiding normal actions did not help enough, so go through actions
736 // with KeepVisible set and try and collapse them to IconOnly.
737 for (auto currentIndex = index - 1; currentIndex >= 0; --currentIndex) {
738 auto previousDelegate = sortedDelegates.at(i: currentIndex);
739 if (!previousDelegate->isVisible() || !previousDelegate->isKeepVisible()) {
740 continue;
741 }
742
743 auto extraSpace = previousDelegate->width() - previousDelegate->iconWidth();
744 previousDelegate->showIcon();
745 currentWidth -= extraSpace;
746
747 if (currentWidth + delegate->fullWidth() <= totalWidth) {
748 delegate->showFull();
749 break;
750 } else if (currentWidth + delegate->iconWidth() <= totalWidth) {
751 delegate->showIcon();
752 break;
753 }
754 }
755
756 // If that also did not work, then hide this action after all.
757 if (currentWidth + delegate->width() > totalWidth) {
758 delegate->hide();
759 hiddenActions.append(t: delegate->action());
760 }
761 } else {
762 delegate->showIcon();
763 }
764 } else {
765 // The action is not marked as KeepVisible and it does not fit within
766 // the current layout, so hide it.
767 delegate->hide();
768 hiddenActions.append(t: delegate->action());
769
770 // If this is the first item to be hidden, mark it so we know we should
771 // also hide the following items.
772 if (firstHiddenIndex < 0) {
773 firstHiddenIndex = index;
774 }
775 }
776}
777
778void ToolBarLayoutPrivate::appendAction(ToolBarLayout::ActionsProperty *list, QObject *action)
779{
780 auto layout = reinterpret_cast<ToolBarLayout *>(list->data);
781 layout->addAction(action);
782}
783
784qsizetype ToolBarLayoutPrivate::actionCount(ToolBarLayout::ActionsProperty *list)
785{
786 return reinterpret_cast<ToolBarLayout *>(list->data)->d->actions.count();
787}
788
789QObject *ToolBarLayoutPrivate::action(ToolBarLayout::ActionsProperty *list, qsizetype index)
790{
791 return reinterpret_cast<ToolBarLayout *>(list->data)->d->actions.at(i: index);
792}
793
794void ToolBarLayoutPrivate::clearActions(ToolBarLayout::ActionsProperty *list)
795{
796 reinterpret_cast<ToolBarLayout *>(list->data)->clearActions();
797}
798
799#include "moc_toolbarlayout.cpp"
800

source code of kirigami/src/layouts/toolbarlayout.cpp