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