diff options
Diffstat (limited to 'src/plugins/platforms')
9 files changed, 125 insertions, 49 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm index 7644867700a..7ca3e61dfa5 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm @@ -25,6 +25,8 @@ #include <qpa/qwindowsysteminterface.h> #include <qwindowdefs.h> +#include <QtCore/private/qdarwinsecurityscopedfileengine_p.h> + QT_USE_NAMESPACE @implementation QCocoaApplicationDelegate { @@ -194,6 +196,23 @@ QT_USE_NAMESPACE QCocoaMenuBar::insertWindowMenu(); } +/*! + Tells the delegate to open the specified files + + Sent by the system when the user drags a file to the app's icon + in places like Finder or the Dock, or opens a file via the "Open + With" menu in Finder. + + These actions can happen when the application is not running, + in which case the call comes in between willFinishLaunching + and didFinishLaunching. In this case we don't pass on the + incoming file paths as file open events, as the paths are + also part of the command line arguments, and Qt applications + normally expect to handle file opening via those. + + \note The app must register itself as a handler for each file + type via the CFBundleDocumentTypes key in the Info.plist. + */ - (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames { Q_UNUSED(filenames); @@ -209,7 +228,10 @@ QT_USE_NAMESPACE if (qApp->arguments().contains(qtFileName)) continue; } - QWindowSystemInterface::handleFileOpenEvent(qtFileName); + QUrl url = qt_apple_urlFromPossiblySecurityScopedURL([NSURL fileURLWithPath:fileName]); + QWindowSystemInterface::handleFileOpenEvent(url); + // FIXME: We're supposed to call [NSApp replyToOpenOrPrint:] here, but we + // don't know if the open operation succeeded, failed, or was cancelled. } if ([reflectionDelegate respondsToSelector:_cmd]) @@ -262,6 +284,17 @@ QT_USE_NAMESPACE } } +/*! + Returns a Boolean value that indicates if the app responds + to reopen AppleEvents. + + These events are sent whenever the Finder reactivates an already + running application because someone double-clicked it again or used + the dock to activate it. + + We pass the activation on to Qt, and return YES, to let AppKit + follow its normal flow. + */ - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag { if ([reflectionDelegate respondsToSelector:_cmd]) @@ -309,6 +342,25 @@ QT_USE_NAMESPACE [self doesNotRecognizeSelector:invocationSelector]; } +/*! + Callback for when the application is asked to pick up a user activity + from another app (also known as Handoff, which is part of the bigger + Continuity story for Apple operating systems). + + This is normally managed by two apps by the same vendor explicitly + initiating a custom NSUserActivity and picking it up in another app + on the same or another device, which we don't have APIs for. + + This is also how the system supports Universal Links, where a web page + can deep-link into an app. In this case the app needs to claim and + validate an associated domain. The resulting link will be delivered + as a special NSUserActivityTypeBrowsingWeb activity type, which we + treat as QDesktopServices::handleUrl(). + + Finally, for NS/UIDocument based apps (which Qt is not), the system + automatically handles document hand-off if the application includes + the NSUbiquitousDocumentUserActivityType key in its Info.plist. + */ - (BOOL)application:(NSApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray<id<NSUserActivityRestoring>> *restorableObjects))restorationHandler { @@ -331,6 +383,18 @@ QT_USE_NAMESPACE return NO; } +/*! + Callback for when the app is asked to open custom URL schemes. + + We register a handler for events of type kInternetEventClass with the + NSAppleEventManager during application start. + + The application must include the schemes in the CFBundleURLTypes + key of the Info.plist. + + \note This callback is not used for http/https URLs, see + continueUserActivity above for how we handle that. + */ - (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent { Q_UNUSED(replyEvent); diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index 52540666718..a79682e4e14 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -19,6 +19,7 @@ #include <QtCore/qregularexpression.h> #include <QtCore/qpointer.h> #include <QtCore/private/qcore_mac_p.h> +#include <QtCore/private/qdarwinsecurityscopedfileengine_p.h> #include <QtGui/qguiapplication.h> #include <QtGui/private/qguiapplication_p.h> @@ -395,14 +396,15 @@ typedef QSharedPointer<QFileDialogOptions> SharedPointerFileDialogOptions; { if (auto *openPanel = openpanel_cast(m_panel)) { QList<QUrl> result; - for (NSURL *url in openPanel.URLs) { - QString path = QString::fromNSString(url.path).normalized(QString::NormalizationForm_C); - result << QUrl::fromLocalFile(path); - } + for (NSURL *url in openPanel.URLs) + result << qt_apple_urlFromPossiblySecurityScopedURL(url); return result; } else { - QString filename = QString::fromNSString(m_panel.URL.path).normalized(QString::NormalizationForm_C); - QFileInfo fileInfo(filename); + QUrl result = qt_apple_urlFromPossiblySecurityScopedURL(m_panel.URL); + if (qt_apple_isSandboxed()) + return { result }; // Can't tweak suffix + + QFileInfo fileInfo(result.toLocalFile()); if (fileInfo.suffix().isEmpty() && ![self fileInfoMatchesCurrentNameFilter:fileInfo]) { // We end up in this situation if we accept a file name without extension diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.mm b/src/plugins/platforms/ios/qiosapplicationdelegate.mm index 380c5a588e6..7cbb4fc40f5 100644 --- a/src/plugins/platforms/ios/qiosapplicationdelegate.mm +++ b/src/plugins/platforms/ios/qiosapplicationdelegate.mm @@ -16,6 +16,8 @@ #include <QtCore/QtCore> +#include <QtCore/private/qdarwinsecurityscopedfileengine_p.h> + @interface QIOSWindowSceneDelegate : NSObject<UIWindowSceneDelegate> @property (nullable, nonatomic, strong) UIWindow *window; @end @@ -112,7 +114,7 @@ QIOSServices *iosServices = static_cast<QIOSServices *>(iosIntegration->services()); for (UIOpenURLContext *urlContext in URLContexts) { - QUrl url = QUrl::fromNSURL(urlContext.URL); + QUrl url = qt_apple_urlFromPossiblySecurityScopedURL(urlContext.URL); if (url.isLocalFile()) QWindowSystemInterface::handleFileOpenEvent(url); else diff --git a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm index c173aa426fc..57a5e100c9e 100644 --- a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm +++ b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm @@ -8,6 +8,7 @@ #include "qiosdocumentpickercontroller.h" #include <QtCore/qpointer.h> +#include <QtCore/private/qdarwinsecurityscopedfileengine_p.h> @implementation QIOSDocumentPickerController { QPointer<QIOSFileDialog> m_fileDialog; @@ -17,9 +18,11 @@ { NSMutableArray <UTType *> *docTypes = [[[NSMutableArray alloc] init] autorelease]; - QStringList nameFilters = fileDialog->options()->nameFilters(); - if (!nameFilters.isEmpty() && (fileDialog->options()->fileMode() != QFileDialogOptions::Directory - || fileDialog->options()->fileMode() != QFileDialogOptions::DirectoryOnly)) + const auto options = fileDialog->options(); + + const QStringList nameFilters = options->nameFilters(); + if (!nameFilters.isEmpty() && (options->fileMode() != QFileDialogOptions::Directory + || options->fileMode() != QFileDialogOptions::DirectoryOnly)) { QStringList results; for (const QString &filter : nameFilters) @@ -28,21 +31,8 @@ docTypes = [self computeAllowedFileTypes:results]; } - // FIXME: Handle security scoped URLs instead of copying resource - bool asCopy = [&]{ - switch (fileDialog->options()->fileMode()) { - case QFileDialogOptions::AnyFile: - case QFileDialogOptions::ExistingFile: - case QFileDialogOptions::ExistingFiles: - return true; - default: - // Folders can't be imported - return false; - } - }(); - if (!docTypes.count) { - switch (fileDialog->options()->fileMode()) { + switch (options->fileMode()) { case QFileDialogOptions::AnyFile: case QFileDialogOptions::ExistingFile: case QFileDialogOptions::ExistingFiles: @@ -58,17 +48,39 @@ } } - if (self = [super initForOpeningContentTypes:docTypes asCopy:asCopy]) { - m_fileDialog = fileDialog; - self.modalPresentationStyle = UIModalPresentationFormSheet; - self.delegate = self; - self.presentationController.delegate = self; + if (options->acceptMode() == QFileDialogOptions::AcceptSave) { + auto selectedUrls = options->initiallySelectedFiles(); + auto suggestedFileName = !selectedUrls.isEmpty() ? selectedUrls.first().fileName() : "Untitled"; - if (m_fileDialog->options()->fileMode() == QFileDialogOptions::ExistingFiles) + // Create an empty dummy file, so that the export dialog will allow us + // to choose the export destination, which we are then given access to + // write to. + NSURL *dummyExportFile = [NSFileManager.defaultManager.temporaryDirectory + URLByAppendingPathComponent:suggestedFileName.toNSString()]; + [NSFileManager.defaultManager createFileAtPath:dummyExportFile.path contents:nil attributes:nil]; + + if (!(self = [super initForExportingURLs:@[dummyExportFile]])) + return nil; + + // Note, we don't set the directoryURL, as if the directory can't be + // accessed, or written to, the file dialog is shown but is empty. + // FIXME: See comment below for open dialogs as well + } else { + if (!(self = [super initForOpeningContentTypes:docTypes asCopy:NO])) + return nil; + + if (options->fileMode() == QFileDialogOptions::ExistingFiles) self.allowsMultipleSelection = YES; - self.directoryURL = m_fileDialog->options()->initialDirectory().toNSURL(); + // FIXME: This doesn't seem to have any effect + self.directoryURL = options->initialDirectory().toNSURL(); } + + m_fileDialog = fileDialog; + self.modalPresentationStyle = UIModalPresentationFormSheet; + self.delegate = self; + self.presentationController.delegate = self; + return self; } @@ -81,7 +93,7 @@ QList<QUrl> files; for (NSURL* url in urls) - files.append(QUrl::fromNSURL(url)); + files.append(qt_apple_urlFromPossiblySecurityScopedURL(url)); m_fileDialog->selectedFilesChanged(files); emit m_fileDialog->accept(); diff --git a/src/plugins/platforms/ios/qiosfiledialog.mm b/src/plugins/platforms/ios/qiosfiledialog.mm index b7d3e488bbb..6e7c10117ed 100644 --- a/src/plugins/platforms/ios/qiosfiledialog.mm +++ b/src/plugins/platforms/ios/qiosfiledialog.mm @@ -49,14 +49,10 @@ bool QIOSFileDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality window // when converted to QUrl, it becames a scheme. const QString scheme = initialDir.scheme(); - if (acceptOpen) { - if (directory.startsWith("assets-library:"_L1) || scheme == "assets-library"_L1) - return showImagePickerDialog(parent); - else - return showNativeDocumentPickerDialog(parent); - } + if (acceptOpen && (directory.startsWith("assets-library:"_L1) || scheme == "assets-library"_L1)) + return showImagePickerDialog(parent); - return false; + return showNativeDocumentPickerDialog(parent); } void QIOSFileDialog::showImagePickerDialog_helper(QWindow *parent) diff --git a/src/plugins/platforms/wayland/qwaylandinputcontext.cpp b/src/plugins/platforms/wayland/qwaylandinputcontext.cpp index 5ab285ad97d..0ccc4dba57a 100644 --- a/src/plugins/platforms/wayland/qwaylandinputcontext.cpp +++ b/src/plugins/platforms/wayland/qwaylandinputcontext.cpp @@ -192,12 +192,10 @@ void QWaylandInputContext::setFocusObject(QObject *object) if (window && window->handle()) { if (mCurrentWindow.data() != window) { if (!inputMethodAccepted()) { - if (mCurrentWindow) { - auto *surface = static_cast<QWaylandWindow *>(mCurrentWindow->handle())->wlSurface(); - if (surface) - inputInterface->disableSurface(surface); - mCurrentWindow.clear(); - } + auto *surface = static_cast<QWaylandWindow *>(window->handle())->wlSurface(); + if (surface) + inputInterface->disableSurface(surface); + mCurrentWindow.clear(); } else { auto *surface = static_cast<QWaylandWindow *>(window->handle())->wlSurface(); if (surface) { diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 2bd2f0c9e3d..0236669d6fb 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -704,7 +704,7 @@ void QWindowsScreenManager::initialize() qCDebug(lcQpaScreen) << "Initializing screen manager"; auto className = QWindowsWindowClassRegistry::instance()->registerWindowClass( - QLatin1String("ScreenChangeObserverWindow"), + "ScreenChangeObserverWindow"_L1, qDisplayChangeObserverWndProc); // HWND_MESSAGE windows do not get WM_DISPLAYCHANGE, so we need to create diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index d132bbb6130..b9f60f7713c 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -549,7 +549,7 @@ QWindowsTheme::QWindowsTheme() refreshIconPixmapSizes(); auto className = QWindowsWindowClassRegistry::instance()->registerWindowClass( - QLatin1String("ThemeChangeObserverWindow"), + "ThemeChangeObserverWindow"_L1, qThemeChangeObserverWndProc); // HWND_MESSAGE windows do not get the required theme events, // so we use a real top-level window that we never show. diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index ed391009423..b77e985c965 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -61,6 +61,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + using QWindowCreationContextPtr = QSharedPointer<QWindowCreationContext>; enum { @@ -889,7 +891,7 @@ QWindowsWindowData const QString windowClassName = QWindowsWindowClassRegistry::instance()->registerWindowClass(w); QWindowsWindowClassDescription windowTitlebarDescription; - windowTitlebarDescription.name = QStringLiteral("_q_titlebar"); + windowTitlebarDescription.name = "_q_titlebar"_L1; windowTitlebarDescription.style = CS_VREDRAW | CS_HREDRAW; windowTitlebarDescription.shouldAddPrefix = false; const QString windowTitlebarName = QWindowsWindowClassRegistry::instance()->registerWindowClass(windowTitlebarDescription); |
