38QQuickItem *QQuickFolderBreadcrumbBarPrivate::createDelegateItem(QQmlComponent *component,
const QVariantMap &initialProperties)
40 Q_Q(QQuickFolderBreadcrumbBar);
43 QQmlContext *context = component->creationContext();
47 context = qmlContext(q);
51 if (!component->isBound() && initialProperties.isEmpty()) {
52 context =
new QQmlContext(context, q);
53 context->setContextObject(q);
56 QQuickItem *item = qobject_cast<QQuickItem*>(component->createWithInitialProperties(initialProperties, context));
58 QQml_setParent_noEvent(item, q);
59 qCDebug(lcDelegates) <<
"- created delegate item" << item <<
"with initialProperties" << initialProperties;
63QString QQuickFolderBreadcrumbBarPrivate::folderBaseName(
const QString &folderPath)
65 if (folderPath == QLatin1String(
"/")) {
68 }
else if (folderPath.endsWith(QLatin1String(
":/"))) {
70 return folderPath.mid(0, folderPath.size() - 1);
72 const QString baseName = folderPath.mid(folderPath.lastIndexOf(QLatin1Char(
'/')) + 1);
93void QQuickFolderBreadcrumbBarPrivate::repopulate()
95 Q_Q(QQuickFolderBreadcrumbBar);
96 qCDebug(lcDelegates) <<
"attemping to repopulate breadcrumb bar using folder...";
101 if (!buttonDelegate || !separatorDelegate || !q->contentItem()) {
102 qCWarning(lcDelegates) <<
"Both delegates and contentItem must be set before repopulating";
106 QScopedValueRollback repopulateGuard(repopulating,
true);
108 auto failureCleanup = [
this, q](){
110 while (q->count() > 0)
111 q->removeItem(q->itemAt(0));
114 qCDebug(lcDelegates) <<
"- getting paths for directory" << dialogFolder();
115 folderPaths = crumbPathsForFolder(dialogFolder());
117 while (q->count() > 0)
118 q->removeItem(q->itemAt(0));
120 for (
int i = 0; i < folderPaths.size(); ++i) {
121 const QString &folderPath = folderPaths.at(i);
123 QVariantMap initialProperties = {
124 { QStringLiteral(
"index"), QVariant::fromValue(i) },
125 { QStringLiteral(
"folderName"), QVariant::fromValue(folderBaseName(folderPath)) }
127 QQuickItem *buttonItem = createDelegateItem(buttonDelegate, initialProperties);
129 qCWarning(lcDelegates) <<
"Failed creating breadcrumb buttonDelegate item:\n" << buttonDelegate->errorString();
133 if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton*>(buttonItem)) {
134 QObjectPrivate::connect(button, &QQuickAbstractButton::clicked,
135 this, &QQuickFolderBreadcrumbBarPrivate::crumbClicked);
137 insertItem(q->count(), buttonItem);
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();
148 insertItem(q->count(), separatorItem);
152 const int finalCount = q->count();
155 const int newCurrentIndex = finalCount > 2 ? finalCount - 1 : -1;
156 qCDebug(lcDelegates) <<
"- setting currentIndex to" << newCurrentIndex;
157 q->setCurrentIndex(newCurrentIndex);
159 updateImplicitContentSize();
161 qCDebug(lcDelegates) <<
"... bar now contains" << q->count()
162 <<
"buttons and separators in total, for the following paths:" << folderPaths;
165void QQuickFolderBreadcrumbBarPrivate::crumbClicked()
167 Q_Q(QQuickFolderBreadcrumbBar);
168 qCDebug(lcCurrentItem) <<
"updateCurrentIndex called by sender" << q->sender();
169 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton*>(q->sender());
171 const int buttonIndex = contentModel->indexOf(button,
nullptr);
172 q->setCurrentIndex(buttonIndex);
173 const QUrl folderUrl = QUrl::fromLocalFile(folderPaths.at(buttonIndex / 2));
175 qCDebug(lcCurrentItem) <<
"setting file dialog's folder to" << folderUrl;
176 setDialogFolder(folderUrl);
197void QQuickFolderBreadcrumbBarPrivate::executeUpButton(
bool complete)
199 Q_Q(QQuickFolderBreadcrumbBar);
200 if (upButton.wasExecuted())
203 if (!upButton || complete)
204 quickBeginDeferred(q, upButtonName(), upButton);
206 quickCompleteDeferred(q, upButtonName(), upButton);
227void QQuickFolderBreadcrumbBarPrivate::executeTextField(
bool complete)
229 Q_Q(QQuickFolderBreadcrumbBar);
230 if (textField.wasExecuted())
233 if (!textField || complete)
234 quickBeginDeferred(q, textFieldName(), textField);
236 quickCompleteDeferred(q, textFieldName(), textField);
239void QQuickFolderBreadcrumbBarPrivate::toggleTextFieldVisibility()
241 textField->setText(QQmlFile::urlToLocalFileOrQrc(dialogFolder()));
243 qCDebug(lcTextInput).nospace() <<
"text field visibility was " << textField->isVisible()
244 <<
"; setting it to " << !textField->isVisible();
245 textField->setVisible(!textField->isVisible());
247 if (textField->isVisible()) {
250 textField->forceActiveFocus(Qt::ShortcutFocusReason);
251 textField->selectAll();
257 contentItem->setVisible(!textField->isVisible());
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();
269void QQuickFolderBreadcrumbBarPrivate::textFieldAccepted()
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();
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;
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();
302 setDialogFolder(fileUrl);
305 qCDebug(lcTextInput) <<
"path entered is not valid; not setting file/folder";
313 if (dialog->isVisible())
314 toggleTextFieldVisibility();
359void QQuickFolderBreadcrumbBarPrivate::handleTextFieldHidden()
361#if QT_CONFIG(shortcut)
362 Q_Q(QQuickFolderBreadcrumbBar);
364 QGuiApplicationPrivate *appPrivate = QGuiApplicationPrivate::instance();
365 qCDebug(lcShortcuts) <<
"text field was hidden; grabbing edit path shortcut";
367 if (editPathToggleShortcutId == 0) {
368 editPathToggleShortcutId = appPrivate->shortcutMap.addShortcut(
369 q, Qt::CTRL | Qt::Key_L, Qt::WindowShortcut, QQuickShortcutContext::matcher);
372 qCDebug(lcShortcuts).nospace() <<
"... editPathToggleShortcutId=" << editPathToggleShortcutId;
376void QQuickFolderBreadcrumbBarPrivate::ungrabEditPathShortcut()
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;
415qreal QQuickFolderBreadcrumbBarPrivate::getContentWidth()
const
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);
423 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
424 if (!p->widthValid())
425 totalWidth += item->implicitWidth();
427 totalWidth += item->width();
430 qCDebug(lcContentSize) <<
"content width:" << totalWidth;
434qreal QQuickFolderBreadcrumbBarPrivate::getContentHeight()
const
436 Q_Q(
const QQuickFolderBreadcrumbBar);
437 const int count = contentModel->count();
439 for (
int i = 0; i < count; ++i) {
440 QQuickItem *item = q->itemAt(i);
442 maxHeight = qMax(maxHeight, item->implicitHeight());
444 qCDebug(lcContentSize) <<
"content height:" << maxHeight;
448void QQuickFolderBreadcrumbBarPrivate::resizeContent()
450 Q_Q(QQuickFolderBreadcrumbBar);
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()));
457 textField->setPosition(contentItem->position());
458 textField->setSize(contentItem->size());
463void QQuickFolderBreadcrumbBarPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change,
const QRectF &diff)
465 QQuickContainerPrivate::itemGeometryChanged(item, change, diff);
466 if (change.sizeChange())
467 updateImplicitContentSize();
493QQuickFolderBreadcrumbBar::QQuickFolderBreadcrumbBar(QQuickItem *parent)
494 : QQuickContainer(*(
new QQuickFolderBreadcrumbBarPrivate), parent)
496 Q_D(QQuickFolderBreadcrumbBar);
497 d->changeTypes |= QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
506void QQuickFolderBreadcrumbBar::setDialog(QQuickDialog *dialog)
508 Q_D(QQuickFolderBreadcrumbBar);
509 if (dialog == d->dialog)
513 if (
auto fileDialog = d->asFileDialog()) {
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);
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);
535 emit dialogChanged();
544void QQuickFolderBreadcrumbBar::setButtonDelegate(QQmlComponent *delegate)
546 Q_D(QQuickFolderBreadcrumbBar);
547 qCDebug(lcFolderBreadcrumbBar) <<
"setButtonDelegate called with" << delegate;
548 if (d->componentComplete) {
550 qCWarning(lcFolderBreadcrumbBar) <<
"BreadcrumbBar does not support setting delegates after component completion";
554 if (delegate == d->buttonDelegate)
557 d->buttonDelegate = delegate;
558 emit buttonDelegateChanged();
567void QQuickFolderBreadcrumbBar::setSeparatorDelegate(QQmlComponent *delegate)
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";
576 if (delegate == d->separatorDelegate)
579 d->separatorDelegate = delegate;
580 emit separatorDelegateChanged();
591void QQuickFolderBreadcrumbBar::setUpButton(QQuickAbstractButton *upButton)
593 Q_D(QQuickFolderBreadcrumbBar);
594 if (upButton == d->upButton)
597 if (!d->upButton.isExecuting())
601 QObjectPrivate::disconnect(d->upButton.data(), &QQuickAbstractButton::clicked,
602 d, &QQuickFolderBreadcrumbBarPrivate::goUp);
605 QQuickControlPrivate::hideOldItem(d->upButton);
606 d->upButton = upButton;
608 if (!d->upButton->parentItem())
609 d->upButton->setParentItem(
this);
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"));
619 if (!d->upButton.isExecuting())
620 emit upButtonChanged();
629void QQuickFolderBreadcrumbBar::setUpButtonSpacing(
int upButtonSpacing)
631 Q_D(QQuickFolderBreadcrumbBar);
632 if (upButtonSpacing == d->upButtonSpacing)
635 d->upButtonSpacing = upButtonSpacing;
636 emit upButtonSpacingChanged();
645void QQuickFolderBreadcrumbBar::setTextField(QQuickTextField *textField)
647 Q_D(QQuickFolderBreadcrumbBar);
648 if (textField == d->textField)
651 if (!d->textField.isExecuting())
655 d->handleTextFieldHidden();
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);
666 QQuickControlPrivate::hideOldItem(d->textField);
667 d->textField = textField;
669 if (!d->textField->parentItem())
670 d->textField->setParentItem(
this);
672 d->textField->setVisible(
false);
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);
681 if (!d->textField.isExecuting())
682 emit textFieldChanged();
685bool QQuickFolderBreadcrumbBar::event(QEvent *event)
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();
694 }
else if (shortcutEvent->shortcutId() == d->goUpShortcutId) {
699 return QQuickItem::event(event);
702void QQuickFolderBreadcrumbBar::keyPressEvent(QKeyEvent *event)
704#if QT_CONFIG(shortcut)
705 Q_D(QQuickFolderBreadcrumbBar);
707 if (event->matches(QKeySequence::Cancel) && d->textField->isVisible()) {
708 d->toggleTextFieldVisibility();
713 QQuickContainer::keyPressEvent(event);
717void QQuickFolderBreadcrumbBar::componentComplete()
719 Q_D(QQuickFolderBreadcrumbBar);
720 qCDebug(lcFolderBreadcrumbBar) <<
"componentComplete";
721 QQuickContainer::componentComplete();
726 d->textFieldVisibleChanged();
730void QQuickFolderBreadcrumbBar::itemChange(QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData &data)
732 Q_D(QQuickFolderBreadcrumbBar);
733 QQuickContainer::itemChange(change, data);
735 if (change == QQuickItem::ItemVisibleHasChanged && isComponentComplete()) {
736 if (data.boolValue && d->dialog->isVisible()) {
738 d->handleTextFieldHidden();
740#if QT_CONFIG(shortcut)
741 d->goUpShortcutId = QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(
742 this, QKeySequence(Qt::ALT | Qt::Key_Up), Qt::WindowShortcut, QQuickShortcutContext::matcher);
748 d->textField->setVisible(
false);
752 d->contentItem->setVisible(
true);
755 d->ungrabEditPathShortcut();
757#if QT_CONFIG(shortcut)
758 if (d->goUpShortcutId != 0) {
759 QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(d->goUpShortcutId,
this);
760 d->goUpShortcutId = 0;