Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qiosfiledialog.mm
Go to the documentation of this file.
1// Copyright (C) 2016 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
4#import <UIKit/UIKit.h>
5
6#import <Photos/Photos.h>
7
8#include <QtCore/qstandardpaths.h>
9#include <QtGui/qwindow.h>
10#include <QDebug>
11
12#include <QtCore/private/qcore_mac_p.h>
13
14#include "qiosfiledialog.h"
15#include "qiosintegration.h"
18
19using namespace Qt::StringLiterals;
20
22 : m_viewController(nullptr)
23{
24}
25
27{
28 [m_viewController release];
29}
30
32{
33 m_eventLoop.exec(QEventLoop::DialogExec);
34}
35
36bool QIOSFileDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent)
37{
38 Q_UNUSED(windowFlags);
39 Q_UNUSED(windowModality);
40
41 const bool acceptOpen = options()->acceptMode() == QFileDialogOptions::AcceptOpen;
42 const auto initialDir = options()->initialDirectory();
43 const QString directory = initialDir.toLocalFile();
44 // We manually add assets-library:// to the list of paths,
45 // when converted to QUrl, it becames a scheme.
46 const QString scheme = initialDir.scheme();
47
48 if (acceptOpen) {
49 if (directory.startsWith("assets-library:"_L1) || scheme == "assets-library"_L1)
50 return showImagePickerDialog(parent);
51 else
52 return showNativeDocumentPickerDialog(parent);
53 }
54
55 return false;
56}
57
58void QIOSFileDialog::showImagePickerDialog_helper(QWindow *parent)
59{
60 UIWindow *window = parent ? reinterpret_cast<UIView *>(parent->winId()).window
61 : qt_apple_sharedApplication().keyWindow;
62 [window.rootViewController presentViewController:m_viewController animated:YES completion:nil];
63}
64
65bool QIOSFileDialog::showImagePickerDialog(QWindow *parent)
66{
67 if (!m_viewController) {
70 for (qsizetype i = 0; i < size; ++i) {
71 QIosOptionalPluginInterface *plugin = qobject_cast<QIosOptionalPluginInterface *>(plugins->instance(i));
72 m_viewController = [plugin->createImagePickerController(this) retain];
73 if (m_viewController)
74 break;
75 }
76 }
77
78 if (!m_viewController) {
79 qWarning() << "QIOSFileDialog: Could not resolve Qt plugin that gives access to photos on iOS";
80 return false;
81 }
82
83 // "Old style" authorization (deprecated, but we have to work with AssetsLibrary anyway).
84 //
85 // From the documentation:
86 // "The authorizationStatus and requestAuthorization: methods aren’t compatible with the
87 // limited library and return PHAuthorizationStatusAuthorized when the user authorizes your
88 // app for limited access only."
89 //
90 // This is good enough for us.
91
92 const auto authStatus = [PHPhotoLibrary authorizationStatus];
93 if (authStatus == PHAuthorizationStatusAuthorized) {
94 showImagePickerDialog_helper(parent);
95 } else if (authStatus == PHAuthorizationStatusNotDetermined) {
96 QPointer<QWindow> winGuard(parent);
97 QPointer<QIOSFileDialog> thisGuard(this);
98 [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
99 dispatch_async(dispatch_get_main_queue(), ^{
100 if (status == PHAuthorizationStatusAuthorized) {
101 if (thisGuard && winGuard)
102 thisGuard->showImagePickerDialog_helper(winGuard);
103
104 } else if (thisGuard) {
105 emit thisGuard->reject();
106 }
107 });
108 }];
109 } else {
110 // Treat 'Limited' (we don't know how to deal with anyway) and 'Denied' as errors.
111 // FIXME: logging category?
112 qWarning() << "QIOSFileDialog: insufficient permission, cannot pick images";
113 return false;
114 }
115
116 return true;
117}
118
119bool QIOSFileDialog::showNativeDocumentPickerDialog(QWindow *parent)
120{
121#ifndef Q_OS_TVOS
122 m_viewController = [[QIOSDocumentPickerController alloc] initWithQIOSFileDialog:this];
123
124 UIWindow *window = parent ? reinterpret_cast<UIView *>(parent->winId()).window
125 : qt_apple_sharedApplication().keyWindow;
126 [window.rootViewController presentViewController:m_viewController animated:YES completion:nil];
127
128 return true;
129#else
130 return false;
131#endif
132}
133
135{
136 // QFileDialog will remember the last directory set, and open subsequent dialogs in the same
137 // directory for convenience. This works for normal file dialogs, but not when using native
138 // pickers. Those can only be used for picking specific types, without support for normal file
139 // system navigation. To avoid showing a native picker by accident, we change directory back
140 // before we return. More could have been done to preserve the "last directory" logic here, but
141 // navigating the file system on iOS is not recommended in the first place, so we keep it simple.
143
144 [m_viewController dismissViewControllerAnimated:YES completion:nil];
145 [m_viewController release];
146 m_viewController = nullptr;
147 m_eventLoop.exit();
148}
149
151{
152 return m_selection;
153}
154
156{
157 m_selection = selection;
158 emit filesSelected(m_selection);
159 if (m_selection.count() == 1)
160 emit fileSelected(m_selection[0]);
161}
static QString currentPath()
Returns the absolute path of the application's current directory.
Definition qdir.cpp:2051
int exec(ProcessEventsFlags flags=AllEvents)
Enters the main event loop and waits until exit() is called.
void exit(int returnCode=0)
Tells the event loop to exit with a return code.
MetaDataList metaData() const
QObject * instance(int index) const
AcceptMode acceptMode() const
void hide() override
QUrl directory() const override
QList< QUrl > selectedFiles() const override
bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override
void selectedFilesChanged(const QList< QUrl > &selection)
void exec() override
QFactoryLoader * optionalPlugins()
static QIOSIntegration * instance()
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
qsizetype count() const noexcept
Definition qlist.h:387
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:311
void fileSelected(const QUrl &file)
void directoryEntered(const QUrl &directory)
const QSharedPointer< QFileDialogOptions > & options() const
void filesSelected(const QList< QUrl > &files)
\inmodule QtCore
Definition qpointer.h:18
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QUrl fromLocalFile(const QString &localfile)
Returns a QUrl representation of localFile, interpreted as a local file.
Definition qurl.cpp:3354
QString toLocalFile() const
Returns the path of this URL formatted as a local file path.
Definition qurl.cpp:3411
\inmodule QtGui
Definition qwindow.h:63
WindowModality
AppleApplication * qt_apple_sharedApplication()
Definition qcore_mac.mm:430
#define qWarning
Definition qlogging.h:162
GLenum GLuint GLintptr GLsizeiptr size
[1]
#define emit
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:70
QObject::connect nullptr
sem release()
QItemSelection * selection
[0]
aWidget window() -> setWindowTitle("New Window Title")
[2]
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent