Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquickfolderdialogimpl.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
6
7#include <QtCore/qloggingcategory.h>
8#include <QtQuickTemplates2/private/qquickdialogbuttonbox_p_p.h>
9
12
14
15Q_LOGGING_CATEGORY(lcFolderDialogCurrentFolder, "qt.quick.dialogs.quickfolderdialogimpl.currentFolder")
16Q_LOGGING_CATEGORY(lcFolderDialogSelectedFolder, "qt.quick.dialogs.quickfolderdialogimpl.selectedFolder")
17Q_LOGGING_CATEGORY(lcFolderDialogOptions, "qt.quick.dialogs.quickfolderdialogimpl.options")
18
22
24{
26 if (!buttonBox)
27 return;
28
30 if (!attached)
31 return;
32
34 if (!openButton) {
35 qmlWarning(q).nospace() << "Can't update Open button's enabled state because it wasn't found";
36 return;
37 }
38
39 openButton->setEnabled(!selectedFolder.isEmpty() && attached->breadcrumbBar()
40 && !attached->breadcrumbBar()->textField()->isVisible());
41}
42
51{
54 if (!attached || !attached->folderDialogListView())
55 return;
56
57 QString newSelectedFolderPath;
58 int newSelectedFolderIndex = 0;
60 if (!oldFolderPath.isEmpty() && !newFolderPath.isEmpty()) {
61 // If the user went up a directory (or several), we should set
62 // selectedFolder to be the directory that we were in (or
63 // its closest ancestor that is a child of the new directory).
64 // E.g. if oldFolderPath is /foo/bar/baz/abc/xyz, and newFolderPath is /foo/bar,
65 // then we want to set selectedFolder to be /foo/bar/baz.
66 const int indexOfFolder = oldFolderPath.indexOf(newFolderPath);
67 if (indexOfFolder != -1) {
68 // [folder]
69 // [ oldFolderPath ]
70 // /foo/bar/baz/abc/xyz
71 // [rel...Paths]
72 QStringList relativePaths = oldFolderPath.mid(indexOfFolder + newFolderPath.size()).split(QLatin1Char('/'), Qt::SkipEmptyParts);
73 newSelectedFolderPath = newFolderPath + QLatin1Char('/') + relativePaths.first();
74
75 // Now find the index of that directory so that we can set the ListView's currentIndex to it.
76 const QDir newFolderDir(newFolderPath);
77 // Just to be safe...
78 if (!newFolderDir.exists()) {
79 qmlWarning(q) << "Directory" << newSelectedFolderPath << "doesn't exist; can't get a file entry list for it";
80 return;
81 }
82
83 const QFileInfoList dirs = newFolderDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::DirsFirst);
84 const QFileInfo newSelectedFileInfo(newSelectedFolderPath);
85 // The directory can contain files, but since we put dirs first, that should never affect the indices.
86 newSelectedFolderIndex = dirs.indexOf(newSelectedFileInfo);
87 }
88 }
89
90 if (newSelectedFolderPath.isEmpty()) {
91 // When entering into a directory that isn't a parent of the old one, the first
92 // file delegate should be selected.
93 // TODO: is there a cheaper way to do this? QDirIterator doesn't support sorting,
94 // so we can't use that. QQuickFolderListModel uses threads to fetch its data,
95 // so should be considered asynchronous. We might be able to use it, but it would
96 // complicate the code even more...
97 QDir newFolderDir(newFolderPath);
98 if (newFolderDir.exists()) {
99 const QFileInfoList files = newFolderDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::DirsFirst);
100 if (!files.isEmpty())
101 newSelectedFolderPath = files.first().absoluteFilePath();
102 }
103 }
104
105 const bool folderSelected = !newSelectedFolderPath.isEmpty();
106 q->setSelectedFolder(folderSelected ? QUrl::fromLocalFile(newSelectedFolderPath) : QUrl());
107 {
108 // Set the appropriate currentIndex for the selected folder. We block signals from ListView
109 // because we don't want folderDialogListViewCurrentIndexChanged to be called, as the file
110 // it gets from the delegate will not be up-to-date (but most importantly because we already
111 // just set the selected folder).
112 QSignalBlocker blocker(attached->folderDialogListView());
113 attached->folderDialogListView()->setCurrentIndex(folderSelected ? newSelectedFolderIndex : -1);
114 }
115 if (folderSelected) {
116 if (QQuickItem *currentItem = attached->folderDialogListView()->currentItem())
117 currentItem->forceActiveFocus();
118 }
119}
120
122{
123 // Let handleClick take care of calling accept().
124}
125
134
148
153
155{
156 Q_D(const QQuickFolderDialogImpl);
157 return d->currentFolder;
158}
159
161{
162 qCDebug(lcFolderDialogCurrentFolder) << "setCurrentFolder called with" << currentFolder;
164 if (currentFolder == d->currentFolder)
165 return;
166
167 const QString oldFolderPath = QQmlFile::urlToLocalFileOrQrc(d->currentFolder);
168
169 d->currentFolder = currentFolder;
170 d->updateSelectedFolder(oldFolderPath);
171 emit currentFolderChanged(d->currentFolder);
172}
173
175{
176 Q_D(const QQuickFolderDialogImpl);
177 return d->selectedFolder;
178}
179
181{
183 qCDebug(lcFolderDialogSelectedFolder).nospace() << "setSelectedFolder called with selectedFolder "
184 << selectedFolder << " (d->selectedFolder is " << d->selectedFolder << ")";
185 if (selectedFolder == d->selectedFolder)
186 return;
187
188 d->selectedFolder = selectedFolder;
189 d->updateEnabled();
191}
192
193QSharedPointer<QFileDialogOptions> QQuickFolderDialogImpl::options() const
194{
195 Q_D(const QQuickFolderDialogImpl);
196 return d->options;
197}
198
199void QQuickFolderDialogImpl::setOptions(const QSharedPointer<QFileDialogOptions> &options)
200{
201 qCDebug(lcFolderDialogOptions).nospace() << "setOptions called with:"
202 << " acceptMode=" << options->acceptMode()
203 << " fileMode=" << options->fileMode()
204 << " initialDirectory=" << options->initialDirectory();
205
207 d->options = options;
208}
209
218{
220 d->acceptLabel = label;
221 QQuickFolderDialogImplAttached *attached = d->attachedOrWarn();
222 if (!attached)
223 return;
224
225 auto acceptButton = d->buttonBox->standardButton(QPlatformDialogHelper::Open);
226 if (!acceptButton) {
227 qmlWarning(this).nospace() << "Can't set accept label to " << label
228 << "; failed to find Open button in DialogButtonBox of " << this;
229 return;
230 }
231
232 acceptButton->setText(!label.isEmpty()
234}
235
237{
239 d->rejectLabel = label;
240 if (!d->buttonBox)
241 return;
242
243 auto rejectButton = d->buttonBox->standardButton(QPlatformDialogHelper::Cancel);
244 if (!rejectButton) {
245 qmlWarning(this).nospace() << "Can't set reject label to " << label
246 << "; failed to find Open button in DialogButtonBox of " << this;
247 return;
248 }
249
250 rejectButton->setText(!label.isEmpty()
252}
253
255{
258
259 // Find the right-most button and set its key navigation so that
260 // tab moves focus to the breadcrumb bar's up button. I tried
261 // doing this via KeyNavigation on the DialogButtonBox in QML,
262 // but it didn't work (probably because it's not the right item).
263 QQuickFolderDialogImplAttached *attached = d->attachedOrWarn();
264 if (!attached)
265 return;
266
267 Q_ASSERT(d->buttonBox);
268 const int buttonCount = d->buttonBox->count();
269 if (buttonCount == 0)
270 return;
271
272 QQuickAbstractButton *rightMostButton = qobject_cast<QQuickAbstractButton *>(
273 d->buttonBox->itemAt(buttonCount - 1));
274 if (!rightMostButton) {
275 qmlWarning(this) << "Can't find right-most button in DialogButtonBox";
276 return;
277 }
278
279 auto keyNavigationAttached = QQuickKeyNavigationAttached::qmlAttachedProperties(rightMostButton);
280 if (!keyNavigationAttached) {
281 qmlWarning(this) << "Can't create attached KeyNavigation object on" << QDebug::toString(rightMostButton);
282 return;
283 }
284
285 keyNavigationAttached->setTab(attached->breadcrumbBar()->upButton());
286}
287
289{
292
293 if (change != QQuickItem::ItemVisibleHasChanged || !isComponentComplete() || !data.boolValue)
294 return;
295
296 QQuickFolderDialogImplAttached *attached = d->attachedOrWarn();
297 if (!attached)
298 return;
299
301 d->updateEnabled();
302}
303
305{
308 qmlAttachedPropertiesObject<QQuickFolderDialogImpl>(q));
309 if (!attached)
310 qmlWarning(q) << "Expected FileDialogImpl attached object to be present on" << this;
311 return attached;
312}
313
315{
316 auto folderDialogImpl = qobject_cast<QQuickFolderDialogImpl*>(parent);
317 if (!folderDialogImpl)
318 return;
319
320 auto folderDialogDelegate = qobject_cast<QQuickFileDialogDelegate*>(folderDialogListView->currentItem());
321 if (!folderDialogDelegate)
322 return;
323
324 folderDialogImpl->setSelectedFolder(folderDialogDelegate->file());
325}
326
329{
330 if (!qobject_cast<QQuickFolderDialogImpl*>(parent)) {
331 qmlWarning(this) << "FolderDialogImpl attached properties should only be "
332 << "accessed through the root FileDialogImpl instance";
333 }
334}
335
337{
339 return d->folderDialogListView;
340}
341
355
361
363{
365 if (breadcrumbBar == d->breadcrumbBar)
366 return;
367
368 d->breadcrumbBar = breadcrumbBar;
370}
371
373
374#include "moc_qquickfolderdialogimpl_p.cpp"
\inmodule QtCore
Definition qdir.h:20
@ DirsFirst
Definition qdir.h:56
@ NoDotAndDotDot
Definition qdir.h:44
@ Dirs
Definition qdir.h:22
AcceptMode acceptMode() const
QObject * parent
Definition qobject.h:73
static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer< Func1 >::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type=Qt::AutoConnection)
Definition qobject_p.h:299
\inmodule QtCore
Definition qobject.h:103
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to \l{QFile}.
Definition qqmlfile.cpp:742
virtual void componentComplete()=0
Invoked after the root component that caused this instantiation has completed construction.
static QString buttonText(QPlatformDialogHelper::StandardButton standardButton)
Q_INVOKABLE QQuickAbstractButton * standardButton(QPlatformDialogHelper::StandardButton button) const
\qmlmethod AbstractButton QtQuick.Controls::DialogButtonBox::standardButton(StandardButton button)
static QPlatformDialogHelper::ButtonRole buttonRole(QQuickAbstractButton *button)
Popup dialog with standard buttons and a title, used for short-term interaction with the user.
QQuickDialogButtonBox * buttonBox
void setBreadcrumbBar(QQuickFolderBreadcrumbBar *breadcrumbBar)
void setFolderDialogListView(QQuickListView *folderDialogListView)
QQuickFolderBreadcrumbBar * breadcrumbBar
QQuickFolderDialogImplAttached(QObject *parent=nullptr)
QQuickFolderDialogImplAttached * attachedOrWarn()
void updateSelectedFolder(const QString &oldFolderPath)
void handleClick(QQuickAbstractButton *button) override
void setAcceptLabel(const QString &label)
QSharedPointer< QFileDialogOptions > options() const
void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) override
void setSelectedFolder(const QUrl &selectedFolder)
void setCurrentFolder(const QUrl &folder)
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
QQuickFolderDialogImpl(QObject *parent=nullptr)
void setRejectLabel(const QString &label)
void selectedFolderChanged(const QUrl &folderUrl)
void setOptions(const QSharedPointer< QFileDialogOptions > &options)
static QQuickFolderDialogImplAttached * qmlAttachedProperties(QObject *object)
void currentFolderChanged(const QUrl &folderUrl)
QQuickItem * currentItem
void currentIndexChanged()
void setCurrentIndex(int idx)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
bool isVisible() const
Q_INVOKABLE void forceActiveFocus()
\qmlmethod point QtQuick::Item::mapToItem(Item item, real x, real y) \qmlmethod point QtQuick::Item::...
ItemChange
Used in conjunction with QQuickItem::itemChange() to notify the item about certain types of changes.
Definition qquickitem.h:144
@ ItemVisibleHasChanged
Definition qquickitem.h:148
static QQuickKeyNavigationAttached * qmlAttachedProperties(QObject *)
virtual void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
bool isComponentComplete() const
Exception-safe wrapper around QObject::blockSignals().
Definition qobject.h:483
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
\inmodule QtCore
Definition qurl.h:94
static QUrl fromLocalFile(const QString &localfile)
Returns a QUrl representation of localFile, interpreted as a local file.
Definition qurl.cpp:3368
bool isValid() const
Returns true if the URL is non-empty and valid; otherwise returns false.
Definition qurl.cpp:1882
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1896
QPushButton * button
[2]
Combined button and popup list for selecting options.
@ SkipEmptyParts
Definition qnamespace.h:128
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint GLsizei const GLchar * label
[43]
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
Q_QML_EXPORT QQmlInfo qmlWarning(const QObject *me)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
QStringList files
[8]
\inmodule QtCore \reentrant
Definition qchar.h:18
\inmodule QtQuick
Definition qquickitem.h:159