diff options
Diffstat (limited to 'src/plugins/platforms/ios')
69 files changed, 2126 insertions, 2953 deletions
diff --git a/src/plugins/platforms/ios/CMakeLists.txt b/src/plugins/platforms/ios/CMakeLists.txt index 204add279c..51c1b52cf3 100644 --- a/src/plugins/platforms/ios/CMakeLists.txt +++ b/src/plugins/platforms/ios/CMakeLists.txt @@ -1,19 +1,32 @@ -# Generated from ios.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## QIOSIntegrationPlugin Plugin: ##################################################################### +if(VISIONOS) + include(SwiftIntegration.cmake) + + qt_install(TARGETS QIOSIntegrationPluginSwift + EXPORT "${INSTALL_CMAKE_NAMESPACE}QIOSIntegrationPluginTargets" + DESTINATION "${INSTALL_LIBDIR}" + ) + qt_internal_add_targets_to_additional_targets_export_file( + TARGETS QIOSIntegrationPluginSwift + EXPORT_NAME_PREFIX "${INSTALL_CMAKE_NAMESPACE}QIOSIntegrationPlugin" + ) +endif() + qt_internal_add_plugin(QIOSIntegrationPlugin OUTPUT_NAME qios - DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES ios # special case + STATIC # Force static, even in shared builds + DEFAULT_IF "ios" IN_LIST QT_QPA_PLATFORMS PLUGIN_TYPE platforms SOURCES plugin.mm qiosapplicationdelegate.h qiosapplicationdelegate.mm qiosapplicationstate.h qiosapplicationstate.mm - qiosbackingstore.h qiosbackingstore.mm - qioscontext.h qioscontext.mm qioseventdispatcher.h qioseventdispatcher.mm qiosglobal.h qiosglobal.mm qiosinputcontext.h qiosinputcontext.mm @@ -27,43 +40,66 @@ qt_internal_add_plugin(QIOSIntegrationPlugin qioswindow.h qioswindow.mm quiaccessibilityelement.h quiaccessibilityelement.mm quiview.h quiview.mm + quiwindow.mm quiwindow.h uistrings_p.h uistrings.cpp + NO_PCH_SOURCES + qioscontext.mm # undef QT_NO_FOREACH + qiosintegration.mm # undef QT_NO_FOREACH + qiosplatformaccessibility.mm # undef QT_NO_FOREACH + qiosscreen.mm # undef QT_NO_FOREACH LIBRARIES ${FWAudioToolbox} ${FWFoundation} ${FWMetal} ${FWQuartzCore} ${FWUIKit} + ${FWCoreGraphics} Qt::CorePrivate Qt::GuiPrivate ) -# special case begin qt_disable_apple_app_extension_api_only(QIOSIntegrationPlugin) -# special case end -#### Keys ignored in scope 2:.:.:kernel.pro:<TRUE>: -# OTHER_FILES = "quiview_textinput.mm" "quiview_accessibility.mm" ## Scopes: ##################################################################### +qt_internal_find_apple_system_framework(FWUniformTypeIdentifiers UniformTypeIdentifiers) qt_internal_extend_target(QIOSIntegrationPlugin CONDITION QT_FEATURE_opengl + SOURCES + qioscontext.h qioscontext.mm LIBRARIES Qt::OpenGLPrivate ) -qt_internal_extend_target(QIOSIntegrationPlugin CONDITION NOT TVOS +qt_internal_extend_target(QIOSIntegrationPlugin CONDITION QT_FEATURE_clipboard SOURCES qiosclipboard.h qiosclipboard.mm - qiosdocumentpickercontroller.h qiosdocumentpickercontroller.mm +) + +qt_internal_extend_target(QIOSIntegrationPlugin CONDITION NOT TVOS + SOURCES qiosfiledialog.h qiosfiledialog.mm - qiosmenu.h qiosmenu.mm + qiosdocumentpickercontroller.h qiosdocumentpickercontroller.mm + LIBRARIES + ${FWUniformTypeIdentifiers} + ${FWPhotos} +) + +qt_internal_extend_target(QIOSIntegrationPlugin CONDITION NOT TVOS + SOURCES + qioscolordialog.h qioscolordialog.mm + qiosfontdialog.h qiosfontdialog.mm qiosmessagedialog.h qiosmessagedialog.mm +) + +qt_internal_extend_target(QIOSIntegrationPlugin CONDITION NOT (TVOS OR VISIONOS) + SOURCES + qiosmenu.h qiosmenu.mm qiostextinputoverlay.h qiostextinputoverlay.mm - LIBRARIES - ${FWAssetsLibrary} ) -#### Keys ignored in scope 6:.:.:kernel.pro:NOT TARGET___equals____ss_QT_DEFAULT_QPA_PLUGIN: -# PLUGIN_EXTENDS = "-" add_subdirectory(optional) + +if(VISIONOS) + target_link_libraries(QIOSIntegrationPlugin PRIVATE QIOSIntegrationPluginSwift) +endif() diff --git a/src/plugins/platforms/ios/SwiftIntegration.cmake b/src/plugins/platforms/ios/SwiftIntegration.cmake new file mode 100644 index 0000000000..d52edb3ad2 --- /dev/null +++ b/src/plugins/platforms/ios/SwiftIntegration.cmake @@ -0,0 +1,78 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +set(CMAKE_Swift_COMPILER_TARGET arm64-apple-xros) +if($CACHE{CMAKE_OSX_SYSROOT} MATCHES "^[a-z]+simulator$") + set(CMAKE_Swift_COMPILER_TARGET "${CMAKE_Swift_COMPILER_TARGET}-simulator") +endif() + +cmake_policy(SET CMP0157 NEW) +enable_language(Swift) + +# Verify that we have a new enough compiler +if("${CMAKE_Swift_COMPILER_VERSION}" VERSION_LESS 5.9) + message(FATAL_ERROR "Swift 5.9 required for C++ interoperability") +endif() + +get_target_property(QT_CORE_INCLUDES Qt6::Core INTERFACE_INCLUDE_DIRECTORIES) +get_target_property(QT_GUI_INCLUDES Qt6::Gui INTERFACE_INCLUDE_DIRECTORIES) +get_target_property(QT_CORE_PRIVATE_INCLUDES Qt6::CorePrivate INTERFACE_INCLUDE_DIRECTORIES) +get_target_property(QT_GUI_PRIVATE_INCLUDES Qt6::GuiPrivate INTERFACE_INCLUDE_DIRECTORIES) + +set(target QIOSIntegrationPluginSwift) +# Swift library +set(SWIFT_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/qiosapplication.swift" +) +add_library(${target} STATIC ${SWIFT_SOURCES}) +set_target_properties(${target} PROPERTIES + Swift_MODULE_NAME ${target}) +target_include_directories(${target} PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${QT_CORE_INCLUDES}" + "${QT_GUI_INCLUDES}" + "${QT_CORE_PRIVATE_INCLUDES}" + "${QT_GUI_PRIVATE_INCLUDES}" + +) +target_compile_options(${target} PUBLIC + $<$<COMPILE_LANGUAGE:Swift>:-cxx-interoperability-mode=default> + $<$<COMPILE_LANGUAGE:Swift>:-Xcc -std=c++17>) + +# Swift to C++ bridging header +set(SWIFT_BRIDGING_HEADER "${CMAKE_CURRENT_BINARY_DIR}/qiosswiftintegration.h") +list(TRANSFORM QT_CORE_INCLUDES PREPEND "-I") +list(TRANSFORM QT_GUI_INCLUDES PREPEND "-I") +list(TRANSFORM QT_CORE_PRIVATE_INCLUDES PREPEND "-I") +list(TRANSFORM QT_GUI_PRIVATE_INCLUDES PREPEND "-I") +add_custom_command( + COMMAND + ${CMAKE_Swift_COMPILER} -frontend -typecheck + ${SWIFT_SOURCES} + -I ${CMAKE_CURRENT_SOURCE_DIR} + ${QT_CORE_INCLUDES} + ${QT_GUI_INCLUDES} + ${QT_CORE_PRIVATE_INCLUDES} + ${QT_GUI_PRIVATE_INCLUDES} + -sdk ${CMAKE_OSX_SYSROOT} + -module-name ${target} + -cxx-interoperability-mode=default + -Xcc -std=c++17 + -emit-clang-header-path "${SWIFT_BRIDGING_HEADER}" + -target ${CMAKE_Swift_COMPILER_TARGET} + OUTPUT + "${SWIFT_BRIDGING_HEADER}" + DEPENDS + ${SWIFT_SOURCES} + ) + +set(header_target "${target}Header") +add_custom_target(${header_target} + DEPENDS "${SWIFT_BRIDGING_HEADER}" +) +# Make sure the "'__bridge_transfer' casts have no effect when not using ARC" +# warning doesn't break warnings-are-error builds. +target_compile_options(${target} INTERFACE + -Wno-error=arc-bridge-casts-disallowed-in-nonarc) + +add_dependencies(${target} ${header_target}) diff --git a/src/plugins/platforms/ios/module.modulemap b/src/plugins/platforms/ios/module.modulemap new file mode 100644 index 0000000000..af42b3e1f5 --- /dev/null +++ b/src/plugins/platforms/ios/module.modulemap @@ -0,0 +1,4 @@ +module QIOSIntegrationPlugin { + header "qiosapplicationdelegate.h" + header "qiosintegration.h" +} diff --git a/src/plugins/platforms/ios/optional/CMakeLists.txt b/src/plugins/platforms/ios/optional/CMakeLists.txt index 6f5d754d4a..a01d7a6441 100644 --- a/src/plugins/platforms/ios/optional/CMakeLists.txt +++ b/src/plugins/platforms/ios/optional/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from optional.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause if(IOS) add_subdirectory(nsphotolibrarysupport) diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/CMakeLists.txt b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/CMakeLists.txt index 2fceac77ab..663878bde7 100644 --- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/CMakeLists.txt +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from nsphotolibrarysupport.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## QIosOptionalPlugin_NSPhotoLibrary Plugin: @@ -6,6 +7,7 @@ qt_internal_add_plugin(QIosOptionalPlugin_NSPhotoLibraryPlugin OUTPUT_NAME qiosnsphotolibrarysupport + STATIC # Force static, even in shared builds PLUGIN_TYPE platforms/darwin CLASS_NAME QIosOptionalPlugin_NSPhotoLibrary DEFAULT_IF FALSE @@ -23,17 +25,7 @@ qt_internal_add_plugin(QIosOptionalPlugin_NSPhotoLibraryPlugin Qt::GuiPrivate ) -# special case begin set_target_properties(QIosOptionalPlugin_NSPhotoLibraryPlugin PROPERTIES DISABLE_PRECOMPILE_HEADERS ON ) -# special case end - - -#### Keys ignored in scope 1:.:.:nsphotolibrarysupport.pro:<TRUE>: -# OTHER_FILES = "plugin.json" -# PLUGIN_EXTENDS = "-" - -## Scopes: -##################################################################### diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/plugin.mm b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/plugin.mm index 2a3d8c603e..693b9e4345 100644 --- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/plugin.mm +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/plugin.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 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 #include "../../qiosoptionalplugininterface.h" #include "../../qiosfiledialog.h" diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h index 3bb9fab1fc..0ad54a9e11 100644 --- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QIOSFILEENGINEASSETSLIBRARY_H #define QIOSFILEENGINEASSETSLIBRARY_H @@ -65,8 +29,8 @@ public: void setFileName(const QString &file) override; #ifndef QT_NO_FILESYSTEMITERATOR - Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override; - Iterator *endEntryList() override; + IteratorUniquePtr beginEntryList(const QString &path, QDir::Filters filters, + const QStringList &filterNames) override; #endif void setError(QFile::FileError error, const QString &str) { QAbstractFileEngine::setError(error, str); } diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm index 9cb38a3461..f7e112ab81 100644 --- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #include "qiosfileengineassetslibrary.h" @@ -47,9 +11,13 @@ #include <QtCore/qurl.h> #include <QtCore/qset.h> #include <QtCore/qthreadstorage.h> +#include <QtCore/qfileselector.h> +#include <QtCore/qpointer.h> QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + static QThreadStorage<QString> g_iteratorCurrentUrl; static QThreadStorage<QPointer<QIOSAssetData> > g_assetDataCache; @@ -222,7 +190,7 @@ public: // We can only load images from the asset library async. And this might take time, since it // involves showing the authorization dialog. But the QFile API is synchronuous, so we need to - // wait until we have access to the data. [ALAssetLibrary assetForUrl:] will shedule a block on + // wait until we have access to the data. [ALAssetLibrary assetForUrl:] will schedule a block on // the current thread. But instead of spinning the event loop to force the block to execute, we // wrap the call inside a synchronuous dispatch queue so that it executes on another thread. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); @@ -249,7 +217,7 @@ public: } if (!asset) - engine->setError(QFile::OpenError, QLatin1String("could not open image")); + engine->setError(QFile::OpenError, "could not open image"_L1); m_asset = [asset retain]; dispatch_semaphore_signal(semaphore); @@ -290,8 +258,8 @@ public: QIOSAssetEnumerator *m_enumerator; QIOSFileEngineIteratorAssetsLibrary( - QDir::Filters filters, const QStringList &nameFilters) - : QAbstractFileEngineIterator(filters, nameFilters) + const QString &path, QDir::Filters filters, const QStringList &nameFilters) + : QAbstractFileEngineIterator(path, filters, nameFilters) , m_enumerator(new QIOSAssetEnumerator([[[ALAssetsLibrary alloc] init] autorelease], ALAssetsGroupAll)) { } @@ -302,8 +270,11 @@ public: g_iteratorCurrentUrl.setLocalData(QString()); } - QString next() override + bool advance() override { + if (!m_enumerator->hasNext()) + return false; + // Cache the URL that we are about to return, since QDir will immediately create a // new file engine on the file and ask if it exists. Unless we do this, we end up // creating a new ALAsset just to verify its existence, which will be especially @@ -311,12 +282,7 @@ public: ALAsset *asset = m_enumerator->next(); QString url = QUrl::fromNSURL([asset valueForProperty:ALAssetPropertyAssetURL]).toString(); g_iteratorCurrentUrl.setLocalData(url); - return url; - } - - bool hasNext() const override - { - return m_enumerator->hasNext(); + return true; } QString currentFileName() const override @@ -377,7 +343,18 @@ bool QIOSFileEngineAssetsLibrary::close() QAbstractFileEngine::FileFlags QIOSFileEngineAssetsLibrary::fileFlags(QAbstractFileEngine::FileFlags type) const { QAbstractFileEngine::FileFlags flags; - const bool isDir = (m_assetUrl == QLatin1String("assets-library://")); + const bool isDir = (m_assetUrl == "assets-library://"_L1); + if (!isDir) { + static const QFileSelector fileSelector; + static const auto selectors = fileSelector.allSelectors(); + if (m_assetUrl.startsWith("assets-library://"_L1)) { + for (const auto &selector : selectors) { + if (m_assetUrl.endsWith(selector)) + return flags; + } + } + } + const bool exists = isDir || m_assetUrl == g_iteratorCurrentUrl.localData() || loadAsset(); if (!exists) @@ -452,24 +429,20 @@ void QIOSFileEngineAssetsLibrary::setFileName(const QString &file) // QUrl::fromLocalFile() will remove double slashes. Since the asset url is // passed around as a file name in the app (and converted to/from a file url, e.g // in QFileDialog), we need to ensure that m_assetUrl ends up being valid. - int index = file.indexOf(QLatin1String("/asset")); + qsizetype index = file.indexOf("/asset"_L1); if (index == -1) - m_assetUrl = QLatin1String("assets-library://"); + m_assetUrl = "assets-library://"_L1; else - m_assetUrl = QLatin1String("assets-library:/") + file.mid(index); + m_assetUrl = "assets-library:/"_L1 + file.mid(index); } #ifndef QT_NO_FILESYSTEMITERATOR -QAbstractFileEngine::Iterator *QIOSFileEngineAssetsLibrary::beginEntryList( - QDir::Filters filters, const QStringList &filterNames) +QAbstractFileEngine::IteratorUniquePtr +QIOSFileEngineAssetsLibrary::beginEntryList( + const QString &path, QDir::Filters filters, const QStringList &filterNames) { - return new QIOSFileEngineIteratorAssetsLibrary(filters, filterNames); -} - -QAbstractFileEngine::Iterator *QIOSFileEngineAssetsLibrary::endEntryList() -{ - return 0; + return std::make_unique<QIOSFileEngineIteratorAssetsLibrary>(path, filters, filterNames); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileenginefactory.h b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileenginefactory.h index b143357aa5..dfffbb8990 100644 --- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileenginefactory.h +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileenginefactory.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QIOSFILEENGINEFACTORY_H #define QIOSFILEENGINEFACTORY_H @@ -48,19 +12,22 @@ QT_BEGIN_NAMESPACE class QIOSFileEngineFactory : public QAbstractFileEngineHandler { + Q_DISABLE_COPY_MOVE(QIOSFileEngineFactory) public: - QAbstractFileEngine* create(const QString &fileName) const + QIOSFileEngineFactory() = default; + + std::unique_ptr<QAbstractFileEngine> create(const QString &fileName) const { - static QLatin1String assetsScheme("assets-library:"); + Q_CONSTINIT static QLatin1StringView assetsScheme("assets-library:"); #ifndef Q_OS_TVOS if (fileName.toLower().startsWith(assetsScheme)) - return new QIOSFileEngineAssetsLibrary(fileName); + return std::make_unique<QIOSFileEngineAssetsLibrary>(fileName); #else Q_UNUSED(fileName); #endif - return 0; + return {}; } }; diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h index 201b277494..bd5c0ae350 100644 --- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 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 #import <UIKit/UIKit.h> diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.mm b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.mm index 6607ee4fcf..2ebd75549f 100644 --- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.mm +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 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 #import <UIKit/UIKit.h> diff --git a/src/plugins/platforms/ios/plugin.mm b/src/plugins/platforms/ios/plugin.mm index 83760f2f39..4fba69eb85 100644 --- a/src/plugins/platforms/ios/plugin.mm +++ b/src/plugins/platforms/ios/plugin.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #include <qpa/qplatformintegrationplugin.h> #include <qpa/qplatformthemeplugin.h> @@ -43,6 +7,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + class QIOSIntegrationPlugin : public QPlatformIntegrationPlugin { Q_OBJECT @@ -54,8 +20,8 @@ class QIOSIntegrationPlugin : public QPlatformIntegrationPlugin QPlatformIntegration * QIOSIntegrationPlugin::create(const QString& system, const QStringList& paramList) { Q_UNUSED(paramList); - if (!system.compare(QLatin1String("ios"), Qt::CaseInsensitive) - || !system.compare(QLatin1String("tvos"), Qt::CaseInsensitive)) { + if (!system.compare("ios"_L1, Qt::CaseInsensitive) + || !system.compare("tvos"_L1, Qt::CaseInsensitive)) { return new QIOSIntegration; } diff --git a/src/plugins/platforms/ios/qiosapplication.swift b/src/plugins/platforms/ios/qiosapplication.swift new file mode 100644 index 0000000000..6f75ebd0b5 --- /dev/null +++ b/src/plugins/platforms/ios/qiosapplication.swift @@ -0,0 +1,82 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import SwiftUI +import CompositorServices +import QIOSIntegrationPlugin +import RealityKit + +struct QIOSSwiftApplication: App { + @UIApplicationDelegateAdaptor private var appDelegate: QIOSApplicationDelegate + + var body: some SwiftUI.Scene { + WindowGroup() { + ImmersiveSpaceControlView() + } + + ImmersiveSpace(id: "QIOSImmersiveSpace") { + CompositorLayer(configuration: QIOSLayerConfiguration()) { layerRenderer in + QIOSIntegration.instance().renderCompositorLayer(layerRenderer) + } + } + // CompositorLayer immersive spaces are always full, and should not need + // to set the immersion style, but lacking this we get a warning in the + // console about not being able to "configure an immersive space with + // selected style 'AutomaticImmersionStyle' since it is not in the list + // of supported styles for this type of content: 'FullImmersionStyle'." + .immersionStyle(selection: .constant(.full), in: .full) + } +} + +public struct QIOSLayerConfiguration: CompositorLayerConfiguration { + public func makeConfiguration(capabilities: LayerRenderer.Capabilities, + configuration: inout LayerRenderer.Configuration) { + // Use reflection to pull out underlying C handles + // FIXME: Use proper bridging APIs when available + let capabilitiesMirror = Mirror(reflecting: capabilities) + let configurationMirror = Mirror(reflecting: configuration) + QIOSIntegration.instance().configureCompositorLayer( + capabilitiesMirror.descendant("c_capabilities") as? cp_layer_renderer_capabilities_t, + configurationMirror.descendant("box", "value") as? cp_layer_renderer_configuration_t + ) + } +} + +public func runSwiftAppMain() { + QIOSSwiftApplication.main() +} + +public class ImmersiveState: ObservableObject { + static let shared = ImmersiveState() + @Published var showImmersiveSpace: Bool = false +} + +struct ImmersiveSpaceControlView: View { + @ObservedObject private var immersiveState = ImmersiveState.shared + + @Environment(\.openImmersiveSpace) var openImmersiveSpace + @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace + + var body: some View { + VStack {} + .onChange(of: immersiveState.showImmersiveSpace) { _, newValue in + Task { + if newValue { + await openImmersiveSpace(id: "QIOSImmersiveSpace") + } else { + await dismissImmersiveSpace() + } + } + } + } +} + +public class ImmersiveSpaceManager : NSObject { + @objc public static func openImmersiveSpace() { + ImmersiveState.shared.showImmersiveSpace = true + } + + @objc public static func dismissImmersiveSpace() { + ImmersiveState.shared.showImmersiveSpace = false + } +} diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.h b/src/plugins/platforms/ios/qiosapplicationdelegate.h index 722c0801a0..7e12d64cbf 100644 --- a/src/plugins/platforms/ios/qiosapplicationdelegate.h +++ b/src/plugins/platforms/ios/qiosapplicationdelegate.h @@ -1,41 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 + +#ifndef QIOSAPPLICATIONDELEGATE_H +#define QIOSAPPLICATIONDELEGATE_H #import <UIKit/UIKit.h> #import <QtGui/QtGui> @@ -44,3 +11,5 @@ @interface QIOSApplicationDelegate : UIResponder <UIApplicationDelegate> @end + +#endif // QIOSAPPLICATIONDELEGATE_H diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.mm b/src/plugins/platforms/ios/qiosapplicationdelegate.mm index c9fcfd23b6..c6e5a83874 100644 --- a/src/plugins/platforms/ios/qiosapplicationdelegate.mm +++ b/src/plugins/platforms/ios/qiosapplicationdelegate.mm @@ -1,53 +1,23 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #include "qiosapplicationdelegate.h" +#include "qiosglobal.h" #include "qiosintegration.h" #include "qiosservices.h" #include "qiosviewcontroller.h" #include "qioswindow.h" +#include "qiosscreen.h" +#include "quiwindow.h" #include <qpa/qplatformintegration.h> #include <QtCore/QtCore> +@interface QIOSWindowSceneDelegate : NSObject<UIWindowSceneDelegate> +@end + @implementation QIOSApplicationDelegate - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler @@ -86,5 +56,44 @@ return iosServices->handleUrl(QUrl::fromNSURL(url)); } +- (UISceneConfiguration *)application:(UIApplication *)application + configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession + options:(UISceneConnectionOptions *)options +{ + qCDebug(lcQpaWindowScene) << "Configuring scene for" << connectingSceneSession + << "with options" << options; + + auto *sceneConfig = connectingSceneSession.configuration; + sceneConfig.delegateClass = QIOSWindowSceneDelegate.class; + return sceneConfig; +} + @end +@implementation QIOSWindowSceneDelegate + +- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions +{ + qCDebug(lcQpaWindowScene) << "Connecting" << scene << "to" << session; + + Q_ASSERT([scene isKindOfClass:UIWindowScene.class]); + UIWindowScene *windowScene = static_cast<UIWindowScene*>(scene); + + QUIWindow *window = [[QUIWindow alloc] initWithWindowScene:windowScene]; + + QIOSScreen *screen = [&]{ + for (auto *screen : qGuiApp->screens()) { + auto *platformScreen = static_cast<QIOSScreen*>(screen->handle()); +#if !defined(Q_OS_VISIONOS) + if (platformScreen->uiScreen() == windowScene.screen) +#endif + return platformScreen; + } + Q_UNREACHABLE(); + }(); + + window.rootViewController = [[[QIOSViewController alloc] + initWithWindow:window andScreen:screen] autorelease]; +} + +@end diff --git a/src/plugins/platforms/ios/qiosapplicationstate.h b/src/plugins/platforms/ios/qiosapplicationstate.h index 8a15a4a51b..ddac0b69de 100644 --- a/src/plugins/platforms/ios/qiosapplicationstate.h +++ b/src/plugins/platforms/ios/qiosapplicationstate.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QIOSAPPLICATIONSTATE_H #define QIOSAPPLICATIONSTATE_H diff --git a/src/plugins/platforms/ios/qiosapplicationstate.mm b/src/plugins/platforms/ios/qiosapplicationstate.mm index bf4e9cc900..fdc2c70df7 100644 --- a/src/plugins/platforms/ios/qiosapplicationstate.mm +++ b/src/plugins/platforms/ios/qiosapplicationstate.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #include "qiosapplicationstate.h" @@ -50,6 +14,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + static void qRegisterApplicationStateNotifications() { NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; @@ -83,11 +49,11 @@ static void qRegisterApplicationStateNotifications() if (qt_apple_isApplicationExtension()) { // Extensions are not allowed to access UIApplication, so we assume the state is active QIOSApplicationState::handleApplicationStateChanged(UIApplicationStateActive, - QLatin1String("Extension loaded, assuming state is active")); + "Extension loaded, assuming state is active"_L1); } else { // Initialize correct startup state, which may not be the Qt default (inactive) UIApplicationState startupState = qt_apple_sharedApplication().applicationState; - QIOSApplicationState::handleApplicationStateChanged(startupState, QLatin1String("Application loaded")); + QIOSApplicationState::handleApplicationStateChanged(startupState, "Application loaded"_L1); } } Q_CONSTRUCTOR_FUNCTION(qRegisterApplicationStateNotifications) @@ -96,7 +62,7 @@ QIOSApplicationState::QIOSApplicationState() { if (!qt_apple_isApplicationExtension()) { UIApplicationState startupState = qt_apple_sharedApplication().applicationState; - QIOSApplicationState::handleApplicationStateChanged(startupState, QLatin1String("Application launched")); + QIOSApplicationState::handleApplicationStateChanged(startupState, "Application launched"_L1); } } diff --git a/src/plugins/platforms/ios/qiosbackingstore.h b/src/plugins/platforms/ios/qiosbackingstore.h deleted file mode 100644 index a32a6d2eed..0000000000 --- a/src/plugins/platforms/ios/qiosbackingstore.h +++ /dev/null @@ -1,62 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QIOSBACKINGSTORE_H -#define QIOSBACKINGSTORE_H - -#include <qpa/qplatformbackingstore.h> - -#include <QtGui/private/qrasterbackingstore_p.h> - -QT_BEGIN_NAMESPACE - -class QOpenGLPaintDevice; - -class QIOSBackingStore : public QRasterBackingStore -{ -public: - QIOSBackingStore(QWindow *window); - ~QIOSBackingStore(); - - void flush(QWindow *window, const QRegion ®ion, const QPoint &offset) override; -}; - -QT_END_NAMESPACE - -#endif // QIOSBACKINGSTORE_H diff --git a/src/plugins/platforms/ios/qiosbackingstore.mm b/src/plugins/platforms/ios/qiosbackingstore.mm deleted file mode 100644 index 074a8ee56d..0000000000 --- a/src/plugins/platforms/ios/qiosbackingstore.mm +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qiosbackingstore.h" -#include "qioswindow.h" - -#include <QtGui/QOpenGLContext> -#include <QtGui/private/qwindow_p.h> - -#include <QtOpenGL/qpa/qplatformbackingstoreopenglsupport.h> - -#include <QtDebug> - -QT_BEGIN_NAMESPACE - -/*! - \class QIOSBackingStore - - QBackingStore enables the use of QPainter to paint on a QWindow, as opposed - to rendering to a QWindow through the use of OpenGL with QOpenGLContext. -*/ -QIOSBackingStore::QIOSBackingStore(QWindow *window) - : QRasterBackingStore(window) -{ - // We use the surface both for raster operations and for GL drawing (when - // we blit the raster image), so the type needs to cover both use cases. - if (window->surfaceType() == QSurface::RasterSurface) - window->setSurfaceType(QSurface::RasterGLSurface); - - Q_ASSERT_X(window->surfaceType() != QSurface::OpenGLSurface, "QIOSBackingStore", - "QBackingStore on iOS can only be used with raster-enabled surfaces."); -} - -QIOSBackingStore::~QIOSBackingStore() -{ -} - -void QIOSBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) -{ - Q_ASSERT(!qt_window_private(window)->compositing); - - Q_UNUSED(region); - Q_UNUSED(offset); - - if (window != this->window()) { - // We skip flushing raster-based child windows, to avoid the extra cost of copying from the - // parent FBO into the child FBO. Since the child is already drawn inside the parent FBO, it - // will become visible when flushing the parent. The only case we end up not supporting is if - // the child window overlaps a sibling window that's draws using a separate QOpenGLContext. - return; - } - - static QPlatformTextureList emptyTextureList; - composeAndFlush(window, region, offset, &emptyTextureList, false); -} - -Q_CONSTRUCTOR_FUNCTION(qt_registerDefaultPlatformBackingStoreOpenGLSupport); - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosclipboard.h b/src/plugins/platforms/ios/qiosclipboard.h index 3fe9b29b71..0e46a65223 100644 --- a/src/plugins/platforms/ios/qiosclipboard.h +++ b/src/plugins/platforms/ios/qiosclipboard.h @@ -1,45 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QIOSCLIPBOARD_H #define QIOSCLIPBOARD_H +#include <QtCore/qmap.h> #include <qpa/qplatformclipboard.h> #ifndef QT_NO_CLIPBOARD diff --git a/src/plugins/platforms/ios/qiosclipboard.mm b/src/plugins/platforms/ios/qiosclipboard.mm index dc441c7194..de8ab69dff 100644 --- a/src/plugins/platforms/ios/qiosclipboard.mm +++ b/src/plugins/platforms/ios/qiosclipboard.mm @@ -1,63 +1,16 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #include "qiosclipboard.h" #ifndef QT_NO_CLIPBOARD #include <QtCore/qurl.h> -#include <QtGui/private/qmacmime_p.h> +#include <QtGui/private/qmacmimeregistry_p.h> +#include <QtGui/qutimimeconverter.h> #include <QtCore/QMimeData> #include <QtGui/QGuiApplication> -@interface UIPasteboard (QUIPasteboard) -+ (instancetype)pasteboardWithQClipboardMode:(QClipboard::Mode)mode; -@end - -@implementation UIPasteboard (QUIPasteboard) -+ (instancetype)pasteboardWithQClipboardMode:(QClipboard::Mode)mode -{ - NSString *name = (mode == QClipboard::Clipboard) ? UIPasteboardNameGeneral : UIPasteboardNameFind; - return [UIPasteboard pasteboardWithName:name create:NO]; -} -@end - // -------------------------------------------------------------------- @interface QUIClipboard : NSObject @@ -66,7 +19,6 @@ @implementation QUIClipboard { QIOSClipboard *m_qiosClipboard; NSInteger m_changeCountClipboard; - NSInteger m_changeCountFindBuffer; } - (instancetype)initWithQIOSClipboard:(QIOSClipboard *)qiosClipboard @@ -74,8 +26,7 @@ self = [super init]; if (self) { m_qiosClipboard = qiosClipboard; - m_changeCountClipboard = [UIPasteboard pasteboardWithQClipboardMode:QClipboard::Clipboard].changeCount; - m_changeCountFindBuffer = [UIPasteboard pasteboardWithQClipboardMode:QClipboard::FindBuffer].changeCount; + m_changeCountClipboard = UIPasteboard.generalPasteboard.changeCount; [[NSNotificationCenter defaultCenter] addObserver:self @@ -112,18 +63,12 @@ - (void)updatePasteboardChanged:(NSNotification *)notification { Q_UNUSED(notification); - NSInteger changeCountClipboard = [UIPasteboard pasteboardWithQClipboardMode:QClipboard::Clipboard].changeCount; - NSInteger changeCountFindBuffer = [UIPasteboard pasteboardWithQClipboardMode:QClipboard::FindBuffer].changeCount; + NSInteger changeCountClipboard = UIPasteboard.generalPasteboard.changeCount; if (m_changeCountClipboard != changeCountClipboard) { m_changeCountClipboard = changeCountClipboard; m_qiosClipboard->emitChanged(QClipboard::Clipboard); } - - if (m_changeCountFindBuffer != changeCountFindBuffer) { - m_changeCountFindBuffer = changeCountFindBuffer; - m_qiosClipboard->emitChanged(QClipboard::FindBuffer); - } } @end @@ -134,25 +79,22 @@ QT_BEGIN_NAMESPACE class QIOSMimeData : public QMimeData { public: - QIOSMimeData(QClipboard::Mode mode) : QMimeData(), m_mode(mode) { } + QIOSMimeData() : QMimeData() { } ~QIOSMimeData() { } QStringList formats() const override; QVariant retrieveData(const QString &mimeType, QMetaType type) const override; - -private: - const QClipboard::Mode m_mode; }; QStringList QIOSMimeData::formats() const { QStringList foundMimeTypes; - UIPasteboard *pb = [UIPasteboard pasteboardWithQClipboardMode:m_mode]; + UIPasteboard *pb = UIPasteboard.generalPasteboard; NSArray<NSString *> *pasteboardTypes = [pb pasteboardTypes]; for (NSUInteger i = 0; i < [pasteboardTypes count]; ++i) { - QString uti = QString::fromNSString([pasteboardTypes objectAtIndex:i]); - QString mimeType = QMacInternalPasteboardMime::flavorToMime(QMacInternalPasteboardMime::MIME_ALL, uti); + const QString uti = QString::fromNSString([pasteboardTypes objectAtIndex:i]); + const QString mimeType = QMacMimeRegistry::flavorToMime(QUtiMimeConverter::HandlerScopeFlag::All, uti); if (!mimeType.isEmpty() && !foundMimeTypes.contains(mimeType)) foundMimeTypes << mimeType; } @@ -162,17 +104,14 @@ QStringList QIOSMimeData::formats() const QVariant QIOSMimeData::retrieveData(const QString &mimeType, QMetaType) const { - UIPasteboard *pb = [UIPasteboard pasteboardWithQClipboardMode:m_mode]; + UIPasteboard *pb = UIPasteboard.generalPasteboard; NSArray<NSString *> *pasteboardTypes = [pb pasteboardTypes]; - foreach (QMacInternalPasteboardMime *converter, - QMacInternalPasteboardMime::all(QMacInternalPasteboardMime::MIME_ALL)) { - if (!converter->canConvert(mimeType, converter->flavorFor(mimeType))) - continue; - + const auto converters = QMacMimeRegistry::all(QUtiMimeConverter::HandlerScopeFlag::All); + for (QUtiMimeConverter *converter : converters) { for (NSUInteger i = 0; i < [pasteboardTypes count]; ++i) { NSString *availableUtiNSString = [pasteboardTypes objectAtIndex:i]; - QString availableUti = QString::fromNSString(availableUtiNSString); + const QString availableUti = QString::fromNSString(availableUtiNSString); if (!converter->canConvert(mimeType, availableUti)) continue; @@ -202,7 +141,7 @@ QMimeData *QIOSClipboard::mimeData(QClipboard::Mode mode) { Q_ASSERT(supportsMode(mode)); if (!m_mimeData.contains(mode)) - return *m_mimeData.insert(mode, new QIOSMimeData(mode)); + return *m_mimeData.insert(mode, new QIOSMimeData); return m_mimeData[mode]; } @@ -210,7 +149,7 @@ void QIOSClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode) { Q_ASSERT(supportsMode(mode)); - UIPasteboard *pb = [UIPasteboard pasteboardWithQClipboardMode:mode]; + UIPasteboard *pb = UIPasteboard.generalPasteboard; if (!mimeData) { pb.items = [NSArray<NSDictionary<NSString *, id> *> array]; return; @@ -219,26 +158,29 @@ void QIOSClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode) mimeData->deleteLater(); NSMutableDictionary<NSString *, id> *pbItem = [NSMutableDictionary<NSString *, id> dictionaryWithCapacity:mimeData->formats().size()]; - foreach (const QString &mimeType, mimeData->formats()) { - foreach (QMacInternalPasteboardMime *converter, - QMacInternalPasteboardMime::all(QMacInternalPasteboardMime::MIME_ALL)) { - QString uti = converter->flavorFor(mimeType); - if (uti.isEmpty() || !converter->canConvert(mimeType, uti)) + const auto formats = mimeData->formats(); + for (const QString &mimeType : formats) { + const auto converters = QMacMimeRegistry::all(QUtiMimeConverter::HandlerScopeFlag::All); + for (const QUtiMimeConverter *converter : converters) { + const QString uti = converter->utiForMime(mimeType); + if (uti.isEmpty()) continue; QVariant mimeDataAsVariant; if (mimeData->hasImage()) { mimeDataAsVariant = mimeData->imageData(); } else if (mimeData->hasUrls()) { + const auto urls = mimeData->urls(); QVariantList urlList; - for (QUrl url : mimeData->urls()) + urlList.reserve(urls.size()); + for (const QUrl& url : urls) urlList << url; mimeDataAsVariant = QVariant(urlList); } else { mimeDataAsVariant = QVariant(mimeData->data(mimeType)); } - QByteArray byteArray = converter->convertFromMime(mimeType, mimeDataAsVariant, uti).first(); + QByteArray byteArray = converter->convertFromMime(mimeType, mimeDataAsVariant, uti).constFirst(); NSData *nsData = [NSData dataWithBytes:byteArray.constData() length:byteArray.size()]; [pbItem setValue:nsData forKey:uti.toNSString()]; break; @@ -250,7 +192,7 @@ void QIOSClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode) bool QIOSClipboard::supportsMode(QClipboard::Mode mode) const { - return (mode == QClipboard::Clipboard || mode == QClipboard::FindBuffer); + return mode == QClipboard::Clipboard; } bool QIOSClipboard::ownsMode(QClipboard::Mode mode) const diff --git a/src/plugins/platforms/ios/qioscolordialog.h b/src/plugins/platforms/ios/qioscolordialog.h new file mode 100644 index 0000000000..1af718949b --- /dev/null +++ b/src/plugins/platforms/ios/qioscolordialog.h @@ -0,0 +1,38 @@ +// Copyright (C) 2022 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 + +#ifndef QIOSCOLORDIALOG_H +#define QIOSCOLORDIALOG_H + +#include <QtCore/qeventloop.h> +#include <qpa/qplatformdialoghelper.h> + +Q_FORWARD_DECLARE_OBJC_CLASS(QIOSColorDialogController); + +QT_BEGIN_NAMESPACE + +class QIOSColorDialog : public QPlatformColorDialogHelper +{ +public: + QIOSColorDialog(); + ~QIOSColorDialog(); + + void exec() override; + bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override; + void hide() override; + + void setCurrentColor(const QColor&) override; + QColor currentColor() const override; + + void updateColor(const QColor&); + +private: + QEventLoop m_eventLoop; + QIOSColorDialogController *m_viewController; + QColor m_currentColor; +}; + +QT_END_NAMESPACE + +#endif // QIOSCOLORDIALOG_H + diff --git a/src/plugins/platforms/ios/qioscolordialog.mm b/src/plugins/platforms/ios/qioscolordialog.mm new file mode 100644 index 0000000000..6651b1791d --- /dev/null +++ b/src/plugins/platforms/ios/qioscolordialog.mm @@ -0,0 +1,159 @@ +// Copyright (C) 2022 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 + +#import <UIKit/UIKit.h> + +#include <QtGui/qwindow.h> +#include <QDebug> + +#include <QtCore/private/qcore_mac_p.h> + +#include "qiosglobal.h" +#include "qioscolordialog.h" +#include "qiosintegration.h" + +@interface QIOSColorDialogController : UIColorPickerViewController <UIColorPickerViewControllerDelegate, + UIAdaptivePresentationControllerDelegate> +@end + +@implementation QIOSColorDialogController { + QIOSColorDialog *m_colorDialog; +} + +- (instancetype)initWithQIOSColorDialog:(QIOSColorDialog *)dialog +{ + if (self = [super init]) { + m_colorDialog = dialog; + self.delegate = self; + self.presentationController.delegate = self; + self.supportsAlpha = dialog->options()->testOption(QColorDialogOptions::ShowAlphaChannel); + } + return self; +} + +- (void)setQColor:(const QColor &)qColor +{ + UIColor *uiColor; + const QColor::Spec spec = qColor.spec(); + if (spec == QColor::Hsv) { + uiColor = [UIColor colorWithHue:qColor.hsvHueF() + saturation:qColor.hsvSaturationF() + brightness:qColor.valueF() + alpha:qColor.alphaF()]; + } else { + uiColor = [UIColor colorWithRed:qColor.redF() + green:qColor.greenF() + blue:qColor.blueF() + alpha:qColor.alphaF()]; + } + self.selectedColor = uiColor; +} + +- (void)updateQColor +{ + UIColor *color = self.selectedColor; + CGFloat red = 0, green = 0, blue = 0, alpha = 0; + + QColor newColor; + if ([color getRed:&red green:&green blue:&blue alpha:&alpha]) + newColor.setRgbF(red, green, blue, alpha); + else + qWarning() << "Incompatible color space"; + + + if (m_colorDialog) { + m_colorDialog->updateColor(newColor); + emit m_colorDialog->currentColorChanged(newColor); + } +} + +// ----------------------UIColorPickerViewControllerDelegate-------------------------- +- (void)colorPickerViewControllerDidSelectColor:(UIColorPickerViewController *)viewController +{ + Q_UNUSED(viewController); + [self updateQColor]; +} + +- (void)colorPickerViewControllerDidFinish:(UIColorPickerViewController *)viewController +{ + Q_UNUSED(viewController); + [self updateQColor]; + emit m_colorDialog->accept(); +} + +// ----------------------UIAdaptivePresentationControllerDelegate-------------------------- +- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController +{ + Q_UNUSED(presentationController); + emit m_colorDialog->reject(); +} + +@end + +QIOSColorDialog::QIOSColorDialog() + : m_viewController(nullptr) +{ +} + +QIOSColorDialog::~QIOSColorDialog() +{ + hide(); +} + +void QIOSColorDialog::exec() +{ + m_eventLoop.exec(QEventLoop::DialogExec); +} + +bool QIOSColorDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) +{ + Q_UNUSED(windowFlags); + + if (!m_viewController) { + m_viewController = [[QIOSColorDialogController alloc] initWithQIOSColorDialog:this]; + if (m_currentColor.isValid()) + [m_viewController setQColor:m_currentColor]; + } + + if (windowModality == Qt::ApplicationModal || windowModality == Qt::WindowModal) + m_viewController.modalInPresentation = YES; + + UIWindow *window = presentationWindow(parent); + if (!window) + return false; + + // We can't present from view controller if already presenting + if (window.rootViewController.presentedViewController) + return false; + + [window.rootViewController presentViewController:m_viewController animated:YES completion:nil]; + + return true; +} + +void QIOSColorDialog::hide() +{ + [m_viewController dismissViewControllerAnimated:YES completion:nil]; + [m_viewController release]; + m_viewController = nullptr; + m_eventLoop.exit(); +} + +void QIOSColorDialog::setCurrentColor(const QColor &color) +{ + updateColor(color); + if (m_viewController) + [m_viewController setQColor:color]; +} + +QColor QIOSColorDialog::currentColor() const +{ + return m_currentColor; +} + +void QIOSColorDialog::updateColor(const QColor &color) +{ + m_currentColor = color; +} + + diff --git a/src/plugins/platforms/ios/qioscontext.h b/src/plugins/platforms/ios/qioscontext.h index a2595877dc..99786951cb 100644 --- a/src/plugins/platforms/ios/qioscontext.h +++ b/src/plugins/platforms/ios/qioscontext.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QIOSCONTEXT_H #define QIOSCONTEXT_H diff --git a/src/plugins/platforms/ios/qioscontext.mm b/src/plugins/platforms/ios/qioscontext.mm index cb1a8a96f8..499adea0fe 100644 --- a/src/plugins/platforms/ios/qioscontext.mm +++ b/src/plugins/platforms/ios/qioscontext.mm @@ -1,41 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 + +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses #include "qioscontext.h" @@ -118,7 +84,7 @@ QSurfaceFormat QIOSContext::format() const return m_format; } -#define QT_IOS_GL_STATUS_CASE(val) case val: return QLatin1String(#val) +#define QT_IOS_GL_STATUS_CASE(val) case val: return QLatin1StringView(#val) static QString fboStatusString(GLenum status) { @@ -368,6 +334,6 @@ bool QIOSContext::isSharing() const return m_sharedContext; } -#include "moc_qioscontext.cpp" - QT_END_NAMESPACE + +#include "moc_qioscontext.cpp" diff --git a/src/plugins/platforms/ios/qiosdocumentpickercontroller.h b/src/plugins/platforms/ios/qiosdocumentpickercontroller.h index dba6f24fc5..f0b7472539 100644 --- a/src/plugins/platforms/ios/qiosdocumentpickercontroller.h +++ b/src/plugins/platforms/ios/qiosdocumentpickercontroller.h @@ -1,46 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2020 Harald Meyer. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 Harald Meyer. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #import <UIKit/UIKit.h> +#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h> #include "qiosfiledialog.h" -@interface QIOSDocumentPickerController : UIDocumentPickerViewController <UIDocumentPickerDelegate, UINavigationControllerDelegate> +@interface QIOSDocumentPickerController : UIDocumentPickerViewController <UIDocumentPickerDelegate, + UINavigationControllerDelegate, + UIAdaptivePresentationControllerDelegate> - (instancetype)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog; @end diff --git a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm index 476480c488..09e2f2f4c3 100644 --- a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm +++ b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 Harald Meyer. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 Harald Meyer. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #import <UIKit/UIKit.h> #import <MobileCoreServices/MobileCoreServices.h> @@ -48,36 +12,46 @@ - (instancetype)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog { - NSMutableArray <NSString *> *docTypes = [[[NSMutableArray alloc] init] autorelease]; - UIDocumentPickerMode importMode; - switch (fileDialog->options()->fileMode()) { - case QFileDialogOptions::AnyFile: - case QFileDialogOptions::ExistingFile: - case QFileDialogOptions::ExistingFiles: - [docTypes addObject:(__bridge NSString *)kUTTypeContent]; - [docTypes addObject:(__bridge NSString *)kUTTypeItem]; - [docTypes addObject:(__bridge NSString *)kUTTypeData]; - importMode = UIDocumentPickerModeImport; - break; - case QFileDialogOptions::Directory: - case QFileDialogOptions::DirectoryOnly: - // Directory picking is not supported because it requires - // special handling not possible with the current QFilePicker - // implementation. - - Q_UNREACHABLE(); + 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)) + { + QStringList results; + for (const QString &filter : nameFilters) + results.append(QPlatformFileDialogHelper::cleanFilterList(filter)); + + docTypes = [self computeAllowedFileTypes:results]; } - if (self = [super initWithDocumentTypes:docTypes inMode:importMode]) { + if (!docTypes.count) { + switch (fileDialog->options()->fileMode()) { + case QFileDialogOptions::AnyFile: + case QFileDialogOptions::ExistingFile: + case QFileDialogOptions::ExistingFiles: + [docTypes addObject:UTTypeContent]; + [docTypes addObject:UTTypeItem]; + [docTypes addObject:UTTypeData]; + break; + // Showing files is not supported in Directory mode in iOS + case QFileDialogOptions::Directory: + case QFileDialogOptions::DirectoryOnly: + [docTypes addObject:UTTypeFolder]; + break; + } + } + + if (self = [super initForOpeningContentTypes:docTypes]) { m_fileDialog = fileDialog; self.modalPresentationStyle = UIModalPresentationFormSheet; self.delegate = self; + self.presentationController.delegate = self; if (m_fileDialog->options()->fileMode() == QFileDialogOptions::ExistingFiles) self.allowsMultipleSelection = YES; - if (@available(ios 13.0, *)) - self.directoryURL = m_fileDialog->options()->initialDirectory().toNSURL(); + self.directoryURL = m_fileDialog->options()->initialDirectory().toNSURL(); } return self; } @@ -100,4 +74,42 @@ emit m_fileDialog->reject(); } +- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController +{ + Q_UNUSED(presentationController); + + // "Called on the delegate when the user has taken action to dismiss the + // presentation successfully, after all animations are finished. + // This is not called if the presentation is dismissed programmatically." + + // So if document picker's view was dismissed, for example by swiping it away, + // we got this method called. But not if the dialog was cancelled or a file + // was selected. + emit m_fileDialog->reject(); +} + +- (NSMutableArray<UTType*>*)computeAllowedFileTypes:(QStringList)filters +{ + QStringList fileTypes; + for (const QString &filter : filters) { + if (filter == (QLatin1String("*"))) + continue; + + if (filter.contains(u'?')) + continue; + + if (filter.count(u'*') != 1) + continue; + + auto extensions = filter.split('.', Qt::SkipEmptyParts); + fileTypes += extensions.last(); + } + + NSMutableArray<UTType *> *result = [NSMutableArray<UTType *> arrayWithCapacity:fileTypes.size()]; + for (const QString &string : fileTypes) + [result addObject:[UTType typeWithFilenameExtension:string.toNSString()]]; + + return result; +} + @end diff --git a/src/plugins/platforms/ios/qioseventdispatcher.h b/src/plugins/platforms/ios/qioseventdispatcher.h index cdbaa0931d..5eee0556f5 100644 --- a/src/plugins/platforms/ios/qioseventdispatcher.h +++ b/src/plugins/platforms/ios/qioseventdispatcher.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QIOSEVENTDISPATCHER_H #define QIOSEVENTDISPATCHER_H @@ -52,6 +16,8 @@ public: static QIOSEventDispatcher* create(); bool processPostedEvents() override; + static bool isQtApplication(); + protected: explicit QIOSEventDispatcher(QObject *parent = nullptr); }; diff --git a/src/plugins/platforms/ios/qioseventdispatcher.mm b/src/plugins/platforms/ios/qioseventdispatcher.mm index 23aac6ad46..710a834bfd 100644 --- a/src/plugins/platforms/ios/qioseventdispatcher.mm +++ b/src/plugins/platforms/ios/qioseventdispatcher.mm @@ -1,46 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 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 #include "qioseventdispatcher.h" #include "qiosapplicationdelegate.h" #include "qiosglobal.h" +#if defined(Q_OS_VISIONOS) +#include "qiosswiftintegration.h" +#endif + #include <QtCore/qprocessordetection.h> #include <QtCore/private/qcoreapplication_p.h> #include <QtCore/private/qthread_p.h> @@ -209,12 +177,16 @@ namespace QAppleLogActivity UIApplicationMain; QAppleLogActivity applicationDidFinishLaunching; } logActivity; + + static bool s_isQtApplication = false; } using namespace QT_PREPEND_NAMESPACE(QtPrivate); extern "C" int qt_main_wrapper(int argc, char *argv[]) { + s_isQtApplication = true; + @autoreleasepool { size_t defaultStackSize = 512 * kBytesPerKiloByte; // Same as secondary threads @@ -238,8 +210,16 @@ extern "C" int qt_main_wrapper(int argc, char *argv[]) logActivity.UIApplicationMain = QT_APPLE_LOG_ACTIVITY( lcEventDispatcher().isDebugEnabled(), "UIApplicationMain").enter(); +#if defined(Q_OS_VISIONOS) + Q_UNUSED(argc); + Q_UNUSED(argv); + qCDebug(lcEventDispatcher) << "Starting Swift app"; + QIOSIntegrationPluginSwift::runSwiftAppMain(); + Q_UNREACHABLE(); +#else qCDebug(lcEventDispatcher) << "Running UIApplicationMain"; return UIApplicationMain(argc, argv, nil, NSStringFromClass([QIOSApplicationDelegate class])); +#endif } } @@ -460,6 +440,11 @@ QIOSEventDispatcher::QIOSEventDispatcher(QObject *parent) QWindowSystemInterface::setSynchronousWindowSystemEvents(true); } +bool QIOSEventDispatcher::isQtApplication() +{ + return s_isQtApplication; +} + /*! Override of the CoreFoundation posted events runloop source callback so that we can send window system (QPA) events in addition to sending diff --git a/src/plugins/platforms/ios/qiosfiledialog.h b/src/plugins/platforms/ios/qiosfiledialog.h index eab05091ef..f00c154c03 100644 --- a/src/plugins/platforms/ios/qiosfiledialog.h +++ b/src/plugins/platforms/ios/qiosfiledialog.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QIOSFILEDIALOG_H #define QIOSFILEDIALOG_H @@ -75,6 +39,7 @@ private: bool showImagePickerDialog(QWindow *parent); bool showNativeDocumentPickerDialog(QWindow *parent); + void showImagePickerDialog_helper(QWindow *parent); }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosfiledialog.mm b/src/plugins/platforms/ios/qiosfiledialog.mm index a56bf25c16..cf9580c17e 100644 --- a/src/plugins/platforms/ios/qiosfiledialog.mm +++ b/src/plugins/platforms/ios/qiosfiledialog.mm @@ -1,55 +1,26 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #import <UIKit/UIKit.h> +#import <Photos/Photos.h> + #include <QtCore/qstandardpaths.h> #include <QtGui/qwindow.h> #include <QDebug> #include <QtCore/private/qcore_mac_p.h> +#include "qiosglobal.h" #include "qiosfiledialog.h" #include "qiosintegration.h" #include "qiosoptionalplugininterface.h" #include "qiosdocumentpickercontroller.h" +#include <QtCore/qpointer.h> + +using namespace Qt::StringLiterals; + QIOSFileDialog::QIOSFileDialog() : m_viewController(nullptr) { @@ -70,11 +41,15 @@ bool QIOSFileDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality window Q_UNUSED(windowFlags); Q_UNUSED(windowModality); - bool acceptOpen = options()->acceptMode() == QFileDialogOptions::AcceptOpen; - QString directory = options()->initialDirectory().toLocalFile(); + const bool acceptOpen = options()->acceptMode() == QFileDialogOptions::AcceptOpen; + const auto initialDir = options()->initialDirectory(); + const QString directory = initialDir.toLocalFile(); + // We manually add assets-library:// to the list of paths, + // when converted to QUrl, it becames a scheme. + const QString scheme = initialDir.scheme(); if (acceptOpen) { - if (directory.startsWith(QLatin1String("assets-library:"))) + if (directory.startsWith("assets-library:"_L1) || scheme == "assets-library"_L1) return showImagePickerDialog(parent); else return showNativeDocumentPickerDialog(parent); @@ -83,6 +58,12 @@ bool QIOSFileDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality window return false; } +void QIOSFileDialog::showImagePickerDialog_helper(QWindow *parent) +{ + UIWindow *window = presentationWindow(parent); + [window.rootViewController presentViewController:m_viewController animated:YES completion:nil]; +} + bool QIOSFileDialog::showImagePickerDialog(QWindow *parent) { if (!m_viewController) { @@ -101,9 +82,38 @@ bool QIOSFileDialog::showImagePickerDialog(QWindow *parent) return false; } - UIWindow *window = parent ? reinterpret_cast<UIView *>(parent->winId()).window - : qt_apple_sharedApplication().keyWindow; - [window.rootViewController presentViewController:m_viewController animated:YES completion:nil]; + // "Old style" authorization (deprecated, but we have to work with AssetsLibrary anyway). + // + // From the documentation: + // "The authorizationStatus and requestAuthorization: methods aren’t compatible with the + // limited library and return PHAuthorizationStatusAuthorized when the user authorizes your + // app for limited access only." + // + // This is good enough for us. + + const auto authStatus = [PHPhotoLibrary authorizationStatus]; + if (authStatus == PHAuthorizationStatusAuthorized) { + showImagePickerDialog_helper(parent); + } else if (authStatus == PHAuthorizationStatusNotDetermined) { + QPointer<QWindow> winGuard(parent); + QPointer<QIOSFileDialog> thisGuard(this); + [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { + dispatch_async(dispatch_get_main_queue(), ^{ + if (status == PHAuthorizationStatusAuthorized) { + if (thisGuard && winGuard) + thisGuard->showImagePickerDialog_helper(winGuard); + + } else if (thisGuard) { + emit thisGuard->reject(); + } + }); + }]; + } else { + // Treat 'Limited' (we don't know how to deal with anyway) and 'Denied' as errors. + // FIXME: logging category? + qWarning() << "QIOSFileDialog: insufficient permission, cannot pick images"; + return false; + } return true; } @@ -111,14 +121,9 @@ bool QIOSFileDialog::showImagePickerDialog(QWindow *parent) bool QIOSFileDialog::showNativeDocumentPickerDialog(QWindow *parent) { #ifndef Q_OS_TVOS - if (options()->fileMode() == QFileDialogOptions::Directory || - options()->fileMode() == QFileDialogOptions::DirectoryOnly) - return false; - m_viewController = [[QIOSDocumentPickerController alloc] initWithQIOSFileDialog:this]; - UIWindow *window = parent ? reinterpret_cast<UIView *>(parent->winId()).window - : qt_apple_sharedApplication().keyWindow; + UIWindow *window = presentationWindow(parent); [window.rootViewController presentViewController:m_viewController animated:YES completion:nil]; return true; diff --git a/src/plugins/platforms/ios/qiosfontdialog.h b/src/plugins/platforms/ios/qiosfontdialog.h new file mode 100644 index 0000000000..f0a92d0d6f --- /dev/null +++ b/src/plugins/platforms/ios/qiosfontdialog.h @@ -0,0 +1,41 @@ +// Copyright (C) 2022 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 + +#ifndef QIOSFONTDIALOG_H +#define QIOSFONTDIALOG_H + +#include <QtCore/qeventloop.h> +#include <qpa/qplatformdialoghelper.h> + +@interface QIOSFontDialogController : UIFontPickerViewController <UIFontPickerViewControllerDelegate, + UIAdaptivePresentationControllerDelegate> +@end + +QT_BEGIN_NAMESPACE + +class QIOSFontDialog : public QPlatformFontDialogHelper +{ +public: + QIOSFontDialog(); + ~QIOSFontDialog(); + + void exec() override; + + bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override; + void hide() override; + + void setCurrentFont(const QFont &) override; + QFont currentFont() const override; + + void updateCurrentFont(const QFont &); + +private: + QEventLoop m_eventLoop; + QIOSFontDialogController *m_viewController; + QFont m_currentFont; + +}; + +QT_END_NAMESPACE + +#endif // QIOSFONTDIALOG_H diff --git a/src/plugins/platforms/ios/qiosfontdialog.mm b/src/plugins/platforms/ios/qiosfontdialog.mm new file mode 100644 index 0000000000..25d0197195 --- /dev/null +++ b/src/plugins/platforms/ios/qiosfontdialog.mm @@ -0,0 +1,190 @@ +// Copyright (C) 2022 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 + +#import <UIKit/UIKit.h> + +#include <QtGui/qwindow.h> +#include <QtGui/qfontdatabase.h> +#include <QDebug> + +#include <QtCore/private/qcore_mac_p.h> +#include <QtGui/private/qfont_p.h> +#include <QtGui/private/qfontengine_p.h> + +#include "qiosglobal.h" +#include "qiosfontdialog.h" +#include "qiosintegration.h" + +@implementation QIOSFontDialogController { + QIOSFontDialog *m_fontDialog; +} + +- (instancetype)initWithQIOSFontDialog:(QIOSFontDialog *)dialog +{ + UIFontPickerViewControllerConfiguration *configuration = [[UIFontPickerViewControllerConfiguration alloc] init]; + if (dialog->options()->testOption(QFontDialogOptions::MonospacedFonts)) { + UIFontDescriptorSymbolicTraits traits = {}; + traits |= UIFontDescriptorTraitMonoSpace; + configuration.filteredTraits = traits; + } + configuration.includeFaces = YES; + if (self = [super initWithConfiguration:configuration]) { + m_fontDialog = dialog; + self.delegate = self; + self.presentationController.delegate = self; + } + [configuration release]; + return self; +} + +- (void)setQFont:(const QFont &)font +{ + QFontInfo fontInfo(font); + auto family = fontInfo.family().toNSString(); + auto size = fontInfo.pointSize(); + + NSDictionary *dictionary = @{ + static_cast<NSString *>(UIFontDescriptorFamilyAttribute): family, + static_cast<NSString *>(UIFontDescriptorSizeAttribute): [NSNumber numberWithInt:size] + }; + UIFontDescriptor *fd = [UIFontDescriptor fontDescriptorWithFontAttributes:dictionary]; + + UIFontDescriptorSymbolicTraits traits = 0; + if (font.style() == QFont::StyleItalic) + traits |= UIFontDescriptorTraitItalic; + if (font.weight() == QFont::Bold) + traits |= UIFontDescriptorTraitBold; + fd = [fd fontDescriptorWithSymbolicTraits:traits]; + + self.selectedFontDescriptor = fd; +} + +- (void)updateQFont +{ + UIFontDescriptor *font = self.selectedFontDescriptor; + if (!font) + return; + + NSDictionary *attributes = font.fontAttributes; + UIFontDescriptorSymbolicTraits traits = font.symbolicTraits; + + QFont newFont; + int size = qRound(font.pointSize); + QString family = QString::fromNSString([attributes objectForKey:UIFontDescriptorFamilyAttribute]); + if (family.isEmpty()) { + // If includeFaces is true, then the font descriptor won't + // have the UIFontDescriptorFamilyAttribute key set so we + // need to create a UIFont to get the font family + UIFont *f = [UIFont fontWithDescriptor:font size:size]; + family = QString::fromNSString(f.familyName); + } + + QString style; + if ((traits & (UIFontDescriptorTraitItalic | UIFontDescriptorTraitBold)) == (UIFontDescriptorTraitItalic | UIFontDescriptorTraitBold)) + style = "Bold Italic"; + else if (traits & UIFontDescriptorTraitItalic) + style = "Italic"; + else if (traits & UIFontDescriptorTraitBold) + style = "Bold"; + + newFont = QFontDatabase::font(family, style, size); + + if (m_fontDialog) { + m_fontDialog->updateCurrentFont(newFont); + emit m_fontDialog->currentFontChanged(newFont); + } +} + +// ----------------------UIFontPickerViewControllerDelegate-------------------------- +- (void)fontPickerViewControllerDidPickFont:(UIFontPickerViewController *)viewController +{ + [self updateQFont]; + emit m_fontDialog->accept(); +} + +- (void)fontPickerViewControllerDidCancel:(UIFontPickerViewController *)viewController +{ + Q_UNUSED(viewController); + emit m_fontDialog->reject(); +} + +// ----------------------UIAdaptivePresentationControllerDelegate-------------------------- +- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController +{ + Q_UNUSED(presentationController); + emit m_fontDialog->reject(); +} + +@end + +QIOSFontDialog::QIOSFontDialog() + : m_viewController(nullptr) +{ +} + +QIOSFontDialog::~QIOSFontDialog() +{ + hide(); +} + +void QIOSFontDialog::exec() +{ + m_eventLoop.exec(QEventLoop::DialogExec); +} + +bool QIOSFontDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) +{ + Q_UNUSED(windowFlags); + Q_UNUSED(windowModality); + + if (!m_viewController) { + m_viewController = [[QIOSFontDialogController alloc] initWithQIOSFontDialog:this]; + [m_viewController setQFont:m_currentFont]; + } + + if (windowModality == Qt::ApplicationModal || windowModality == Qt::WindowModal) + m_viewController.modalInPresentation = YES; + + UIWindow *window = presentationWindow(parent); + if (!window) + return false; + + // We can't present from view controller if already presenting + if (window.rootViewController.presentedViewController) + return false; + + [window.rootViewController presentViewController:m_viewController animated:YES completion:nil]; + + return true; +} + +void QIOSFontDialog::hide() +{ + [m_viewController dismissViewControllerAnimated:YES completion:nil]; + [m_viewController release]; + m_viewController = nullptr; + if (m_eventLoop.isRunning()) + m_eventLoop.exit(); +} + +void QIOSFontDialog::setCurrentFont(const QFont &font) +{ + if (m_currentFont == font) + return; + + m_currentFont = font; + if (m_viewController) + [m_viewController setQFont:font]; +} + +QFont QIOSFontDialog::currentFont() const +{ + return m_currentFont; +} + +void QIOSFontDialog::updateCurrentFont(const QFont &font) +{ + m_currentFont = font; +} + + diff --git a/src/plugins/platforms/ios/qiosglobal.h b/src/plugins/platforms/ios/qiosglobal.h index 8b39aded06..9428487a00 100644 --- a/src/plugins/platforms/ios/qiosglobal.h +++ b/src/plugins/platforms/ios/qiosglobal.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QIOSGLOBAL_H #define QIOSGLOBAL_H @@ -50,6 +14,7 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcQpaApplication); Q_DECLARE_LOGGING_CATEGORY(lcQpaInputMethods); Q_DECLARE_LOGGING_CATEGORY(lcQpaWindow); +Q_DECLARE_LOGGING_CATEGORY(lcQpaWindowScene); #if !defined(QT_NO_DEBUG) #define qImDebug \ @@ -62,6 +27,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaWindow); class QPlatformScreen; bool isQtApplication(); +bool isRunningOnVisionOS(); #ifndef Q_OS_TVOS Qt::ScreenOrientation toQtScreenOrientation(UIDeviceOrientation uiDeviceOrientation); @@ -70,10 +36,15 @@ UIDeviceOrientation fromQtScreenOrientation(Qt::ScreenOrientation qtOrientation) int infoPlistValue(NSString* key, int defaultValue); +class QWindow; +class QScreen; +UIWindow *presentationWindow(QWindow *); +UIView *rootViewForScreen(QScreen *); + QT_END_NAMESPACE @interface UIResponder (QtFirstResponder) -+ (id)currentFirstResponder; ++ (id)qt_currentFirstResponder; @end QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosglobal.mm b/src/plugins/platforms/ios/qiosglobal.mm index c0428dacb9..1722e09aaa 100644 --- a/src/plugins/platforms/ios/qiosglobal.mm +++ b/src/plugins/platforms/ios/qiosglobal.mm @@ -1,46 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #include "qiosglobal.h" #include "qiosapplicationdelegate.h" #include "qiosviewcontroller.h" #include "qiosscreen.h" +#include "quiwindow.h" +#include "qioseventdispatcher.h" #include <QtCore/private/qcore_mac_p.h> @@ -49,20 +15,26 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaApplication, "qt.qpa.application"); Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods"); Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window"); +Q_LOGGING_CATEGORY(lcQpaWindowScene, "qt.qpa.window.scene"); bool isQtApplication() { - if (qt_apple_isApplicationExtension()) - return false; - // Returns \c true if the plugin is in full control of the whole application. This means // that we control the application delegate and the top view controller, and can take // actions that impacts all parts of the application. The opposite means that we are // embedded inside a native iOS application, and should be more focused on playing along // with native UIControls, and less inclined to change structures that lies outside the // scope of our QWindows/UIViews. - static bool isQt = ([qt_apple_sharedApplication().delegate isKindOfClass:[QIOSApplicationDelegate class]]); - return isQt; + return QIOSEventDispatcher::isQtApplication(); +} + +bool isRunningOnVisionOS() +{ + static bool result = []{ + // This class is documented to only be available on visionOS + return NSClassFromString(@"UIWindowSceneGeometryPreferencesVision"); + }(); + return result; } #ifndef Q_OS_TVOS @@ -121,6 +93,53 @@ int infoPlistValue(NSString* key, int defaultValue) return value ? [value intValue] : defaultValue; } +UIWindow *presentationWindow(QWindow *window) +{ + UIWindow *uiWindow = window ? reinterpret_cast<UIView *>(window->winId()).window : nullptr; + if (!uiWindow) { + auto *scenes = [qt_apple_sharedApplication().connectedScenes allObjects]; + if (scenes.count > 0) { + auto *windowScene = static_cast<UIWindowScene*>(scenes[0]); + uiWindow = windowScene.keyWindow; + if (!uiWindow && windowScene.windows.count) + uiWindow = windowScene.windows[0]; + } + } + return uiWindow; +} + +UIView *rootViewForScreen(QScreen *screen) +{ + const auto *iosScreen = static_cast<QIOSScreen *>(screen->handle()); + for (UIScene *scene in [qt_apple_sharedApplication().connectedScenes allObjects]) { + if (![scene isKindOfClass:UIWindowScene.class]) + continue; + + auto *windowScene = static_cast<UIWindowScene*>(scene); + +#if !defined(Q_OS_VISIONOS) + if (windowScene.screen != iosScreen->uiScreen()) + continue; +#else + Q_UNUSED(iosScreen); +#endif + + UIWindow *uiWindow = qt_objc_cast<QUIWindow*>(windowScene.keyWindow); + if (!uiWindow) { + for (UIWindow *win in windowScene.windows) { + if (qt_objc_cast<QUIWindow*>(win)) { + uiWindow = win; + break; + } + } + } + + return uiWindow.rootViewController.view; + } + + return nullptr; +} + QT_END_NAMESPACE // ------------------------------------------------------------------------- @@ -155,7 +174,7 @@ QT_END_NAMESPACE @implementation UIResponder (QtFirstResponder) -+ (id)currentFirstResponder ++ (id)qt_currentFirstResponder { if (qt_apple_isApplicationExtension()) { qWarning() << "can't get first responder in application extensions!"; diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h index d834abda40..370032a1f8 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.h +++ b/src/plugins/platforms/ios/qiosinputcontext.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 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 #ifndef QIOSINPUTCONTEXT_H #define QIOSINPUTCONTEXT_H diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm index 1ae7f953b2..5716ad041e 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.mm +++ b/src/plugins/platforms/ios/qiosinputcontext.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 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 #include "qiosinputcontext.h" @@ -54,6 +18,8 @@ #include <QGuiApplication> #include <QtGui/private/qwindow_p.h> +#include <QtCore/qpointer.h> + // ------------------------------------------------------------------------- static QUIView *focusView() @@ -155,12 +121,12 @@ static QUIView *focusView() { [self keyboardWillOrDidChange:notification]; - UIResponder *firstResponder = [UIResponder currentFirstResponder]; + UIResponder *firstResponder = [UIResponder qt_currentFirstResponder]; if (![firstResponder isKindOfClass:[QIOSTextInputResponder class]]) return; // Enable hide-keyboard gesture - self.enabled = YES; + self.enabled = m_context->isInputPanelVisible(); m_context->scrollToCursor(); } @@ -210,7 +176,11 @@ static QUIView *focusView() { [super touchesBegan:touches withEvent:event]; - Q_ASSERT(m_context->isInputPanelVisible()); + if (!m_context->isInputPanelVisible()) { + qImDebug("keyboard was hidden by sliding it down, disabling hide-keyboard gesture"); + self.enabled = NO; + return; + } if ([touches count] != 1) self.state = UIGestureRecognizerStateFailed; @@ -264,7 +234,7 @@ static QUIView *focusView() if (self.state == UIGestureRecognizerStateBegan) { qImDebug("hide keyboard gesture was triggered"); - UIResponder *firstResponder = [UIResponder currentFirstResponder]; + UIResponder *firstResponder = [UIResponder qt_currentFirstResponder]; Q_ASSERT([firstResponder isKindOfClass:[QIOSTextInputResponder class]]); [firstResponder resignFirstResponder]; } @@ -333,11 +303,7 @@ QIOSInputContext::QIOSInputContext() , m_keyboardHideGesture([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this]) , m_textResponder(0) { - if (isQtApplication()) { - QIOSScreen *iosScreen = static_cast<QIOSScreen*>(QGuiApplication::primaryScreen()->handle()); - [iosScreen->uiWindow() addGestureRecognizer:m_keyboardHideGesture]; - } - + Q_ASSERT(!qGuiApp->focusWindow()); connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &QIOSInputContext::focusWindowChanged); } @@ -382,7 +348,7 @@ void QIOSInputContext::clearCurrentFocusObject() void QIOSInputContext::updateKeyboardState(NSNotification *notification) { -#ifdef Q_OS_TVOS +#if defined(Q_OS_TVOS) || defined(Q_OS_VISIONOS) Q_UNUSED(notification); #else static CGRect currentKeyboardRect = CGRectZero; @@ -402,7 +368,7 @@ void QIOSInputContext::updateKeyboardState(NSNotification *notification) // The isInputPanelVisible() property is based on whether or not the virtual keyboard // is visible on screen, and does not follow the logic of the iOS WillShow and WillHide // notifications which are not emitted for undocked keyboards, and are buggy when dealing - // with input-accesosory-views. The reason for using frameEnd here (the future state), + // with input-accessory-views. The reason for using frameEnd here (the future state), // instead of the current state reflected in frameBegin, is that QInputMethod::isVisible() // is documented to reflect the future state in the case of animated transitions. m_keyboardState.keyboardVisible = CGRectIntersectsRect(frameEnd, [UIScreen mainScreen].bounds); @@ -472,6 +438,7 @@ UIView *QIOSInputContext::scrollableRootView() void QIOSInputContext::scrollToCursor() { +#if !defined(Q_OS_VISIONOS) if (!isQtApplication()) return; @@ -528,6 +495,7 @@ void QIOSInputContext::scrollToCursor() } else { scroll(0); } +#endif } void QIOSInputContext::scroll(int y) @@ -639,12 +607,15 @@ void QIOSInputContext::setFocusObject(QObject *focusObject) void QIOSInputContext::focusWindowChanged(QWindow *focusWindow) { - Q_UNUSED(focusWindow); - qImDebug() << "new focus window =" << focusWindow; reset(); + if (isQtApplication()) { + [m_keyboardHideGesture.view removeGestureRecognizer:m_keyboardHideGesture]; + [focusView().window addGestureRecognizer:m_keyboardHideGesture]; + } + // The keyboard rectangle depend on the focus window, so // we need to re-evaluate the keyboard state. updateKeyboardState(); @@ -735,11 +706,33 @@ bool QIOSInputContext::inputMethodAccepted() const */ void QIOSInputContext::reset() { - qImDebug("updating Qt::ImQueryAll and unmarking text"); + qImDebug("releasing text responder"); + + // UIKit will sometimes, for unknown reasons, unset the input delegate on the + // current text responder. This seems to happen as a result of us calling + // [self.inputDelegate textDidChange:self] from [m_textResponder reset]. + // But it won't be set to nil directly, only after a character is typed on + // the input panel after the reset. This strange behavior seems to be related + // to us overriding [QUIView setInteraction] to ignore UITextInteraction. If we + // didn't do that, the delegate would be kept. But not overriding that function + // has its own share of issues, so it seems better to keep that way for now. + // Instead, we choose to recreate the text responder as a brute-force solution + // until we have better knowledge of what is going on (or implement the new + // UITextInteraction protocol). + const auto oldResponder = m_textResponder; + [m_textResponder reset]; + [m_textResponder autorelease]; + m_textResponder = nullptr; update(Qt::ImQueryAll); - [m_textResponder reset]; + // If update() didn't end up creating a new text responder, oldResponder will still be + // the first responder. In that case we need to resign it, so that the input panel hides. + // (the input panel will apparently not hide if the first responder is only released). + if ([oldResponder isFirstResponder]) { + qImDebug("IM not enabled, resigning autoreleased text responder as first responder"); + [oldResponder resignFirstResponder]; + } } /*! diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h index f1e0fe0641..53f64c1748 100644 --- a/src/plugins/platforms/ios/qiosintegration.h +++ b/src/plugins/platforms/ios/qiosintegration.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QPLATFORMINTEGRATION_UIKIT_H #define QPLATFORMINTEGRATION_UIKIT_H @@ -47,15 +11,29 @@ #include <QtCore/private/qfactoryloader_p.h> #include "qiosapplicationstate.h" -#ifndef Q_OS_TVOS + +#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS) #include "qiostextinputoverlay.h" #endif +#if defined(Q_OS_VISIONOS) +#include <swift/bridging> +#endif + QT_BEGIN_NAMESPACE +using namespace QNativeInterface; + class QIOSServices; -class QIOSIntegration : public QPlatformNativeInterface, public QPlatformIntegration +class +#if defined(Q_OS_VISIONOS) + SWIFT_IMMORTAL_REFERENCE +#endif +QIOSIntegration : public QPlatformNativeInterface, public QPlatformIntegration +#if defined(Q_OS_VISIONOS) + , public QVisionOSApplication +#endif { Q_OBJECT public: @@ -67,15 +45,21 @@ public: bool hasCapability(Capability cap) const override; QPlatformWindow *createPlatformWindow(QWindow *window) const override; + QPlatformWindow *createForeignWindow(QWindow *window, WId nativeHandle) const override; QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override; +#if QT_CONFIG(opengl) QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override; +#endif + QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override; QPlatformFontDatabase *fontDatabase() const override; -#ifndef QT_NO_CLIPBOARD + +#if QT_CONFIG(clipboard) QPlatformClipboard *clipboard() const override; #endif + QPlatformInputContext *inputContext() const override; QPlatformServices *services() const override; @@ -88,12 +72,14 @@ public: QPlatformNativeInterface *nativeInterface() const override; QPointingDevice *touchDevice(); -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) QPlatformAccessibility *accessibility() const override; #endif void beep() const override; + void setApplicationBadge(qint64 number) override; + static QIOSIntegration *instance(); // -- QPlatformNativeInterface -- @@ -104,9 +90,20 @@ public: QIOSApplicationState applicationState; +#if defined(Q_OS_VISIONOS) + void openImmersiveSpace() override; + void dismissImmersiveSpace() override; + + using CompositorLayer = QVisionOSApplication::ImmersiveSpaceCompositorLayer; + void setImmersiveSpaceCompositorLayer(CompositorLayer *layer) override; + + void configureCompositorLayer(cp_layer_renderer_capabilities_t, cp_layer_renderer_configuration_t); + void renderCompositorLayer(cp_layer_renderer_t); +#endif + private: QPlatformFontDatabase *m_fontDatabase; -#ifndef Q_OS_TVOS +#if QT_CONFIG(clipboard) QPlatformClipboard *m_clipboard; #endif QPlatformInputContext *m_inputContext; @@ -114,9 +111,13 @@ private: QIOSServices *m_platformServices; mutable QPlatformAccessibility *m_accessibility; QFactoryLoader *m_optionalPlugins; -#ifndef Q_OS_TVOS +#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS) QIOSTextInputOverlay m_textInputOverlay; #endif + +#if defined(Q_OS_VISIONOS) + CompositorLayer *m_immersiveSpaceCompositorLayer = nullptr; +#endif }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index 8938f97ae5..2c32957c03 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -1,51 +1,15 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 + +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses #include "qiosintegration.h" #include "qioseventdispatcher.h" #include "qiosglobal.h" #include "qioswindow.h" -#include "qiosbackingstore.h" #include "qiosscreen.h" #include "qiosplatformaccessibility.h" -#include "qioscontext.h" -#ifndef Q_OS_TVOS +#if QT_CONFIG(clipboard) #include "qiosclipboard.h" #endif #include "qiosinputcontext.h" @@ -53,23 +17,35 @@ #include "qiosservices.h" #include "qiosoptionalplugininterface.h" +#if defined(Q_OS_VISIONOS) +#include "qiosswiftintegration.h" +#endif + #include <QtGui/qpointingdevice.h> #include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qrhibackingstore_p.h> #include <qoffscreensurface.h> #include <qpa/qplatformoffscreensurface.h> #include <QtGui/private/qcoretextfontdatabase_p.h> -#include <QtGui/private/qmacmime_p.h> +#include <QtGui/private/qmacmimeregistry_p.h> +#include <QtGui/qutimimeconverter.h> #include <QDir> #include <QOperatingSystemVersion> +#if QT_CONFIG(opengl) +#include "qioscontext.h" +#endif + #import <AudioToolbox/AudioServices.h> #include <QtDebug> QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + class QCoreTextFontEngine; QIOSIntegration *QIOSIntegration::instance() @@ -79,13 +55,13 @@ QIOSIntegration *QIOSIntegration::instance() QIOSIntegration::QIOSIntegration() : m_fontDatabase(new QCoreTextFontDatabaseEngineFactory<QCoreTextFontEngine>) -#if !defined(Q_OS_TVOS) && !defined(QT_NO_CLIPBOARD) +#if QT_CONFIG(clipboard) , m_clipboard(new QIOSClipboard) #endif , m_inputContext(0) , m_platformServices(new QIOSServices) , m_accessibility(0) - , m_optionalPlugins(new QFactoryLoader(QIosOptionalPluginInterface_iid, QLatin1String("/platforms/darwin"))) + , m_optionalPlugins(new QFactoryLoader(QIosOptionalPluginInterface_iid, "/platforms/darwin"_L1)) { if (Q_UNLIKELY(!qt_apple_isApplicationExtension() && !qt_apple_sharedApplication())) { qFatal("Error: You are creating QApplication before calling UIApplicationMain.\n" \ @@ -100,6 +76,10 @@ QIOSIntegration::QIOSIntegration() void QIOSIntegration::initialize() { +#if defined(Q_OS_VISIONOS) + // Qt requires a screen, so let's give it a dummy one + QWindowSystemInterface::handleScreenAdded(new QIOSScreen); +#else UIScreen *mainScreen = [UIScreen mainScreen]; NSMutableArray<UIScreen *> *screens = [[[UIScreen screens] mutableCopy] autorelease]; if (![screens containsObject:mainScreen]) { @@ -109,6 +89,7 @@ void QIOSIntegration::initialize() for (UIScreen *screen in screens) QWindowSystemInterface::handleScreenAdded(new QIOSScreen(screen)); +#endif // Depends on a primary screen being present m_inputContext = new QIOSInputContext; @@ -116,14 +97,16 @@ void QIOSIntegration::initialize() m_touchDevice = new QPointingDevice; m_touchDevice->setType(QInputDevice::DeviceType::TouchScreen); QPointingDevice::Capabilities touchCapabilities = QPointingDevice::Capability::Position | QPointingDevice::Capability::NormalizedPosition; +#if !defined(Q_OS_VISIONOS) if (mainScreen.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) touchCapabilities |= QPointingDevice::Capability::Pressure; +#endif m_touchDevice->setCapabilities(touchCapabilities); QWindowSystemInterface::registerInputDevice(m_touchDevice); #if QT_CONFIG(tabletevent) QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false); #endif - QMacInternalPasteboardMime::initializeMimeTypes(); + QMacMimeRegistry::initializeMimeTypes(); qsizetype size = QList<QPluginParsedMetaData>(m_optionalPlugins->metaData()).size(); for (qsizetype i = 0; i < size; ++i) @@ -135,11 +118,12 @@ QIOSIntegration::~QIOSIntegration() delete m_fontDatabase; m_fontDatabase = 0; -#if !defined(Q_OS_TVOS) && !defined(QT_NO_CLIPBOARD) +#if QT_CONFIG(clipboard) delete m_clipboard; m_clipboard = 0; #endif - QMacInternalPasteboardMime::destroyMimeTypes(); + + QMacMimeRegistry::destroyMimeTypes(); delete m_inputContext; m_inputContext = 0; @@ -160,11 +144,15 @@ QIOSIntegration::~QIOSIntegration() bool QIOSIntegration::hasCapability(Capability cap) const { switch (cap) { +#if QT_CONFIG(opengl) case BufferQueueingOpenGL: return true; case OpenGL: case ThreadedOpenGL: return true; + case RasterGLSurface: + return true; +#endif case ThreadedPixmaps: return true; case MultipleWindows: @@ -173,7 +161,7 @@ bool QIOSIntegration::hasCapability(Capability cap) const return false; case ApplicationState: return true; - case RasterGLSurface: + case ForeignWindows: return true; default: return QPlatformIntegration::hasCapability(cap); @@ -185,17 +173,23 @@ QPlatformWindow *QIOSIntegration::createPlatformWindow(QWindow *window) const return new QIOSWindow(window); } -// Used when the QWindow's surface type is set by the client to QSurface::RasterSurface +QPlatformWindow *QIOSIntegration::createForeignWindow(QWindow *window, WId nativeHandle) const +{ + return new QIOSWindow(window, nativeHandle); +} + QPlatformBackingStore *QIOSIntegration::createPlatformBackingStore(QWindow *window) const { - return new QIOSBackingStore(window); + return new QRhiBackingStore(window); } +#if QT_CONFIG(opengl) // Used when the QWindow's surface type is set by the client to QSurface::OpenGLSurface QPlatformOpenGLContext *QIOSIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const { return new QIOSContext(context); } +#endif class QIOSOffscreenSurface : public QPlatformOffscreenSurface { @@ -225,14 +219,10 @@ QPlatformFontDatabase * QIOSIntegration::fontDatabase() const return m_fontDatabase; } -#ifndef QT_NO_CLIPBOARD +#if QT_CONFIG(clipboard) QPlatformClipboard *QIOSIntegration::clipboard() const { -#ifndef Q_OS_TVOS return m_clipboard; -#else - return QPlatformIntegration::clipboard(); -#endif } #endif @@ -266,12 +256,12 @@ QVariant QIOSIntegration::styleHint(StyleHint hint) const QStringList QIOSIntegration::themeNames() const { - return QStringList(QLatin1String(QIOSTheme::name)); + return QStringList(QLatin1StringView(QIOSTheme::name)); } QPlatformTheme *QIOSIntegration::createPlatformTheme(const QString &name) const { - if (name == QLatin1String(QIOSTheme::name)) + if (name == QLatin1StringView(QIOSTheme::name)) return new QIOSTheme; return QPlatformIntegration::createPlatformTheme(name); @@ -282,7 +272,7 @@ QPointingDevice *QIOSIntegration::touchDevice() return m_touchDevice; } -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) QPlatformAccessibility *QIOSIntegration::accessibility() const { if (!m_accessibility) @@ -303,6 +293,44 @@ void QIOSIntegration::beep() const #endif } +void QIOSIntegration::setApplicationBadge(qint64 number) +{ + UIApplication.sharedApplication.applicationIconBadgeNumber = number; +} + +// --------------------------------------------------------- + +#if defined(Q_OS_VISIONOS) +void QIOSIntegration::openImmersiveSpace() +{ + [ImmersiveSpaceManager openImmersiveSpace]; +} + +void QIOSIntegration::dismissImmersiveSpace() +{ + [ImmersiveSpaceManager dismissImmersiveSpace]; +} + +void QIOSIntegration::setImmersiveSpaceCompositorLayer(CompositorLayer *layer) +{ + m_immersiveSpaceCompositorLayer = layer; +} + +void QIOSIntegration::configureCompositorLayer(cp_layer_renderer_capabilities_t capabilities, + cp_layer_renderer_configuration_t configuration) +{ + if (m_immersiveSpaceCompositorLayer) + m_immersiveSpaceCompositorLayer->configure(capabilities, configuration); +} + +void QIOSIntegration::renderCompositorLayer(cp_layer_renderer_t renderer) +{ + if (m_immersiveSpaceCompositorLayer) + m_immersiveSpaceCompositorLayer->render(renderer); +} + +#endif + // --------------------------------------------------------- void *QIOSIntegration::nativeResourceForWindow(const QByteArray &resource, QWindow *window) @@ -322,6 +350,6 @@ void *QIOSIntegration::nativeResourceForWindow(const QByteArray &resource, QWind // --------------------------------------------------------- -#include "moc_qiosintegration.cpp" - QT_END_NAMESPACE + +#include "moc_qiosintegration.cpp" diff --git a/src/plugins/platforms/ios/qiosmenu.h b/src/plugins/platforms/ios/qiosmenu.h index 32022a3bb8..b0c8e7e10c 100644 --- a/src/plugins/platforms/ios/qiosmenu.h +++ b/src/plugins/platforms/ios/qiosmenu.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QIOSMENU_H #define QIOSMENU_H @@ -47,6 +11,8 @@ #import "quiview.h" +#include <QtCore/qpointer.h> + class QIOSMenu; @class QUIMenuController; @class QUIPickerView; diff --git a/src/plugins/platforms/ios/qiosmenu.mm b/src/plugins/platforms/ios/qiosmenu.mm index 737d7798ab..227ad2c7f5 100644 --- a/src/plugins/platforms/ios/qiosmenu.mm +++ b/src/plugins/platforms/ios/qiosmenu.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #include <qglobal.h> #include <qguiapplication.h> @@ -527,7 +491,7 @@ QIOSMenuItemList QIOSMenu::filterFirstResponderActions(const QIOSMenuItemList &m // In case of QIOSTextResponder, edit actions will be converted to key events that ends up // triggering the shortcuts of the filtered menu items. QIOSMenuItemList filteredMenuItems; - UIResponder *responder = [UIResponder currentFirstResponder]; + UIResponder *responder = [UIResponder qt_currentFirstResponder]; for (int i = 0; i < menuItems.count(); ++i) { QIOSMenuItem *menuItem = menuItems.at(i); @@ -538,8 +502,8 @@ QIOSMenuItemList QIOSMenu::filterFirstResponderActions(const QIOSMenuItemList &m || (shortcut == QKeySequence::Paste && [responder canPerformAction:@selector(paste:) withSender:nil]) || (shortcut == QKeySequence::Delete && [responder canPerformAction:@selector(delete:) withSender:nil]) || (shortcut == QKeySequence::SelectAll && [responder canPerformAction:@selector(selectAll:) withSender:nil]) - || (shortcut == QKeySequence::Undo && [responder canPerformAction:@selector(undo:) withSender:nil]) - || (shortcut == QKeySequence::Redo && [responder canPerformAction:@selector(redo:) withSender:nil]) + || (shortcut == QKeySequence::Undo && [responder canPerformAction:@selector(undo) withSender:nil]) + || (shortcut == QKeySequence::Redo && [responder canPerformAction:@selector(redo) withSender:nil]) || (shortcut == QKeySequence::Bold && [responder canPerformAction:@selector(toggleBoldface:) withSender:nil]) || (shortcut == QKeySequence::Italic && [responder canPerformAction:@selector(toggleItalics:) withSender:nil]) || (shortcut == QKeySequence::Underline && [responder canPerformAction:@selector(toggleUnderline:) withSender:nil])) { diff --git a/src/plugins/platforms/ios/qiosmessagedialog.h b/src/plugins/platforms/ios/qiosmessagedialog.h index 913fe0c2e9..7987956d45 100644 --- a/src/plugins/platforms/ios/qiosmessagedialog.h +++ b/src/plugins/platforms/ios/qiosmessagedialog.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QIOSMESSAGEDIALOG_H #define QIOSMESSAGEDIALOG_H diff --git a/src/plugins/platforms/ios/qiosmessagedialog.mm b/src/plugins/platforms/ios/qiosmessagedialog.mm index 254922701a..7fbd5d8729 100644 --- a/src/plugins/platforms/ios/qiosmessagedialog.mm +++ b/src/plugins/platforms/ios/qiosmessagedialog.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #import <UIKit/UIKit.h> @@ -47,8 +11,11 @@ #include "qiosglobal.h" #include "quiview.h" +#include "qiosscreen.h" #include "qiosmessagedialog.h" +using namespace Qt::StringLiterals; + QIOSMessageDialog::QIOSMessageDialog() : m_alertController(nullptr) { @@ -63,7 +30,7 @@ inline QString QIOSMessageDialog::messageTextPlain() { // Concatenate text fragments, and remove HTML tags const QSharedPointer<QMessageDialogOptions> &opt = options(); - const QString &lineShift = QStringLiteral("\n\n"); + constexpr auto lineShift = "\n\n"_L1; const QString &informativeText = opt->informativeText(); const QString &detailedText = opt->detailedText(); @@ -73,7 +40,7 @@ inline QString QIOSMessageDialog::messageTextPlain() if (!detailedText.isEmpty()) text += lineShift + detailedText; - text.replace(QLatin1String("<p>"), QStringLiteral("\n"), Qt::CaseInsensitive); + text.replace("<p>"_L1, "\n"_L1, Qt::CaseInsensitive); text.remove(QRegularExpression(QStringLiteral("<[^>]*>"))); return text; @@ -125,6 +92,9 @@ bool QIOSMessageDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality win || windowModality == Qt::NonModal) // We can only do modal dialogs return false; + if (!options()->checkBoxLabel().isNull()) + return false; // Can't support + m_alertController = [[UIAlertController alertControllerWithTitle:options()->windowTitle().toNSString() message:messageTextPlain().toNSString() @@ -146,7 +116,18 @@ bool QIOSMessageDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality win [m_alertController addAction:createAction(NoButton)]; } - UIWindow *window = parent ? reinterpret_cast<UIView *>(parent->winId()).window : qt_apple_sharedApplication().keyWindow; + UIWindow *window = presentationWindow(parent); + if (!window) + return false; + + if (window.hidden) { + // With a window hidden, an attempt to present view controller + // below fails with a warning, that a view "is not a part of + // any view hierarchy". The UIWindow is initially hidden, + // as unhiding it is what hides the splash screen. + window.hidden = NO; + } + [window.rootViewController presentViewController:m_alertController animated:YES completion:nil]; return true; } diff --git a/src/plugins/platforms/ios/qiosoptionalplugininterface.h b/src/plugins/platforms/ios/qiosoptionalplugininterface.h index a88697aae6..c9d96409b9 100644 --- a/src/plugins/platforms/ios/qiosoptionalplugininterface.h +++ b/src/plugins/platforms/ios/qiosoptionalplugininterface.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 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 #ifndef QIOPLUGININTERFACE_H #define QIOPLUGININTERFACE_H diff --git a/src/plugins/platforms/ios/qiosplatformaccessibility.h b/src/plugins/platforms/ios/qiosplatformaccessibility.h index 989eaa4fb8..96efc663ba 100644 --- a/src/plugins/platforms/ios/qiosplatformaccessibility.h +++ b/src/plugins/platforms/ios/qiosplatformaccessibility.h @@ -1,48 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QIOSPLATFORMACCESSIBILITY_H #define QIOSPLATFORMACCESSIBILITY_H #include <qpa/qplatformaccessibility.h> -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosplatformaccessibility.mm b/src/plugins/platforms/ios/qiosplatformaccessibility.mm index aef4216e03..eb18ee637e 100644 --- a/src/plugins/platforms/ios/qiosplatformaccessibility.mm +++ b/src/plugins/platforms/ios/qiosplatformaccessibility.mm @@ -1,48 +1,15 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 + +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses #include "qiosplatformaccessibility.h" -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) #include <QtGui/QtGui> #include "qioswindow.h" +#include "quiaccessibilityelement.h" QIOSPlatformAccessibility::QIOSPlatformAccessibility() {} @@ -61,8 +28,6 @@ void invalidateCache(QAccessibleInterface *iface) // This will invalidate everything regardless of what window the // interface belonged to. We might want to revisit this strategy later. // (Therefore this function still takes the interface as argument) - // It is also responsible for the bug that focus gets temporary lost - // when items get added or removed from the screen foreach (QWindow *win, QGuiApplication::topLevelWindows()) { if (win && win->handle()) { QT_PREPEND_NAMESPACE(QIOSWindow) *window = static_cast<QT_PREPEND_NAMESPACE(QIOSWindow) *>(win->handle()); @@ -74,14 +39,35 @@ void invalidateCache(QAccessibleInterface *iface) void QIOSPlatformAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) { - if (!isActive() || !event->accessibleInterface()) + auto *accessibleInterface = event->accessibleInterface(); + if (!isActive() || !accessibleInterface) return; switch (event->type()) { + case QAccessible::Focus: { + auto *element = [QMacAccessibilityElement elementWithId:event->uniqueId()]; + Q_ASSERT(element); + // There's no NSAccessibilityFocusedUIElementChangedNotification, like we have on + // macOS. Instead, the documentation for UIAccessibilityLayoutChangedNotification + // specifies that the optional argument to UIAccessibilityPostNotification is the + // accessibility element for VoiceOver to move to after processing the notification. + UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, element); + break; + } case QAccessible::ObjectCreated: case QAccessible::ObjectShow: case QAccessible::ObjectHide: case QAccessible::ObjectDestroyed: - invalidateCache(event->accessibleInterface()); + invalidateCache(accessibleInterface); + switch (accessibleInterface->role()) { + case QAccessible::Window: + case QAccessible::Dialog: + // Bigger changes to the UI require a full reset of VoiceOver + UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil); + break; + default: + // While smaller changes can be handled by re-reading the layout + UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); + } break; default: break; diff --git a/src/plugins/platforms/ios/qiosscreen.h b/src/plugins/platforms/ios/qiosscreen.h index ee0a816142..dd69428390 100644 --- a/src/plugins/platforms/ios/qiosscreen.h +++ b/src/plugins/platforms/ios/qiosscreen.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QIOSSCREEN_H #define QIOSSCREEN_H @@ -46,10 +10,6 @@ @class QIOSOrientationListener; -@interface QUIWindow : UIWindow -@property (nonatomic, readonly) BOOL sendingEvent; -@end - QT_BEGIN_NAMESPACE class QIOSScreen : public QObject, public QPlatformScreen @@ -57,7 +17,11 @@ class QIOSScreen : public QObject, public QPlatformScreen Q_OBJECT public: +#if !defined(Q_OS_VISIONOS) QIOSScreen(UIScreen *screen); +#else + QIOSScreen(); +#endif ~QIOSScreen(); QString name() const override; @@ -76,8 +40,9 @@ public: QPixmap grabWindow(WId window, int x, int y, int width, int height) const override; +#if !defined(Q_OS_VISIONOS) UIScreen *uiScreen() const; - UIWindow *uiWindow() const; +#endif void setUpdatesPaused(bool); @@ -86,15 +51,17 @@ public: private: void deliverUpdateRequests() const; - UIScreen *m_uiScreen; - UIWindow *m_uiWindow; +#if !defined(Q_OS_VISIONOS) + UIScreen *m_uiScreen = nullptr; +#endif QRect m_geometry; QRect m_availableGeometry; int m_depth; +#if !defined(Q_OS_VISIONOS) uint m_physicalDpi; +#endif QSizeF m_physicalSize; - QIOSOrientationListener *m_orientationListener; - CADisplayLink *m_displayLink; + CADisplayLink *m_displayLink = nullptr; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm index 2a9968c841..7559979f33 100644 --- a/src/plugins/platforms/ios/qiosscreen.mm +++ b/src/plugins/platforms/ios/qiosscreen.mm @@ -1,41 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 + +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses #include "qiosglobal.h" #include "qiosintegration.h" @@ -46,11 +12,13 @@ #include "qiosviewcontroller.h" #include "quiview.h" #include "qiostheme.h" +#include "quiwindow.h" #include <QtCore/private/qcore_mac_p.h> #include <QtGui/qpointingdevice.h> #include <QtGui/private/qwindow_p.h> +#include <QtGui/private/qguiapplication_p.h> #include <private/qcoregraphics_p.h> #include <qpa/qwindowsysteminterface.h> @@ -79,6 +47,7 @@ typedef void (^DisplayLinkBlock)(CADisplayLink *displayLink); // ------------------------------------------------------------------------- +#if !defined(Q_OS_VISIONOS) static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen) { foreach (QScreen *screen, QGuiApplication::screens()) { @@ -108,14 +77,17 @@ static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen) + (void)screenConnected:(NSNotification*)notification { - Q_ASSERT_X(QIOSIntegration::instance(), Q_FUNC_INFO, - "Screen connected before QIOSIntegration creation"); + if (!QIOSIntegration::instance()) + return; // Will be added when QIOSIntegration is created QWindowSystemInterface::handleScreenAdded(new QIOSScreen([notification object])); } + (void)screenDisconnected:(NSNotification*)notification { + if (!QIOSIntegration::instance()) + return; + QIOSScreen *screen = qtPlatformScreenFor([notification object]); Q_ASSERT_X(screen, Q_FUNC_INFO, "Screen disconnected that we didn't know about"); @@ -124,6 +96,9 @@ static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen) + (void)screenModeChanged:(NSNotification*)notification { + if (!QIOSIntegration::instance()) + return; + QIOSScreen *screen = qtPlatformScreenFor([notification object]); Q_ASSERT_X(screen, Q_FUNC_INFO, "Screen changed that we didn't know about"); @@ -132,103 +107,15 @@ static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen) @end -// ------------------------------------------------------------------------- - -@interface QIOSOrientationListener : NSObject -@end - -@implementation QIOSOrientationListener { - QIOSScreen *m_screen; -} - -- (instancetype)initWithQIOSScreen:(QIOSScreen *)screen -{ - self = [super init]; - if (self) { - m_screen = screen; -#ifndef Q_OS_TVOS - [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(orientationChanged:) - name:@"UIDeviceOrientationDidChangeNotification" object:nil]; -#endif - } - return self; -} - -- (void)dealloc -{ -#ifndef Q_OS_TVOS - [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; - [[NSNotificationCenter defaultCenter] - removeObserver:self - name:@"UIDeviceOrientationDidChangeNotification" object:nil]; -#endif - [super dealloc]; -} - -- (void)orientationChanged:(NSNotification *)notification -{ - Q_UNUSED(notification); - m_screen->updateProperties(); -} - -@end - -@interface UIScreen (Compatibility) -@property (nonatomic, readonly) CGRect qt_applicationFrame; -@end - -@implementation UIScreen (Compatibility) -- (CGRect)qt_applicationFrame -{ -#ifdef Q_OS_IOS - return self.applicationFrame; -#else - return self.bounds; -#endif -} -@end - -// ------------------------------------------------------------------------- - -@implementation QUIWindow - -- (instancetype)initWithFrame:(CGRect)frame -{ - if ((self = [super initWithFrame:frame])) - self->_sendingEvent = NO; - - return self; -} - -- (void)sendEvent:(UIEvent *)event -{ - QScopedValueRollback<BOOL> sendingEvent(self->_sendingEvent, YES); - [super sendEvent:event]; -} - -- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection -{ - [super traitCollectionDidChange:previousTraitCollection]; - - if (@available(iOS 12, *)) { - if (self.screen == UIScreen.mainScreen) { - if (previousTraitCollection.userInterfaceStyle != self.traitCollection.userInterfaceStyle) { - QIOSTheme::initializeSystemPalette(); - QWindowSystemInterface::handleThemeChange<QWindowSystemInterface::SynchronousDelivery>(nullptr); - } - } - } -} - -@end +#endif // !defined(Q_OS_VISIONOS) // ------------------------------------------------------------------------- QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + +#if !defined(Q_OS_VISIONOS) /*! Returns the model identifier of the device. */ @@ -248,12 +135,14 @@ static QString deviceModelIdentifier() return QString::fromLatin1(QByteArrayView(value, qsizetype(size))); #endif } +#endif // !defined(Q_OS_VISIONOS) +#if defined(Q_OS_VISIONOS) +QIOSScreen::QIOSScreen() +{ +#else QIOSScreen::QIOSScreen(UIScreen *screen) - : QPlatformScreen() - , m_uiScreen(screen) - , m_uiWindow(0) - , m_orientationListener(0) + : m_uiScreen(screen) { QString deviceIdentifier = deviceModelIdentifier(); @@ -287,46 +176,30 @@ QIOSScreen::QIOSScreen(UIScreen *screen) m_physicalDpi = 96; } - if (!qt_apple_isApplicationExtension()) { - for (UIWindow *existingWindow in qt_apple_sharedApplication().windows) { - if (existingWindow.screen == m_uiScreen) { - m_uiWindow = [m_uiWindow retain]; - break; - } - } - - if (!m_uiWindow) { - // Create a window and associated view-controller that we can use - m_uiWindow = [[QUIWindow alloc] initWithFrame:[m_uiScreen bounds]]; - m_uiWindow.rootViewController = [[[QIOSViewController alloc] initWithQIOSScreen:this] autorelease]; - } - } - - m_orientationListener = [[QIOSOrientationListener alloc] initWithQIOSScreen:this]; - - updateProperties(); - m_displayLink = [m_uiScreen displayLinkWithBlock:^(CADisplayLink *) { deliverUpdateRequests(); }]; m_displayLink.paused = YES; // Enabled when clients call QWindow::requestUpdate() [m_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; + +#endif // !defined(Q_OS_VISIONOS)) + + updateProperties(); } QIOSScreen::~QIOSScreen() { [m_displayLink invalidate]; - - [m_orientationListener release]; - [m_uiWindow release]; } QString QIOSScreen::name() const { - if (m_uiScreen == [UIScreen mainScreen]) { - return QString::fromNSString([UIDevice currentDevice].model) - + QLatin1String(" built-in display"); - } else { - return QLatin1String("External display"); - } +#if defined(Q_OS_VISIONOS) + return {}; +#else + if (m_uiScreen == [UIScreen mainScreen]) + return QString::fromNSString([UIDevice currentDevice].model) + " built-in display"_L1; + else + return "External display"_L1; +#endif } void QIOSScreen::updateProperties() @@ -334,42 +207,13 @@ void QIOSScreen::updateProperties() QRect previousGeometry = m_geometry; QRect previousAvailableGeometry = m_availableGeometry; +#if defined(Q_OS_VISIONOS) + // Based on what iPad app reports + m_geometry = QRect(0, 0, 1194, 834); + m_depth = 24; +#else m_geometry = QRectF::fromCGRect(m_uiScreen.bounds).toRect(); - // The application frame doesn't take safe area insets into account, and - // the safe area insets are not available before the UIWindow is shown, - // and do not take split-view constraints into account, so we have to - // combine the two to get the correct available geometry. - QRect applicationFrame = QRectF::fromCGRect(m_uiScreen.qt_applicationFrame).toRect(); - UIEdgeInsets safeAreaInsets = m_uiWindow.qt_safeAreaInsets; - m_availableGeometry = m_geometry.adjusted(safeAreaInsets.left, safeAreaInsets.top, - -safeAreaInsets.right, -safeAreaInsets.bottom).intersected(applicationFrame); - -#ifndef Q_OS_TVOS - if (m_uiScreen == [UIScreen mainScreen]) { - QIOSViewController *qtViewController = [m_uiWindow.rootViewController isKindOfClass:[QIOSViewController class]] ? - static_cast<QIOSViewController *>(m_uiWindow.rootViewController) : nil; - - if (qtViewController.lockedOrientation) { - Q_ASSERT(!qt_apple_isApplicationExtension()); - - // Setting the statusbar orientation (content orientation) on will affect the screen geometry, - // which is not what we want. We want to reflect the screen geometry based on the locked orientation, - // and adjust the available geometry based on the repositioned status bar for the current status - // bar orientation. - - Qt::ScreenOrientation statusBarOrientation = toQtScreenOrientation( - UIDeviceOrientation(qt_apple_sharedApplication().statusBarOrientation)); - - Qt::ScreenOrientation lockedOrientation = toQtScreenOrientation(UIDeviceOrientation(qtViewController.lockedOrientation)); - QTransform transform = transformBetween(lockedOrientation, statusBarOrientation, m_geometry).inverted(); - - m_geometry = transform.mapRect(m_geometry); - m_availableGeometry = transform.mapRect(m_availableGeometry); - } - } -#endif - if (m_geometry != previousGeometry) { // We can't use the primaryOrientation of screen(), as we haven't reported the new geometry yet Qt::ScreenOrientation primaryOrientation = m_geometry.width() >= m_geometry.height() ? @@ -385,6 +229,14 @@ void QIOSScreen::updateProperties() m_physicalSize = physicalGeometry.size() / m_physicalDpi * millimetersPerInch; } +#endif // defined(Q_OS_VISIONOS) + + // UIScreen does not provide a consistent accessor for the safe area margins + // of the screen, and on visionOS we won't even have a UIScreen, so we report + // the available geometry of the screen to be the same as the full geometry. + // Safe area margins and maximized state is handled in QIOSWindow::setWindowState. + m_availableGeometry = m_geometry; + // At construction time, we don't yet have an associated QScreen, but we still want // to compute the properties above so they are ready for when the QScreen attaches. // Also, at destruction time the QScreen has already been torn down, so notifying @@ -469,16 +321,28 @@ QDpi QIOSScreen::logicalBaseDpi() const qreal QIOSScreen::devicePixelRatio() const { +#if defined(Q_OS_VISIONOS) + return 2.0; // Based on what iPad app reports +#else return [m_uiScreen scale]; +#endif } qreal QIOSScreen::refreshRate() const { +#if defined(Q_OS_VISIONOS) + return 120.0; // Based on what iPad app reports +#else return m_uiScreen.maximumFramesPerSecond; +#endif } Qt::ScreenOrientation QIOSScreen::nativeOrientation() const { +#if defined(Q_OS_VISIONOS) + // Based on iPad app reporting native bounds 1668x2388 + return Qt::PortraitOrientation; +#else CGRect nativeBounds = #if defined(Q_OS_IOS) m_uiScreen.nativeBounds; @@ -490,38 +354,18 @@ Qt::ScreenOrientation QIOSScreen::nativeOrientation() const // be on the safe side we compare the width and height of the bounds. return nativeBounds.size.width >= nativeBounds.size.height ? Qt::LandscapeOrientation : Qt::PortraitOrientation; +#endif } Qt::ScreenOrientation QIOSScreen::orientation() const { -#ifdef Q_OS_TVOS - return Qt::PrimaryOrientation; -#else - // Auxiliary screens are always the same orientation as their primary orientation - if (m_uiScreen != [UIScreen mainScreen]) - return Qt::PrimaryOrientation; - - UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation; - - // At startup, iOS will report an unknown orientation for the device, even - // if we've asked it to begin generating device orientation notifications. - // In this case we fall back to the status bar orientation, which reflects - // the orientation the application was started up in (which may not match - // the physical orientation of the device, but typically does unless the - // application has been locked to a subset of the available orientations). - if (deviceOrientation == UIDeviceOrientationUnknown && !qt_apple_isApplicationExtension()) - deviceOrientation = UIDeviceOrientation(qt_apple_sharedApplication().statusBarOrientation); - - // If the device reports face up or face down orientations, we can't map - // them to Qt orientations, so we pretend we're in the same orientation - // as before. - if (deviceOrientation == UIDeviceOrientationFaceUp || deviceOrientation == UIDeviceOrientationFaceDown) { - Q_ASSERT(screen()); - return screen()->orientation(); - } - - return toQtScreenOrientation(deviceOrientation); -#endif + // We don't report UIDevice.currentDevice.orientation here, + // as that would report the actual orientation of the device, + // even if the orientation of the UI was locked to a subset + // of the possible orientations via the app's Info.plist or + // via [UIViewController supportedInterfaceOrientations]. + return m_geometry.width() >= m_geometry.height() ? + Qt::LandscapeOrientation : Qt::PortraitOrientation; } QPixmap QIOSScreen::grabWindow(WId window, int x, int y, int width, int height) const @@ -529,26 +373,27 @@ QPixmap QIOSScreen::grabWindow(WId window, int x, int y, int width, int height) if (window && ![reinterpret_cast<id>(window) isKindOfClass:[UIView class]]) return QPixmap(); - UIView *view = window ? reinterpret_cast<UIView *>(window) : m_uiWindow; + UIView *view = window ? reinterpret_cast<UIView *>(window) + : rootViewForScreen(screen()); if (width < 0) width = qMax(view.bounds.size.width - x, CGFloat(0)); if (height < 0) height = qMax(view.bounds.size.height - y, CGFloat(0)); - CGRect captureRect = [m_uiWindow convertRect:CGRectMake(x, y, width, height) fromView:view]; - captureRect = CGRectIntersection(captureRect, m_uiWindow.bounds); + CGRect captureRect = [view.window convertRect:CGRectMake(x, y, width, height) fromView:view]; + captureRect = CGRectIntersection(captureRect, view.window.bounds); UIGraphicsBeginImageContextWithOptions(captureRect.size, NO, 0.0); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextTranslateCTM(context, -captureRect.origin.x, -captureRect.origin.y); - // Draws the complete view hierarchy of m_uiWindow into the given rect, which - // needs to be the same aspect ratio as the m_uiWindow's size. Since we've + // Draws the complete view hierarchy of view.window into the given rect, which + // needs to be the same aspect ratio as the view.window's size. Since we've // translated the graphics context, and are potentially drawing into a smaller // context than the full window, the resulting image will be a subsection of the // full screen. - [m_uiWindow drawViewHierarchyInRect:m_uiWindow.bounds afterScreenUpdates:NO]; + [view.window drawViewHierarchyInRect:view.window.bounds afterScreenUpdates:NO]; UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); @@ -556,16 +401,13 @@ QPixmap QIOSScreen::grabWindow(WId window, int x, int y, int width, int height) return QPixmap::fromImage(qt_mac_toQImage(screenshot.CGImage)); } +#if !defined(Q_OS_VISIONOS) UIScreen *QIOSScreen::uiScreen() const { return m_uiScreen; } +#endif -UIWindow *QIOSScreen::uiWindow() const -{ - return m_uiWindow; -} +QT_END_NAMESPACE #include "moc_qiosscreen.cpp" - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosservices.h b/src/plugins/platforms/ios/qiosservices.h index a8575a9151..1f4a828e24 100644 --- a/src/plugins/platforms/ios/qiosservices.h +++ b/src/plugins/platforms/ios/qiosservices.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QIOSSERVICES_H #define QIOSSERVICES_H diff --git a/src/plugins/platforms/ios/qiosservices.mm b/src/plugins/platforms/ios/qiosservices.mm index 41c75dcac8..3e898cfffa 100644 --- a/src/plugins/platforms/ios/qiosservices.mm +++ b/src/plugins/platforms/ios/qiosservices.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #include "qiosservices.h" diff --git a/src/plugins/platforms/ios/qiostextinputoverlay.h b/src/plugins/platforms/ios/qiostextinputoverlay.h index 9ed3a9b271..e502340912 100644 --- a/src/plugins/platforms/ios/qiostextinputoverlay.h +++ b/src/plugins/platforms/ios/qiostextinputoverlay.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 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 #ifndef QIOSTEXTEDITOVERLAY_H #define QIOSTEXTEDITOVERLAY_H diff --git a/src/plugins/platforms/ios/qiostextinputoverlay.mm b/src/plugins/platforms/ios/qiostextinputoverlay.mm index c72d8dc596..83170c1851 100644 --- a/src/plugins/platforms/ios/qiostextinputoverlay.mm +++ b/src/plugins/platforms/ios/qiostextinputoverlay.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 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 #import <UIKit/UIGestureRecognizerSubclass.h> #import <UIKit/UITextView.h> @@ -87,7 +51,7 @@ static void executeBlockWithoutAnimation(Block block) // ------------------------------------------------------------------------- /** QIOSEditMenu is just a wrapper class around UIMenuController to - ease showing and hiding it correcly. + ease showing and hiding it correctly. */ @interface QIOSEditMenu : NSObject @property (nonatomic, assign) BOOL visible; @@ -470,7 +434,7 @@ static void executeBlockWithoutAnimation(Block block) if (enabled) { _focusView = [reinterpret_cast<UIView *>(qApp->focusWindow()->winId()) retain]; - _desktopView = [qt_apple_sharedApplication().keyWindow.rootViewController.view retain]; + _desktopView = [presentationWindow(nullptr).rootViewController.view retain]; Q_ASSERT(_focusView && _desktopView && _desktopView.superview); [_desktopView addGestureRecognizer:self]; } else { @@ -828,7 +792,7 @@ static void executeBlockWithoutAnimation(Block block) SelectionPair selection = querySelection(); int touchTextPos = QPlatformInputContext::queryFocusObject(Qt::ImCursorPosition, touchPoint).toInt(); - // Ensure that the handels cannot be dragged past each other + // Ensure that the handles cannot be dragged past each other if (_dragOnCursor) selection.second = (touchTextPos > selection.first) ? touchTextPos : selection.first + 1; else @@ -956,7 +920,7 @@ static void executeBlockWithoutAnimation(Block block) // But note, we only want to hide the menu, and not clear the selection. // Only when the user taps inside the input area do we want to clear the // selection as well. This is different from native behavior, but done so - // deliberatly for cross-platform consistency. This will let the user click on + // deliberately for cross-platform consistency. This will let the user click on // e.g "Bold" and "Italic" buttons elsewhere in the UI to modify the selected text. return; } @@ -969,7 +933,7 @@ static void executeBlockWithoutAnimation(Block block) } // When no menu is showing, and the touch is inside the input - // area, we check if we should show it. We wan't to do so if + // area, we check if we should show it. We want to do so if // the tap doesn't result in the cursor changing position. _cursorPosOnPress = QInputMethod::queryFocusObject(Qt::ImCursorPosition, QVariant()).toInt(); } @@ -983,7 +947,17 @@ static void executeBlockWithoutAnimation(Block block) int cursorPosOnRelease = QPlatformInputContext::queryFocusObject(Qt::ImCursorPosition, touchPos).toInt(); if (cursorPosOnRelease == _cursorPosOnPress) { + // We've recognized a gesture to open the menu, but we don't know + // whether the user tapped a control that was overlaid our input + // area, since we don't do any granular hit-testing in touchesBegan. + // To ensure that the gesture doesn't eat touch events that should + // have reached another UI control we report the gesture as failed + // here, and then manually show the menu at the next runloop pass. _menuShouldBeVisible = true; + self.state = UIGestureRecognizerStateFailed; + dispatch_async(dispatch_get_main_queue(), ^{ + QIOSTextInputOverlay::s_editMenu.visible = _menuShouldBeVisible; + }); } else { // The menu is hidden, and the cursor will change position once // Qt receive the touch release. We therefore fail so that we @@ -1032,19 +1006,26 @@ QIOSTextInputOverlay::~QIOSTextInputOverlay() void QIOSTextInputOverlay::updateFocusObject() { + // Destroy old recognizers since they were created with + // dependencies to the old focus object (focus view). if (m_cursorRecognizer) { - // Destroy old recognizers since they were created with - // dependencies to the old focus object (focus view). m_cursorRecognizer.enabled = NO; - m_selectionRecognizer.enabled = NO; - m_openMenuOnTapRecognizer.enabled = NO; [m_cursorRecognizer release]; - [m_selectionRecognizer release]; - [m_openMenuOnTapRecognizer release]; - [s_editMenu release]; m_cursorRecognizer = nullptr; + } + if (m_selectionRecognizer) { + m_selectionRecognizer.enabled = NO; + [m_selectionRecognizer release]; m_selectionRecognizer = nullptr; + } + if (m_openMenuOnTapRecognizer) { + m_openMenuOnTapRecognizer.enabled = NO; + [m_openMenuOnTapRecognizer release]; m_openMenuOnTapRecognizer = nullptr; + } + + if (s_editMenu) { + [s_editMenu release]; s_editMenu = nullptr; } diff --git a/src/plugins/platforms/ios/qiostextresponder.h b/src/plugins/platforms/ios/qiostextresponder.h index d6f1cfd03f..491dc0b632 100644 --- a/src/plugins/platforms/ios/qiostextresponder.h +++ b/src/plugins/platforms/ios/qiostextresponder.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #import <UIKit/UIKit.h> @@ -63,6 +27,8 @@ QT_END_NAMESPACE - (instancetype)initWithInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)context; - (BOOL)needsKeyboardReconfigure:(Qt::InputMethodQueries)updatedProperties; +- (void)reset; +- (void)commit; - (void)notifyInputDelegate:(Qt::InputMethodQueries)updatedProperties; diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm index deca8c7617..5231a3adde 100644 --- a/src/plugins/platforms/ios/qiostextresponder.mm +++ b/src/plugins/platforms/ios/qiostextresponder.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #include "qiostextresponder.h" @@ -147,7 +111,7 @@ // By keeping the responder (QIOSTextInputResponder in this case) // retained, we ensure that all messages sent to the view during -// its lifetime in a window hierarcy will be able to traverse the +// its lifetime in a window hierarchy will be able to traverse the // responder chain. - (void)willMoveToWindow:(UIWindow *)window { @@ -200,7 +164,7 @@ { FirstResponderCandidate firstResponderCandidate(self); - qImDebug() << "self:" << self << "first:" << [UIResponder currentFirstResponder]; + qImDebug() << "self:" << self << "first:" << [UIResponder qt_currentFirstResponder]; if (![super becomeFirstResponder]) { qImDebug() << self << "was not allowed to become first responder"; @@ -214,7 +178,7 @@ - (BOOL)resignFirstResponder { - qImDebug() << "self:" << self << "first:" << [UIResponder currentFirstResponder]; + qImDebug() << "self:" << self << "first:" << [UIResponder qt_currentFirstResponder]; // Don't allow activation events of the window that we're doing text on behalf on // to steal responder. @@ -232,11 +196,11 @@ // a regular responder transfer to another window. In the former case, iOS // will set the new first-responder to our next-responder, and in the latter // case we'll have an active responder candidate. - if (![UIResponder currentFirstResponder] && !FirstResponderCandidate::currentCandidate()) { + if (![UIResponder qt_currentFirstResponder] && !FirstResponderCandidate::currentCandidate()) { // No first responder set anymore, sync this with Qt by clearing the // focus object. m_inputContext->clearCurrentFocusObject(); - } else if ([UIResponder currentFirstResponder] == [self nextResponder]) { + } else if ([UIResponder qt_currentFirstResponder] == [self nextResponder]) { // We have resigned the keyboard, and transferred first responder back to the parent view Q_ASSERT(!FirstResponderCandidate::currentCandidate()); if ([self currentImeState:Qt::ImEnabled].toBool()) { @@ -258,8 +222,12 @@ - (UIResponder*)nextResponder { - return qApp->focusWindow() ? - reinterpret_cast<QUIView *>(qApp->focusWindow()->handle()->winId()) : 0; + // Make sure we have a handle/platform window before getting the winId(). + // In the dtor of QIOSWindow the platform window is set to null before calling + // removeFromSuperview which will end up calling nextResponder. That means it's + // possible that we can get here while the window is being torn down. + return (qApp->focusWindow() && qApp->focusWindow()->handle()) ? + reinterpret_cast<QUIView *>(qApp->focusWindow()->handle()->winId()) : 0; } // ------------------------------------------------------------------------- @@ -433,7 +401,7 @@ if (UIView *accessoryView = static_cast<UIView *>(platformData.value(kImePlatformDataInputAccessoryView).value<void *>())) self.inputAccessoryView = [[[WrapperView alloc] initWithView:accessoryView] autorelease]; -#ifndef Q_OS_TVOS +#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS) if (platformData.value(kImePlatformDataHideShortcutsBar).toBool()) { // According to the docs, leadingBarButtonGroups/trailingBarButtonGroups should be set to nil to hide the shortcuts bar. // However, starting with iOS 10, the API has been surrounded with NS_ASSUME_NONNULL, which contradicts this and causes @@ -490,6 +458,18 @@ return [super needsKeyboardReconfigure:updatedProperties]; } +- (void)reset +{ + [self setMarkedText:@"" selectedRange:NSMakeRange(0, 0)]; + [self notifyInputDelegate:Qt::ImSurroundingText]; +} + +- (void)commit +{ + [self unmarkText]; + [self notifyInputDelegate:Qt::ImSurroundingText]; +} + // ------------------------------------------------------------------------- #ifndef QT_NO_SHORTCUT @@ -922,7 +902,7 @@ QInputMethodEvent e(m_markedText, attrs); [self sendEventToFocusObject:e]; } - QRectF startRect = QPlatformInputContext::cursorRectangle();; + QRectF startRect = QPlatformInputContext::cursorRectangle(); attrs = QList<QInputMethodEvent::Attribute>(); attrs << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, r.location + r.length, 0, 0); @@ -930,7 +910,7 @@ QInputMethodEvent e(m_markedText, attrs); [self sendEventToFocusObject:e]; } - QRectF endRect = QPlatformInputContext::cursorRectangle();; + QRectF endRect = QPlatformInputContext::cursorRectangle(); if (cursorPos != int(r.location + r.length) || cursorPos != anchorPos) { attrs = QList<QInputMethodEvent::Attribute>(); @@ -968,20 +948,20 @@ [self sendEventToFocusObject:e]; } -- (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection forRange:(UITextRange *)range +- (void)setBaseWritingDirection:(NSWritingDirection)writingDirection forRange:(UITextRange *)range { Q_UNUSED(writingDirection); Q_UNUSED(range); // Writing direction is handled by QLocale } -- (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction +- (NSWritingDirection)baseWritingDirectionForPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction { Q_UNUSED(position); Q_UNUSED(direction); if (QLocale::system().textDirection() == Qt::RightToLeft) - return UITextWritingDirectionRightToLeft; - return UITextWritingDirectionLeftToRight; + return NSWritingDirectionRightToLeft; + return NSWritingDirectionLeftToRight; } - (UITextRange *)characterRangeByExtendingPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction diff --git a/src/plugins/platforms/ios/qiostheme.h b/src/plugins/platforms/ios/qiostheme.h index c9d833713d..70e2c37ff1 100644 --- a/src/plugins/platforms/ios/qiostheme.h +++ b/src/plugins/platforms/ios/qiostheme.h @@ -1,49 +1,17 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QIOSTHEME_H #define QIOSTHEME_H +#import <UIKit/UIKit.h> + #include <QtCore/QHash> #include <QtGui/QPalette> #include <qpa/qplatformtheme.h> +#include <QtCore/private/qcore_mac_p.h> + QT_BEGIN_NAMESPACE class QIOSTheme : public QPlatformTheme @@ -55,22 +23,29 @@ public: const QPalette *palette(Palette type = SystemPalette) const override; QVariant themeHint(ThemeHint hint) const override; + Qt::ColorScheme colorScheme() const override; + void requestColorScheme(Qt::ColorScheme scheme) override; + +#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS) QPlatformMenuItem* createPlatformMenuItem() const override; QPlatformMenu* createPlatformMenu() const override; +#endif bool usePlatformNativeDialog(DialogType type) const override; QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override; const QFont *font(Font type = SystemFont) const override; + QIconEngine *createIconEngine(const QString &iconName) const override; static const char *name; static void initializeSystemPalette(); + static void applyTheme(UIWindow *window); private: - mutable QHash<QPlatformTheme::Font, QFont *> m_fonts; - static QPalette s_systemPalette; + static inline Qt::ColorScheme s_colorSchemeOverride = Qt::ColorScheme::Unknown; + QMacNotificationObserver m_contentSizeCategoryObserver; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiostheme.mm b/src/plugins/platforms/ios/qiostheme.mm index 584dd1c8fc..0b420a875a 100644 --- a/src/plugins/platforms/ios/qiostheme.mm +++ b/src/plugins/platforms/ios/qiostheme.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #include "qiostheme.h" @@ -47,16 +11,24 @@ #include <QtGui/private/qcoregraphics_p.h> #include <QtGui/private/qcoretextfontdatabase_p.h> +#include <QtGui/private/qappleiconengine_p.h> #include <QtGui/private/qguiapplication_p.h> #include <qpa/qplatformintegration.h> #include <UIKit/UIFont.h> #include <UIKit/UIInterface.h> -#ifndef Q_OS_TVOS +#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS) #include "qiosmenu.h" +#endif + +#if !defined(Q_OS_TVOS) #include "qiosfiledialog.h" +#include "qioscolordialog.h" +#include "qiosfontdialog.h" #include "qiosmessagedialog.h" +#include "qiosscreen.h" +#include "quiwindow.h" #endif QT_BEGIN_NAMESPACE @@ -66,11 +38,16 @@ const char *QIOSTheme::name = "ios"; QIOSTheme::QIOSTheme() { initializeSystemPalette(); + + m_contentSizeCategoryObserver = QMacNotificationObserver(nil, + UIContentSizeCategoryDidChangeNotification, [] { + qCDebug(lcQpaFonts) << "Contents size category changed to" << UIApplication.sharedApplication.preferredContentSizeCategory; + QPlatformFontDatabase::repopulateFontDatabase(); + }); } QIOSTheme::~QIOSTheme() { - qDeleteAll(m_fonts); } QPalette QIOSTheme::s_systemPalette; @@ -80,28 +57,26 @@ void QIOSTheme::initializeSystemPalette() Q_DECL_IMPORT QPalette qt_fusionPalette(void); s_systemPalette = qt_fusionPalette(); - if (@available(ios 13.0, *)) { - s_systemPalette.setBrush(QPalette::Window, qt_mac_toQBrush(UIColor.systemGroupedBackgroundColor.CGColor)); - s_systemPalette.setBrush(QPalette::Active, QPalette::WindowText, qt_mac_toQBrush(UIColor.labelColor.CGColor)); + s_systemPalette.setBrush(QPalette::Window, qt_mac_toQBrush(UIColor.systemGroupedBackgroundColor.CGColor)); + s_systemPalette.setBrush(QPalette::Active, QPalette::WindowText, qt_mac_toQBrush(UIColor.labelColor.CGColor)); - s_systemPalette.setBrush(QPalette::Base, qt_mac_toQBrush(UIColor.secondarySystemGroupedBackgroundColor.CGColor)); - s_systemPalette.setBrush(QPalette::Active, QPalette::Text, qt_mac_toQBrush(UIColor.labelColor.CGColor)); + s_systemPalette.setBrush(QPalette::Base, qt_mac_toQBrush(UIColor.secondarySystemGroupedBackgroundColor.CGColor)); + s_systemPalette.setBrush(QPalette::Active, QPalette::Text, qt_mac_toQBrush(UIColor.labelColor.CGColor)); - s_systemPalette.setBrush(QPalette::Button, qt_mac_toQBrush(UIColor.secondarySystemBackgroundColor.CGColor)); - s_systemPalette.setBrush(QPalette::Active, QPalette::ButtonText, qt_mac_toQBrush(UIColor.labelColor.CGColor)); + s_systemPalette.setBrush(QPalette::Button, qt_mac_toQBrush(UIColor.secondarySystemBackgroundColor.CGColor)); + s_systemPalette.setBrush(QPalette::Active, QPalette::ButtonText, qt_mac_toQBrush(UIColor.labelColor.CGColor)); - s_systemPalette.setBrush(QPalette::Active, QPalette::BrightText, qt_mac_toQBrush(UIColor.lightTextColor.CGColor)); - s_systemPalette.setBrush(QPalette::Active, QPalette::PlaceholderText, qt_mac_toQBrush(UIColor.placeholderTextColor.CGColor)); + s_systemPalette.setBrush(QPalette::Active, QPalette::BrightText, qt_mac_toQBrush(UIColor.lightTextColor.CGColor)); + s_systemPalette.setBrush(QPalette::Active, QPalette::PlaceholderText, qt_mac_toQBrush(UIColor.placeholderTextColor.CGColor)); - s_systemPalette.setBrush(QPalette::Active, QPalette::Link, qt_mac_toQBrush(UIColor.linkColor.CGColor)); - s_systemPalette.setBrush(QPalette::Active, QPalette::LinkVisited, qt_mac_toQBrush(UIColor.linkColor.CGColor)); + s_systemPalette.setBrush(QPalette::Active, QPalette::Link, qt_mac_toQBrush(UIColor.linkColor.CGColor)); + s_systemPalette.setBrush(QPalette::Active, QPalette::LinkVisited, qt_mac_toQBrush(UIColor.linkColor.CGColor)); - s_systemPalette.setBrush(QPalette::Highlight, QColor(11, 70, 150, 60)); - s_systemPalette.setBrush(QPalette::HighlightedText, qt_mac_toQBrush(UIColor.labelColor.CGColor)); - } else { - s_systemPalette.setBrush(QPalette::Highlight, QColor(204, 221, 237)); - s_systemPalette.setBrush(QPalette::HighlightedText, Qt::black); - } + s_systemPalette.setBrush(QPalette::Highlight, QColor(11, 70, 150, 60)); + s_systemPalette.setBrush(QPalette::HighlightedText, qt_mac_toQBrush(UIColor.labelColor.CGColor)); + + if (@available(ios 15.0, *)) + s_systemPalette.setBrush(QPalette::Accent, qt_mac_toQBrush(UIColor.tintColor.CGColor)); } const QPalette *QIOSTheme::palette(QPlatformTheme::Palette type) const @@ -111,29 +86,25 @@ const QPalette *QIOSTheme::palette(QPlatformTheme::Palette type) const return 0; } +#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS) QPlatformMenuItem* QIOSTheme::createPlatformMenuItem() const { -#ifdef Q_OS_TVOS - return 0; -#else - return new QIOSMenuItem(); -#endif + return new QIOSMenuItem; } QPlatformMenu* QIOSTheme::createPlatformMenu() const { -#ifdef Q_OS_TVOS - return 0; -#else - return new QIOSMenu(); -#endif + return new QIOSMenu; } +#endif bool QIOSTheme::usePlatformNativeDialog(QPlatformTheme::DialogType type) const { switch (type) { case FileDialog: case MessageDialog: + case ColorDialog: + case FontDialog: return !qt_apple_isApplicationExtension(); default: return false; @@ -150,6 +121,12 @@ QPlatformDialogHelper *QIOSTheme::createPlatformDialogHelper(QPlatformTheme::Dia case MessageDialog: return new QIOSMessageDialog(); break; + case ColorDialog: + return new QIOSColorDialog(); + break; + case FontDialog: + return new QIOSFontDialog(); + break; #endif default: return 0; @@ -168,14 +145,77 @@ QVariant QIOSTheme::themeHint(ThemeHint hint) const } } -const QFont *QIOSTheme::font(Font type) const +Qt::ColorScheme QIOSTheme::colorScheme() const { - if (m_fonts.isEmpty()) { - QCoreTextFontDatabase *ctfd = static_cast<QCoreTextFontDatabase *>(QGuiApplicationPrivate::platformIntegration()->fontDatabase()); - m_fonts = ctfd->themeFonts(); +#if defined(Q_OS_VISIONOS) + // On visionOS the concept of light or dark mode does not + // apply, as the UI is constantly changing based on what + // the lighting conditions are outside the headset, but + // the OS reports itself as always being in dark mode. + return Qt::ColorScheme::Dark; +#else + if (s_colorSchemeOverride != Qt::ColorScheme::Unknown) + return s_colorSchemeOverride; + + // Set the appearance based on the QUIWindow + // Fallback to the UIScreen if no window is created yet + UIUserInterfaceStyle appearance = UIScreen.mainScreen.traitCollection.userInterfaceStyle; + NSArray<UIWindow *> *windows = qt_apple_sharedApplication().windows; + for (UIWindow *window in windows) { + if ([window isKindOfClass:[QUIWindow class]]) { + appearance = static_cast<QUIWindow*>(window).traitCollection.userInterfaceStyle; + break; + } } - return m_fonts.value(type, 0); + return appearance == UIUserInterfaceStyleDark + ? Qt::ColorScheme::Dark + : Qt::ColorScheme::Light; +#endif +} + +void QIOSTheme::requestColorScheme(Qt::ColorScheme scheme) +{ +#if defined(Q_OS_VISIONOS) + Q_UNUSED(scheme); +#else + s_colorSchemeOverride = scheme; + + const NSArray<UIWindow *> *windows = qt_apple_sharedApplication().windows; + for (UIWindow *window in windows) { + // don't apply a theme to windows we don't own + if (qt_objc_cast<QUIWindow*>(window)) + applyTheme(window); + } +#endif +} + +void QIOSTheme::applyTheme(UIWindow *window) +{ + const UIUserInterfaceStyle style = []{ + switch (s_colorSchemeOverride) { + case Qt::ColorScheme::Dark: + return UIUserInterfaceStyleDark; + case Qt::ColorScheme::Light: + return UIUserInterfaceStyleLight; + case Qt::ColorScheme::Unknown: + return UIUserInterfaceStyleUnspecified; + } + }(); + + window.overrideUserInterfaceStyle = style; +} + +const QFont *QIOSTheme::font(Font type) const +{ + const auto *platformIntegration = QGuiApplicationPrivate::platformIntegration(); + const auto *coreTextFontDatabase = static_cast<QCoreTextFontDatabase *>(platformIntegration->fontDatabase()); + return coreTextFontDatabase->themeFont(type); +} + +QIconEngine *QIOSTheme::createIconEngine(const QString &iconName) const +{ + return new QAppleIconEngine(iconName); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosviewcontroller.h b/src/plugins/platforms/ios/qiosviewcontroller.h index 79fe5ae3c5..1f8da41ba4 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.h +++ b/src/plugins/platforms/ios/qiosviewcontroller.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #import <UIKit/UIKit.h> #include <QtCore/QtGlobal> @@ -48,14 +12,12 @@ QT_END_NAMESPACE @interface QIOSViewController : UIViewController -- (instancetype)initWithQIOSScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen; +- (instancetype)initWithWindow:(UIWindow*)window andScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen; - (void)updateProperties; - (NSArray*)keyCommands; - (void)handleShortcut:(UIKeyCommand*)keyCommand; #ifndef Q_OS_TVOS -@property (nonatomic, assign) UIInterfaceOrientation lockedOrientation; - // UIViewController @property (nonatomic, assign) BOOL prefersStatusBarHidden; @property (nonatomic, assign) UIStatusBarAnimation preferredStatusBarUpdateAnimation; diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm index 40beda6f5f..436d1e7bed 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.mm +++ b/src/plugins/platforms/ios/qiosviewcontroller.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #include "qiosglobal.h" #import "qiosviewcontroller.h" @@ -57,9 +21,12 @@ #include "qioswindow.h" #include "quiview.h" +#include <QtCore/qpointer.h> + // ------------------------------------------------------------------------- @interface QIOSViewController () +@property (nonatomic, assign) UIWindow *window; @property (nonatomic, assign) QPointer<QT_PREPEND_NAMESPACE(QIOSScreen)> platformScreen; @property (nonatomic, assign) BOOL changingOrientation; @end @@ -124,27 +91,30 @@ { Q_UNUSED(subview); - QT_PREPEND_NAMESPACE(QIOSScreen) *screen = self.qtViewController.platformScreen; - - // The 'window' property of our view is not valid until the window - // has been shown, so we have to access it through the QIOSScreen. - UIWindow *uiWindow = screen->uiWindow(); + // Track UIWindow via explicit property on QIOSViewController, + // as the window property of our own view is not valid until + // the window has been shown (below). + UIWindow *uiWindow = self.qtViewController.window; if (uiWindow.hidden) { - // Associate UIWindow to screen and show it the first time a QWindow - // is mapped to the screen. For external screens this means disabling - // mirroring mode and presenting alternate content on the screen. - uiWindow.screen = screen->uiScreen(); + // Show the UIWindow the first time a QWindow is mapped to the screen. + // For the main screen this hides the launch screen, while for external + // screens this disables mirroring of the main screen, so the external + // screen can be used for alternate content. uiWindow.hidden = NO; } } +#if !defined(Q_OS_VISIONOS) - (void)willRemoveSubview:(UIView *)subview { Q_UNUSED(subview); - Q_ASSERT(self.window); UIWindow *uiWindow = self.window; + // uiWindow can be null when closing from the ios "app manager" and the app is + // showing a native window like UIDocumentBrowserViewController + if (!uiWindow) + return; if (uiWindow.screen != [UIScreen mainScreen] && self.subviews.count == 1) { // We're about to remove the last view of an external screen, so go back @@ -152,10 +122,10 @@ // to ensure that we don't try to layout the view that's being removed. dispatch_async(dispatch_get_main_queue(), ^{ uiWindow.hidden = YES; - uiWindow.screen = [UIScreen mainScreen]; }); } } +#endif - (void)layoutSubviews { @@ -241,7 +211,7 @@ { // The initial frame computed during startup may happen before the view has // a window, meaning our calculations above will be wrong. We ensure that the - // frame is set correctly once we have a window to base our calulations on. + // frame is set correctly once we have a window to base our calculations on. [self setFrame:self.window.bounds]; } @@ -261,15 +231,14 @@ @synthesize preferredStatusBarStyle; #endif -- (instancetype)initWithQIOSScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen +- (instancetype)initWithWindow:(UIWindow*)window andScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen { if (self = [self init]) { + self.window = window; self.platformScreen = screen; self.changingOrientation = NO; #ifndef Q_OS_TVOS - self.lockedOrientation = UIInterfaceOrientationUnknown; - // Status bar may be initially hidden at startup through Info.plist self.prefersStatusBarHidden = infoPlistValue(@"UIStatusBarHidden", false); self.preferredStatusBarUpdateAnimation = UIStatusBarAnimationNone; @@ -316,7 +285,7 @@ Q_ASSERT(!qt_apple_isApplicationExtension()); -#ifndef Q_OS_TVOS +#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS) NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center addObserver:self selector:@selector(willChangeStatusBarFrame:) name:UIApplicationWillChangeStatusBarFrameNotification @@ -326,6 +295,15 @@ name:UIApplicationDidChangeStatusBarOrientationNotification object:qt_apple_sharedApplication()]; #endif + + // Make sure any top level windows that have already been created + // for this screen are reparented into our desktop manager view. + for (auto *window : qGuiApp->topLevelWindows()) { + if (window->screen()->handle() != self.platformScreen) + continue; + if (auto *platformWindow = window->handle()) + platformWindow->setParent(nullptr); + } } - (void)viewDidUnload @@ -336,26 +314,6 @@ // ------------------------------------------------------------------------- -- (BOOL)shouldAutorotate -{ -#ifndef Q_OS_TVOS - return self.platformScreen && self.platformScreen->uiScreen() == [UIScreen mainScreen] && !self.lockedOrientation; -#else - return NO; -#endif -} - -- (NSUInteger)supportedInterfaceOrientations -{ - // As documented by Apple in the iOS 6.0 release notes, setStatusBarOrientation:animated: - // only works if the supportedInterfaceOrientations of the view controller is 0, making - // us responsible for ensuring that the status bar orientation is consistent. We enter - // this mode when auto-rotation is disabled due to an explicit content orientation being - // set on the focus window. Note that this is counter to what the documentation for - // supportedInterfaceOrientations says, which states that the method should not return 0. - return [self shouldAutorotate] ? UIInterfaceOrientationMaskAll : 0; -} - - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration { self.changingOrientation = YES; @@ -370,6 +328,7 @@ [super didRotateFromInterfaceOrientation:orientation]; } +#if !defined(Q_OS_VISIONOS) - (void)willChangeStatusBarFrame:(NSNotification*)notification { Q_UNUSED(notification); @@ -413,6 +372,7 @@ [self.view setNeedsLayout]; } +#endif - (void)viewWillLayoutSubviews { @@ -433,15 +393,17 @@ if (!self.platformScreen || !self.platformScreen->screen()) return; +#if !defined(Q_OS_VISIONOS) // For now we only care about the main screen, as both the statusbar // visibility and orientation is only appropriate for the main screen. if (self.platformScreen->uiScreen() != [UIScreen mainScreen]) return; +#endif // Prevent recursion caused by updating the status bar appearance (position // or visibility), which in turn may cause a layout of our subviews, and // a reset of window-states, which themselves affect the view controller - // properties such as the statusbar visibilty. + // properties such as the statusbar visibility. if (m_updatingProperties) return; @@ -463,7 +425,7 @@ // All decisions are based on the top level window focusWindow = qt_window_private(focusWindow)->topLevelWindow(); -#ifndef Q_OS_TVOS +#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS) // -------------- Status bar style and visbility --------------- @@ -483,51 +445,6 @@ [self setNeedsStatusBarAppearanceUpdate]; [self.view setNeedsLayout]; } - - - // -------------- Content orientation --------------- - - UIApplication *uiApplication = qt_apple_sharedApplication(); - - static BOOL kAnimateContentOrientationChanges = YES; - - Qt::ScreenOrientation contentOrientation = focusWindow->contentOrientation(); - if (contentOrientation != Qt::PrimaryOrientation) { - // An explicit content orientation has been reported for the focus window, - // so we keep the status bar in sync with content orientation. This will ensure - // that the task bar (and associated gestures) are also rotated accordingly. - - if (!self.lockedOrientation) { - // We are moving from Qt::PrimaryOrientation to an explicit orientation, - // so we need to store the current statusbar orientation, as we need it - // later when mapping screen coordinates for QScreen and for returning - // to Qt::PrimaryOrientation. - self.lockedOrientation = uiApplication.statusBarOrientation; - } - - [uiApplication setStatusBarOrientation: - UIInterfaceOrientation(fromQtScreenOrientation(contentOrientation)) - animated:kAnimateContentOrientationChanges]; - - } else { - // The content orientation is set to Qt::PrimaryOrientation, meaning - // that auto-rotation should be enabled. But we may be coming out of - // a state of locked orientation, which needs some cleanup before we - // can enable auto-rotation again. - if (self.lockedOrientation) { - // First we need to restore the statusbar to what it was at the - // time of locking the orientation, otherwise iOS will be very - // confused when it starts doing auto-rotation again. - [uiApplication setStatusBarOrientation:self.lockedOrientation - animated:kAnimateContentOrientationChanges]; - - // Then we can re-enable auto-rotation - self.lockedOrientation = UIInterfaceOrientationUnknown; - - // And finally let iOS rotate the root view to match the device orientation - [UIViewController attemptRotationToDeviceOrientation]; - } - } #endif } diff --git a/src/plugins/platforms/ios/qioswindow.h b/src/plugins/platforms/ios/qioswindow.h index 2028fc2a42..88afee80c3 100644 --- a/src/plugins/platforms/ios/qioswindow.h +++ b/src/plugins/platforms/ios/qioswindow.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QIOSWINDOW_H #define QIOSWINDOW_H @@ -57,14 +21,13 @@ class QIOSWindow : public QObject, public QPlatformWindow Q_OBJECT public: - explicit QIOSWindow(QWindow *window); + explicit QIOSWindow(QWindow *window, WId nativeHandle = 0); ~QIOSWindow(); void setGeometry(const QRect &rect) override; void setWindowState(Qt::WindowStates state) override; void setParent(const QPlatformWindow *window) override; - void handleContentOrientationChange(Qt::ScreenOrientation orientation) override; void setVisible(bool visible) override; void setOpacity(qreal level) override; @@ -92,13 +55,20 @@ public: void requestUpdate() override; + void setMask(const QRegion ®ion) override; + +#if QT_CONFIG(opengl) CAEAGLLayer *eaglLayer() const; +#endif + + bool isForeignWindow() const override; + UIView *view() const; private: void applicationStateChanged(Qt::ApplicationState state); void applyGeometry(const QRect &rect); - QUIView *m_view; + UIView *m_view; QRect m_normalGeometry; int m_windowLevel; @@ -114,6 +84,8 @@ private: QDebug operator<<(QDebug debug, const QIOSWindow *window); #endif +QT_MANGLE_NAMESPACE(QUIView) *quiview_cast(UIView *view); + QT_END_NAMESPACE #endif // QIOSWINDOW_H diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm index 864eef3641..f461a5f55b 100644 --- a/src/plugins/platforms/ios/qioswindow.mm +++ b/src/plugins/platforms/ios/qioswindow.mm @@ -1,46 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #include "qioswindow.h" #include "qiosapplicationdelegate.h" -#include "qioscontext.h" #include "qiosglobal.h" #include "qiosintegration.h" #include "qiosscreen.h" @@ -48,11 +11,17 @@ #include "quiview.h" #include "qiosinputcontext.h" +#include <QtCore/private/qcore_mac_p.h> + #include <QtGui/private/qwindow_p.h> +#include <QtGui/private/qhighdpiscaling_p.h> #include <qpa/qplatformintegration.h> +#if QT_CONFIG(opengl) #import <QuartzCore/CAEAGLLayer.h> -#ifdef Q_OS_IOS +#endif + +#if QT_CONFIG(metal) #import <QuartzCore/CAMetalLayer.h> #endif @@ -60,31 +29,50 @@ QT_BEGIN_NAMESPACE -QIOSWindow::QIOSWindow(QWindow *window) +enum { + defaultWindowWidth = 160, + defaultWindowHeight = 160 +}; + +QIOSWindow::QIOSWindow(QWindow *window, WId nativeHandle) : QPlatformWindow(window) , m_windowLevel(0) { -#ifdef Q_OS_IOS - if (window->surfaceType() == QSurface::MetalSurface) - m_view = [[QUIMetalView alloc] initWithQIOSWindow:this]; - else + if (nativeHandle) { + m_view = reinterpret_cast<UIView *>(nativeHandle); + [m_view retain]; + } else { +#if QT_CONFIG(metal) + if (window->surfaceType() == QSurface::RasterSurface) + window->setSurfaceType(QSurface::MetalSurface); + + if (window->surfaceType() == QSurface::MetalSurface) + m_view = [[QUIMetalView alloc] initWithQIOSWindow:this]; + else #endif - m_view = [[QUIView alloc] initWithQIOSWindow:this]; + m_view = [[QUIView alloc] initWithQIOSWindow:this]; + } connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QIOSWindow::applicationStateChanged); + // Always set parent, even if we don't have a parent window, + // as we use setParent to reparent top levels into our desktop + // manager view. setParent(QPlatformWindow::parent()); - // Resolve default window geometry in case it was not set before creating the - // platform window. This picks up eg. minimum-size if set, and defaults to - // the "maxmized" geometry (even though we're not in that window state). - // FIXME: Detect if we apply a maximized geometry and send a window state - // change event in that case. - m_normalGeometry = initialGeometry(window, QPlatformWindow::geometry(), - screen()->availableGeometry().width(), screen()->availableGeometry().height()); + if (!isForeignWindow()) { + // Resolve default window geometry in case it was not set before creating the + // platform window. This picks up eg. minimum-size if set. + m_normalGeometry = initialGeometry(window, QPlatformWindow::geometry(), + defaultWindowWidth, defaultWindowHeight); - setWindowState(window->windowStates()); - setOpacity(window->opacity()); + setWindowState(window->windowStates()); + setOpacity(window->opacity()); + setMask(QHighDpi::toNativeLocalRegion(window->mask(), window)); + } else { + // Pick up essential foreign window state + QPlatformWindow::setGeometry(QRectF::fromCGRect(m_view.frame).toRect()); + } Qt::ScreenOrientation initialOrientation = window->contentOrientation(); if (initialOrientation != Qt::PrimaryOrientation) { @@ -101,13 +89,20 @@ QIOSWindow::~QIOSWindow() // According to the UIResponder documentation, Cocoa Touch should react to system interruptions // that "might cause the view to be removed from the window" by sending touchesCancelled, but in // practice this doesn't seem to happen when removing the view from its superview. To ensure that - // Qt's internal state for touch and mouse handling is kept consistent, we therefor have to force + // Qt's internal state for touch and mouse handling is kept consistent, we therefore have to force // cancellation of all touch events. [m_view touchesCancelled:[NSSet set] withEvent:0]; clearAccessibleCache(); - m_view.platformWindow = 0; - [m_view removeFromSuperview]; + + quiview_cast(m_view).platformWindow = nullptr; + + // Remove from superview, unless we're a foreign window without a + // Qt window parent, in which case the foreign window is used as + // a window container for a Qt UI hierarchy inside a native UI. + if (!(isForeignWindow() && !QPlatformWindow::parent())) + [m_view removeFromSuperview]; + [m_view release]; } @@ -146,7 +141,7 @@ void QIOSWindow::setVisible(bool visible) if (visible && shouldAutoActivateWindow()) { if (!window()->property("_q_showWithoutActivating").toBool()) requestActivateWindow(); - } else if (!visible && [m_view isActiveWindow]) { + } else if (!visible && [quiview_cast(m_view) isActiveWindow]) { // Our window was active/focus window but now hidden, so relinquish // focus to the next possible window in the stack. NSArray<UIView *> *subviews = m_view.viewController.view.subviews; @@ -235,7 +230,7 @@ void QIOSWindow::applyGeometry(const QRect &rect) QMargins QIOSWindow::safeAreaMargins() const { - UIEdgeInsets safeAreaInsets = m_view.qt_safeAreaInsets; + UIEdgeInsets safeAreaInsets = m_view.safeAreaInsets; return QMargins(safeAreaInsets.left, safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom); } @@ -259,22 +254,45 @@ void QIOSWindow::setWindowState(Qt::WindowStates state) if (state & Qt::WindowMinimized) { applyGeometry(QRect()); } else if (state & (Qt::WindowFullScreen | Qt::WindowMaximized)) { - // When an application is in split-view mode, the UIScreen still has the - // same geometry, but the UIWindow is resized to the area reserved for the - // application. We use this to constrain the geometry used when applying the - // fullscreen or maximized window states. Note that we do not do this - // in applyGeometry(), as we don't want to artificially limit window - // placement "outside" of the screen bounds if that's what the user wants. - QRect uiWindowBounds = QRectF::fromCGRect(m_view.window.bounds).toRect(); - QRect fullscreenGeometry = screen()->geometry().intersected(uiWindowBounds); - QRect maximizedGeometry = window()->flags() & Qt::MaximizeUsingFullscreenGeometryHint ? - fullscreenGeometry : screen()->availableGeometry().intersected(uiWindowBounds); + if (NSProcessInfo.processInfo.iOSAppOnMac) { + // iOS apps running as "Designed for iPad" on macOS do not match + // our current window management implementation where a single + // UIWindow is tied to a single screen. And even if we're on the + // right screen, the UIScreen does not account for the 77% scale + // of the UIUserInterfaceIdiomPad environment, so we can't use + // it to clamp the window geometry. Instead just use the UIWindow + // directly, which represents our "screen". + applyGeometry(uiWindowBounds); + } else if (isRunningOnVisionOS()) { + // On visionOS there is no concept of a screen, and hence no concept of + // screen-relative system UI that we should keep top level windows away + // from, so don't apply the UIWindow safe area insets to the screen. + applyGeometry(uiWindowBounds); + } else { + QRect fullscreenGeometry = screen()->geometry(); + QRect maximizedGeometry = fullscreenGeometry; + +#if !defined(Q_OS_VISIONOS) + if (!(window()->flags() & Qt::MaximizeUsingFullscreenGeometryHint)) { + // If the safe area margins reflect the screen's outer edges, + // then reduce the maximized geometry accordingly. Otherwise + // leave it as is, and assume the client will take the safe + // are margins into account explicitly. + UIScreen *uiScreen = m_view.window.windowScene.screen; + UIEdgeInsets safeAreaInsets = m_view.window.safeAreaInsets; + if (m_view.window.bounds.size.width == uiScreen.bounds.size.width) + maximizedGeometry.adjust(safeAreaInsets.left, 0, -safeAreaInsets.right, 0); + if (m_view.window.bounds.size.height == uiScreen.bounds.size.height) + maximizedGeometry.adjust(0, safeAreaInsets.top, 0, -safeAreaInsets.bottom); + } +#endif - if (state & Qt::WindowFullScreen) - applyGeometry(fullscreenGeometry); - else - applyGeometry(maximizedGeometry); + if (state & Qt::WindowFullScreen) + applyGeometry(fullscreenGeometry.intersected(uiWindowBounds)); + else + applyGeometry(maximizedGeometry.intersected(uiWindowBounds)); + } } else { applyGeometry(m_normalGeometry); } @@ -282,21 +300,26 @@ void QIOSWindow::setWindowState(Qt::WindowStates state) void QIOSWindow::setParent(const QPlatformWindow *parentWindow) { - UIView *parentView = parentWindow ? reinterpret_cast<UIView *>(parentWindow->winId()) - : isQtApplication() ? static_cast<QIOSScreen *>(screen())->uiWindow().rootViewController.view : 0; - - [parentView addSubview:m_view]; + UIView *superview = nullptr; + if (parentWindow) + superview = reinterpret_cast<UIView *>(parentWindow->winId()); + else if (isQtApplication() && !isForeignWindow()) + superview = rootViewForScreen(window()->screen()); + + if (superview) + [superview addSubview:m_view]; + else if (quiview_cast(m_view.superview)) + [m_view removeFromSuperview]; } void QIOSWindow::requestActivateWindow() { // Note that several windows can be active at the same time if they exist in the same // hierarchy (transient children). But only one window can be QGuiApplication::focusWindow(). - // Dispite the name, 'requestActivateWindow' means raise and transfer focus to the window: + // Despite the name, 'requestActivateWindow' means raise and transfer focus to the window: if (blockedByModal()) return; - Q_ASSERT(m_view.window); [m_view.window makeKeyWindow]; [m_view becomeFirstResponder]; @@ -306,8 +329,6 @@ void QIOSWindow::requestActivateWindow() void QIOSWindow::raiseOrLower(bool raise) { - // Re-insert m_view at the correct index among its sibling views - // (QWindows) according to their current m_windowLevel: if (!isQtApplication()) return; @@ -315,17 +336,27 @@ void QIOSWindow::raiseOrLower(bool raise) if (subviews.count == 1) return; - for (int i = int(subviews.count) - 1; i >= 0; --i) { - UIView *view = static_cast<UIView *>([subviews objectAtIndex:i]); - if (view.hidden || view == m_view || !view.qwindow) - continue; - int level = static_cast<QIOSWindow *>(view.qwindow->handle())->m_windowLevel; - if (m_windowLevel > level || (raise && m_windowLevel == level)) { - [m_view.superview insertSubview:m_view aboveSubview:view]; - return; + if (m_view.superview == m_view.qtViewController.view) { + // We're a top level window, so we need to take window + // levels into account. + for (int i = int(subviews.count) - 1; i >= 0; --i) { + UIView *view = static_cast<UIView *>([subviews objectAtIndex:i]); + if (view.hidden || view == m_view || !view.qwindow) + continue; + int level = static_cast<QIOSWindow *>(view.qwindow->handle())->m_windowLevel; + if (m_windowLevel > level || (raise && m_windowLevel == level)) { + [m_view.superview insertSubview:m_view aboveSubview:view]; + return; + } } + [m_view.superview insertSubview:m_view atIndex:0]; + } else { + // Child window, or embedded into a non-Qt view controller + if (raise) + [m_view.superview bringSubviewToFront:m_view]; + else + [m_view.superview sendSubviewToBack:m_view]; } - [m_view.superview insertSubview:m_view atIndex:0]; } void QIOSWindow::updateWindowLevel() @@ -354,20 +385,13 @@ void QIOSWindow::updateWindowLevel() m_windowLevel = qMax(transientParentWindow->m_windowLevel, m_windowLevel); } -void QIOSWindow::handleContentOrientationChange(Qt::ScreenOrientation orientation) -{ - // Update the QWindow representation straight away, so that - // we can update the statusbar orientation based on the new - // content orientation. - qt_window_private(window())->contentOrientation = orientation; - - [m_view.qtViewController updateProperties]; -} - void QIOSWindow::applicationStateChanged(Qt::ApplicationState) { + if (isForeignWindow()) + return; + if (window()->isExposed() != isExposed()) - [m_view sendUpdatedExposeEvent]; + [quiview_cast(m_view) sendUpdatedExposeEvent]; } qreal QIOSWindow::devicePixelRatio() const @@ -377,7 +401,10 @@ qreal QIOSWindow::devicePixelRatio() const void QIOSWindow::clearAccessibleCache() { - [m_view clearAccessibleCache]; + if (isForeignWindow()) + return; + + [quiview_cast(m_view) clearAccessibleCache]; } void QIOSWindow::requestUpdate() @@ -385,11 +412,27 @@ void QIOSWindow::requestUpdate() static_cast<QIOSScreen *>(screen())->setUpdatesPaused(false); } +void QIOSWindow::setMask(const QRegion ®ion) +{ + if (!region.isEmpty()) { + QCFType<CGMutablePathRef> maskPath = CGPathCreateMutable(); + for (const QRect &r : region) + CGPathAddRect(maskPath, nullptr, r.toCGRect()); + CAShapeLayer *maskLayer = [CAShapeLayer layer]; + maskLayer.path = maskPath; + m_view.layer.mask = maskLayer; + } else { + m_view.layer.mask = nil; + } +} + +#if QT_CONFIG(opengl) CAEAGLLayer *QIOSWindow::eaglLayer() const { Q_ASSERT([m_view.layer isKindOfClass:[CAEAGLLayer class]]); return static_cast<CAEAGLLayer *>(m_view.layer); } +#endif #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug debug, const QIOSWindow *window) @@ -404,6 +447,37 @@ QDebug operator<<(QDebug debug, const QIOSWindow *window) } #endif // !QT_NO_DEBUG_STREAM -#include "moc_qioswindow.cpp" +/*! + Returns the view cast to a QUIview if possible. + + If the view is not a QUIview, nil is returned, which is safe to + send messages to, effectively making [quiview_cast(view) message] + a no-op. + + For extra verbosity and clearer code, please consider checking + that the platform window is not a foreign window before using + this cast, via QPlatformWindow::isForeignWindow(). + + Do not use this method solely to check for foreign windows, as + that will make the code harder to read for people not working + primarily on iOS, who do not know the difference between the + UIView and QUIView cases. +*/ +QUIView *quiview_cast(UIView *view) +{ + return qt_objc_cast<QUIView *>(view); +} + +bool QIOSWindow::isForeignWindow() const +{ + return ![m_view isKindOfClass:QUIView.class]; +} + +UIView *QIOSWindow::view() const +{ + return m_view; +} QT_END_NAMESPACE + +#include "moc_qioswindow.cpp" diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.h b/src/plugins/platforms/ios/quiaccessibilityelement.h index 6b8efdcede..8580325436 100644 --- a/src/plugins/platforms/ios/quiaccessibilityelement.h +++ b/src/plugins/platforms/ios/quiaccessibilityelement.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #ifndef QUIACCESSIBILITYELEMENT_H #define QUIACCESSIBILITYELEMENT_H @@ -43,14 +7,14 @@ #import <UIKit/UIKit.h> #import <QtGui/QtGui> -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) @interface QT_MANGLE_NAMESPACE(QMacAccessibilityElement) : UIAccessibilityElement @property (readonly) QAccessible::Id axid; - (instancetype)initWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view; -+ (instancetype)elementWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view; ++ (instancetype)elementWithId:(QAccessible::Id)anId; @end diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.mm b/src/plugins/platforms/ios/quiaccessibilityelement.mm index b6c8ca0620..39b2cb8a50 100644 --- a/src/plugins/platforms/ios/quiaccessibilityelement.mm +++ b/src/plugins/platforms/ios/quiaccessibilityelement.mm @@ -1,49 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #include "quiaccessibilityelement.h" -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) #include "private/qaccessiblecache_p.h" #include "private/qcore_mac_p.h" #include "uistrings_p.h" +#include "qioswindow.h" QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); @@ -59,7 +24,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); return self; } -+ (instancetype)elementWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view ++ (instancetype)elementWithId:(QAccessible::Id)anId { Q_ASSERT(anId); if (!anId) @@ -69,9 +34,17 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); QMacAccessibilityElement *element = cache->elementForId(anId); if (!element) { - Q_ASSERT(QAccessible::accessibleInterface(anId)); - element = [[self alloc] initWithId:anId withAccessibilityContainer:view]; - cache->insertElement(anId, element); + auto *a11yInterface = QAccessible::accessibleInterface(anId); + Q_ASSERT(a11yInterface); + auto *window = a11yInterface->window(); + if (window && window->handle()) { + auto *platformWindow = static_cast<QIOSWindow*>(window->handle()); + element = [[self alloc] initWithId:anId withAccessibilityContainer:platformWindow->view()]; + cache->insertElement(anId, element); + } else { + qWarning() << "Could not create a11y element for" << window + << "with platform window" << (window ? window->handle() : nullptr); + } } return element; } @@ -160,6 +133,9 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); if (state.searchEdit) traits |= UIAccessibilityTraitSearchField; + if (state.selected) + traits |= UIAccessibilityTraitSelected; + const auto accessibleRole = iface->role(); if (accessibleRole == QAccessible::Button) { traits |= UIAccessibilityTraitButton; @@ -169,6 +145,14 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); return textField.accessibilityTraits; }(); traits |= defaultTextFieldTraits; + } else if (accessibleRole == QAccessible::Graphic) { + traits |= UIAccessibilityTraitImage; + } else if (accessibleRole == QAccessible::Heading) { + traits |= UIAccessibilityTraitHeader; + } else if (accessibleRole == QAccessible::Link) { + traits |= UIAccessibilityTraitLink; + } else if (accessibleRole == QAccessible::StaticText) { + traits |= UIAccessibilityTraitStaticText; } if (iface->valueInterface()) diff --git a/src/plugins/platforms/ios/quiview.h b/src/plugins/platforms/ios/quiview.h index 6a8e5a7707..7899ec6e0e 100644 --- a/src/plugins/platforms/ios/quiview.h +++ b/src/plugins/platforms/ios/quiview.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #import <UIKit/UIKit.h> @@ -68,10 +32,9 @@ QT_END_NAMESPACE - (QWindow *)qwindow; - (UIViewController *)viewController; - (QIOSViewController*)qtViewController; -@property (nonatomic, readonly) UIEdgeInsets qt_safeAreaInsets; @end -#ifdef Q_OS_IOS +#if QT_CONFIG(metal) @interface QUIMetalView : QUIView @end #endif diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm index 1c97eee216..d5808db305 100644 --- a/src/plugins/platforms/ios/quiview.mm +++ b/src/plugins/platforms/ios/quiview.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 #include "quiview.h" @@ -46,6 +10,7 @@ #include "qiosscreen.h" #include "qioswindow.h" #include "qiosinputcontext.h" +#include "quiwindow.h" #ifndef Q_OS_TVOS #include "qiosmenu.h" #endif @@ -58,17 +23,46 @@ #include <qpa/qwindowsysteminterface_p.h> Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") +Q_LOGGING_CATEGORY(lcQpaInputEvents, "qt.qpa.input.events") + +namespace { +inline ulong getTimeStamp(UIEvent *event) +{ +#if TARGET_OS_SIMULATOR == 1 + // We currently build Qt for simulator using X86_64, even on ARM based macs. + // This results in the simulator running on ARM, while the app is running + // inside it using Rosetta. And with this combination, the event.timestamp, which is + // documented to be in seconds, looks to be something else, and is not progressing + // in sync with a normal clock. + // Sending out mouse events with a timestamp that doesn't follow normal clock time + // will cause problems for mouse-, and pointer handlers that uses them to e.g calculate + // the time between a press and release, and to decide if the user is performing a tap + // or a drag. + // For that reason, we choose to ignore UIEvent.timestamp under the mentioned condition, and + // instead rely on NSProcessInfo. Note that if we force the whole simulator to use Rosetta + // (and not only the Qt app), the timestamps will progress normally. +#if defined(Q_PROCESSOR_ARM) + #warning The timestamp work-around for x86_64 can (probably) be removed when building for ARM +#endif + return ulong(NSProcessInfo.processInfo.systemUptime * 1000); +#endif + + return ulong(event.timestamp * 1000); +} +} @implementation QUIView { QHash<NSUInteger, QWindowSystemInterface::TouchPoint> m_activeTouches; UITouch *m_activePencilTouch; - int m_nextTouchId; NSMutableArray<UIAccessibilityElement *> *m_accessibleElements; + UIPanGestureRecognizer *m_scrollGestureRecognizer; + CGPoint m_lastScrollCursorPos; + CGPoint m_lastScrollDelta; } + (void)load { -#ifndef Q_OS_TVOS +#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS) if (QOperatingSystemVersion::current() < QOperatingSystemVersion(QOperatingSystemVersion::IOS, 11)) { // iOS 11 handles this though [UIView safeAreaInsetsDidChange], but there's no signal for // the corresponding top and bottom layout guides that we use on earlier versions. Note @@ -88,7 +82,10 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") + (Class)layerClass { +#if QT_CONFIG(opengl) return [CAEAGLLayer class]; +#endif + return [super layerClass]; } - (instancetype)initWithQIOSWindow:(QT_PREPEND_NAMESPACE(QIOSWindow) *)window @@ -96,6 +93,32 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") if (self = [self initWithFrame:window->geometry().toCGRect()]) { self.platformWindow = window; m_accessibleElements = [[NSMutableArray<UIAccessibilityElement *> alloc] init]; + m_scrollGestureRecognizer = [[UIPanGestureRecognizer alloc] + initWithTarget:self + action:@selector(handleScroll:)]; + // The gesture recognizer should only care about scroll gestures (for now) + // Set allowedTouchTypes to empty array here to not interfere with touch events + // handled by the UIView. Scroll gestures, even those coming from touch devices, + // such as trackpads will still be received as they are not touch events + m_scrollGestureRecognizer.allowedTouchTypes = [NSArray array]; + if (@available(ios 13.4, *)) { + m_scrollGestureRecognizer.allowedScrollTypesMask = UIScrollTypeMaskAll; + } + m_scrollGestureRecognizer.maximumNumberOfTouches = 0; + m_lastScrollDelta = CGPointZero; + m_lastScrollCursorPos = CGPointZero; + [self addGestureRecognizer:m_scrollGestureRecognizer]; + + if ([self.layer isKindOfClass:CAMetalLayer.class]) { + QWindow *window = self.platformWindow->window(); + if (QColorSpace colorSpace = window->format().colorSpace(); colorSpace.isValid()) { + QCFType<CFDataRef> iccData = colorSpace.iccProfile().toCFData(); + QCFType<CGColorSpaceRef> cgColorSpace = CGColorSpaceCreateWithICCData(iccData); + CAMetalLayer *metalLayer = static_cast<CAMetalLayer *>(self.layer); + metalLayer.colorspace = cgColorSpace; + qCDebug(lcQpaWindow) << "Set" << self << "color space to" << metalLayer.colorspace; + } + } } return self; @@ -104,6 +127,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") - (instancetype)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { +#if QT_CONFIG(opengl) if ([self.layer isKindOfClass:[CAEAGLLayer class]]) { // Set up EAGL layer CAEAGLLayer *eaglLayer = static_cast<CAEAGLLayer *>(self.layer); @@ -113,6 +137,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8 }; } +#endif if (isQtApplication()) self.hidden = YES; @@ -153,6 +178,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") - (void)dealloc { [m_accessibleElements release]; + [m_scrollGestureRecognizer release]; [super dealloc]; } @@ -172,6 +198,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") return description; } +#if !defined(Q_OS_VISIONOS) - (void)willMoveToWindow:(UIWindow *)newWindow { // UIKIt will normally set the scale factor of a view to match the corresponding @@ -181,6 +208,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") // FIXME: Allow the scale factor to be customized through QSurfaceFormat. } +#endif - (void)didAddSubview:(UIView *)subview { @@ -238,6 +266,9 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") Q_UNUSED(layer); Q_ASSERT(layer == self.layer); + if (!self.platformWindow) + return; + [self sendUpdatedExposeEvent]; } @@ -279,7 +310,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") // blocked by this guard. FirstResponderCandidate firstResponderCandidate(self); - qImDebug() << "self:" << self << "first:" << [UIResponder currentFirstResponder]; + qImDebug() << "self:" << self << "first:" << [UIResponder qt_currentFirstResponder]; if (![super becomeFirstResponder]) { qImDebug() << self << "was not allowed to become first responder"; @@ -290,7 +321,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") } if (qGuiApp->focusWindow() != self.platformWindow->window()) - QWindowSystemInterface::handleWindowActivated(self.platformWindow->window(), Qt::ActiveWindowFocusReason); + QWindowSystemInterface::handleFocusWindowChanged(self.platformWindow->window(), Qt::ActiveWindowFocusReason); else qImDebug() << self.platformWindow->window() << "already active, not sending window activation"; @@ -318,7 +349,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") - (BOOL)resignFirstResponder { - qImDebug() << "self:" << self << "first:" << [UIResponder currentFirstResponder]; + qImDebug() << "self:" << self << "first:" << [UIResponder qt_currentFirstResponder]; if (![super resignFirstResponder]) return NO; @@ -327,7 +358,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") UIResponder *newResponder = FirstResponderCandidate::currentCandidate(); if ([self responderShouldTriggerWindowDeactivation:newResponder]) - QWindowSystemInterface::handleWindowActivated(nullptr, Qt::ActiveWindowFocusReason); + QWindowSystemInterface::handleFocusWindowChanged(nullptr, Qt::ActiveWindowFocusReason); return YES; } @@ -341,7 +372,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") if ([self isFirstResponder]) return YES; - UIResponder *firstResponder = [UIResponder currentFirstResponder]; + UIResponder *firstResponder = [UIResponder qt_currentFirstResponder]; if ([firstResponder isKindOfClass:[QIOSTextInputResponder class]] && [firstResponder nextResponder] == self) return YES; @@ -466,7 +497,10 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::AsynchronousDelivery>( self.platformWindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values()); } else { - QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>( + // Send the touch event asynchronously, as the application might spin a recursive + // event loop in response to the touch event (a dialog e.g.), which will deadlock + // the UIKit event delivery system (QTBUG-98651). + QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::AsynchronousDelivery>( self.platformWindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values()); } } @@ -489,7 +523,10 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") { Q_ASSERT(!m_activeTouches.contains(touch.hash)); #endif - m_activeTouches[touch.hash].id = m_nextTouchId++; + // Use window-independent touch identifiers, so that + // multi-touch works across windows. + static quint16 nextTouchId = 0; + m_activeTouches[touch.hash].id = nextTouchId++; #if QT_CONFIG(tabletevent) } #endif @@ -503,17 +540,17 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") topLevel->requestActivateWindow(); } - [self handleTouches:touches withEvent:event withState:QEventPoint::State::Pressed withTimestamp:ulong(event.timestamp * 1000)]; + [self handleTouches:touches withEvent:event withState:QEventPoint::State::Pressed withTimestamp:getTimeStamp(event)]; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { - [self handleTouches:touches withEvent:event withState:QEventPoint::State::Updated withTimestamp:ulong(event.timestamp * 1000)]; + [self handleTouches:touches withEvent:event withState:QEventPoint::State::Updated withTimestamp:getTimeStamp(event)]; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - [self handleTouches:touches withEvent:event withState:QEventPoint::State::Released withTimestamp:ulong(event.timestamp * 1000)]; + [self handleTouches:touches withEvent:event withState:QEventPoint::State::Released withTimestamp:getTimeStamp(event)]; // Remove ended touch points from the active set: #ifndef Q_OS_TVOS @@ -531,9 +568,6 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") // tvOS only supports single touch m_activeTouches.clear(); #endif - - if (m_activeTouches.isEmpty() && !m_activePencilTouch) - m_nextTouchId = 0; } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event @@ -566,13 +600,17 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") qWarning("Subset of active touches cancelled by UIKit"); m_activeTouches.clear(); - m_nextTouchId = 0; m_activePencilTouch = nil; - NSTimeInterval timestamp = event ? event.timestamp : [[NSProcessInfo processInfo] systemUptime]; + ulong timestamp = event ? getTimeStamp(event) : ([[NSProcessInfo processInfo] systemUptime] * 1000); QIOSIntegration *iosIntegration = static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration()); - QWindowSystemInterface::handleTouchCancelEvent(self.platformWindow->window(), ulong(timestamp * 1000), iosIntegration->touchDevice()); + + // Send the touch event asynchronously, as the application might spin a recursive + // event loop in response to the touch event (a dialog e.g.), which will deadlock + // the UIKit event delivery system (QTBUG-98651). + QWindowSystemInterface::handleTouchCancelEvent<QWindowSystemInterface::AsynchronousDelivery>( + self.platformWindow->window(), timestamp, iosIntegration->touchDevice()); } - (int)mapPressTypeToKey:(UIPress*)press withModifiers:(Qt::KeyboardModifiers)qtModifiers text:(QString &)text @@ -586,7 +624,6 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") case UIPressTypeMenu: return Qt::Key_Menu; case UIPressTypePlayPause: return Qt::Key_MediaTogglePlayPause; } -#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_13_4) if (@available(ios 13.4, *)) { NSString *charactersIgnoringModifiers = press.key.charactersIgnoringModifiers; Qt::Key key = QAppleKeyMapper::fromUIKitKey(charactersIgnoringModifiers); @@ -595,47 +632,53 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") return QAppleKeyMapper::fromNSString(qtModifiers, press.key.characters, charactersIgnoringModifiers, text); } -#endif return Qt::Key_unknown; } -- (bool)processPresses:(NSSet *)presses withType:(QEvent::Type)type { +- (bool)isControlKey:(Qt::Key)key +{ + switch (key) { + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Left: + case Qt::Key_Right: + return true; + default: + break; + } + + return false; +} + +- (bool)handlePresses:(NSSet<UIPress *> *)presses eventType:(QEvent::Type)type +{ // Presses on Menu button will generate a Menu key event. By default, not handling // this event will cause the application to return to Headboard (tvOS launcher). // When handling the event (for example, as a back button), both press and // release events must be handled accordingly. + if (!qApp->focusWindow()) + return false; + + bool eventHandled = false; + const bool imEnabled = QIOSInputContext::instance()->inputMethodAccepted(); - bool handled = false; for (UIPress* press in presses) { Qt::KeyboardModifiers qtModifiers = Qt::NoModifier; -#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_13_4) if (@available(ios 13.4, *)) qtModifiers = QAppleKeyMapper::fromUIKitModifiers(press.key.modifierFlags); -#endif QString text; int key = [self mapPressTypeToKey:press withModifiers:qtModifiers text:text]; if (key == Qt::Key_unknown) continue; - if (QWindowSystemInterface::handleKeyEvent(self.platformWindow->window(), type, key, - qtModifiers, text)) { - handled = true; - } - } - - return handled; -} + if (imEnabled && ![self isControlKey:Qt::Key(key)]) + continue; -- (BOOL)handlePresses:(NSSet<UIPress *> *)presses eventType:(QEvent::Type)type -{ - bool handlePress = false; - if (qApp->focusWindow()) { - QInputMethodQueryEvent queryEvent(Qt::ImEnabled); - if (qApp->focusObject() && QCoreApplication::sendEvent(qApp->focusObject(), &queryEvent)) - handlePress = queryEvent.value(Qt::ImEnabled).toBool(); - if (!handlePress && [self processPresses:presses withType:type]) - return true; + bool keyHandled = QWindowSystemInterface::handleKeyEvent( + self.platformWindow->window(), type, key, qtModifiers, text); + eventHandled = eventHandled || keyHandled; } - return false; + + return eventHandled; } - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event @@ -647,20 +690,20 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") - (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event { if (![self handlePresses:presses eventType:QEvent::KeyPress]) - [super pressesBegan:presses withEvent:event]; + [super pressesChanged:presses withEvent:event]; [super pressesChanged:presses withEvent:event]; } - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event { if (![self handlePresses:presses eventType:QEvent::KeyRelease]) - [super pressesBegan:presses withEvent:event]; + [super pressesEnded:presses withEvent:event]; [super pressesEnded:presses withEvent:event]; } - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { -#ifndef Q_OS_TVOS +#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS) // Check first if QIOSMenu should handle the action before continuing up the responder chain return [QIOSMenu::menuActionTarget() targetForAction:action withSender:sender] != 0; #else @@ -673,7 +716,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") - (id)forwardingTargetForSelector:(SEL)selector { Q_UNUSED(selector); -#ifndef Q_OS_TVOS +#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS) return QIOSMenu::menuActionTarget(); #else return nil; @@ -700,6 +743,63 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") UIEditingInteractionConfigurationDefault : UIEditingInteractionConfigurationNone; } +#if QT_CONFIG(wheelevent) +- (void)handleScroll:(UIPanGestureRecognizer *)recognizer +{ + if (!self.platformWindow->window()) + return; + + if (!self.canBecomeFirstResponder) + return; + + CGPoint translation = [recognizer translationInView:self]; + CGFloat deltaX = translation.x - m_lastScrollDelta.x; + CGFloat deltaY = translation.y - m_lastScrollDelta.y; + + QPoint angleDelta; + // From QNSView implementation: + // "Since deviceDelta is delivered as pixels rather than degrees, we need to + // convert from pixels to degrees in a sensible manner. + // It looks like 1/4 degrees per pixel behaves most native. + // (NB: Qt expects the unit for delta to be 8 per degree):" + const int pixelsToDegrees = 2; // 8 * 1/4 + angleDelta.setX(deltaX * pixelsToDegrees); + angleDelta.setY(deltaY * pixelsToDegrees); + + QPoint pixelDelta; + pixelDelta.setX(deltaX); + pixelDelta.setY(deltaY); + + NSTimeInterval time_stamp = [[NSProcessInfo processInfo] systemUptime]; + ulong qt_timestamp = time_stamp * 1000; + + Qt::KeyboardModifiers qt_modifierFlags = Qt::NoModifier; + if (@available(ios 13.4, *)) + qt_modifierFlags = QAppleKeyMapper::fromUIKitModifiers(recognizer.modifierFlags); + + if (recognizer.state == UIGestureRecognizerStateBegan) + // locationInView: doesn't return the cursor position at the time of the wheel event, + // but rather gives us the position with the deltas applied, so we need to save the + // cursor position at the beginning of the gesture + m_lastScrollCursorPos = [recognizer locationInView:self]; + + if (recognizer.state != UIGestureRecognizerStateEnded) { + m_lastScrollDelta.x = translation.x; + m_lastScrollDelta.y = translation.y; + } else { + m_lastScrollDelta = CGPointZero; + } + + QPoint qt_local = QPointF::fromCGPoint(m_lastScrollCursorPos).toPoint(); + QPoint qt_global = self.platformWindow->mapToGlobal(qt_local); + + qCInfo(lcQpaInputEvents).nospace() << "wheel event" << " at " << qt_local + << " pixelDelta=" << pixelDelta << " angleDelta=" << angleDelta; + + QWindowSystemInterface::handleWheelEvent(self.platformWindow->window(), qt_timestamp, qt_local, qt_global, pixelDelta, angleDelta, qt_modifierFlags); +} +#endif // QT_CONFIG(wheelevent) + @end @implementation UIView (QtHelpers) @@ -732,33 +832,20 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") return nil; } -- (UIEdgeInsets)qt_safeAreaInsets -{ - return self.safeAreaInsets; -} - @end -#ifdef Q_OS_IOS +#if QT_CONFIG(metal) @implementation QUIMetalView + (Class)layerClass { -#ifdef TARGET_IPHONE_SIMULATOR - if (@available(ios 13.0, *)) -#endif - return [CAMetalLayer class]; - -#ifdef TARGET_IPHONE_SIMULATOR - return nil; -#endif } @end #endif -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) // Include category as an alternative to using -ObjC (Apple QA1490) #include "quiview_accessibility.mm" #endif diff --git a/src/plugins/platforms/ios/quiview_accessibility.mm b/src/plugins/platforms/ios/quiview_accessibility.mm index 9a103509cc..04e1f8cfb3 100644 --- a/src/plugins/platforms/ios/quiview_accessibility.mm +++ b/src/plugins/platforms/ios/quiview_accessibility.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 #include "qiosplatformaccessibility.h" #include "quiaccessibilityelement.h" @@ -56,12 +20,14 @@ - (void)createAccessibleContainer:(QAccessibleInterface *)iface { - if (!iface) + if (!iface || iface->state().invisible) return; - [self createAccessibleElement: iface]; for (int i = 0; i < iface->childCount(); ++i) [self createAccessibleContainer: iface->child(i)]; + + // The container element must go last, so that it underlays all its children + [self createAccessibleElement:iface]; } - (void)initAccessibility @@ -88,7 +54,6 @@ - (void)clearAccessibleCache { [m_accessibleElements removeAllObjects]; - UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, @""); } // this is a container, returning yes here means the functions below will never be called diff --git a/src/plugins/platforms/ios/quiwindow.h b/src/plugins/platforms/ios/quiwindow.h new file mode 100644 index 0000000000..b5587411e4 --- /dev/null +++ b/src/plugins/platforms/ios/quiwindow.h @@ -0,0 +1,13 @@ +// Copyright (C) 2024 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 + +#ifndef QUIWINDOW_H +#define QUIWINDOW_H + +#include <UIKit/UIWindow.h> + +@interface QUIWindow : UIWindow +@property (nonatomic, readonly) BOOL sendingEvent; +@end + +#endif // QUIWINDOW_H diff --git a/src/plugins/platforms/ios/quiwindow.mm b/src/plugins/platforms/ios/quiwindow.mm new file mode 100644 index 0000000000..783e243e10 --- /dev/null +++ b/src/plugins/platforms/ios/quiwindow.mm @@ -0,0 +1,65 @@ +// Copyright (C) 2024 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 + +#include "quiwindow.h" + +#include "qiostheme.h" + +#include <QtCore/qscopedvaluerollback.h> + +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/qpa/qplatformtheme.h> + +#include <UIKit/UIKit.h> + +@implementation QUIWindow + +- (instancetype)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) + self->_sendingEvent = NO; + + return self; +} + +- (instancetype)initWithWindowScene:(UIWindowScene *)windowScene +{ + if ((self = [super initWithWindowScene:windowScene])) + self->_sendingEvent = NO; + + QIOSTheme::applyTheme(self); + return self; +} + +- (void)sendEvent:(UIEvent *)event +{ + QScopedValueRollback<BOOL> sendingEvent(self->_sendingEvent, YES); + [super sendEvent:event]; +} + +#if !defined(Q_OS_VISIONOS) +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection +{ + [super traitCollectionDidChange:previousTraitCollection]; + + if (!qGuiApp) + return; + + Qt::ColorScheme colorScheme = self.traitCollection.userInterfaceStyle + == UIUserInterfaceStyleDark + ? Qt::ColorScheme::Dark + : Qt::ColorScheme::Light; + + if (self.screen == UIScreen.mainScreen) { + // Check if the current userInterfaceStyle reports a different appearance than + // the platformTheme's appearance. We might have set that one based on the UIScreen + if (previousTraitCollection.userInterfaceStyle != self.traitCollection.userInterfaceStyle + || QGuiApplicationPrivate::platformTheme()->colorScheme() != colorScheme) { + QIOSTheme::initializeSystemPalette(); + QWindowSystemInterface::handleThemeChange<QWindowSystemInterface::SynchronousDelivery>(); + } + } +} +#endif + +@end diff --git a/src/plugins/platforms/ios/uistrings.cpp b/src/plugins/platforms/ios/uistrings.cpp index 2772d28bc8..b17438ba93 100644 --- a/src/plugins/platforms/ios/uistrings.cpp +++ b/src/plugins/platforms/ios/uistrings.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 #include "uistrings_p.h" diff --git a/src/plugins/platforms/ios/uistrings_p.h b/src/plugins/platforms/ios/uistrings_p.h index e77afb2740..cbe139afac 100644 --- a/src/plugins/platforms/ios/uistrings_p.h +++ b/src/plugins/platforms/ios/uistrings_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 #ifndef UISTRINGS_P_H #define UISTRINGS_P_H |