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 "toolbarlayoutdelegate.h"
8
9#include "loggingcategory.h"
10#include "toolbarlayout.h"
11
12using namespace Qt::StringLiterals;
13
14ToolBarDelegateIncubator::ToolBarDelegateIncubator(QQmlComponent *component, QQmlContext *context)
15 : QQmlIncubator(QQmlIncubator::Asynchronous)
16 , m_component(component)
17 , m_context(context)
18{
19 // Start with the delegate invisible in order to avoid flickering it in the wrong place when created
20 setInitialProperties({{u"visible"_s, false}});
21}
22
23void ToolBarDelegateIncubator::setStateCallback(std::function<void(QQuickItem *)> callback)
24{
25 m_stateCallback = callback;
26}
27
28void ToolBarDelegateIncubator::setCompletedCallback(std::function<void(ToolBarDelegateIncubator *)> callback)
29{
30 m_completedCallback = callback;
31}
32
33void ToolBarDelegateIncubator::create()
34{
35 m_component->create(*this, context: m_context);
36}
37
38bool ToolBarDelegateIncubator::isFinished()
39{
40 return m_finished;
41}
42
43void ToolBarDelegateIncubator::setInitialState(QObject *object)
44{
45 auto item = qobject_cast<QQuickItem *>(o: object);
46 if (item) {
47 m_stateCallback(item);
48 }
49}
50
51void ToolBarDelegateIncubator::statusChanged(QQmlIncubator::Status status)
52{
53 if (status == QQmlIncubator::Error) {
54 qCWarning(KirigamiLayoutsLog) << "Could not create delegate for ToolBarLayout";
55 const auto e = errors();
56 for (const auto &error : e) {
57 qCWarning(KirigamiLayoutsLog) << error;
58 }
59 m_finished = true;
60 }
61
62 if (status == QQmlIncubator::Ready) {
63 m_completedCallback(this);
64 m_finished = true;
65 }
66}
67
68ToolBarLayoutDelegate::ToolBarLayoutDelegate(ToolBarLayout *parent)
69 : QObject() // Note: delegates are managed by unique_ptr, so don't parent
70 , m_parent(parent)
71{
72}
73
74ToolBarLayoutDelegate::~ToolBarLayoutDelegate()
75{
76 if (m_fullIncubator) {
77 m_fullIncubator->clear();
78 delete m_fullIncubator;
79 }
80 if (m_iconIncubator) {
81 m_iconIncubator->clear();
82 delete m_iconIncubator;
83 }
84 if (m_full) {
85 m_full->disconnect(receiver: this);
86 delete m_full;
87 }
88 if (m_icon) {
89 m_icon->disconnect(receiver: this);
90 delete m_icon;
91 }
92}
93
94QObject *ToolBarLayoutDelegate::action() const
95{
96 return m_action;
97}
98
99void ToolBarLayoutDelegate::setAction(QObject *action)
100{
101 if (action == m_action) {
102 return;
103 }
104
105 if (m_action) {
106 QObject::disconnect(sender: m_action, SIGNAL(visibleChanged()), receiver: this, SLOT(actionVisibleChanged()));
107 QObject::disconnect(sender: m_action, SIGNAL(displayHintChanged()), receiver: this, SLOT(displayHintChanged()));
108 }
109
110 m_action = action;
111 if (m_action) {
112 if (m_action->property(name: "visible").isValid()) {
113 QObject::connect(sender: m_action, SIGNAL(visibleChanged()), receiver: this, SLOT(actionVisibleChanged()));
114 m_actionVisible = m_action->property(name: "visible").toBool();
115 }
116
117 if (m_action->property(name: "displayHint").isValid()) {
118 QObject::connect(sender: m_action, SIGNAL(displayHintChanged()), receiver: this, SLOT(displayHintChanged()));
119 m_displayHint = DisplayHint::DisplayHints{m_action->property(name: "displayHint").toInt()};
120 }
121 }
122}
123
124void ToolBarLayoutDelegate::createItems(QQmlComponent *fullComponent, QQmlComponent *iconComponent, std::function<void(QQuickItem *)> callback)
125{
126 m_fullIncubator = new ToolBarDelegateIncubator(fullComponent, qmlContext(fullComponent));
127 m_fullIncubator->setStateCallback(callback);
128 m_fullIncubator->setCompletedCallback([this](ToolBarDelegateIncubator *incubator) {
129 if (incubator->isError()) {
130 qCWarning(KirigamiLayoutsLog) << "Could not create delegate for ToolBarLayout";
131 const auto errors = incubator->errors();
132 for (const auto &error : errors) {
133 qCWarning(KirigamiLayoutsLog) << error;
134 }
135 return;
136 }
137
138 m_full = qobject_cast<QQuickItem *>(o: incubator->object());
139 m_full->setVisible(false);
140 connect(sender: m_full, signal: &QQuickItem::implicitWidthChanged, context: this, slot: &ToolBarLayoutDelegate::triggerRelayout);
141 connect(sender: m_full, signal: &QQuickItem::implicitHeightChanged, context: this, slot: &ToolBarLayoutDelegate::triggerRelayout);
142 connect(sender: m_full, signal: &QQuickItem::visibleChanged, context: this, slot: &ToolBarLayoutDelegate::ensureItemVisibility);
143
144 if (m_icon) {
145 m_ready = true;
146 }
147
148 m_parent->relayout();
149
150 QMetaObject::invokeMethod(object: this, function: &ToolBarLayoutDelegate::cleanupIncubators, type: Qt::QueuedConnection);
151 });
152 m_iconIncubator = new ToolBarDelegateIncubator(iconComponent, qmlContext(iconComponent));
153 m_iconIncubator->setStateCallback(callback);
154 m_iconIncubator->setCompletedCallback([this](ToolBarDelegateIncubator *incubator) {
155 if (incubator->isError()) {
156 qCWarning(KirigamiLayoutsLog) << "Could not create delegate for ToolBarLayout";
157 const auto errors = incubator->errors();
158 for (const auto &error : errors) {
159 qCWarning(KirigamiLayoutsLog) << error;
160 }
161 return;
162 }
163
164 m_icon = qobject_cast<QQuickItem *>(o: incubator->object());
165 m_icon->setVisible(false);
166 connect(sender: m_icon, signal: &QQuickItem::implicitWidthChanged, context: this, slot: &ToolBarLayoutDelegate::triggerRelayout);
167 connect(sender: m_icon, signal: &QQuickItem::implicitHeightChanged, context: this, slot: &ToolBarLayoutDelegate::triggerRelayout);
168 connect(sender: m_icon, signal: &QQuickItem::visibleChanged, context: this, slot: &ToolBarLayoutDelegate::ensureItemVisibility);
169
170 if (m_full) {
171 m_ready = true;
172 }
173
174 m_parent->relayout();
175
176 QMetaObject::invokeMethod(object: this, function: &ToolBarLayoutDelegate::cleanupIncubators, type: Qt::QueuedConnection);
177 });
178
179 m_fullIncubator->create();
180 m_iconIncubator->create();
181
182 // If we are in a visible parent and the action doesn't want to always be hidden, create it immadiately,
183 // in order to not have a frame be drawn with an empty toolbar
184 if (m_parent->isVisible() && isActionVisible() && !isHidden()) {
185 forceCompletion();
186 }
187}
188
189bool ToolBarLayoutDelegate::isReady() const
190{
191 return m_ready;
192}
193
194bool ToolBarLayoutDelegate::isActionVisible() const
195{
196 return m_actionVisible;
197}
198
199bool ToolBarLayoutDelegate::isHidden() const
200{
201 return DisplayHint::isDisplayHintSet(values: m_displayHint, hint: DisplayHint::AlwaysHide);
202}
203
204bool ToolBarLayoutDelegate::isIconOnly() const
205{
206 return DisplayHint::isDisplayHintSet(values: m_displayHint, hint: DisplayHint::IconOnly);
207}
208
209bool ToolBarLayoutDelegate::isKeepVisible() const
210{
211 return DisplayHint::isDisplayHintSet(values: m_displayHint, hint: DisplayHint::KeepVisible);
212}
213
214bool ToolBarLayoutDelegate::isVisible() const
215{
216 return m_iconVisible || m_fullVisible;
217}
218
219void ToolBarLayoutDelegate::hide()
220{
221 m_iconVisible = false;
222 m_fullVisible = false;
223 ensureItemVisibility();
224}
225
226void ToolBarLayoutDelegate::showFull()
227{
228 m_iconVisible = false;
229 m_fullVisible = true;
230}
231
232void ToolBarLayoutDelegate::showIcon()
233{
234 m_iconVisible = true;
235 m_fullVisible = false;
236}
237
238void ToolBarLayoutDelegate::show()
239{
240 ensureItemVisibility();
241}
242
243void ToolBarLayoutDelegate::forceCompletion()
244{
245 if (m_iconIncubator) {
246 m_iconIncubator->forceCompletion();
247 }
248 if (m_fullIncubator) {
249 m_fullIncubator->forceCompletion();
250 }
251 m_ready = true;
252}
253
254void ToolBarLayoutDelegate::setPosition(qreal x, qreal y)
255{
256 m_full->setX(x);
257 m_icon->setX(x);
258 m_full->setY(y);
259 m_icon->setY(y);
260}
261
262void ToolBarLayoutDelegate::setHeight(qreal height)
263{
264 m_full->setHeight(height);
265 m_icon->setHeight(height);
266}
267
268void ToolBarLayoutDelegate::resetHeight()
269{
270 m_full->resetHeight();
271 m_icon->resetHeight();
272}
273
274qreal ToolBarLayoutDelegate::width() const
275{
276 if (m_iconVisible) {
277 return m_icon->width();
278 }
279 return m_full->width();
280}
281
282qreal ToolBarLayoutDelegate::height() const
283{
284 if (m_iconVisible) {
285 return m_icon->height();
286 }
287 return m_full->height();
288}
289
290qreal ToolBarLayoutDelegate::implicitWidth() const
291{
292 if (m_iconVisible) {
293 return m_icon->implicitWidth();
294 }
295 return m_full->implicitWidth();
296}
297
298qreal ToolBarLayoutDelegate::implicitHeight() const
299{
300 if (m_iconVisible) {
301 return m_icon->implicitHeight();
302 }
303 return m_full->implicitHeight();
304}
305
306qreal ToolBarLayoutDelegate::maxHeight() const
307{
308 return std::max(a: m_full->implicitHeight(), b: m_icon->implicitHeight());
309}
310
311qreal ToolBarLayoutDelegate::iconWidth() const
312{
313 return m_icon->width();
314}
315
316qreal ToolBarLayoutDelegate::fullWidth() const
317{
318 return m_full->width();
319}
320
321void ToolBarLayoutDelegate::actionVisibleChanged()
322{
323 m_actionVisible = m_action->property(name: "visible").toBool();
324 m_parent->relayout();
325}
326
327void ToolBarLayoutDelegate::displayHintChanged()
328{
329 m_displayHint = DisplayHint::DisplayHints{m_action->property(name: "displayHint").toInt()};
330 m_parent->relayout();
331}
332
333void ToolBarLayoutDelegate::cleanupIncubators()
334{
335 if (m_fullIncubator && m_fullIncubator->isFinished()) {
336 delete m_fullIncubator;
337 m_fullIncubator = nullptr;
338 }
339
340 if (m_iconIncubator && m_iconIncubator->isFinished()) {
341 delete m_iconIncubator;
342 m_iconIncubator = nullptr;
343 }
344}
345
346void ToolBarLayoutDelegate::triggerRelayout()
347{
348 m_parent->relayout();
349}
350
351#include "moc_toolbarlayoutdelegate.cpp"
352

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