Revision control
Copy as Markdown
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "qquickfolderbreadcrumbbar_p.h"
#include "qquickfolderbreadcrumbbar_p_p.h"
#include <QtCore/qdir.h>
#include <QtCore/qloggingcategory.h>
#if QT_CONFIG(shortcut)
#include <QtGui/private/qshortcutmap_p.h>
#endif
#include <QtGui/private/qguiapplication_p.h>
#include <QtQml/QQmlFile>
#if QT_CONFIG(accessibility)
#include <QtQuick/private/qquickaccessibleattached_p.h>
#endif
#include <QtQuick/private/qquicktextinput_p.h>
#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
#include <QtQuickTemplates2/private/qquickpopupitem_p_p.h>
#include <QtQuickTemplates2/private/qquickshortcutcontext_p_p.h>
#include "qquickfiledialogimpl_p.h"
#include "qquickfiledialogimpl_p_p.h"
#include "qquickfolderdialogimpl_p.h"
#include "qquickfolderdialogimpl_p_p.h"
QT_BEGIN_NAMESPACE
Q_STATIC_LOGGING_CATEGORY(lcFolderBreadcrumbBar, "qt.quick.dialogs.folderbreadcrumbbar")
Q_STATIC_LOGGING_CATEGORY(lcContentSize, "qt.quick.dialogs.folderbreadcrumbbar.contentsize")
Q_STATIC_LOGGING_CATEGORY(lcDelegates, "qt.quick.dialogs.folderbreadcrumbbar.delegates")
Q_STATIC_LOGGING_CATEGORY(lcShortcuts, "qt.quick.dialogs.folderbreadcrumbbar.shortcuts")
Q_STATIC_LOGGING_CATEGORY(lcTextInput, "qt.quick.dialogs.folderbreadcrumbbar.textinput")
Q_STATIC_LOGGING_CATEGORY(lcCurrentItem, "qt.quick.dialogs.folderbreadcrumbbar.currentitem")
QQuickItem *QQuickFolderBreadcrumbBarPrivate::createDelegateItem(QQmlComponent *component, const QVariantMap &initialProperties)
{
Q_Q(QQuickFolderBreadcrumbBar);
// If we don't use the correct context, it won't be possible to refer to
// the control's id from within the delegates.
QQmlContext *context = component->creationContext();
// The component might not have been created in QML, in which case
// the creation context will be null and we have to create it ourselves.
if (!context)
context = qmlContext(q);
// If we have initial properties we assume that all necessary information is passed via
// initial properties.
if (!component->isBound() && initialProperties.isEmpty()) {
context = new QQmlContext(context, q);
context->setContextObject(q);
}
QQuickItem *item = qobject_cast<QQuickItem*>(component->createWithInitialProperties(initialProperties, context));
if (item)
QQml_setParent_noEvent(item, q);
qCDebug(lcDelegates) << "- created delegate item" << item << "with initialProperties" << initialProperties;
return item;
}
QString QQuickFolderBreadcrumbBarPrivate::folderBaseName(const QString &folderPath)
{
if (folderPath == QLatin1String("/")) {
// Unix root.
return folderPath;
} else if (folderPath.endsWith(QLatin1String(":/"))) {
// Windows drive.
return folderPath.mid(0, folderPath.size() - 1);
}
const QString baseName = folderPath.mid(folderPath.lastIndexOf(QLatin1Char('/')) + 1);
return baseName;
}
/*!
\internal
Returns \c { "/foo", "/foo/bar", "/foo/bar/baz" } if \a folder is \c "/foo/bar/baz".
*/
QStringList QQuickFolderBreadcrumbBarPrivate::crumbPathsForFolder(const QUrl &folder)
{
const QString folderPath = QDir::fromNativeSeparators(QQmlFile::urlToLocalFileOrQrc(folder));
QDir dir(folderPath);
// In order to collect the paths for each breadcrumb, we need to work backwards, so we prepend.
QStringList paths;
do {
paths.prepend(dir.absolutePath());
} while (dir.cdUp());
return paths;
}
void QQuickFolderBreadcrumbBarPrivate::repopulate()
{
Q_Q(QQuickFolderBreadcrumbBar);
qCDebug(lcDelegates) << "attemping to repopulate breadcrumb bar using folder...";
if (repopulating)
return;
if (!buttonDelegate || !separatorDelegate || !q->contentItem()) {
qCWarning(lcDelegates) << "Both delegates and contentItem must be set before repopulating";
return;
}
QScopedValueRollback repopulateGuard(repopulating, true);
auto failureCleanup = [this, q](){
folderPaths.clear();
while (q->count() > 0)
q->removeItem(q->itemAt(0));
};
qCDebug(lcDelegates) << "- getting paths for directory" << dialogFolder();
folderPaths = crumbPathsForFolder(dialogFolder());
while (q->count() > 0)
q->removeItem(q->itemAt(0));
for (int i = 0; i < folderPaths.size(); ++i) {
const QString &folderPath = folderPaths.at(i);
QVariantMap initialProperties = {
{ QStringLiteral("index"), QVariant::fromValue(i) },
{ QStringLiteral("folderName"), QVariant::fromValue(folderBaseName(folderPath)) }
};
QQuickItem *buttonItem = createDelegateItem(buttonDelegate, initialProperties);
if (!buttonItem) {
qCWarning(lcDelegates) << "Failed creating breadcrumb buttonDelegate item:\n" << buttonDelegate->errorString();
failureCleanup();
break;
}
if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton*>(buttonItem)) {
QObjectPrivate::connect(button, &QQuickAbstractButton::clicked,
this, &QQuickFolderBreadcrumbBarPrivate::crumbClicked);
}
insertItem(q->count(), buttonItem);
// Don't add a separator for the last button.
if (i < folderPaths.size() - 1) {
initialProperties = {};
QQuickItem *separatorItem = createDelegateItem(separatorDelegate, initialProperties);
if (!separatorItem) {
qCWarning(lcDelegates) << "Failed creating breadcrumb separatorDelegate item:\n" << buttonDelegate->errorString();
failureCleanup();
break;
}
insertItem(q->count(), separatorItem);
}
}
const int finalCount = q->count();
// We would do - 2, since separators are included in the count,
// but as we don't add a separator for the last button, we only need to subtract 1.
const int newCurrentIndex = finalCount > 2 ? finalCount - 1 : -1;
qCDebug(lcDelegates) << "- setting currentIndex to" << newCurrentIndex;
q->setCurrentIndex(newCurrentIndex);
updateImplicitContentSize();
qCDebug(lcDelegates) << "... bar now contains" << q->count()
<< "buttons and separators in total, for the following paths:" << folderPaths;
}
void QQuickFolderBreadcrumbBarPrivate::crumbClicked()
{
Q_Q(QQuickFolderBreadcrumbBar);
qCDebug(lcCurrentItem) << "updateCurrentIndex called by sender" << q->sender();
QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton*>(q->sender());
if (button) {
const int buttonIndex = contentModel->indexOf(button, nullptr);
q->setCurrentIndex(buttonIndex);
const QUrl folderUrl = QUrl::fromLocalFile(folderPaths.at(buttonIndex / 2));
// TODO: don't repopulate the whole model when clicking on crumbs
qCDebug(lcCurrentItem) << "setting file dialog's folder to" << folderUrl;
setDialogFolder(folderUrl);
}
}
void QQuickFolderBreadcrumbBarPrivate::folderChanged()
{
if (componentComplete)
repopulate();
}
static inline QString upButtonName()
{
return QStringLiteral("upButton");
}
void QQuickFolderBreadcrumbBarPrivate::cancelUpButton()
{
Q_Q(QQuickFolderBreadcrumbBar);
quickCancelDeferred(q, upButtonName());
}
void QQuickFolderBreadcrumbBarPrivate::executeUpButton(bool complete)
{
Q_Q(QQuickFolderBreadcrumbBar);
if (upButton.wasExecuted())
return;
if (!upButton || complete)
quickBeginDeferred(q, upButtonName(), upButton);
if (complete)
quickCompleteDeferred(q, upButtonName(), upButton);
}
void QQuickFolderBreadcrumbBarPrivate::goUp()
{
QDir dir(QQmlFile::urlToLocalFileOrQrc(dialogFolder()));
dir.cdUp();
setDialogFolder(QUrl::fromLocalFile(dir.absolutePath()));
}
static inline QString textFieldName()
{
return QStringLiteral("textField");
}
void QQuickFolderBreadcrumbBarPrivate::cancelTextField()
{
Q_Q(QQuickFolderBreadcrumbBar);
quickCancelDeferred(q, textFieldName());
}
void QQuickFolderBreadcrumbBarPrivate::executeTextField(bool complete)
{
Q_Q(QQuickFolderBreadcrumbBar);
if (textField.wasExecuted())
return;
if (!textField || complete)
quickBeginDeferred(q, textFieldName(), textField);
if (complete)
quickCompleteDeferred(q, textFieldName(), textField);
}
void QQuickFolderBreadcrumbBarPrivate::toggleTextFieldVisibility()
{
textField->setText(QQmlFile::urlToLocalFileOrQrc(dialogFolder()));
qCDebug(lcTextInput).nospace() << "text field visibility was " << textField->isVisible()
<< "; setting it to " << !textField->isVisible();
textField->setVisible(!textField->isVisible());
if (textField->isVisible()) {
// The text field is now visible, so give it focus,
// select the text, and let it handle escape/back.
textField->forceActiveFocus(Qt::ShortcutFocusReason);
textField->selectAll();
}
// We connect to the TextField's visibleChanged signal, so textFieldVisibleChanged()
// will get called automatically and we don't need to call it here.
contentItem->setVisible(!textField->isVisible());
// When the TextField is visible, certain items in the dialog need to be disabled.
if (auto fileDialog = asFileDialog()) {
auto fileDialogPrivate = QQuickFileDialogImplPrivate::get(fileDialog);
fileDialogPrivate->updateEnabled();
} else if (auto folderDialog = asFolderDialog()) {
auto folderDialogPrivate = QQuickFolderDialogImplPrivate::get(folderDialog);
folderDialogPrivate->updateEnabled();
}
}
void QQuickFolderBreadcrumbBarPrivate::textFieldAccepted()
{
const QUrl fileUrl = QUrl::fromLocalFile(textField->text());
const auto fileDialog = asFileDialog();
const bool mustExist = fileDialog ? fileDialog->options()->acceptMode() != QFileDialogOptions::AcceptSave : true;
const bool enteredPathIsValidUrl = fileUrl.isValid();
bool enteredPathExists = false;
bool enteredPathIsDir = false;
if (enteredPathIsValidUrl) {
const QFileInfo fileInfo(textField->text());
enteredPathExists = fileInfo.exists();
if (enteredPathExists)
enteredPathIsDir = fileInfo.isDir();
}
qCDebug(lcTextInput).nospace() << "text field accepted -"
<< " text=" << textField->text()
<< " fileUrl=" << fileUrl
<< " mustExist=" << mustExist
<< " enteredPathIsValidUrl=" << enteredPathIsValidUrl
<< " enteredPathExists=" << enteredPathExists
<< " enteredPathIsDir=" << enteredPathIsDir;
if (enteredPathIsDir && (enteredPathExists || !mustExist)) {
qCDebug(lcTextInput) << "path entered is a folder; setting folder";
setDialogFolder(fileUrl);
} else if (!enteredPathIsDir && (enteredPathExists || !mustExist)) {
qCDebug(lcTextInput) << "path entered is a file; setting file and calling accept()";
if (isFileDialog()) {
auto fileDialog = asFileDialog();
fileDialog->setSelectedFile(fileUrl);
fileDialog->accept();
} else {
setDialogFolder(fileUrl);
}
} else {
qCDebug(lcTextInput) << "path entered is not valid; not setting file/folder";
}
// If the enter key was pressed and the dialog closed, the text input will lose
// active focus, and textFieldActiveFocusChanged() will toggle its visibility.
// We should only toggle visibility if the dialog is actually closed, otherwise
// we'll end up toggling twice, and the text input will be visible the next time
// the dialog is opened.
if (dialog->isVisible())
toggleTextFieldVisibility();
}
void QQuickFolderBreadcrumbBarPrivate::textFieldVisibleChanged()
{
qCDebug(lcShortcuts) << "text field was either hidden or shown";
if (textField && textField->isVisible())
handleTextFieldShown();
else
handleTextFieldHidden();
}
void QQuickFolderBreadcrumbBarPrivate::textFieldActiveFocusChanged()
{
qCDebug(lcTextInput) << "text field activeFocus changed to" << textField->hasActiveFocus();
// When the text field loses focus, it should be hidden.
if (!textField->hasActiveFocus() && textField->isVisible())
toggleTextFieldVisibility();
}
/*
When the text field is visible:
- Ctrl+L should do nothing (matches e.g. Ubuntu and Windows)
- Escape/back should hide it
*/
void QQuickFolderBreadcrumbBarPrivate::handleTextFieldShown()
{
#if QT_CONFIG(shortcut)
if (editPathToggleShortcutId == 0)
return;
qCDebug(lcShortcuts) << "text field was shown; ungrabbing edit path shortcut";
ungrabEditPathShortcut();
#endif
}
/*
When the text field is not visible:
- Ctrl+L should make it visible
- Escape/back should close the dialog
*/
void QQuickFolderBreadcrumbBarPrivate::handleTextFieldHidden()
{
#if QT_CONFIG(shortcut)
Q_Q(QQuickFolderBreadcrumbBar);
QGuiApplicationPrivate *appPrivate = QGuiApplicationPrivate::instance();
qCDebug(lcShortcuts) << "text field was hidden; grabbing edit path shortcut";
if (editPathToggleShortcutId == 0) {
editPathToggleShortcutId = appPrivate->shortcutMap.addShortcut(
q, Qt::CTRL | Qt::Key_L, Qt::WindowShortcut, QQuickShortcutContext::matcher);
}
qCDebug(lcShortcuts).nospace() << "... editPathToggleShortcutId=" << editPathToggleShortcutId;
#endif
}
void QQuickFolderBreadcrumbBarPrivate::ungrabEditPathShortcut()
{
#if QT_CONFIG(shortcut)
Q_Q(QQuickFolderBreadcrumbBar);
QGuiApplicationPrivate *appPrivate = QGuiApplicationPrivate::instance();
if (editPathToggleShortcutId != 0) {
appPrivate->shortcutMap.removeShortcut(editPathToggleShortcutId, q);
editPathToggleShortcutId = 0;
}
#endif
}
QQuickFileDialogImpl *QQuickFolderBreadcrumbBarPrivate::asFileDialog() const
{
return qobject_cast<QQuickFileDialogImpl*>(dialog);
}
QQuickFolderDialogImpl *QQuickFolderBreadcrumbBarPrivate::asFolderDialog() const
{
return qobject_cast<QQuickFolderDialogImpl*>(dialog);
}
bool QQuickFolderBreadcrumbBarPrivate::isFileDialog() const
{
return asFileDialog();
}
QUrl QQuickFolderBreadcrumbBarPrivate::dialogFolder() const
{
return dialog->property("currentFolder").toUrl();
}
void QQuickFolderBreadcrumbBarPrivate::setDialogFolder(const QUrl &folder)
{
Q_Q(QQuickFolderBreadcrumbBar);
if (!dialog->setProperty("currentFolder", folder))
qmlWarning(q) << "Failed to set currentFolder property of dialog" << dialog->objectName() << "to" << folder;
}
qreal QQuickFolderBreadcrumbBarPrivate::getContentWidth() const
{
Q_Q(const QQuickFolderBreadcrumbBar);
const int count = contentModel->count();
qreal totalWidth = qMax(0, count - 1) * spacing;
for (int i = 0; i < count; ++i) {
QQuickItem *item = q->itemAt(i);
if (item) {
QQuickItemPrivate *p = QQuickItemPrivate::get(item);
if (!p->widthValid())
totalWidth += item->implicitWidth();
else
totalWidth += item->width();
}
}
qCDebug(lcContentSize) << "content width:" << totalWidth;
return totalWidth;
}
qreal QQuickFolderBreadcrumbBarPrivate::getContentHeight() const
{
Q_Q(const QQuickFolderBreadcrumbBar);
const int count = contentModel->count();
qreal maxHeight = 0;
for (int i = 0; i < count; ++i) {
QQuickItem *item = q->itemAt(i);
if (item)
maxHeight = qMax(maxHeight, item->implicitHeight());
}
qCDebug(lcContentSize) << "content height:" << maxHeight;
return maxHeight;
}
void QQuickFolderBreadcrumbBarPrivate::resizeContent()
{
Q_Q(QQuickFolderBreadcrumbBar);
if (contentItem) {
const int upButtonSpace = q->upButton() ? q->upButton()->width() + upButtonSpacing : 0;
contentItem->setPosition(QPointF(q->leftPadding() + upButtonSpace, q->topPadding()));
contentItem->setSize(QSizeF(q->availableWidth() - upButtonSpace, q->availableHeight()));
if (textField) {
textField->setPosition(contentItem->position());
textField->setSize(contentItem->size());
}
}
}
void QQuickFolderBreadcrumbBarPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
{
QQuickContainerPrivate::itemGeometryChanged(item, change, diff);
if (change.sizeChange())
updateImplicitContentSize();
}
void QQuickFolderBreadcrumbBarPrivate::itemImplicitWidthChanged(QQuickItem *item)
{
QQuickContainerPrivate::itemImplicitWidthChanged(item);
if (item != contentItem)
updateImplicitContentWidth();
}
void QQuickFolderBreadcrumbBarPrivate::itemImplicitHeightChanged(QQuickItem *item)
{
QQuickContainerPrivate::itemImplicitHeightChanged(item);
if (item != contentItem)
updateImplicitContentHeight();
}
/*!
\internal
Private class for breadcrumb navigation of a directory.
Given a FileDialog, FolderBreadCrumbbar creates breadcrumb buttons and
separators from the specified delegate components.
*/
QQuickFolderBreadcrumbBar::QQuickFolderBreadcrumbBar(QQuickItem *parent)
: QQuickContainer(*(new QQuickFolderBreadcrumbBarPrivate), parent)
{
Q_D(QQuickFolderBreadcrumbBar);
d->changeTypes |= QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
}
QQuickDialog *QQuickFolderBreadcrumbBar::dialog() const
{
Q_D(const QQuickFolderBreadcrumbBar);
return d->dialog;
}
void QQuickFolderBreadcrumbBar::setDialog(QQuickDialog *dialog)
{
Q_D(QQuickFolderBreadcrumbBar);
if (dialog == d->dialog)
return;
if (d->dialog) {
if (auto fileDialog = d->asFileDialog()) {
// TODO: rename impl's currentFolder too, when name is decided
QObjectPrivate::disconnect(fileDialog, &QQuickFileDialogImpl::currentFolderChanged,
d, &QQuickFolderBreadcrumbBarPrivate::folderChanged);
} else if (auto folderDialog = d->asFolderDialog()) {
QObjectPrivate::disconnect(folderDialog, &QQuickFolderDialogImpl::currentFolderChanged,
d, &QQuickFolderBreadcrumbBarPrivate::folderChanged);
}
}
d->dialog = dialog;
if (d->dialog) {
if (auto fileDialog = d->asFileDialog()) {
QObjectPrivate::connect(fileDialog, &QQuickFileDialogImpl::currentFolderChanged,
d, &QQuickFolderBreadcrumbBarPrivate::folderChanged);
} else if (auto folderDialog = d->asFolderDialog()) {
QObjectPrivate::connect(folderDialog, &QQuickFolderDialogImpl::currentFolderChanged,
d, &QQuickFolderBreadcrumbBarPrivate::folderChanged);
}
}
emit dialogChanged();
}
QQmlComponent *QQuickFolderBreadcrumbBar::buttonDelegate()
{
Q_D(QQuickFolderBreadcrumbBar);
return d->buttonDelegate;
}
void QQuickFolderBreadcrumbBar::setButtonDelegate(QQmlComponent *delegate)
{
Q_D(QQuickFolderBreadcrumbBar);
qCDebug(lcFolderBreadcrumbBar) << "setButtonDelegate called with" << delegate;
if (d->componentComplete) {
// Simplify the code by disallowing this.
qCWarning(lcFolderBreadcrumbBar) << "BreadcrumbBar does not support setting delegates after component completion";
return;
}
if (delegate == d->buttonDelegate)
return;
d->buttonDelegate = delegate;
emit buttonDelegateChanged();
}
QQmlComponent *QQuickFolderBreadcrumbBar::separatorDelegate()
{
Q_D(QQuickFolderBreadcrumbBar);
return d->separatorDelegate;
}
void QQuickFolderBreadcrumbBar::setSeparatorDelegate(QQmlComponent *delegate)
{
Q_D(QQuickFolderBreadcrumbBar);
qCDebug(lcFolderBreadcrumbBar) << "setSeparatorDelegate called with" << delegate;
if (d->componentComplete) {
qCWarning(lcFolderBreadcrumbBar) << "BreadcrumbBar does not support setting delegates after component completion";
return;
}
if (delegate == d->separatorDelegate)
return;
d->separatorDelegate = delegate;
emit separatorDelegateChanged();
}
QQuickAbstractButton *QQuickFolderBreadcrumbBar::upButton()
{
Q_D(QQuickFolderBreadcrumbBar);
if (!d->upButton)
d->executeUpButton();
return d->upButton;
}
void QQuickFolderBreadcrumbBar::setUpButton(QQuickAbstractButton *upButton)
{
Q_D(QQuickFolderBreadcrumbBar);
if (upButton == d->upButton)
return;
if (!d->upButton.isExecuting())
d->cancelUpButton();
if (d->upButton) {
QObjectPrivate::disconnect(d->upButton.data(), &QQuickAbstractButton::clicked,
d, &QQuickFolderBreadcrumbBarPrivate::goUp);
}
QQuickControlPrivate::hideOldItem(d->upButton);
d->upButton = upButton;
if (d->upButton) {
if (!d->upButton->parentItem())
d->upButton->setParentItem(this);
QObjectPrivate::connect(d->upButton.data(), &QQuickAbstractButton::clicked,
d, &QQuickFolderBreadcrumbBarPrivate::goUp);
#if QT_CONFIG(accessibility)
QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached*>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(upButton, true));
if (Q_LIKELY(accessibleAttached))
accessibleAttached->setName(QCoreApplication::translate("FileDialog", "Up"));
#endif
}
if (!d->upButton.isExecuting())
emit upButtonChanged();
}
int QQuickFolderBreadcrumbBar::upButtonSpacing() const
{
Q_D(const QQuickFolderBreadcrumbBar);
return d->upButtonSpacing;
}
void QQuickFolderBreadcrumbBar::setUpButtonSpacing(int upButtonSpacing)
{
Q_D(QQuickFolderBreadcrumbBar);
if (upButtonSpacing == d->upButtonSpacing)
return;
d->upButtonSpacing = upButtonSpacing;
emit upButtonSpacingChanged();
}
QQuickTextField *QQuickFolderBreadcrumbBar::textField()
{
Q_D(QQuickFolderBreadcrumbBar);
return d->textField;
}
void QQuickFolderBreadcrumbBar::setTextField(QQuickTextField *textField)
{
Q_D(QQuickFolderBreadcrumbBar);
if (textField == d->textField)
return;
if (!d->textField.isExecuting())
d->cancelUpButton();
if (d->textField)
d->handleTextFieldHidden();
if (d->textField) {
QObjectPrivate::disconnect(d->textField.data(), &QQuickTextInput::visibleChanged,
d, &QQuickFolderBreadcrumbBarPrivate::textFieldVisibleChanged);
QObjectPrivate::disconnect(d->textField.data(), &QQuickTextInput::activeFocusChanged,
d, &QQuickFolderBreadcrumbBarPrivate::textFieldActiveFocusChanged);
QObjectPrivate::disconnect(d->textField.data(), &QQuickTextInput::accepted,
d, &QQuickFolderBreadcrumbBarPrivate::textFieldAccepted);
}
QQuickControlPrivate::hideOldItem(d->textField);
d->textField = textField;
if (d->textField) {
if (!d->textField->parentItem())
d->textField->setParentItem(this);
d->textField->setVisible(false);
QObjectPrivate::connect(d->textField.data(), &QQuickTextInput::visibleChanged,
d, &QQuickFolderBreadcrumbBarPrivate::textFieldVisibleChanged);
QObjectPrivate::connect(d->textField.data(), &QQuickTextInput::activeFocusChanged,
d, &QQuickFolderBreadcrumbBarPrivate::textFieldActiveFocusChanged);
QObjectPrivate::connect(d->textField.data(), &QQuickTextInput::accepted,
d, &QQuickFolderBreadcrumbBarPrivate::textFieldAccepted);
}
if (!d->textField.isExecuting())
emit textFieldChanged();
}
bool QQuickFolderBreadcrumbBar::event(QEvent *event)
{
#if QT_CONFIG(shortcut)
Q_D(QQuickFolderBreadcrumbBar);
if (event->type() == QEvent::Shortcut) {
QShortcutEvent *shortcutEvent = static_cast<QShortcutEvent *>(event);
if (shortcutEvent->shortcutId() == d->editPathToggleShortcutId) {
d->toggleTextFieldVisibility();
return true;
} else if (shortcutEvent->shortcutId() == d->goUpShortcutId) {
d->goUp();
}
}
#endif
return QQuickItem::event(event);
}
void QQuickFolderBreadcrumbBar::keyPressEvent(QKeyEvent *event)
{
#if QT_CONFIG(shortcut)
Q_D(QQuickFolderBreadcrumbBar);
if (event->matches(QKeySequence::Cancel) && d->textField->isVisible()) {
d->toggleTextFieldVisibility();
event->accept();
} else
#endif
{
QQuickContainer::keyPressEvent(event);
}
}
void QQuickFolderBreadcrumbBar::componentComplete()
{
Q_D(QQuickFolderBreadcrumbBar);
qCDebug(lcFolderBreadcrumbBar) << "componentComplete";
QQuickContainer::componentComplete();
d->repopulate();
if (d->textField) {
// Force it to be updated as setTextField() is too early to do it.
d->textFieldVisibleChanged();
}
}
void QQuickFolderBreadcrumbBar::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
{
Q_D(QQuickFolderBreadcrumbBar);
QQuickContainer::itemChange(change, data);
if (change == QQuickItem::ItemVisibleHasChanged && isComponentComplete()) {
if (data.boolValue && d->dialog->isVisible()) {
// It's visible.
d->handleTextFieldHidden();
#if QT_CONFIG(shortcut)
d->goUpShortcutId = QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(
this, QKeySequence(Qt::ALT | Qt::Key_Up), Qt::WindowShortcut, QQuickShortcutContext::matcher);
#endif
} else {
// It's hidden.
// Hide the text field so that when the dialog gets opened again, it's not still visible.
if (d->textField)
d->textField->setVisible(false);
// Make the ListView visible again.
if (d->contentItem)
d->contentItem->setVisible(true);
// We also need to ungrab the edit path shortcut when we're not visible.
d->ungrabEditPathShortcut();
#if QT_CONFIG(shortcut)
if (d->goUpShortcutId != 0) {
QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(d->goUpShortcutId, this);
d->goUpShortcutId = 0;
}
#endif
}
}
}
bool QQuickFolderBreadcrumbBar::isContent(QQuickItem *item) const
{
if (!qmlContext(item))
return false;
if (QQuickItemPrivate::get(item)->isTransparentForPositioner())
return false;
return true;
}
QFont QQuickFolderBreadcrumbBar::defaultFont() const
{
// TODO
return QQuickTheme::font(QQuickTheme::TabBar);
}
#if QT_CONFIG(accessibility)
QAccessible::Role QQuickFolderBreadcrumbBar::accessibleRole() const
{
// TODO
return QAccessible::PageTabList;
}
#endif
QT_END_NAMESPACE
#include "moc_qquickfolderbreadcrumbbar_p.cpp"