Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickfolderbreadcrumbbar.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7
8#include <QtCore/qdir.h>
9#include <QtCore/qloggingcategory.h>
10#if QT_CONFIG(shortcut)
11#include <QtGui/private/qshortcutmap_p.h>
12#endif
13#include <QtGui/private/qguiapplication_p.h>
14#include <QtQml/QQmlFile>
15#if QT_CONFIG(accessibility)
16#include <QtQuick/private/qquickaccessibleattached_p.h>
17#endif
18#include <QtQuick/private/qquicktextinput_p.h>
19#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
20#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
21#include <QtQuickTemplates2/private/qquickpopupitem_p_p.h>
22#include <QtQuickTemplates2/private/qquickshortcutcontext_p_p.h>
23
28
30
31Q_STATIC_LOGGING_CATEGORY(lcFolderBreadcrumbBar, "qt.quick.dialogs.folderbreadcrumbbar")
32Q_STATIC_LOGGING_CATEGORY(lcContentSize, "qt.quick.dialogs.folderbreadcrumbbar.contentsize")
33Q_STATIC_LOGGING_CATEGORY(lcDelegates, "qt.quick.dialogs.folderbreadcrumbbar.delegates")
34Q_STATIC_LOGGING_CATEGORY(lcShortcuts, "qt.quick.dialogs.folderbreadcrumbbar.shortcuts")
35Q_STATIC_LOGGING_CATEGORY(lcTextInput, "qt.quick.dialogs.folderbreadcrumbbar.textinput")
36Q_STATIC_LOGGING_CATEGORY(lcCurrentItem, "qt.quick.dialogs.folderbreadcrumbbar.currentitem")
37
38QQuickItem *QQuickFolderBreadcrumbBarPrivate::createDelegateItem(QQmlComponent *component, const QVariantMap &initialProperties)
39{
40 Q_Q(QQuickFolderBreadcrumbBar);
41 // If we don't use the correct context, it won't be possible to refer to
42 // the control's id from within the delegates.
43 QQmlContext *context = component->creationContext();
44 // The component might not have been created in QML, in which case
45 // the creation context will be null and we have to create it ourselves.
46 if (!context)
47 context = qmlContext(q);
48
49 // If we have initial properties we assume that all necessary information is passed via
50 // initial properties.
51 if (!component->isBound() && initialProperties.isEmpty()) {
52 context = new QQmlContext(context, q);
53 context->setContextObject(q);
54 }
55
56 QQuickItem *item = qobject_cast<QQuickItem*>(component->createWithInitialProperties(initialProperties, context));
57 if (item)
58 QQml_setParent_noEvent(item, q);
59 qCDebug(lcDelegates) << "- created delegate item" << item << "with initialProperties" << initialProperties;
60 return item;
61}
62
63QString QQuickFolderBreadcrumbBarPrivate::folderBaseName(const QString &folderPath)
64{
65 if (folderPath == QLatin1String("/")) {
66 // Unix root.
67 return folderPath;
68 } else if (folderPath.endsWith(QLatin1String(":/"))) {
69 // Windows drive.
70 return folderPath.mid(0, folderPath.size() - 1);
71 }
72 const QString baseName = folderPath.mid(folderPath.lastIndexOf(QLatin1Char('/')) + 1);
73 return baseName;
74}
75
76/*!
77 \internal
78
79 Returns \c { "/foo", "/foo/bar", "/foo/bar/baz" } if \a folder is \c "/foo/bar/baz".
80*/
81QStringList QQuickFolderBreadcrumbBarPrivate::crumbPathsForFolder(const QUrl &folder)
82{
83 const QString folderPath = QDir::fromNativeSeparators(QQmlFile::urlToLocalFileOrQrc(folder));
84 QDir dir(folderPath);
85 // In order to collect the paths for each breadcrumb, we need to work backwards, so we prepend.
86 QStringList paths;
87 do {
88 paths.prepend(dir.absolutePath());
89 } while (dir.cdUp());
90 return paths;
91}
92
93void QQuickFolderBreadcrumbBarPrivate::repopulate()
94{
95 Q_Q(QQuickFolderBreadcrumbBar);
96 qCDebug(lcDelegates) << "attemping to repopulate breadcrumb bar using folder...";
97
98 if (repopulating)
99 return;
100
101 if (!buttonDelegate || !separatorDelegate || !q->contentItem()) {
102 qCWarning(lcDelegates) << "Both delegates and contentItem must be set before repopulating";
103 return;
104 }
105
106 QScopedValueRollback repopulateGuard(repopulating, true);
107
108 auto failureCleanup = [this, q](){
109 folderPaths.clear();
110 while (q->count() > 0)
111 q->removeItem(q->itemAt(0));
112 };
113
114 qCDebug(lcDelegates) << "- getting paths for directory" << dialogFolder();
115 folderPaths = crumbPathsForFolder(dialogFolder());
116
117 while (q->count() > 0)
118 q->removeItem(q->itemAt(0));
119
120 for (int i = 0; i < folderPaths.size(); ++i) {
121 const QString &folderPath = folderPaths.at(i);
122
123 QVariantMap initialProperties = {
124 { QStringLiteral("index"), QVariant::fromValue(i) },
125 { QStringLiteral("folderName"), QVariant::fromValue(folderBaseName(folderPath)) }
126 };
127 QQuickItem *buttonItem = createDelegateItem(buttonDelegate, initialProperties);
128 if (!buttonItem) {
129 qCWarning(lcDelegates) << "Failed creating breadcrumb buttonDelegate item:\n" << buttonDelegate->errorString();
130 failureCleanup();
131 break;
132 }
133 if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton*>(buttonItem)) {
134 QObjectPrivate::connect(button, &QQuickAbstractButton::clicked,
135 this, &QQuickFolderBreadcrumbBarPrivate::crumbClicked);
136 }
137 insertItem(q->count(), buttonItem);
138
139 // Don't add a separator for the last button.
140 if (i < folderPaths.size() - 1) {
141 initialProperties = {};
142 QQuickItem *separatorItem = createDelegateItem(separatorDelegate, initialProperties);
143 if (!separatorItem) {
144 qCWarning(lcDelegates) << "Failed creating breadcrumb separatorDelegate item:\n" << buttonDelegate->errorString();
145 failureCleanup();
146 break;
147 }
148 insertItem(q->count(), separatorItem);
149 }
150 }
151
152 const int finalCount = q->count();
153 // We would do - 2, since separators are included in the count,
154 // but as we don't add a separator for the last button, we only need to subtract 1.
155 const int newCurrentIndex = finalCount > 2 ? finalCount - 1 : -1;
156 qCDebug(lcDelegates) << "- setting currentIndex to" << newCurrentIndex;
157 q->setCurrentIndex(newCurrentIndex);
158
159 updateImplicitContentSize();
160
161 qCDebug(lcDelegates) << "... bar now contains" << q->count()
162 << "buttons and separators in total, for the following paths:" << folderPaths;
163}
164
165void QQuickFolderBreadcrumbBarPrivate::crumbClicked()
166{
167 Q_Q(QQuickFolderBreadcrumbBar);
168 qCDebug(lcCurrentItem) << "updateCurrentIndex called by sender" << q->sender();
169 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton*>(q->sender());
170 if (button) {
171 const int buttonIndex = contentModel->indexOf(button, nullptr);
172 q->setCurrentIndex(buttonIndex);
173 const QUrl folderUrl = QUrl::fromLocalFile(folderPaths.at(buttonIndex / 2));
174 // TODO: don't repopulate the whole model when clicking on crumbs
175 qCDebug(lcCurrentItem) << "setting file dialog's folder to" << folderUrl;
176 setDialogFolder(folderUrl);
177 }
178}
179
180void QQuickFolderBreadcrumbBarPrivate::folderChanged()
181{
182 if (componentComplete)
183 repopulate();
184}
185
186static inline QString upButtonName()
187{
188 return QStringLiteral("upButton");
189}
190
191void QQuickFolderBreadcrumbBarPrivate::cancelUpButton()
192{
193 Q_Q(QQuickFolderBreadcrumbBar);
194 quickCancelDeferred(q, upButtonName());
195}
196
197void QQuickFolderBreadcrumbBarPrivate::executeUpButton(bool complete)
198{
199 Q_Q(QQuickFolderBreadcrumbBar);
200 if (upButton.wasExecuted())
201 return;
202
203 if (!upButton || complete)
204 quickBeginDeferred(q, upButtonName(), upButton);
205 if (complete)
206 quickCompleteDeferred(q, upButtonName(), upButton);
207}
208
209void QQuickFolderBreadcrumbBarPrivate::goUp()
210{
211 QDir dir(QQmlFile::urlToLocalFileOrQrc(dialogFolder()));
212 dir.cdUp();
213 setDialogFolder(QUrl::fromLocalFile(dir.absolutePath()));
214}
215
216static inline QString textFieldName()
217{
218 return QStringLiteral("textField");
219}
220
221void QQuickFolderBreadcrumbBarPrivate::cancelTextField()
222{
223 Q_Q(QQuickFolderBreadcrumbBar);
224 quickCancelDeferred(q, textFieldName());
225}
226
227void QQuickFolderBreadcrumbBarPrivate::executeTextField(bool complete)
228{
229 Q_Q(QQuickFolderBreadcrumbBar);
230 if (textField.wasExecuted())
231 return;
232
233 if (!textField || complete)
234 quickBeginDeferred(q, textFieldName(), textField);
235 if (complete)
236 quickCompleteDeferred(q, textFieldName(), textField);
237}
238
239void QQuickFolderBreadcrumbBarPrivate::toggleTextFieldVisibility()
240{
241 textField->setText(QQmlFile::urlToLocalFileOrQrc(dialogFolder()));
242
243 qCDebug(lcTextInput).nospace() << "text field visibility was " << textField->isVisible()
244 << "; setting it to " << !textField->isVisible();
245 textField->setVisible(!textField->isVisible());
246
247 if (textField->isVisible()) {
248 // The text field is now visible, so give it focus,
249 // select the text, and let it handle escape/back.
250 textField->forceActiveFocus(Qt::ShortcutFocusReason);
251 textField->selectAll();
252 }
253
254 // We connect to the TextField's visibleChanged signal, so textFieldVisibleChanged()
255 // will get called automatically and we don't need to call it here.
256
257 contentItem->setVisible(!textField->isVisible());
258
259 // When the TextField is visible, certain items in the dialog need to be disabled.
260 if (auto fileDialog = asFileDialog()) {
261 auto fileDialogPrivate = QQuickFileDialogImplPrivate::get(fileDialog);
262 fileDialogPrivate->updateEnabled();
263 } else if (auto folderDialog = asFolderDialog()) {
264 auto folderDialogPrivate = QQuickFolderDialogImplPrivate::get(folderDialog);
265 folderDialogPrivate->updateEnabled();
266 }
267}
268
269void QQuickFolderBreadcrumbBarPrivate::textFieldAccepted()
270{
271 const QUrl fileUrl = QUrl::fromLocalFile(textField->text());
272 const auto fileDialog = asFileDialog();
273 const bool mustExist = fileDialog ? fileDialog->options()->acceptMode() != QFileDialogOptions::AcceptSave : true;
274 const bool enteredPathIsValidUrl = fileUrl.isValid();
275 bool enteredPathExists = false;
276 bool enteredPathIsDir = false;
277 if (enteredPathIsValidUrl) {
278 const QFileInfo fileInfo(textField->text());
279 enteredPathExists = fileInfo.exists();
280 if (enteredPathExists)
281 enteredPathIsDir = fileInfo.isDir();
282 }
283
284 qCDebug(lcTextInput).nospace() << "text field accepted -"
285 << " text=" << textField->text()
286 << " fileUrl=" << fileUrl
287 << " mustExist=" << mustExist
288 << " enteredPathIsValidUrl=" << enteredPathIsValidUrl
289 << " enteredPathExists=" << enteredPathExists
290 << " enteredPathIsDir=" << enteredPathIsDir;
291
292 if (enteredPathIsDir && (enteredPathExists || !mustExist)) {
293 qCDebug(lcTextInput) << "path entered is a folder; setting folder";
294 setDialogFolder(fileUrl);
295 } else if (!enteredPathIsDir && (enteredPathExists || !mustExist)) {
296 qCDebug(lcTextInput) << "path entered is a file; setting file and calling accept()";
297 if (isFileDialog()) {
298 auto fileDialog = asFileDialog();
299 fileDialog->setSelectedFile(fileUrl);
300 fileDialog->accept();
301 } else {
302 setDialogFolder(fileUrl);
303 }
304 } else {
305 qCDebug(lcTextInput) << "path entered is not valid; not setting file/folder";
306 }
307
308 // If the enter key was pressed and the dialog closed, the text input will lose
309 // active focus, and textFieldActiveFocusChanged() will toggle its visibility.
310 // We should only toggle visibility if the dialog is actually closed, otherwise
311 // we'll end up toggling twice, and the text input will be visible the next time
312 // the dialog is opened.
313 if (dialog->isVisible())
314 toggleTextFieldVisibility();
315}
316
317void QQuickFolderBreadcrumbBarPrivate::textFieldVisibleChanged()
318{
319 qCDebug(lcShortcuts) << "text field was either hidden or shown";
320
321 if (textField && textField->isVisible())
322 handleTextFieldShown();
323 else
324 handleTextFieldHidden();
325}
326
327void QQuickFolderBreadcrumbBarPrivate::textFieldActiveFocusChanged()
328{
329 qCDebug(lcTextInput) << "text field activeFocus changed to" << textField->hasActiveFocus();
330
331 // When the text field loses focus, it should be hidden.
332 if (!textField->hasActiveFocus() && textField->isVisible())
333 toggleTextFieldVisibility();
334}
335
336/*
337 When the text field is visible:
338
339 - Ctrl+L should do nothing (matches e.g. Ubuntu and Windows)
340 - Escape/back should hide it
341*/
342void QQuickFolderBreadcrumbBarPrivate::handleTextFieldShown()
343{
344#if QT_CONFIG(shortcut)
345 if (editPathToggleShortcutId == 0)
346 return;
347
348 qCDebug(lcShortcuts) << "text field was shown; ungrabbing edit path shortcut";
349 ungrabEditPathShortcut();
350#endif
351}
352
353/*
354 When the text field is not visible:
355
356 - Ctrl+L should make it visible
357 - Escape/back should close the dialog
358*/
359void QQuickFolderBreadcrumbBarPrivate::handleTextFieldHidden()
360{
361#if QT_CONFIG(shortcut)
362 Q_Q(QQuickFolderBreadcrumbBar);
363
364 QGuiApplicationPrivate *appPrivate = QGuiApplicationPrivate::instance();
365 qCDebug(lcShortcuts) << "text field was hidden; grabbing edit path shortcut";
366
367 if (editPathToggleShortcutId == 0) {
368 editPathToggleShortcutId = appPrivate->shortcutMap.addShortcut(
369 q, Qt::CTRL | Qt::Key_L, Qt::WindowShortcut, QQuickShortcutContext::matcher);
370 }
371
372 qCDebug(lcShortcuts).nospace() << "... editPathToggleShortcutId=" << editPathToggleShortcutId;
373#endif
374}
375
376void QQuickFolderBreadcrumbBarPrivate::ungrabEditPathShortcut()
377{
378#if QT_CONFIG(shortcut)
379 Q_Q(QQuickFolderBreadcrumbBar);
380 QGuiApplicationPrivate *appPrivate = QGuiApplicationPrivate::instance();
381 if (editPathToggleShortcutId != 0) {
382 appPrivate->shortcutMap.removeShortcut(editPathToggleShortcutId, q);
383 editPathToggleShortcutId = 0;
384 }
385#endif
386}
387
388QQuickFileDialogImpl *QQuickFolderBreadcrumbBarPrivate::asFileDialog() const
389{
390 return qobject_cast<QQuickFileDialogImpl*>(dialog);
391}
392
393QQuickFolderDialogImpl *QQuickFolderBreadcrumbBarPrivate::asFolderDialog() const
394{
395 return qobject_cast<QQuickFolderDialogImpl*>(dialog);
396}
397
398bool QQuickFolderBreadcrumbBarPrivate::isFileDialog() const
399{
400 return asFileDialog();
401}
402
403QUrl QQuickFolderBreadcrumbBarPrivate::dialogFolder() const
404{
405 return dialog->property("currentFolder").toUrl();
406}
407
408void QQuickFolderBreadcrumbBarPrivate::setDialogFolder(const QUrl &folder)
409{
410 Q_Q(QQuickFolderBreadcrumbBar);
411 if (!dialog->setProperty("currentFolder", folder))
412 qmlWarning(q) << "Failed to set currentFolder property of dialog" << dialog->objectName() << "to" << folder;
413}
414
415qreal QQuickFolderBreadcrumbBarPrivate::getContentWidth() const
416{
417 Q_Q(const QQuickFolderBreadcrumbBar);
418 const int count = contentModel->count();
419 qreal totalWidth = qMax(0, count - 1) * spacing;
420 for (int i = 0; i < count; ++i) {
421 QQuickItem *item = q->itemAt(i);
422 if (item) {
423 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
424 if (!p->widthValid())
425 totalWidth += item->implicitWidth();
426 else
427 totalWidth += item->width();
428 }
429 }
430 qCDebug(lcContentSize) << "content width:" << totalWidth;
431 return totalWidth;
432}
433
434qreal QQuickFolderBreadcrumbBarPrivate::getContentHeight() const
435{
436 Q_Q(const QQuickFolderBreadcrumbBar);
437 const int count = contentModel->count();
438 qreal maxHeight = 0;
439 for (int i = 0; i < count; ++i) {
440 QQuickItem *item = q->itemAt(i);
441 if (item)
442 maxHeight = qMax(maxHeight, item->implicitHeight());
443 }
444 qCDebug(lcContentSize) << "content height:" << maxHeight;
445 return maxHeight;
446}
447
448void QQuickFolderBreadcrumbBarPrivate::resizeContent()
449{
450 Q_Q(QQuickFolderBreadcrumbBar);
451 if (contentItem) {
452 const int upButtonSpace = q->upButton() ? q->upButton()->width() + upButtonSpacing : 0;
453 contentItem->setPosition(QPointF(q->leftPadding() + upButtonSpace, q->topPadding()));
454 contentItem->setSize(QSizeF(q->availableWidth() - upButtonSpace, q->availableHeight()));
455
456 if (textField) {
457 textField->setPosition(contentItem->position());
458 textField->setSize(contentItem->size());
459 }
460 }
461}
462
463void QQuickFolderBreadcrumbBarPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
464{
465 QQuickContainerPrivate::itemGeometryChanged(item, change, diff);
466 if (change.sizeChange())
467 updateImplicitContentSize();
468}
469
470void QQuickFolderBreadcrumbBarPrivate::itemImplicitWidthChanged(QQuickItem *item)
471{
472 QQuickContainerPrivate::itemImplicitWidthChanged(item);
473 if (item != contentItem)
474 updateImplicitContentWidth();
475}
476
477void QQuickFolderBreadcrumbBarPrivate::itemImplicitHeightChanged(QQuickItem *item)
478{
479 QQuickContainerPrivate::itemImplicitHeightChanged(item);
480 if (item != contentItem)
481 updateImplicitContentHeight();
482}
483
484/*!
485 \internal
486
487 Private class for breadcrumb navigation of a directory.
488
489 Given a FileDialog, FolderBreadCrumbbar creates breadcrumb buttons and
490 separators from the specified delegate components.
491*/
492
493QQuickFolderBreadcrumbBar::QQuickFolderBreadcrumbBar(QQuickItem *parent)
494 : QQuickContainer(*(new QQuickFolderBreadcrumbBarPrivate), parent)
495{
496 Q_D(QQuickFolderBreadcrumbBar);
497 d->changeTypes |= QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
498}
499
500QQuickDialog *QQuickFolderBreadcrumbBar::dialog() const
501{
502 Q_D(const QQuickFolderBreadcrumbBar);
503 return d->dialog;
504}
505
506void QQuickFolderBreadcrumbBar::setDialog(QQuickDialog *dialog)
507{
508 Q_D(QQuickFolderBreadcrumbBar);
509 if (dialog == d->dialog)
510 return;
511
512 if (d->dialog) {
513 if (auto fileDialog = d->asFileDialog()) {
514 // TODO: rename impl's currentFolder too, when name is decided
515 QObjectPrivate::disconnect(fileDialog, &QQuickFileDialogImpl::currentFolderChanged,
516 d, &QQuickFolderBreadcrumbBarPrivate::folderChanged);
517 } else if (auto folderDialog = d->asFolderDialog()) {
518 QObjectPrivate::disconnect(folderDialog, &QQuickFolderDialogImpl::currentFolderChanged,
519 d, &QQuickFolderBreadcrumbBarPrivate::folderChanged);
520 }
521 }
522
523 d->dialog = dialog;
524
525 if (d->dialog) {
526 if (auto fileDialog = d->asFileDialog()) {
527 QObjectPrivate::connect(fileDialog, &QQuickFileDialogImpl::currentFolderChanged,
528 d, &QQuickFolderBreadcrumbBarPrivate::folderChanged);
529 } else if (auto folderDialog = d->asFolderDialog()) {
530 QObjectPrivate::connect(folderDialog, &QQuickFolderDialogImpl::currentFolderChanged,
531 d, &QQuickFolderBreadcrumbBarPrivate::folderChanged);
532 }
533 }
534
535 emit dialogChanged();
536}
537
538QQmlComponent *QQuickFolderBreadcrumbBar::buttonDelegate()
539{
540 Q_D(QQuickFolderBreadcrumbBar);
541 return d->buttonDelegate;
542}
543
544void QQuickFolderBreadcrumbBar::setButtonDelegate(QQmlComponent *delegate)
545{
546 Q_D(QQuickFolderBreadcrumbBar);
547 qCDebug(lcFolderBreadcrumbBar) << "setButtonDelegate called with" << delegate;
548 if (d->componentComplete) {
549 // Simplify the code by disallowing this.
550 qCWarning(lcFolderBreadcrumbBar) << "BreadcrumbBar does not support setting delegates after component completion";
551 return;
552 }
553
554 if (delegate == d->buttonDelegate)
555 return;
556
557 d->buttonDelegate = delegate;
558 emit buttonDelegateChanged();
559}
560
561QQmlComponent *QQuickFolderBreadcrumbBar::separatorDelegate()
562{
563 Q_D(QQuickFolderBreadcrumbBar);
564 return d->separatorDelegate;
565}
566
567void QQuickFolderBreadcrumbBar::setSeparatorDelegate(QQmlComponent *delegate)
568{
569 Q_D(QQuickFolderBreadcrumbBar);
570 qCDebug(lcFolderBreadcrumbBar) << "setSeparatorDelegate called with" << delegate;
571 if (d->componentComplete) {
572 qCWarning(lcFolderBreadcrumbBar) << "BreadcrumbBar does not support setting delegates after component completion";
573 return;
574 }
575
576 if (delegate == d->separatorDelegate)
577 return;
578
579 d->separatorDelegate = delegate;
580 emit separatorDelegateChanged();
581}
582
583QQuickAbstractButton *QQuickFolderBreadcrumbBar::upButton()
584{
585 Q_D(QQuickFolderBreadcrumbBar);
586 if (!d->upButton)
587 d->executeUpButton();
588 return d->upButton;
589}
590
591void QQuickFolderBreadcrumbBar::setUpButton(QQuickAbstractButton *upButton)
592{
593 Q_D(QQuickFolderBreadcrumbBar);
594 if (upButton == d->upButton)
595 return;
596
597 if (!d->upButton.isExecuting())
598 d->cancelUpButton();
599
600 if (d->upButton) {
601 QObjectPrivate::disconnect(d->upButton.data(), &QQuickAbstractButton::clicked,
602 d, &QQuickFolderBreadcrumbBarPrivate::goUp);
603 }
604
605 QQuickControlPrivate::hideOldItem(d->upButton);
606 d->upButton = upButton;
607 if (d->upButton) {
608 if (!d->upButton->parentItem())
609 d->upButton->setParentItem(this);
610
611 QObjectPrivate::connect(d->upButton.data(), &QQuickAbstractButton::clicked,
612 d, &QQuickFolderBreadcrumbBarPrivate::goUp);
613#if QT_CONFIG(accessibility)
614 QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached*>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(upButton, true));
615 if (Q_LIKELY(accessibleAttached))
616 accessibleAttached->setName(QCoreApplication::translate("FileDialog", "Up"));
617#endif
618 }
619 if (!d->upButton.isExecuting())
620 emit upButtonChanged();
621}
622
623int QQuickFolderBreadcrumbBar::upButtonSpacing() const
624{
625 Q_D(const QQuickFolderBreadcrumbBar);
626 return d->upButtonSpacing;
627}
628
629void QQuickFolderBreadcrumbBar::setUpButtonSpacing(int upButtonSpacing)
630{
631 Q_D(QQuickFolderBreadcrumbBar);
632 if (upButtonSpacing == d->upButtonSpacing)
633 return;
634
635 d->upButtonSpacing = upButtonSpacing;
636 emit upButtonSpacingChanged();
637}
638
639QQuickTextField *QQuickFolderBreadcrumbBar::textField()
640{
641 Q_D(QQuickFolderBreadcrumbBar);
642 return d->textField;
643}
644
645void QQuickFolderBreadcrumbBar::setTextField(QQuickTextField *textField)
646{
647 Q_D(QQuickFolderBreadcrumbBar);
648 if (textField == d->textField)
649 return;
650
651 if (!d->textField.isExecuting())
652 d->cancelUpButton();
653
654 if (d->textField)
655 d->handleTextFieldHidden();
656
657 if (d->textField) {
658 QObjectPrivate::disconnect(d->textField.data(), &QQuickTextInput::visibleChanged,
659 d, &QQuickFolderBreadcrumbBarPrivate::textFieldVisibleChanged);
660 QObjectPrivate::disconnect(d->textField.data(), &QQuickTextInput::activeFocusChanged,
661 d, &QQuickFolderBreadcrumbBarPrivate::textFieldActiveFocusChanged);
662 QObjectPrivate::disconnect(d->textField.data(), &QQuickTextInput::accepted,
663 d, &QQuickFolderBreadcrumbBarPrivate::textFieldAccepted);
664 }
665
666 QQuickControlPrivate::hideOldItem(d->textField);
667 d->textField = textField;
668 if (d->textField) {
669 if (!d->textField->parentItem())
670 d->textField->setParentItem(this);
671
672 d->textField->setVisible(false);
673
674 QObjectPrivate::connect(d->textField.data(), &QQuickTextInput::visibleChanged,
675 d, &QQuickFolderBreadcrumbBarPrivate::textFieldVisibleChanged);
676 QObjectPrivate::connect(d->textField.data(), &QQuickTextInput::activeFocusChanged,
677 d, &QQuickFolderBreadcrumbBarPrivate::textFieldActiveFocusChanged);
678 QObjectPrivate::connect(d->textField.data(), &QQuickTextInput::accepted,
679 d, &QQuickFolderBreadcrumbBarPrivate::textFieldAccepted);
680 }
681 if (!d->textField.isExecuting())
682 emit textFieldChanged();
683}
684
685bool QQuickFolderBreadcrumbBar::event(QEvent *event)
686{
687#if QT_CONFIG(shortcut)
688 Q_D(QQuickFolderBreadcrumbBar);
689 if (event->type() == QEvent::Shortcut) {
690 QShortcutEvent *shortcutEvent = static_cast<QShortcutEvent *>(event);
691 if (shortcutEvent->shortcutId() == d->editPathToggleShortcutId) {
692 d->toggleTextFieldVisibility();
693 return true;
694 } else if (shortcutEvent->shortcutId() == d->goUpShortcutId) {
695 d->goUp();
696 }
697 }
698#endif
699 return QQuickItem::event(event);
700}
701
702void QQuickFolderBreadcrumbBar::keyPressEvent(QKeyEvent *event)
703{
704#if QT_CONFIG(shortcut)
705 Q_D(QQuickFolderBreadcrumbBar);
706
707 if (event->matches(QKeySequence::Cancel) && d->textField->isVisible()) {
708 d->toggleTextFieldVisibility();
709 event->accept();
710 } else
711#endif
712 {
713 QQuickContainer::keyPressEvent(event);
714 }
715}
716
717void QQuickFolderBreadcrumbBar::componentComplete()
718{
719 Q_D(QQuickFolderBreadcrumbBar);
720 qCDebug(lcFolderBreadcrumbBar) << "componentComplete";
721 QQuickContainer::componentComplete();
722 d->repopulate();
723
724 if (d->textField) {
725 // Force it to be updated as setTextField() is too early to do it.
726 d->textFieldVisibleChanged();
727 }
728}
729
730void QQuickFolderBreadcrumbBar::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
731{
732 Q_D(QQuickFolderBreadcrumbBar);
733 QQuickContainer::itemChange(change, data);
734
735 if (change == QQuickItem::ItemVisibleHasChanged && isComponentComplete()) {
736 if (data.boolValue && d->dialog->isVisible()) {
737 // It's visible.
738 d->handleTextFieldHidden();
739
740#if QT_CONFIG(shortcut)
741 d->goUpShortcutId = QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(
742 this, QKeySequence(Qt::ALT | Qt::Key_Up), Qt::WindowShortcut, QQuickShortcutContext::matcher);
743#endif
744 } else {
745 // It's hidden.
746 // Hide the text field so that when the dialog gets opened again, it's not still visible.
747 if (d->textField)
748 d->textField->setVisible(false);
749
750 // Make the ListView visible again.
751 if (d->contentItem)
752 d->contentItem->setVisible(true);
753
754 // We also need to ungrab the edit path shortcut when we're not visible.
755 d->ungrabEditPathShortcut();
756
757#if QT_CONFIG(shortcut)
758 if (d->goUpShortcutId != 0) {
759 QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(d->goUpShortcutId, this);
760 d->goUpShortcutId = 0;
761 }
762#endif
763 }
764 }
765}
766
767bool QQuickFolderBreadcrumbBar::isContent(QQuickItem *item) const
768{
769 if (!qmlContext(item))
770 return false;
771
772 if (QQuickItemPrivate::get(item)->isTransparentForPositioner())
773 return false;
774
775 return true;
776}
777
778QFont QQuickFolderBreadcrumbBar::defaultFont() const
779{
780 // TODO
781 return QQuickTheme::font(QQuickTheme::TabBar);
782}
783
784#if QT_CONFIG(accessibility)
785QAccessible::Role QQuickFolderBreadcrumbBar::accessibleRole() const
786{
787 // TODO
788 return QAccessible::PageTabList;
789}
790#endif
791
792QT_END_NAMESPACE
793
794#include "moc_qquickfolderbreadcrumbbar_p.cpp"
Combined button and popup list for selecting options.
static QString upButtonName()
static QString textFieldName()