34QQuickItem *QQuickFolderBreadcrumbBarPrivate::createDelegateItem(QQmlComponent *component,
const QVariantMap &initialProperties)
36 Q_Q(QQuickFolderBreadcrumbBar);
39 QQmlContext *context = component->creationContext();
43 context = qmlContext(q);
47 if (!component->isBound() && initialProperties.isEmpty()) {
48 context =
new QQmlContext(context, q);
49 context->setContextObject(q);
52 QQuickItem *item = qobject_cast<QQuickItem*>(component->createWithInitialProperties(initialProperties, context));
54 QQml_setParent_noEvent(item, q);
55 qCDebug(lcDelegates) <<
"- created delegate item" << item <<
"with initialProperties" << initialProperties;
59QString QQuickFolderBreadcrumbBarPrivate::folderBaseName(
const QString &folderPath)
61 if (folderPath == QLatin1String(
"/")) {
64 }
else if (folderPath.endsWith(QLatin1String(
":/"))) {
66 return folderPath.mid(0, folderPath.size() - 1);
68 const QString baseName = folderPath.mid(folderPath.lastIndexOf(QLatin1Char(
'/')) + 1);
89void QQuickFolderBreadcrumbBarPrivate::repopulate()
91 Q_Q(QQuickFolderBreadcrumbBar);
92 qCDebug(lcDelegates) <<
"attemping to repopulate breadcrumb bar using folder...";
97 if (!buttonDelegate || !separatorDelegate || !q->contentItem()) {
98 qCWarning(lcDelegates) <<
"Both delegates and contentItem must be set before repopulating";
102 QScopedValueRollback repopulateGuard(repopulating,
true);
104 auto failureCleanup = [
this, q](){
106 while (q->count() > 0)
107 q->removeItem(q->itemAt(0));
110 qCDebug(lcDelegates) <<
"- getting paths for directory" << dialogFolder();
111 folderPaths = crumbPathsForFolder(dialogFolder());
113 while (q->count() > 0)
114 q->removeItem(q->itemAt(0));
116 for (
int i = 0; i < folderPaths.size(); ++i) {
117 const QString &folderPath = folderPaths.at(i);
119 QVariantMap initialProperties = {
120 { QStringLiteral(
"index"), QVariant::fromValue(i) },
121 { QStringLiteral(
"folderName"), QVariant::fromValue(folderBaseName(folderPath)) }
123 QQuickItem *buttonItem = createDelegateItem(buttonDelegate, initialProperties);
125 qCWarning(lcDelegates) <<
"Failed creating breadcrumb buttonDelegate item:\n" << buttonDelegate->errorString();
129 if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton*>(buttonItem)) {
130 QObjectPrivate::connect(button, &QQuickAbstractButton::clicked,
131 this, &QQuickFolderBreadcrumbBarPrivate::crumbClicked);
133 insertItem(q->count(), buttonItem);
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();
144 insertItem(q->count(), separatorItem);
148 const int finalCount = q->count();
151 const int newCurrentIndex = finalCount > 2 ? finalCount - 1 : -1;
152 qCDebug(lcDelegates) <<
"- setting currentIndex to" << newCurrentIndex;
153 q->setCurrentIndex(newCurrentIndex);
155 updateImplicitContentSize();
157 qCDebug(lcDelegates) <<
"... bar now contains" << q->count()
158 <<
"buttons and separators in total, for the following paths:" << folderPaths;
161void QQuickFolderBreadcrumbBarPrivate::crumbClicked()
163 Q_Q(QQuickFolderBreadcrumbBar);
164 qCDebug(lcCurrentItem) <<
"updateCurrentIndex called by sender" << q->sender();
165 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton*>(q->sender());
167 const int buttonIndex = contentModel->indexOf(button,
nullptr);
168 q->setCurrentIndex(buttonIndex);
169 const QUrl folderUrl = QUrl::fromLocalFile(folderPaths.at(buttonIndex / 2));
171 qCDebug(lcCurrentItem) <<
"setting file dialog's folder to" << folderUrl;
172 setDialogFolder(folderUrl);
193void QQuickFolderBreadcrumbBarPrivate::executeUpButton(
bool complete)
195 Q_Q(QQuickFolderBreadcrumbBar);
196 if (upButton.wasExecuted())
199 if (!upButton || complete)
200 quickBeginDeferred(q, upButtonName(), upButton);
202 quickCompleteDeferred(q, upButtonName(), upButton);
223void QQuickFolderBreadcrumbBarPrivate::executeTextField(
bool complete)
225 Q_Q(QQuickFolderBreadcrumbBar);
226 if (textField.wasExecuted())
229 if (!textField || complete)
230 quickBeginDeferred(q, textFieldName(), textField);
232 quickCompleteDeferred(q, textFieldName(), textField);
235void QQuickFolderBreadcrumbBarPrivate::toggleTextFieldVisibility()
237 textField->setText(QQmlFile::urlToLocalFileOrQrc(dialogFolder()));
239 qCDebug(lcTextInput).nospace() <<
"text field visibility was " << textField->isVisible()
240 <<
"; setting it to " << !textField->isVisible();
241 textField->setVisible(!textField->isVisible());
243 if (textField->isVisible()) {
246 textField->forceActiveFocus(Qt::ShortcutFocusReason);
247 textField->selectAll();
253 contentItem->setVisible(!textField->isVisible());
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();
265void QQuickFolderBreadcrumbBarPrivate::textFieldAccepted()
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();
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;
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();
298 setDialogFolder(fileUrl);
301 qCDebug(lcTextInput) <<
"path entered is not valid; not setting file/folder";
309 if (dialog->isVisible())
310 toggleTextFieldVisibility();
355void QQuickFolderBreadcrumbBarPrivate::handleTextFieldHidden()
357#if QT_CONFIG(shortcut)
358 Q_Q(QQuickFolderBreadcrumbBar);
360 QGuiApplicationPrivate *appPrivate = QGuiApplicationPrivate::instance();
361 qCDebug(lcShortcuts) <<
"text field was hidden; grabbing edit path shortcut";
363 if (editPathToggleShortcutId == 0) {
364 editPathToggleShortcutId = appPrivate->shortcutMap.addShortcut(
365 q, Qt::CTRL | Qt::Key_L, Qt::WindowShortcut, QQuickShortcutContext::matcher);
368 qCDebug(lcShortcuts).nospace() <<
"... editPathToggleShortcutId=" << editPathToggleShortcutId;
372void QQuickFolderBreadcrumbBarPrivate::ungrabEditPathShortcut()
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;
411qreal QQuickFolderBreadcrumbBarPrivate::getContentWidth()
const
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);
419 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
420 if (!p->widthValid())
421 totalWidth += item->implicitWidth();
423 totalWidth += item->width();
426 qCDebug(lcContentSize) <<
"content width:" << totalWidth;
430qreal QQuickFolderBreadcrumbBarPrivate::getContentHeight()
const
432 Q_Q(
const QQuickFolderBreadcrumbBar);
433 const int count = contentModel->count();
435 for (
int i = 0; i < count; ++i) {
436 QQuickItem *item = q->itemAt(i);
438 maxHeight = qMax(maxHeight, item->implicitHeight());
440 qCDebug(lcContentSize) <<
"content height:" << maxHeight;
444void QQuickFolderBreadcrumbBarPrivate::resizeContent()
446 Q_Q(QQuickFolderBreadcrumbBar);
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()));
453 textField->setPosition(contentItem->position());
454 textField->setSize(contentItem->size());
459void QQuickFolderBreadcrumbBarPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change,
const QRectF &diff)
461 QQuickContainerPrivate::itemGeometryChanged(item, change, diff);
462 if (change.sizeChange())
463 updateImplicitContentSize();
489QQuickFolderBreadcrumbBar::QQuickFolderBreadcrumbBar(QQuickItem *parent)
490 : QQuickContainer(*(
new QQuickFolderBreadcrumbBarPrivate), parent)
492 Q_D(QQuickFolderBreadcrumbBar);
493 d->changeTypes |= QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
502void QQuickFolderBreadcrumbBar::setDialog(QQuickDialog *dialog)
504 Q_D(QQuickFolderBreadcrumbBar);
505 if (dialog == d->dialog)
509 if (
auto fileDialog = d->asFileDialog()) {
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);
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);
531 emit dialogChanged();
540void QQuickFolderBreadcrumbBar::setButtonDelegate(QQmlComponent *delegate)
542 Q_D(QQuickFolderBreadcrumbBar);
543 qCDebug(lcFolderBreadcrumbBar) <<
"setButtonDelegate called with" << delegate;
544 if (d->componentComplete) {
546 qCWarning(lcFolderBreadcrumbBar) <<
"BreadcrumbBar does not support setting delegates after component completion";
550 if (delegate == d->buttonDelegate)
553 d->buttonDelegate = delegate;
554 emit buttonDelegateChanged();
563void QQuickFolderBreadcrumbBar::setSeparatorDelegate(QQmlComponent *delegate)
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";
572 if (delegate == d->separatorDelegate)
575 d->separatorDelegate = delegate;
576 emit separatorDelegateChanged();
587void QQuickFolderBreadcrumbBar::setUpButton(QQuickAbstractButton *upButton)
589 Q_D(QQuickFolderBreadcrumbBar);
590 if (upButton == d->upButton)
593 if (!d->upButton.isExecuting())
597 QObjectPrivate::disconnect(d->upButton.data(), &QQuickAbstractButton::clicked,
598 d, &QQuickFolderBreadcrumbBarPrivate::goUp);
601 QQuickControlPrivate::hideOldItem(d->upButton);
602 d->upButton = upButton;
604 if (!d->upButton->parentItem())
605 d->upButton->setParentItem(
this);
607 QObjectPrivate::connect(d->upButton.data(), &QQuickAbstractButton::clicked,
608 d, &QQuickFolderBreadcrumbBarPrivate::goUp);
610 if (!d->upButton.isExecuting())
611 emit upButtonChanged();
620void QQuickFolderBreadcrumbBar::setUpButtonSpacing(
int upButtonSpacing)
622 Q_D(QQuickFolderBreadcrumbBar);
623 if (upButtonSpacing == d->upButtonSpacing)
626 d->upButtonSpacing = upButtonSpacing;
627 emit upButtonSpacingChanged();
636void QQuickFolderBreadcrumbBar::setTextField(QQuickTextField *textField)
638 Q_D(QQuickFolderBreadcrumbBar);
639 if (textField == d->textField)
642 if (!d->textField.isExecuting())
646 d->handleTextFieldHidden();
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);
657 QQuickControlPrivate::hideOldItem(d->textField);
658 d->textField = textField;
660 if (!d->textField->parentItem())
661 d->textField->setParentItem(
this);
663 d->textField->setVisible(
false);
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);
672 if (!d->textField.isExecuting())
673 emit textFieldChanged();
676bool QQuickFolderBreadcrumbBar::event(QEvent *event)
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();
685 }
else if (shortcutEvent->shortcutId() == d->goUpShortcutId) {
690 return QQuickItem::event(event);
693void QQuickFolderBreadcrumbBar::keyPressEvent(QKeyEvent *event)
695#if QT_CONFIG(shortcut)
696 Q_D(QQuickFolderBreadcrumbBar);
698 if (event->matches(QKeySequence::Cancel) && d->textField->isVisible()) {
699 d->toggleTextFieldVisibility();
704 QQuickContainer::keyPressEvent(event);
708void QQuickFolderBreadcrumbBar::componentComplete()
710 Q_D(QQuickFolderBreadcrumbBar);
711 qCDebug(lcFolderBreadcrumbBar) <<
"componentComplete";
712 QQuickContainer::componentComplete();
717 d->textFieldVisibleChanged();
721void QQuickFolderBreadcrumbBar::itemChange(QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData &data)
723 Q_D(QQuickFolderBreadcrumbBar);
724 QQuickContainer::itemChange(change, data);
726 if (change == QQuickItem::ItemVisibleHasChanged && isComponentComplete()) {
727 if (data.boolValue && d->dialog->isVisible()) {
729 d->handleTextFieldHidden();
731#if QT_CONFIG(shortcut)
732 d->goUpShortcutId = QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(
733 this, QKeySequence(Qt::ALT | Qt::Key_Up), Qt::WindowShortcut, QQuickShortcutContext::matcher);
739 d->textField->setVisible(
false);
743 d->contentItem->setVisible(
true);
746 d->ungrabEditPathShortcut();
748#if QT_CONFIG(shortcut)
749 if (d->goUpShortcutId != 0) {
750 QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(d->goUpShortcutId,
this);
751 d->goUpShortcutId = 0;