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#ifndef TOOLBARLAYOUT_H
8#define TOOLBARLAYOUT_H
9
10#include <QQuickItem>
11#include <memory>
12
13class ToolBarLayoutPrivate;
14
15/**
16 * Attached property for ToolBarLayout delegates.
17 */
18class ToolBarLayoutAttached : public QObject
19{
20 Q_OBJECT
21 /**
22 * The action this delegate was created for.
23 */
24 Q_PROPERTY(QObject *action READ action CONSTANT FINAL)
25public:
26 ToolBarLayoutAttached(QObject *parent = nullptr);
27
28 QObject *action() const;
29 void setAction(QObject *action);
30
31private:
32 QObject *m_action = nullptr;
33};
34
35/**
36 * An item that creates delegates for actions and lays them out in a row.
37 *
38 * This effectively combines RowLayout and Repeater in a single item, with the
39 * addition of some extra performance enhancing tweaks. It will create instances
40 * of ::fullDelegate and ::itemDelegate for each action in ::actions . These are
41 * then positioned horizontally. Any action that ends up being placed outside
42 * the width of the item is hidden and will be part of ::hiddenActions.
43 *
44 * The items created as delegates are always created asynchronously, to avoid
45 * creation lag spikes. Each delegate has access to the action it was created
46 * for through the ToolBarLayoutAttached attached property.
47 */
48class ToolBarLayout : public QQuickItem
49{
50 Q_OBJECT
51 QML_ELEMENT
52 QML_ATTACHED(ToolBarLayoutAttached)
53 /**
54 * The actions this layout should create delegates for.
55 */
56 Q_PROPERTY(QQmlListProperty<QObject> actions READ actionsProperty NOTIFY actionsChanged FINAL)
57 /**
58 * A list of actions that do not fit in the current view and are thus hidden.
59 */
60 Q_PROPERTY(QList<QObject *> hiddenActions READ hiddenActions NOTIFY hiddenActionsChanged FINAL)
61 /**
62 * A component that is used to create full-size delegates from.
63 *
64 * Each delegate has three states, it can be full-size, icon-only or hidden.
65 * By default, the full-size delegate is used. When the action has the
66 * DisplayHint::IconOnly hint set, it will always use the iconDelegate. When
67 * it has the DisplayHint::KeepVisible hint set, it will use the full-size
68 * delegate when it fits. If not, it will use the iconDelegate, unless even
69 * that does not fit, in which case it will still be hidden.
70 */
71 Q_PROPERTY(QQmlComponent *fullDelegate READ fullDelegate WRITE setFullDelegate NOTIFY fullDelegateChanged FINAL)
72 /**
73 * A component that is used to create icon-only delegates from.
74 *
75 * \sa fullDelegate
76 */
77 Q_PROPERTY(QQmlComponent *iconDelegate READ iconDelegate WRITE setIconDelegate NOTIFY iconDelegateChanged FINAL)
78 /**
79 * A component that is used to create the "more button" item from.
80 *
81 * The more button is shown when there are actions that do not fit the
82 * current view. It is intended to have functionality to show these hidden
83 * actions, like popup a menu with them showing.
84 */
85 Q_PROPERTY(QQmlComponent *moreButton READ moreButton WRITE setMoreButton NOTIFY moreButtonChanged FINAL)
86 /**
87 * The amount of spacing between individual delegates.
88 */
89 Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged FINAL)
90 /**
91 * How to align the delegates within this layout.
92 *
93 * When there is more space available than required by the visible delegates,
94 * we need to determine how to place the delegates. This property determines
95 * how to do that. Note that the moreButton, if visible, will always be
96 * placed at the end of the layout.
97 */
98 Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged FINAL)
99 /**
100 * The combined width of visible delegates in this layout.
101 */
102 Q_PROPERTY(qreal visibleWidth READ visibleWidth NOTIFY visibleWidthChanged FINAL)
103 /**
104 * The minimum width this layout can have.
105 *
106 * This is equal to the width of the moreButton.
107 */
108 Q_PROPERTY(qreal minimumWidth READ minimumWidth NOTIFY minimumWidthChanged FINAL)
109 /**
110 * Which direction to layout in.
111 *
112 * This is primarily intended to support right-to-left layouts. When set to
113 * LeftToRight, delegates will be layout with the first item on the left and
114 * following items to the right of that. The more button will be placed at
115 * the rightmost position. Alignment flags work normally.
116 *
117 * When set to RightToLeft, delegates will be layout with the first item on
118 * the right and following items to the left of that. The more button will
119 * be placed at the leftmost position. Alignment flags are inverted, so
120 * AlignLeft will align items to the right, and vice-versa.
121 */
122 Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged FINAL)
123 /**
124 * How to handle items that do not match the toolbar's height.
125 *
126 * When toolbar items do not match the height of the toolbar, there are
127 * several ways we can deal with this. This property sets the preferred way.
128 *
129 * The default is HeightMode::ConstrainIfLarger .
130 *
131 * \sa HeightMode
132 */
133 Q_PROPERTY(HeightMode heightMode READ heightMode WRITE setHeightMode NOTIFY heightModeChanged FINAL)
134
135public:
136 using ActionsProperty = QQmlListProperty<QObject>;
137
138 /**
139 * An enum describing several modes that can be used to deal with items with
140 * a height that does not match the toolbar's height.
141 */
142 enum HeightMode {
143 AlwaysCenter, ///< Always center items, allowing them to go outside the bounds of the layout if they are larger.
144 AlwaysFill, ///< Always match the height of the layout. Larger items will be reduced in height, smaller items will be increased.
145 ConstrainIfLarger, ///< If the item is larger than the toolbar, reduce its height. Otherwise center it in the toolbar.
146 };
147 Q_ENUM(HeightMode)
148
149 ToolBarLayout(QQuickItem *parent = nullptr);
150 ~ToolBarLayout() override;
151
152 ActionsProperty actionsProperty() const;
153 /**
154 * Add an action to the list of actions.
155 *
156 * \param action The action to add.
157 */
158 void addAction(QObject *action);
159 /**
160 * Remove an action from the list of actions.
161 *
162 * \param action The action to remove.
163 */
164 void removeAction(QObject *action);
165 /**
166 * Clear the list of actions.
167 */
168 void clearActions();
169 Q_SIGNAL void actionsChanged();
170
171 QList<QObject *> hiddenActions() const;
172 Q_SIGNAL void hiddenActionsChanged();
173
174 QQmlComponent *fullDelegate() const;
175 void setFullDelegate(QQmlComponent *newFullDelegate);
176 Q_SIGNAL void fullDelegateChanged();
177
178 QQmlComponent *iconDelegate() const;
179 void setIconDelegate(QQmlComponent *newIconDelegate);
180 Q_SIGNAL void iconDelegateChanged();
181
182 QQmlComponent *moreButton() const;
183 void setMoreButton(QQmlComponent *newMoreButton);
184 Q_SIGNAL void moreButtonChanged();
185
186 qreal spacing() const;
187 void setSpacing(qreal newSpacing);
188 Q_SIGNAL void spacingChanged();
189
190 Qt::Alignment alignment() const;
191 void setAlignment(Qt::Alignment newAlignment);
192 Q_SIGNAL void alignmentChanged();
193
194 qreal visibleWidth() const;
195 Q_SIGNAL void visibleWidthChanged();
196
197 qreal minimumWidth() const;
198 Q_SIGNAL void minimumWidthChanged();
199
200 Qt::LayoutDirection layoutDirection() const;
201 void setLayoutDirection(Qt::LayoutDirection &newLayoutDirection);
202 Q_SIGNAL void layoutDirectionChanged();
203
204 HeightMode heightMode() const;
205 void setHeightMode(HeightMode newHeightMode);
206 Q_SIGNAL void heightModeChanged();
207
208 /**
209 * Queue a relayout of this layout.
210 *
211 * \note The layouting happens during the next scene graph polishing phase.
212 */
213 Q_SLOT void relayout();
214
215 static ToolBarLayoutAttached *qmlAttachedProperties(QObject *object)
216 {
217 return new ToolBarLayoutAttached(object);
218 }
219
220protected:
221 void componentComplete() override;
222 void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
223 void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) override;
224 void updatePolish() override;
225
226private:
227 friend class ToolBarLayoutPrivate;
228 const std::unique_ptr<ToolBarLayoutPrivate> d;
229};
230
231QML_DECLARE_TYPEINFO(ToolBarLayout, QML_HAS_ATTACHED_PROPERTIES)
232
233#endif // TOOLBARLAYOUT_H
234

source code of kirigami/src/toolbarlayout.h