7#include "toolbarlayout.h"
10#include <unordered_map>
12#include <QDeadlineTimer>
13#include <QQmlComponent>
16#include "loggingcategory.h"
17#include "toolbarlayoutdelegate.h"
19ToolBarLayoutAttached::ToolBarLayoutAttached(
QObject *parent)
29void ToolBarLayoutAttached::setAction(
QObject *action)
34class ToolBarLayoutPrivate
43 ~ToolBarLayoutPrivate()
45 if (moreButtonIncubator) {
46 moreButtonIncubator->
clear();
47 delete moreButtonIncubator;
51 void calculateImplicitSize();
54 ToolBarLayoutDelegate *createDelegate(
QObject *action);
55 qreal layoutStart(qreal layoutWidth);
56 void maybeHideDelegate(
int index, qreal ¤tWidth, qreal totalWidth);
66 qreal visibleActionsWidth = 0.0;
67 qreal visibleWidth = 0.0;
71 bool completed =
false;
72 bool actionsChanged =
false;
73 bool implicitSizeValid =
false;
75 std::unordered_map<QObject *, std::unique_ptr<ToolBarLayoutDelegate>> delegates;
78 ToolBarDelegateIncubator *moreButtonIncubator =
nullptr;
79 bool shouldShowMoreButton =
false;
80 int firstHiddenIndex = -1;
83 QTimer *removalTimer =
nullptr;
93ToolBarLayout::ToolBarLayout(
QQuickItem *parent)
95 , d(
std::make_unique<ToolBarLayoutPrivate>(this))
97 d->actionsProperty = ActionsProperty(
this,
99 ToolBarLayoutPrivate::appendAction,
100 ToolBarLayoutPrivate::actionCount,
101 ToolBarLayoutPrivate::action,
102 ToolBarLayoutPrivate::clearActions);
107 d->removalTimer =
new QTimer{
this};
109 d->removalTimer->setSingleShot(
true);
111 for (auto action : std::as_const(d->removedActions)) {
112 if (!d->actions.contains(action)) {
113 d->delegates.erase(action);
116 d->removedActions.clear();
120ToolBarLayout::~ToolBarLayout()
126 return d->actionsProperty;
131 if (action ==
nullptr) {
134 d->actions.append(action);
135 d->actionsChanged =
true;
138 auto itr = d->delegates.find(action);
139 if (itr != d->delegates.end()) {
140 d->delegates.erase(itr);
143 d->actions.removeOne(action);
144 d->actionsChanged =
true;
154 auto itr = d->delegates.find(action);
155 if (itr != d->delegates.end()) {
159 d->actions.removeOne(action);
160 d->removedActions.append(action);
161 d->removalTimer->start();
162 d->actionsChanged =
true;
169 for (
auto action : std::as_const(d->actions)) {
170 auto itr = d->delegates.find(action);
171 if (itr != d->delegates.end()) {
176 d->removedActions.append(d->actions);
178 d->actionsChanged =
true;
185 return d->hiddenActions;
190 return d->fullDelegate;
193void ToolBarLayout::setFullDelegate(
QQmlComponent *newFullDelegate)
195 if (newFullDelegate == d->fullDelegate) {
199 d->fullDelegate = newFullDelegate;
200 d->delegates.clear();
202 Q_EMIT fullDelegateChanged();
207 return d->iconDelegate;
210void ToolBarLayout::setIconDelegate(
QQmlComponent *newIconDelegate)
212 if (newIconDelegate == d->iconDelegate) {
216 d->iconDelegate = newIconDelegate;
217 d->delegates.clear();
219 Q_EMIT iconDelegateChanged();
224 return d->moreButton;
227void ToolBarLayout::setMoreButton(
QQmlComponent *newMoreButton)
229 if (newMoreButton == d->moreButton) {
233 d->moreButton = newMoreButton;
234 if (d->moreButtonInstance) {
235 d->moreButtonInstance->deleteLater();
236 d->moreButtonInstance =
nullptr;
239 Q_EMIT moreButtonChanged();
247void ToolBarLayout::setSpacing(qreal newSpacing)
249 if (newSpacing == d->spacing) {
253 d->spacing = newSpacing;
265 if (newAlignment == d->alignment) {
269 d->alignment = newAlignment;
271 Q_EMIT alignmentChanged();
276 return d->visibleWidth;
281 return d->moreButtonInstance ? d->moreButtonInstance->width() : 0;
286 return d->layoutDirection;
291 if (newLayoutDirection == d->layoutDirection) {
295 d->layoutDirection = newLayoutDirection;
297 Q_EMIT layoutDirectionChanged();
302 return d->heightMode;
305void ToolBarLayout::setHeightMode(HeightMode newHeightMode)
307 if (newHeightMode == d->heightMode) {
311 d->heightMode = newHeightMode;
313 Q_EMIT heightModeChanged();
318 d->implicitSizeValid =
false;
322void ToolBarLayout::componentComplete()
329void ToolBarLayout::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
331 if (newGeometry != oldGeometry) {
332 if (newGeometry.
size() !=
QSizeF{implicitWidth(), implicitHeight()}) {
349void ToolBarLayout::updatePolish()
368void ToolBarLayoutPrivate::calculateImplicitSize()
374 if (!fullDelegate || !iconDelegate || !moreButton) {
375 qCWarning(KirigamiLog) <<
"ToolBarLayout: Unable to layout, required properties are not set";
380 q->setImplicitSize(0., 0.);
384 hiddenActions.
clear();
385 firstHiddenIndex = -1;
387 sortedDelegates = createDelegates();
389 bool ready = std::all_of(delegates.cbegin(), delegates.cend(), [](
const std::pair<
QObject *
const, std::unique_ptr<ToolBarLayoutDelegate>> &entry) {
390 return entry.second->isReady();
392 if (!ready || !moreButtonInstance) {
396 qreal maxHeight = 0.0;
397 qreal maxWidth = 0.0;
402 for (
auto entry :
std::as_const(sortedDelegates)) {
403 if (!entry->isActionVisible()) {
408 if (entry->isHidden()) {
410 hiddenActions.
append(entry->action());
414 if (entry->isIconOnly()) {
420 maxWidth += entry->width() + spacing;
421 maxHeight = std::max(maxHeight, entry->maxHeight());
427 visibleActionsWidth = 0.0;
429 if (maxWidth > q->
width() - (hiddenActions.
isEmpty() ? 0.0 : moreButtonInstance->width() + spacing)) {
432 qreal layoutWidth = q->
width() - (moreButtonInstance->
width() + spacing);
437 layoutWidth -= (moreButtonInstance->
width() + spacing);
440 for (
int i = 0; i < sortedDelegates.
size(); ++i) {
441 auto delegate = sortedDelegates.
at(i);
443 maybeHideDelegate(i, visibleActionsWidth, layoutWidth);
445 if (delegate->isVisible()) {
446 visibleActionsWidth += delegate->width() + spacing;
449 if (!qFuzzyIsNull(visibleActionsWidth)) {
451 visibleActionsWidth -= spacing;
454 visibleActionsWidth = maxWidth;
457 if (!hiddenActions.
isEmpty()) {
458 maxHeight = std::max(maxHeight, moreButtonInstance->
implicitHeight());
461 q->setImplicitSize(maxWidth, maxHeight);
462 Q_EMIT q->hiddenActionsChanged();
464 implicitSizeValid =
true;
469void ToolBarLayoutPrivate::performLayout()
471 if (!completed || actions.
isEmpty()) {
475 if (!implicitSizeValid) {
476 calculateImplicitSize();
479 if (sortedDelegates.
isEmpty()) {
480 sortedDelegates = createDelegates();
483 bool ready = std::all_of(delegates.cbegin(), delegates.cend(), [](
const std::pair<
QObject *
const, std::unique_ptr<ToolBarLayoutDelegate>> &entry) {
484 return entry.second->isReady();
486 if (!ready || !moreButtonInstance) {
490 qreal width = q->
width();
491 qreal height = q->
height();
493 if (!hiddenActions.
isEmpty()) {
495 moreButtonInstance->
setX(width - moreButtonInstance->
width());
497 moreButtonInstance->
setX(0.0);
512 moreButtonInstance->
setY(qRound((height - moreButtonInstance->
height()) / 2.0));
513 shouldShowMoreButton =
true;
516 shouldShowMoreButton =
false;
520 qreal currentX = layoutStart(visibleActionsWidth);
521 for (
auto entry :
std::as_const(sortedDelegates)) {
522 if (!entry->isVisible()) {
527 entry->setHeight(height);
529 if (entry->implicitHeight() > height) {
530 entry->setHeight(height);
532 entry->resetHeight();
535 entry->resetHeight();
538 qreal y = qRound((height - entry->height()) / 2.0);
541 entry->setPosition(currentX, y);
542 currentX += entry->width() + spacing;
544 entry->setPosition(currentX - entry->width(), y);
545 currentX -= entry->width() + spacing;
551 qreal newVisibleWidth = visibleActionsWidth;
553 newVisibleWidth += moreButtonInstance->
width() + (newVisibleWidth > 0.0 ? spacing : 0.0);
555 if (!qFuzzyCompare(newVisibleWidth, visibleWidth)) {
556 visibleWidth = newVisibleWidth;
557 Q_EMIT q->visibleWidthChanged();
560 if (actionsChanged) {
564 Q_EMIT q->actionsChanged();
565 actionsChanged =
false;
568 sortedDelegates.
clear();
574 for (
auto action :
std::as_const(actions)) {
575 if (delegates.find(action) != delegates.end()) {
576 result.
append(delegates.at(action).get());
578 auto delegate = std::unique_ptr<ToolBarLayoutDelegate>(createDelegate(action));
580 result.
append(delegate.get());
581 delegates.emplace(action, std::move(delegate));
586 if (!moreButtonInstance && !moreButtonIncubator) {
587 moreButtonIncubator =
new ToolBarDelegateIncubator(moreButton, qmlContext(moreButton));
588 moreButtonIncubator->setStateCallback([
this](
QQuickItem *item) {
591 moreButtonIncubator->setCompletedCallback([
this](ToolBarDelegateIncubator *incubator) {
592 moreButtonInstance = qobject_cast<QQuickItem *>(incubator->
object());
596 moreButtonInstance->
setVisible(shouldShowMoreButton);
600 Q_EMIT q->minimumWidthChanged();
603 delete moreButtonIncubator;
604 moreButtonIncubator =
nullptr;
607 moreButtonIncubator->create();
613ToolBarLayoutDelegate *ToolBarLayoutPrivate::createDelegate(
QObject *action)
616 auto displayComponent = action->
property(
"displayComponent");
617 if (displayComponent.isValid()) {
621 if (!fullComponent) {
622 fullComponent = fullDelegate;
625 auto result =
new ToolBarLayoutDelegate(q);
626 result->setAction(action);
627 result->createItems(fullComponent, iconDelegate, [
this, action](
QQuickItem *newItem) {
629 auto attached =
static_cast<ToolBarLayoutAttached *
>(qmlAttachedPropertiesObject<ToolBarLayout>(newItem,
true));
630 attached->setAction(action);
636qreal ToolBarLayoutPrivate::layoutStart(qreal layoutWidth)
638 qreal availableWidth = moreButtonInstance->
isVisible() ? q->
width() - (moreButtonInstance->
width() + spacing) : q->width();
643 return (q->
width() / 2) + (layoutDirection ==
Qt::LeftToRight ? -layoutWidth / 2.0 : layoutWidth / 2.0);
645 qreal offset = availableWidth - layoutWidth;
651void ToolBarLayoutPrivate::maybeHideDelegate(
int index, qreal ¤tWidth, qreal totalWidth)
653 auto delegate = sortedDelegates.
at(index);
655 if (!delegate->isVisible()) {
660 if (currentWidth + delegate->width() < totalWidth && (firstHiddenIndex < 0 || index < firstHiddenIndex)) {
666 if (delegate->isKeepVisible()) {
672 if (currentWidth + delegate->iconWidth() > totalWidth) {
674 for (
auto currentIndex = index - 1; currentIndex >= 0; --currentIndex) {
675 auto previousDelegate = sortedDelegates.
at(currentIndex);
676 if (!previousDelegate->isVisible() || previousDelegate->isKeepVisible()) {
680 auto width = previousDelegate->width();
681 previousDelegate->hide();
682 hiddenActions.
append(previousDelegate->action());
683 currentWidth -= (width + spacing);
685 if (currentWidth + delegate->fullWidth() <= totalWidth) {
686 delegate->showFull();
688 }
else if (currentWidth + delegate->iconWidth() <= totalWidth) {
689 delegate->showIcon();
694 if (currentWidth + delegate->width() <= totalWidth) {
700 for (
auto currentIndex = index - 1; currentIndex >= 0; --currentIndex) {
701 auto previousDelegate = sortedDelegates.
at(currentIndex);
702 if (!previousDelegate->isVisible() || !previousDelegate->isKeepVisible()) {
706 auto extraSpace = previousDelegate->width() - previousDelegate->iconWidth();
707 previousDelegate->showIcon();
708 currentWidth -= extraSpace;
710 if (currentWidth + delegate->fullWidth() <= totalWidth) {
711 delegate->showFull();
713 }
else if (currentWidth + delegate->iconWidth() <= totalWidth) {
714 delegate->showIcon();
720 if (currentWidth + delegate->width() > totalWidth) {
722 hiddenActions.
append(delegate->action());
725 delegate->showIcon();
731 hiddenActions.
append(delegate->action());
735 if (firstHiddenIndex < 0) {
736 firstHiddenIndex = index;
762#include "moc_toolbarlayout.cpp"
KIOCORE_EXPORT QStringList list(const QString &fileClass)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
bool isEmpty() const const
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void destroyed(QObject *obj)
QVariant property(const char *name) const const
QObject * object() const const
virtual void componentComplete() override
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
virtual void itemChange(ItemChange change, const ItemChangeData &value)
void setParentItem(QQuickItem *parent)
QSizeF size() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void setInterval(int msec)