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 #ifndef TOOLBARLAYOUT_H
0008 #define TOOLBARLAYOUT_H
0009 
0010 #include <QQuickItem>
0011 #include <memory>
0012 
0013 class ToolBarLayoutPrivate;
0014 
0015 /**
0016  * Attached property for ToolBarLayout delegates.
0017  */
0018 class ToolBarLayoutAttached : public QObject
0019 {
0020     Q_OBJECT
0021     /**
0022      * The action this delegate was created for.
0023      */
0024     Q_PROPERTY(QObject *action READ action CONSTANT FINAL)
0025 public:
0026     ToolBarLayoutAttached(QObject *parent = nullptr);
0027 
0028     QObject *action() const;
0029     void setAction(QObject *action);
0030 
0031 private:
0032     QObject *m_action = nullptr;
0033 };
0034 
0035 /**
0036  * An item that creates delegates for actions and lays them out in a row.
0037  *
0038  * This effectively combines RowLayout and Repeater in a single item, with the
0039  * addition of some extra performance enhancing tweaks. It will create instances
0040  * of ::fullDelegate and ::itemDelegate for each action in ::actions . These are
0041  * then positioned horizontally. Any action that ends up being placed outside
0042  * the width of the item is hidden and will be part of ::hiddenActions.
0043  *
0044  * The items created as delegates are always created asynchronously, to avoid
0045  * creation lag spikes. Each delegate has access to the action it was created
0046  * for through the ToolBarLayoutAttached attached property.
0047  */
0048 class ToolBarLayout : public QQuickItem
0049 {
0050     Q_OBJECT
0051     QML_ELEMENT
0052     QML_ATTACHED(ToolBarLayoutAttached)
0053     /**
0054      * The actions this layout should create delegates for.
0055      */
0056     Q_PROPERTY(QQmlListProperty<QObject> actions READ actionsProperty NOTIFY actionsChanged FINAL)
0057     /**
0058      * A list of actions that do not fit in the current view and are thus hidden.
0059      */
0060     Q_PROPERTY(QList<QObject *> hiddenActions READ hiddenActions NOTIFY hiddenActionsChanged FINAL)
0061     /**
0062      * A component that is used to create full-size delegates from.
0063      *
0064      * Each delegate has three states, it can be full-size, icon-only or hidden.
0065      * By default, the full-size delegate is used. When the action has the
0066      * DisplayHint::IconOnly hint set, it will always use the iconDelegate. When
0067      * it has the DisplayHint::KeepVisible hint set, it will use the full-size
0068      * delegate when it fits. If not, it will use the iconDelegate, unless even
0069      * that does not fit, in which case it will still be hidden.
0070      */
0071     Q_PROPERTY(QQmlComponent *fullDelegate READ fullDelegate WRITE setFullDelegate NOTIFY fullDelegateChanged FINAL)
0072     /**
0073      * A component that is used to create icon-only delegates from.
0074      *
0075      * \sa fullDelegate
0076      */
0077     Q_PROPERTY(QQmlComponent *iconDelegate READ iconDelegate WRITE setIconDelegate NOTIFY iconDelegateChanged FINAL)
0078     /**
0079      * A component that is used to create the "more button" item from.
0080      *
0081      * The more button is shown when there are actions that do not fit the
0082      * current view. It is intended to have functionality to show these hidden
0083      * actions, like popup a menu with them showing.
0084      */
0085     Q_PROPERTY(QQmlComponent *moreButton READ moreButton WRITE setMoreButton NOTIFY moreButtonChanged FINAL)
0086     /**
0087      * The amount of spacing between individual delegates.
0088      */
0089     Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged FINAL)
0090     /**
0091      * How to align the delegates within this layout.
0092      *
0093      * When there is more space available than required by the visible delegates,
0094      * we need to determine how to place the delegates. This property determines
0095      * how to do that. Note that the moreButton, if visible, will always be
0096      * placed at the end of the layout.
0097      */
0098     Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged FINAL)
0099     /**
0100      * The combined width of visible delegates in this layout.
0101      */
0102     Q_PROPERTY(qreal visibleWidth READ visibleWidth NOTIFY visibleWidthChanged FINAL)
0103     /**
0104      * The minimum width this layout can have.
0105      *
0106      * This is equal to the width of the moreButton.
0107      */
0108     Q_PROPERTY(qreal minimumWidth READ minimumWidth NOTIFY minimumWidthChanged FINAL)
0109     /**
0110      * Which direction to layout in.
0111      *
0112      * This is primarily intended to support right-to-left layouts. When set to
0113      * LeftToRight, delegates will be layout with the first item on the left and
0114      * following items to the right of that. The more button will be placed at
0115      * the rightmost position. Alignment flags work normally.
0116      *
0117      * When set to RightToLeft, delegates will be layout with the first item on
0118      * the right and following items to the left of that. The more button will
0119      * be placed at the leftmost position. Alignment flags are inverted, so
0120      * AlignLeft will align items to the right, and vice-versa.
0121      */
0122     Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged FINAL)
0123     /**
0124      * How to handle items that do not match the toolbar's height.
0125      *
0126      * When toolbar items do not match the height of the toolbar, there are
0127      * several ways we can deal with this. This property sets the preferred way.
0128      *
0129      * The default is HeightMode::ConstrainIfLarger .
0130      *
0131      * \sa HeightMode
0132      */
0133     Q_PROPERTY(HeightMode heightMode READ heightMode WRITE setHeightMode NOTIFY heightModeChanged FINAL)
0134 
0135 public:
0136     using ActionsProperty = QQmlListProperty<QObject>;
0137 
0138     /**
0139      * An enum describing several modes that can be used to deal with items with
0140      * a height that does not match the toolbar's height.
0141      */
0142     enum HeightMode {
0143         AlwaysCenter, ///< Always center items, allowing them to go outside the bounds of the layout if they are larger.
0144         AlwaysFill, ///< Always match the height of the layout. Larger items will be reduced in height, smaller items will be increased.
0145         ConstrainIfLarger, ///< If the item is larger than the toolbar, reduce its height. Otherwise center it in the toolbar.
0146     };
0147     Q_ENUM(HeightMode)
0148 
0149     ToolBarLayout(QQuickItem *parent = nullptr);
0150     ~ToolBarLayout() override;
0151 
0152     ActionsProperty actionsProperty() const;
0153     /**
0154      * Add an action to the list of actions.
0155      *
0156      * \param action The action to add.
0157      */
0158     void addAction(QObject *action);
0159     /**
0160      * Remove an action from the list of actions.
0161      *
0162      * \param action The action to remove.
0163      */
0164     void removeAction(QObject *action);
0165     /**
0166      * Clear the list of actions.
0167      */
0168     void clearActions();
0169     Q_SIGNAL void actionsChanged();
0170 
0171     QList<QObject *> hiddenActions() const;
0172     Q_SIGNAL void hiddenActionsChanged();
0173 
0174     QQmlComponent *fullDelegate() const;
0175     void setFullDelegate(QQmlComponent *newFullDelegate);
0176     Q_SIGNAL void fullDelegateChanged();
0177 
0178     QQmlComponent *iconDelegate() const;
0179     void setIconDelegate(QQmlComponent *newIconDelegate);
0180     Q_SIGNAL void iconDelegateChanged();
0181 
0182     QQmlComponent *moreButton() const;
0183     void setMoreButton(QQmlComponent *newMoreButton);
0184     Q_SIGNAL void moreButtonChanged();
0185 
0186     qreal spacing() const;
0187     void setSpacing(qreal newSpacing);
0188     Q_SIGNAL void spacingChanged();
0189 
0190     Qt::Alignment alignment() const;
0191     void setAlignment(Qt::Alignment newAlignment);
0192     Q_SIGNAL void alignmentChanged();
0193 
0194     qreal visibleWidth() const;
0195     Q_SIGNAL void visibleWidthChanged();
0196 
0197     qreal minimumWidth() const;
0198     Q_SIGNAL void minimumWidthChanged();
0199 
0200     Qt::LayoutDirection layoutDirection() const;
0201     void setLayoutDirection(Qt::LayoutDirection &newLayoutDirection);
0202     Q_SIGNAL void layoutDirectionChanged();
0203 
0204     HeightMode heightMode() const;
0205     void setHeightMode(HeightMode newHeightMode);
0206     Q_SIGNAL void heightModeChanged();
0207 
0208     /**
0209      * Queue a relayout of this layout.
0210      *
0211      * \note The layouting happens during the next scene graph polishing phase.
0212      */
0213     Q_SLOT void relayout();
0214 
0215     static ToolBarLayoutAttached *qmlAttachedProperties(QObject *object)
0216     {
0217         return new ToolBarLayoutAttached(object);
0218     }
0219 
0220 protected:
0221     void componentComplete() override;
0222     void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
0223     void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) override;
0224     void updatePolish() override;
0225 
0226 private:
0227     friend class ToolBarLayoutPrivate;
0228     const std::unique_ptr<ToolBarLayoutPrivate> d;
0229 };
0230 
0231 QML_DECLARE_TYPEINFO(ToolBarLayout, QML_HAS_ATTACHED_PROPERTIES)
0232 
0233 #endif // TOOLBARLAYOUT_H