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

source code of kirigami/src/toolbarlayout.cpp