New upstream version 6.10.3
authorStuart Prescott <stuart@debian.org>
Tue, 7 Apr 2026 04:51:34 +0000 (14:51 +1000)
committerStuart Prescott <stuart@debian.org>
Tue, 7 Apr 2026 04:51:34 +0000 (14:51 +1000)
200 files changed:
LICENSES/BSD-3-Clause.txt
README.md
README.pyside6.md
README.pyside6_addons.md
README.pyside6_essentials.md
README.pyside6_examples.md
build_history/blacklist.txt
build_scripts/main.py
build_scripts/wheel_files.py
coin/dependencies.yaml
doc/changelogs/changes-6.10.3 [new file with mode: 0644]
examples/corelib/mimetypesbrowser/mimetypemodel.py
examples/corelib/settingseditor/settingseditor.py
examples/demos/colorpaletteclient/basiclogin.py
examples/demos/colorpaletteclient/doc/colorpaletteclient.rst
examples/demos/colorpaletteclient/paginatedresource.py
examples/demos/documentviewer/imageviewer/imageviewer.py
examples/demos/documentviewer/mainwindow.py
examples/graphs/common/graphprinting/GraphPrintingExample/Graph2D.qml [new file with mode: 0644]
examples/graphs/common/graphprinting/GraphPrintingExample/Graph3D.qml [new file with mode: 0644]
examples/graphs/common/graphprinting/GraphPrintingExample/Main.qml [new file with mode: 0644]
examples/graphs/common/graphprinting/GraphPrintingExample/box_left.svg [new file with mode: 0644]
examples/graphs/common/graphprinting/GraphPrintingExample/box_left_fill.svg [new file with mode: 0644]
examples/graphs/common/graphprinting/GraphPrintingExample/documents.svg [new file with mode: 0644]
examples/graphs/common/graphprinting/GraphPrintingExample/documents_fill.svg [new file with mode: 0644]
examples/graphs/common/graphprinting/GraphPrintingExample/flatten.svg [new file with mode: 0644]
examples/graphs/common/graphprinting/GraphPrintingExample/flatten_square_fill.svg [new file with mode: 0644]
examples/graphs/common/graphprinting/GraphPrintingExample/print.svg [new file with mode: 0644]
examples/graphs/common/graphprinting/GraphPrintingExample/print_fill.svg [new file with mode: 0644]
examples/graphs/common/graphprinting/GraphPrintingExample/qmldir [new file with mode: 0644]
examples/graphs/common/graphprinting/doc/graphprinting.rst [new file with mode: 0644]
examples/graphs/common/graphprinting/doc/graphprinting.webp [new file with mode: 0644]
examples/graphs/common/graphprinting/graphprinter.py [new file with mode: 0644]
examples/graphs/common/graphprinting/graphprinting.pyproject [new file with mode: 0644]
examples/graphs/common/graphprinting/main.py [new file with mode: 0644]
examples/multimedia/audiooutput/audiooutput.py
examples/multimedia/camera/camera.py
examples/multimedia/camera/camera.pyproject
examples/multimedia/camera/camera.qrc [new file with mode: 0644]
examples/multimedia/camera/camera.ui
examples/multimedia/camera/camera_mobile.ui
examples/multimedia/camera/images/shutter.svg [new file with mode: 0644]
examples/multimedia/camera/rc_camera.py [new file with mode: 0644]
examples/multimedia/camera/shutter.svg [deleted file]
examples/multimedia/camera/ui_camera.py
examples/multimedia/camera/ui_camera_mobile.py
examples/multimedia/camera/ui_videosettings_mobile.py
examples/multimedia/screencapture/screencapturepreview.py
examples/network/googlesuggest/googlesuggest.py
examples/pdf/quickpdfviewer/SinglePageModule/Viewer.qml [new file with mode: 0644]
examples/pdf/quickpdfviewer/SinglePageModule/qmldir [new file with mode: 0644]
examples/pdf/quickpdfviewer/main.py
examples/pdf/quickpdfviewer/quickpdfviewer.pyproject
examples/pdf/quickpdfviewer/viewer.qml [deleted file]
examples/qml/editingmodel/EditingModel/Main.qml [new file with mode: 0644]
examples/qml/editingmodel/EditingModel/MovingRectangle.qml [new file with mode: 0644]
examples/qml/editingmodel/EditingModel/qmldir [new file with mode: 0644]
examples/qml/editingmodel/MovingRectangle.qml [deleted file]
examples/qml/editingmodel/doc/editingmodel.rst
examples/qml/editingmodel/main.py
examples/qml/editingmodel/main.pyproject
examples/qml/editingmodel/main.qml [deleted file]
examples/qml/textproperties/TextProperties/Main.qml [new file with mode: 0644]
examples/qml/textproperties/TextProperties/qmldir [new file with mode: 0644]
examples/qml/textproperties/doc/textproperties.rst
examples/qml/textproperties/main.py
examples/qml/textproperties/textproperties.pyproject
examples/qml/textproperties/view.qml [deleted file]
examples/qml/tutorials/extending-qml-advanced/adding/main.py
examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/main.py
examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/main.py
examples/qml/tutorials/extending-qml-advanced/binding/main.py
examples/qml/tutorials/extending-qml-advanced/properties/main.py
examples/qml/usingmodel/UsingModel/Main.qml [new file with mode: 0644]
examples/qml/usingmodel/UsingModel/qmldir [new file with mode: 0644]
examples/qml/usingmodel/doc/usingmodel.rst
examples/qml/usingmodel/main.py [new file with mode: 0644]
examples/qml/usingmodel/usingmodel.py [deleted file]
examples/qml/usingmodel/usingmodel.pyproject
examples/qml/usingmodel/view.qml [deleted file]
examples/quick/models/objectlistmodel/ObjectListModel/Main.qml [new file with mode: 0644]
examples/quick/models/objectlistmodel/ObjectListModel/qmldir [new file with mode: 0644]
examples/quick/models/objectlistmodel/doc/objectlistmodel.rst
examples/quick/models/objectlistmodel/main.py [new file with mode: 0644]
examples/quick/models/objectlistmodel/objectlistmodel.py [deleted file]
examples/quick/models/objectlistmodel/objectlistmodel.pyproject
examples/quick/models/objectlistmodel/view.qml [deleted file]
examples/quickcontrols/gallery/gallery.py
examples/quickcontrols/gallery/gallery.qml
examples/quickcontrols/gallery/pages/DialPage.qml
examples/quickcontrols/gallery/pages/ProgressBarPage.qml
examples/quickcontrols/gallery/pages/RangeSliderPage.qml
examples/quickcontrols/gallery/pages/SliderPage.qml
examples/quickcontrols/gallery/pages/SpinBoxPage.qml
examples/quickcontrols/gallery/pages/TextAreaPage.qml
examples/quickcontrols/gallery/pages/TextFieldPage.qml
examples/quickcontrols/gallery/rc_gallery.py
examples/samplebinding/main.py
examples/scriptableapplication/mainwindow.h
examples/webenginequick/nanobrowser/ApplicationRoot.qml [deleted file]
examples/webenginequick/nanobrowser/BrowserDialog.qml [deleted file]
examples/webenginequick/nanobrowser/BrowserUtils/ApplicationRoot.qml [new file with mode: 0644]
examples/webenginequick/nanobrowser/BrowserUtils/BrowserDialog.qml [new file with mode: 0644]
examples/webenginequick/nanobrowser/BrowserUtils/BrowserWindow.qml [new file with mode: 0644]
examples/webenginequick/nanobrowser/BrowserUtils/DownloadView.qml [new file with mode: 0644]
examples/webenginequick/nanobrowser/BrowserUtils/FindBar.qml [new file with mode: 0644]
examples/webenginequick/nanobrowser/BrowserUtils/FullScreenNotification.qml [new file with mode: 0644]
examples/webenginequick/nanobrowser/BrowserUtils/WebAuthDialog.qml [new file with mode: 0644]
examples/webenginequick/nanobrowser/BrowserUtils/qmldir [new file with mode: 0644]
examples/webenginequick/nanobrowser/BrowserWindow.qml [deleted file]
examples/webenginequick/nanobrowser/DownloadView.qml [deleted file]
examples/webenginequick/nanobrowser/FindBar.qml [deleted file]
examples/webenginequick/nanobrowser/FullScreenNotification.qml [deleted file]
examples/webenginequick/nanobrowser/WebAuthDialog.qml [deleted file]
examples/webenginequick/nanobrowser/doc/nanobrowser.png [deleted file]
examples/webenginequick/nanobrowser/doc/nanobrowser.rst
examples/webenginequick/nanobrowser/doc/nanobrowser.webp [new file with mode: 0644]
examples/webenginequick/nanobrowser/main.py [new file with mode: 0644]
examples/webenginequick/nanobrowser/nanobrowser.pyproject
examples/webenginequick/nanobrowser/qmldir
examples/webenginequick/nanobrowser/quicknanobrowser.py [deleted file]
examples/webenginequick/nanobrowser/rc_resources.py
examples/webenginequick/nanobrowser/resources.qrc
examples/webenginewidgets/simplebrowser/browserwindow.py
examples/webenginewidgets/simplebrowser/tabwidget.py
examples/webenginewidgets/simplebrowser/webpopupwindow.py
examples/webview/minibrowser/main.qml
examples/webview/minibrowser/qml.qrc
examples/webview/minibrowser/rc_qml.py
examples/widgets/desktop/screenshot/screenshot.py
examples/widgets/dialogs/classwizard/classwizard.py
examples/widgets/dialogs/classwizard/listchooser.py
examples/widgets/dialogs/standarddialogs/standarddialogs.py
examples/widgets/gettext/main.py
examples/widgets/itemviews/editabletreemodel/mainwindow.py
examples/widgets/itemviews/editabletreemodel/treemodel.py
examples/widgets/itemviews/spreadsheet/spreadsheet.py
examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.py
examples/widgets/mainwindows/mdi/mdi.py
examples/widgets/painting/painter/painter.py
examples/widgets/richtext/textedit/textedit.py
sources/pyside-tools/deploy_lib/__init__.py
sources/pyside-tools/project.py
sources/pyside6/.cmake.conf
sources/pyside6/PySide6/QtHttpServer/CMakeLists.txt
sources/pyside6/PySide6/QtHttpServer/typesystem_httpserver.xml
sources/pyside6/PySide6/QtWebEngineCore/typesystem_webenginecore.xml
sources/pyside6/doc/building_from_source/index.rst
sources/pyside6/doc/building_from_source/linux.rst
sources/pyside6/doc/building_from_source/macOS.rst
sources/pyside6/doc/building_from_source/windows.rst
sources/pyside6/doc/commercial/index.rst
sources/pyside6/doc/deployment/deployment-pyside6-android-deploy.rst
sources/pyside6/doc/developer/signalmanager.md
sources/pyside6/doc/extras/QtQuick.rst
sources/pyside6/doc/faq/whatisshiboken.rst
sources/pyside6/doc/gettingstarted.rst
sources/pyside6/doc/index.rst
sources/pyside6/doc/modules.rst
sources/pyside6/doc/qtattributionsscannertorst.py
sources/pyside6/doc/tools/pyside-project.rst
sources/pyside6/doc/tutorials/basictutorial/qrcfiles.rst
sources/pyside6/doc/tutorials/basictutorial/signals_and_slots.rst
sources/pyside6/doc/tutorials/basictutorial/uifiles.rst
sources/pyside6/doc/tutorials/basictutorial/widgetstyling.rst
sources/pyside6/doc/tutorials/datavisualize/add_mainwindow.rst
sources/pyside6/doc/tutorials/datavisualize/add_tableview.rst
sources/pyside6/doc/tutorials/datavisualize/filter_data.rst
sources/pyside6/doc/tutorials/expenses/expenses.rst
sources/pyside6/doc/tutorials/qmlintegration/qmlintegration.rst
sources/pyside6/doc/tutorials/qmlsqlintegration/qmlsqlintegration.rst
sources/pyside6/libpyside/pysideslot.cpp
sources/pyside6/libpyside/signalmanager.cpp
sources/pyside6/tests/QtCore/attr_cache_py3k.py
sources/pyside6/tests/QtCore/emoji_string_test.py
sources/pyside6/tests/QtTextToSpeech/qtexttospeech_test.py
sources/pyside6/tests/QtWidgets/private_mangle_test.py
sources/pyside6/tests/manually/README.txt
sources/pyside6/tests/pysidetest/mixin_signal_slots_test.py
sources/pyside6/tests/pysidetest/mock_as_slot_test.py
sources/pyside6/tests/pysidetest/qapp_like_a_macro_test.py
sources/pyside6/tests/pysidetest/signal_slot_warning.py
sources/pyside6/tests/pysidetest/signal_tp_descr_get_test.py
sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py
sources/shiboken6/.cmake.conf
sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp
sources/shiboken6/doc/gettingstarted.rst
sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp
sources/shiboken6/generator/shiboken/cppgenerator.cpp
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/mapping.py
sources/shiboken6/tests/libsample/functions.cpp
sources/shiboken6/tests/libsample/functions.h
sources/shiboken6/tests/libsample/polygon.cpp
sources/shiboken6/tests/libsample/polygon.h
sources/shiboken6/tests/samplebinding/decisor_test.py
sources/shiboken6/tests/samplebinding/sample_test.py
sources/shiboken6/tests/samplebinding/samplesnippets.cpp
sources/shiboken6/tests/samplebinding/typesystem_sample.xml
wheel_artifacts/pyproject.toml.base

index b91bbd89468099ae5c3028f176783976ca538187..23e8629f0a7706f95550633a6a3e6df57954ba68 100644 (file)
@@ -1,4 +1,4 @@
-Copyright (c) <year> <owner>.
+Copyright (c) 2026 The Qt Company Ltd.
 
 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 
index bb98271d3554ba32f8c3951dce384a8b5216a1a2..4d8cc05bdcc18a156d30ad273de638ac043a295e 100644 (file)
--- a/README.md
+++ b/README.md
@@ -118,7 +118,7 @@ using `setup.py build`:
 
 ## Requirements
 
- * Python 3.9+ is supported (for Qt 6.7+)
+ * Python 3.9+ is supported (for Qt 6.10+)
  * CMake: Specify the path to cmake with `--cmake` option or add cmake to the
    system path.
  * Qt 6.x is supported. Specify the path to qtpaths with `--qtpaths` option or
index 95e75a4c6dba896247e3971eeb6a14562ba81bd8..39dbf9da5ad092f3813bc65e69113b9be4e4e49f 100644 (file)
@@ -78,7 +78,7 @@ You can find more information about the PySide6 module API in the
 [official Qt for Python documentation](https://doc.qt.io/qtforpython/).
 
 If you come across any issue, please file a bug report at our
-[JIRA tracker](https://bugreports.qt.io/projects/PYSIDE) following
+[JIRA tracker](https://qt-project.atlassian.net/browse/PYSIDE) following
 our [guidelines](https://wiki.qt.io/Qt_for_Python/Reporting_Bugs).
 
 ### Community
index e232565a2efdb309b3bc2e34b17ca98f9ce1c614..9121d661876dfa9a730f11f8038c59f38a54f85c 100644 (file)
@@ -57,7 +57,7 @@ You can find more information about the PySide6 module API in the
 [official Qt for Python documentation](https://doc.qt.io/qtforpython/).
 
 If you come across any issue, please file a bug report at our
-[JIRA tracker](https://bugreports.qt.io/projects/PYSIDE) following
+[JIRA tracker](https://qt-project.atlassian.net/browse/PYSIDE) following
 our [guidelines](https://wiki.qt.io/Qt_for_Python/Reporting_Bugs).
 
 ### Community
index 73d8b66c25b2ae9988c2566828f001303b18ed90..5c0424fa83c1b2102a32e6c4168dd1098188f705 100644 (file)
@@ -41,7 +41,7 @@ You can find more information about the PySide6 module API in the
 [official Qt for Python documentation](https://doc.qt.io/qtforpython/).
 
 If you come across any issue, please file a bug report at our
-[JIRA tracker](https://bugreports.qt.io/projects/PYSIDE) following
+[JIRA tracker](https://qt-project.atlassian.net/browse/PYSIDE) following
 our [guidelines](https://wiki.qt.io/Qt_for_Python/Reporting_Bugs).
 
 ### Community
index b176139dfde45e057ce398d70845a5508f3f3d88..3fa59bbbfb9ac6d4363c85c8587b2d8098c413ad 100644 (file)
@@ -18,7 +18,7 @@ You can find more information about the PySide6 module API in the
 [official Qt for Python documentation](https://doc.qt.io/qtforpython/).
 
 If you come across any issue, please file a bug report at our
-[JIRA tracker](https://bugreports.qt.io/projects/PYSIDE) following
+[JIRA tracker](https://qt-project.atlassian.net/browse/PYSIDE) following
 our [guidelines](https://wiki.qt.io/Qt_for_Python/Reporting_Bugs).
 
 ### Community
index adfb0c6023decbddbe89980b977e05a7a9abae4d..b2aaecb8eff4b056853a40682eeb306e0beeff4c 100644 (file)
@@ -15,7 +15,6 @@
     darwin ci
 # PYSIDE-474
 [QtWebEngineWidgets::pyside-474-qtwebengineview]
-    py2
     py3
 [QtCore::qthread_test]
 # PYSIDE-754
index 1819b7e431ff4ea6b3314a51d22228604efbe4d3..02ad4020cb3e0939ffccf717661074c06aa7104d 100644 (file)
@@ -667,8 +667,7 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
 
         # Use Legacy OpenGL to avoid issues on systems like Ubuntu 20.04
         # which require to manually install the libraries which
-        # were previously linked to the QtGui module in 6.1
-        # https://bugreports.qt.io/browse/QTBUG-89754
+        # were previously linked to the QtGui module in 6.1 (QTBUG-89754).
         cmake_cmd.append("-DOpenGL_GL_PREFERENCE=LEGACY")
 
         if OPTION['AVOID_PROTECTED_HACK']:
index 6aaa66567acb550d4d8b5a70c0da24441213dda2..79bd6fa0a39761258205d8c842e725d80056860a 100644 (file)
@@ -594,6 +594,13 @@ def module_QtQuick() -> ModuleData:
     data.metatypes.extend(_metatypes)
     json_data = get_module_json_data("Quick")
     data.plugins = get_module_plugins(json_data)
+
+    # This includes the vectorimageformats plugin
+    json_data_vector = get_module_json_data("QuickVectorImageGeneratorPrivate")
+    vector_plugins = get_module_plugins(json_data_vector)
+    if vector_plugins:
+        data.plugins += vector_plugins
+
     data.extra_files.append("svgtoqml*")
 
     return data
index 9d0157f7e9343a4cab21c2688aff63ce1ad1bd97..3c24055f2bc8b0f9048990638a9e2e0f68ac4ed1 100644 (file)
@@ -1,6 +1,6 @@
 product_dependency:
   ../../qt/qt5:
-    ref: "abfb3788bcfd08d6192899eb39a8ff34d7546da8"
+    ref: "6417867c2da7aa2b1b94fb9ab084b514fb7009bb"
 dependency_source: supermodule
 dependencies: [
       "../../qt/qt3d",
diff --git a/doc/changelogs/changes-6.10.3 b/doc/changelogs/changes-6.10.3
new file mode 100644 (file)
index 0000000..649880f
--- /dev/null
@@ -0,0 +1,40 @@
+Qt for Python 6.10.3 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+*                                  PySide6                                 *
+****************************************************************************
+
+ - [PYSIDE-2862] QHttpServerConfiguration has been added.
+ - [PYSIDE-3079] type hints: The type hints of the Slot decorator have
+                 been fixed.
+ - [PYSIDE-3263] Documentation on how to use image resources in ui files
+                 has been improved.
+ - [PYSIDE-3265] pyside6-project: Additional command line arguments to "run"
+                 are now suported.
+ - [PYSIDE-3266] A crash when connecting a slot with result to a signal
+                 has been fixed.
+ - [PYSIDE-3267] Missing libraries and plugins required for pyside6-svgtoqml
+                 have been added.
+ - [PYSIDE-3280] A bug related to QWebEngineExtensionManager.extensions()
+                 has been fixed.
+
+****************************************************************************
+*                                  Shiboken6                               *
+****************************************************************************
+
+- [PYSIDE-3281] A bug affecting functions with "char" parameters with
+                default values, such as QLocale.toString(double,char,...)
+                has been fixed.
index 12df5c863f1f7330f880a38b8d26b2468da47ea7..9c6ff1d017a58ef1c1a17c352fc6c86d266f6927 100644 (file)
@@ -98,8 +98,7 @@ class MimeTypeModel(QStandardItemModel):
     @staticmethod
     def formatMimeTypeInfo(t: QMimeType):
         out = f"<html><head/><body><h3><center>{t.name()}</center></h3><br><table>"
-        aliases_str = ", ".join(t.aliases())
-        if aliases_str:
+        if aliases_str := ", ".join(t.aliases()):
             out += f"<tr><td>Aliases:</td><td> ({aliases_str})"
 
         out += (
@@ -108,23 +107,18 @@ class MimeTypeModel(QStandardItemModel):
             f"<tr><td>Generic icon name</td><td>{t.genericIconName()}</td></tr>"
         )
 
-        filter_str = t.filterString()
-        if filter_str:
+        if filter_str := t.filterString():
             out += f"<tr><td>Filter:</td><td>{filter_str}</td></tr>"
 
-        patterns_str = ", ".join(t.globPatterns())
-        if patterns_str:
+        if patterns_str := ", ".join(t.globPatterns()):
             out += f"<tr><td>Glob patterns:</td><td>{patterns_str}</td></tr>"
 
-        parentMimeTypes_str = ", ".join(t.parentMimeTypes())
-        if parentMimeTypes_str:
+        if parentMimeTypes_str := ", ".join(t.parentMimeTypes()):
             out += f"<tr><td>Parent types:</td><td>{parentMimeTypes_str}</td></tr>"
 
-        suffixes = t.suffixes()
-        if suffixes:
+        if suffixes := t.suffixes():
             out += "<tr><td>Suffixes:</td><td>"
-            preferredSuffix = t.preferredSuffix()
-            if preferredSuffix:
+            if preferredSuffix := t.preferredSuffix():
                 suffixes.remove(preferredSuffix)
                 out += f"<b>{preferredSuffix}</b> "
             suffixes_str = ", ".join(suffixes)
index 7da39a77c376f7db510dc07cc2ef77249d75714d..960c513d6f8829617721fbc4ef65da05152131d4 100644 (file)
@@ -578,8 +578,7 @@ class SettingsTree(QTreeWidget):
             else:
                 # Try to convert to type unless a QByteArray is received
                 if isinstance(value, str):
-                    value_type = self._type_checker.type_from_text(value)
-                    if value_type:
+                    if value_type := self._type_checker.type_from_text(value):
                         value = self.settings.value(key, type=value_type)
                 child.setText(1, value.__class__.__name__)
             child.setText(2, VariantDelegate.display_text(value))
@@ -666,8 +665,7 @@ class VariantDelegate(QItemDelegate):
         else:
             editor = QLineEdit(parent)
             editor.setFrame(False)
-            validator = self._type_checker.create_validator(original_value, editor)
-            if validator:
+            if validator := self._type_checker.create_validator(original_value, editor):
                 editor.setValidator(validator)
         return editor
 
index 1090f4d06bad760846c0f166ff501b215a82ff82..d8c433708704da8a45002d99b9b47224e8dd4ee7 100644 (file)
@@ -75,8 +75,7 @@ class BasicLogin(AbstractResource):
         (json, error) = reply.readJson()
         if json and json.isObject():
             json_object = json.object()
-            token = json_object.get(tokenField)
-            if token:
+            if token := json_object.get(tokenField):
                 email = data[emailField]
                 token = json_object[tokenField]
                 id = data[idField]
index 0dcb91d4e101c672d186d866a0329747e92cb919..87e5016a98018cd0f2ccb294fd60db1f6ff13b5b 100644 (file)
@@ -13,6 +13,7 @@ Application functionality
 -------------------------
 
 The example provides the following basic functionalities:
+
 * Select the server to communicate with
 * List users and colors
 * Login and logout users
@@ -25,7 +26,7 @@ At start the application presents the options for the color palette server to co
 with. The predefined options are:
 
 * ``https://reqres.in``, a publicly available REST API test service
-* A Qt-based REST API server example in ``QtHttpServer``
+* A `Qt-based REST API server C++ example`_ in the `QtHttpServer Module`_
 
 Once selected, the RESTful API client issues a test HTTP GET to the color API
 to check if the service is accessible.
@@ -70,10 +71,14 @@ authorization token.
 
 The actual communication is done with a rest access manager which implements
 some convenience functionality to deal specifically with HTTP REST APIs and
-effectively deals with sending and receiving the ``QNetworkRequest`` and
-``QNetworkReply`` as needed.
+effectively deals with sending and receiving the
+:class:`~PySide6.QtNetwork.QNetworkRequest` and
+:class:`~PySide6.QtNetwork.QNetworkReply` as needed.
 
 .. image:: colorpaletteclient.webp
-   :width: 90%
    :align: center
    :alt: RESTful API client
+
+.. _`Qt-based REST API server C++ example`: https://doc.qt.io/qt-6/qthttpserver-colorpalette-example.html
+.. _`QtHttpServer Module`: https://doc.qt.io/qt-6/qthttpserver-index.html
+.. _`QML views`: https://doc.qt.io/qt-6/qml-qtquick-listview.html
index db399a7dc68d404eb0a11c40069a9324e97b70f0..24be77fc3a3ce450d04778a884040fc418054a58 100644 (file)
@@ -190,19 +190,30 @@ class PaginatedResource(AbstractResource):
         self.m_manager.get(request, self, self.refreshCurrentPageReply)
 
     def refreshCurrentPageReply(self, reply):
-        if not reply.isSuccess():
-            print("PaginatedResource: ", reply.errorString(), file=sys.stderr)
-        (json, error) = reply.readJson()
-        if json:
-            self.refreshRequestFinished(json)
+        error = ""
+        if reply.isSuccess():
+            (json, jsonError) = reply.readJson()
+            if json:
+                self.refreshRequestFinished(json)
+            else:
+                error = jsonError.errorString()
         else:
+            reply_error = reply.errorString()
+            error = reply_error if reply_error else "Network error"
+
+        if error:
+            url = reply.networkReply().url().toString()
+            print(f'PaginatedResource: request "{url}" failed: "{error}"', file=sys.stderr)
             self.refreshRequestFailed()
 
     def refreshRequestFinished(self, json):
         json_object = json.object()
-        self._populateModel(json_object["data"])
-        self.m_pages = int(json_object[totalPagesField])
-        self.m_currentPage = int(json_object[currentPageField])
+        data = json_object.get("data")
+        totalPages = json_object.get(totalPagesField)
+        currentPage = json_object.get(currentPageField)
+        self._populateModel(data if data else [])
+        self.m_pages = int(totalPages) if totalPages else 1
+        self.m_currentPage = int(currentPage) if currentPage else 1
         self.pageUpdated.emit()
         self.pagesUpdated.emit()
         self.dataUpdated.emit()
@@ -222,7 +233,7 @@ class PaginatedResource(AbstractResource):
     @Slot("QVariantMap", int)
     def update(self, data, id):
         request = self.m_api.createRequest(f"{self.m_path}/{id}")
-        self.m_manager.put(request, self, self.updateReply)
+        self.m_manager.put(request, data, self, self.updateReply)
 
     def updateReply(self, reply):
         if reply.isSuccess():
index a2155acac5b2c5ec3f2ee2fdb0e59457f069c6de..6843c5d6b4ef643ad8c3ca0c7afdf0ecd36e1ac5 100644 (file)
@@ -18,7 +18,7 @@ def imageFormats():
 
     for format_bytes in all_formats:
         format_str = bytes(format_bytes).decode("utf-8")  # Convert QByteArray to str
-        if format_str not in ["tif", "cur"]:  # Exclude duplicate/non-existent formats
+        if format_str not in ["pdf", "tif", "cur"]:  # Exclude duplicate/non-existent formats
             result.append(f"image/{format_str}")
 
     return result
index d72cca283769b983d84c8203c4ac758a084f0d32..d35f76e6ceac557b8bed5ff69fc046381ad5888a 100644 (file)
@@ -52,8 +52,7 @@ class MainWindow(QMainWindow):
         menu = RecentFileMenu(self, self._recentFiles)
         self.ui.actionRecent.setMenu(menu)
         menu.fileOpened.connect(self.openFile)
-        button = self.ui.mainToolBar.widgetForAction(self.ui.actionRecent)
-        if button:
+        if button := self.ui.mainToolBar.widgetForAction(self.ui.actionRecent):
             self.ui.actionRecent.triggered.connect(button.showMenu)
 
     @Slot(int)
diff --git a/examples/graphs/common/graphprinting/GraphPrintingExample/Graph2D.qml b/examples/graphs/common/graphprinting/GraphPrintingExample/Graph2D.qml
new file mode 100644 (file)
index 0000000..738f717
--- /dev/null
@@ -0,0 +1,75 @@
+// Copyright (C) 2026 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtGraphs
+
+Rectangle {
+    id: graphContainer
+    width: 1280
+    height: 720
+    property alias theme: lines.theme
+
+    color: "white"
+
+    GraphsView {
+        id: lines
+        anchors.fill: parent
+        anchors.margins: 16
+        theme: GraphsTheme {
+            grid.mainColor: "darkgrey"
+            grid.subColor: "lightgrey"
+            labelTextColor: "black"
+            plotAreaBackgroundColor: "white"
+            backgroundColor: "white"
+            colorScheme: Qt.Light
+        }
+        axisX: ValueAxis {
+            max: 5
+            tickInterval: 1
+            subTickCount: 9
+            labelDecimals: 1
+        }
+        axisY: ValueAxis {
+            max: 10
+            tickInterval: 1
+            subTickCount: 4
+            labelDecimals: 1
+        }
+
+        component Marker : Rectangle {
+            width: 8
+            height: 8
+            color: "#ffffff"
+            radius: width * 0.5
+            border.width: 4
+            border.color: "#000000"
+        }
+
+        LineSeries {
+            id: lineSeries1
+            width: 4
+            pointDelegate: Marker { }
+            color: "black"
+            XYPoint { x: 0; y: 0 }
+            XYPoint { x: 1; y: 2.1 }
+            XYPoint { x: 2; y: 3.3 }
+            XYPoint { x: 3; y: 2.1 }
+            XYPoint { x: 4; y: 4.9 }
+            XYPoint { x: 5; y: 3.0 }
+        }
+
+        LineSeries {
+            id: lineSeries2
+            width: 4
+            pointDelegate: Marker { }
+            color: "black"
+            XYPoint { x: 0; y: 5.0 }
+            XYPoint { x: 1; y: 3.3 }
+            XYPoint { x: 2; y: 7.1 }
+            XYPoint { x: 3; y: 7.5 }
+            XYPoint { x: 4; y: 6.1 }
+            XYPoint { x: 5; y: 3.2 }
+        }
+    }
+}
diff --git a/examples/graphs/common/graphprinting/GraphPrintingExample/Graph3D.qml b/examples/graphs/common/graphprinting/GraphPrintingExample/Graph3D.qml
new file mode 100644 (file)
index 0000000..4ba0e18
--- /dev/null
@@ -0,0 +1,64 @@
+// Copyright (C) 2026 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtGraphs
+
+Item {
+    id: graphContainer
+    width: 1280
+    height: 720
+    property alias theme: bars.theme
+
+    Bars3D {
+        id: bars
+        anchors.fill: parent
+        msaaSamples: 8
+        cameraPreset: Graphs3D.CameraPreset.IsometricLeftHigh
+
+        theme: GraphsTheme {
+            backgroundColor: "white"
+            plotAreaBackgroundVisible: false
+            grid.mainColor: "black"
+            labelFont.pointSize: 20
+            labelBackgroundVisible: false
+            colorScheme: Qt.Light
+        }
+
+        Bar3DSeries {
+            id: series
+            itemLabelFormat: "Expenses, @colLabel, @rowLabel: -@valueLabel"
+            baseGradient: gradient
+            colorStyle: GraphsTheme.ColorStyle.RangeGradient
+
+            ItemModelBarDataProxy {
+                id: barProxy
+                itemModel: ListModel {
+                    ListElement{ coords: "0,0"; data: "4.75"; }
+                    ListElement{ coords: "1,0"; data: "3.00"; }
+                    ListElement{ coords: "0,1"; data: "3.55"; }
+                    ListElement{ coords: "1,1"; data: "3.03"; }
+                    ListElement{ coords: "0,2"; data: "3.37"; }
+                    ListElement{ coords: "1,2"; data: "2.98"; }
+                    ListElement{ coords: "0,3"; data: "5.34"; }
+                    ListElement{ coords: "1,3"; data: "4.54"; }
+                    ListElement{ coords: "0,4"; data: "6.01"; }
+                    ListElement{ coords: "1,4"; data: "5.83"; }
+                }
+                rowRole: "coords"
+                columnRole: "coords"
+                valueRole: "data"
+                rowRolePattern: /(\d),(\d)/
+                columnRolePattern: /(\d),(\d)/
+                rowRoleReplace: "\\1"
+                columnRoleReplace: "\\2"
+            }
+
+            Gradient {
+                id: gradient
+                GradientStop { position: 1.0; color: "#5000FF" }
+                GradientStop { position: 0.0; color: "#2000FF" }
+            }
+        }
+    }
+}
diff --git a/examples/graphs/common/graphprinting/GraphPrintingExample/Main.qml b/examples/graphs/common/graphprinting/GraphPrintingExample/Main.qml
new file mode 100644 (file)
index 0000000..28e9bb8
--- /dev/null
@@ -0,0 +1,259 @@
+// Copyright (C) 2026 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtCore
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls.Fusion
+import QtQuick.Dialogs
+
+Rectangle {
+    id: mainView
+    width: 1280
+    height: 720
+    color: Application.styleHints.colorScheme === Qt.Dark ? "darkgray" : "lightgray"
+
+    property var item: stackLayout.itemAt(stackLayout.currentIndex)
+    property var outputsize: Qt.size(linegraph.width * 4, linegraph.height * 4)
+
+    RowLayout {
+        id: rowLayout
+        anchors.fill: parent
+        anchors.leftMargin: 5
+        anchors.rightMargin: 5
+        anchors.topMargin: 5
+        anchors.bottomMargin: 5
+        spacing: 5
+
+        GroupBox {
+            id: groupBox
+            Layout.alignment: Qt.AlignLeft | Qt.AlignTop
+            title: qsTr("Printing and exporting")
+
+            ColumnLayout {
+                id: buttonLayout
+                spacing: 0
+                uniformCellSizes: true
+                Button {
+                    id: captureButton
+                    text: qsTr("Save to PDF")
+                    flat: true
+
+                    icon.source: pressed ? "documents_fill.svg" : "documents.svg"
+                    icon.height: 36
+                    icon.width: 36
+
+                    onPressed: dialog.open()
+                }
+
+                Button {
+                    id: printButton
+                    text: qsTr("Send to printer")
+                    flat: true
+
+                    icon.source: pressed ? "print_fill.svg" : "print.svg"
+                    icon.height: 36
+                    icon.width: 36
+
+                    onPressed: printerDialog.open()
+                }
+            }
+        }
+
+        Item {
+            id: tabGroup
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+
+            TabBar {
+                id: tabBar
+                anchors.left: parent.left
+                anchors.right: parent.right
+
+                TabButton {
+                    text: "2D Graph"
+                    implicitHeight: 48
+                    icon.source: checked ? "flatten_square_fill.svg" : "flatten.svg"
+                    icon.height: 36
+                    icon.width: 36
+                }
+
+                TabButton {
+                    text: "3D Graph"
+                    implicitHeight: 48
+                    icon.source: checked ? "box_left_fill.svg" : "box_left.svg"
+                    icon.height: 36
+                    icon.width: 36
+                }
+            }
+            Frame {
+                id: tabFrame
+                anchors.left: parent.left
+                anchors.right: parent.right
+                anchors.top: tabBar.bottom
+                anchors.bottom: parent.bottom
+
+                StackLayout {
+                    id: stackLayout
+
+                    anchors.fill: parent
+                    currentIndex: tabBar.currentIndex
+
+                    Graph2D {
+                        id: linegraph
+                    }
+
+                    Graph3D {
+                        id: bargraph
+                    }
+                }
+            }
+        }
+    }
+
+    MessageDialog {
+        id: message
+        onButtonClicked: mainView.cleanAfterPrint()
+    }
+
+    FileDialog {
+        id: dialog
+        currentFolder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
+        nameFilters: ["PDF files (*.pdf)"]
+        defaultSuffix: "pdf"
+
+        fileMode: FileDialog.SaveFile
+        onAccepted: {
+            mainView.prepareForPrint()
+
+            mainView.item.grabToImage(function (result) {
+                message.title = "Save PDF"
+                message.text = "PDF saved to " + graphPrinter.generatePDF(
+                            dialog.selectedFile, result.image)
+                message.open()
+            }, mainView.outputsize)
+        }
+    }
+
+    Dialog {
+        id: printerDialog
+        anchors.centerIn: parent
+        contentHeight: printerListView.height
+        contentWidth: printerListView.width
+
+        title: qsTr("Available Printers")
+        modal: true
+
+        onOpened: {
+            printerModel.clear()
+            var printers = graphPrinter.getPrinters()
+            printers.forEach((x, i) => printerModel.append({
+                                                               "name": x
+                                                           }))
+        }
+
+        onAccepted: {
+            var selectedPrinter = printerModel.get(printerListView.currentIndex)
+            mainView.prepareForPrint()
+            mainView.item.grabToImage(function (result) {
+                message.title = "Print"
+                message.text = graphPrinter.print(result.image,
+                                                  selectedPrinter.name)
+                message.open()
+            }, mainView.outputsize)
+        }
+
+        onClosed: {
+            mainView.cleanAfterPrint()
+        }
+
+        Component {
+            id: printerDelegate
+            Rectangle {
+                width: 198
+                height: 25
+                color: "transparent"
+                border.color: mainView.item.theme.grid.mainColor
+                clip: true
+
+                Text {
+                    padding: 5
+                    text: qsTr("<b>%1</b>").arg(name)
+                    color: mainView.item.theme.labelTextColor
+                }
+
+                MouseArea {
+                    anchors.fill: parent
+                    onClicked: printerListView.currentIndex = index
+                }
+            }
+        }
+
+        contentItem: Rectangle {
+            id: printerItem
+            height: printerListView.height
+            width: printerListView.width
+            color: mainView.item.theme.plotAreaBackgroundColor
+
+            ListView {
+                id: printerListView
+                height: 100
+                width: 200
+                clip: true
+
+                model: printerModel
+                delegate: printerDelegate
+                highlight: Rectangle {
+                    color: mainView.item.theme.grid.subColor
+                }
+            }
+        }
+
+        footer: DialogButtonBox {
+            Button {
+                text: "Print"
+                DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
+            }
+            Button {
+                text: "Cancel"
+                DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
+            }
+        }
+    }
+
+    ListModel {
+        id: printerModel
+    }
+
+    function prepareForPrint() {
+        if (stackLayout.currentIndex === 1) {
+            var newsize = Qt.size(bargraph.width * 4, bargraph.height * 4)
+
+            // check that we do not exceed maximum texture size
+            if (newsize.width * Screen.devicePixelRatio > graphPrinter.maxTextureSize ) {
+                // scale to 25% under max texture size to be on the safe side; some GPUs seem
+                // to glitch when using the abosulute max
+                var ratio = (newsize.width * Screen.devicePixelRatio * 1.25)
+                        / graphPrinter.maxTextureSize
+                newsize.width /= ratio
+                newsize.height /= ratio
+            }
+            outputsize.width = Math.round(newsize.width)
+            outputsize.height = Math.round(newsize.height)
+
+            // resize the bar graph to match the PDF output size
+            item.width = outputsize.width
+            item.height = outputsize.height
+        } else {
+            outputsize = Qt.size(linegraph.width * 4, linegraph.height * 4)
+        }
+    }
+
+    function cleanAfterPrint() {
+        if (stackLayout.currentIndex === 1) {
+            // resize the bar graph back to the actual visual size
+            item.width = stackLayout.width
+            item.height = stackLayout.height
+        }
+    }
+}
diff --git a/examples/graphs/common/graphprinting/GraphPrintingExample/box_left.svg b/examples/graphs/common/graphprinting/GraphPrintingExample/box_left.svg
new file mode 100644 (file)
index 0000000..8da6693
--- /dev/null
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M4 7.35407L4 16.6459L12 19.8459L20 16.6459V7.35407L12 4.15407L4 7.35407ZM2 16.6459V7.35407C2 6.53626 2.4979 5.80084 3.25722 5.49711L11.2572 2.29711C11.734 2.10639 12.266 2.10639 12.7428 2.29711L20.7428 5.49711C21.5021 5.80084 22 6.53626 22 7.35407V16.6459C22 17.4637 21.5021 18.1992 20.7428 18.5029L12.7428 21.7029C12.266 21.8936 11.734 21.8936 11.2572 21.7029L3.25722 18.5029C2.4979 18.1992 2 17.4637 2 16.6459Z" fill="#181818"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.2903 10.2903L10.8761 11.7045L4 8.95407V11.3864V16.6459L11 19.4459L11 11.8284L10.8761 11.7045L12.2903 10.2903ZM2 16.6459V11.3864V8.95407C2 7.53914 3.42905 6.57162 4.74278 7.09711L11.6188 9.84754C11.8703 9.94813 12.0988 10.0988 12.2903 10.2903L12.4142 10.4142C12.7893 10.7893 13 11.298 13 11.8284V19.5434C13 19.8437 12.9301 20.1398 12.7958 20.4083C12.346 21.308 11.2817 21.7127 10.3478 21.3391L3.25722 18.5029C2.4979 18.1992 2 17.4637 2 16.6459Z" fill="#181818"/>
+</svg>
diff --git a/examples/graphs/common/graphprinting/GraphPrintingExample/box_left_fill.svg b/examples/graphs/common/graphprinting/GraphPrintingExample/box_left_fill.svg
new file mode 100644 (file)
index 0000000..9b5b2f0
--- /dev/null
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M4 7.35407L4 16.6459L12 19.8459L20 16.6459V7.35407L12 4.15407L4 7.35407ZM2 16.6459V7.35407C2 6.53626 2.4979 5.80084 3.25722 5.49711L11.2572 2.29711C11.734 2.10639 12.266 2.10639 12.7428 2.29711L20.7428 5.49711C21.5021 5.80084 22 6.53626 22 7.35407V16.6459C22 17.4637 21.5021 18.1992 20.7428 18.5029L12.7428 21.7029C12.266 21.8936 11.734 21.8936 11.2572 21.7029L3.25722 18.5029C2.4979 18.1992 2 17.4637 2 16.6459Z" fill="#181818"/>
+<path d="M2 11.3864V16.6459C2 17.4637 2.4979 18.1992 3.25722 18.5029L10.3478 21.3391C11.2817 21.7127 12.346 21.308 12.7958 20.4083C12.9301 20.1398 13 19.8437 13 19.5434V11.8284C13 11.298 12.7893 10.7893 12.4142 10.4142L12.2903 10.2903C12.0988 10.0988 11.8703 9.94813 11.6188 9.84754L4.74278 7.09711C3.42905 6.57162 2 7.53914 2 8.95407V11.3864Z" fill="#181818"/>
+</svg>
diff --git a/examples/graphs/common/graphprinting/GraphPrintingExample/documents.svg b/examples/graphs/common/graphprinting/GraphPrintingExample/documents.svg
new file mode 100644 (file)
index 0000000..94a33ff
--- /dev/null
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6 4L6 20H18L18 8.40053L13.4737 4H6ZM4 4V20C4 21.1046 4.89543 22 6 22H18C19.1046 22 20 21.1046 20 20V8.40053C20 7.86033 19.7815 7.3431 19.3942 6.96654L14.8679 2.56601C14.4946 2.20306 13.9944 2 13.4737 2H6C4.89543 2 4 2.89543 4 4Z" fill="#181818"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M13 3.70711V7C13 8.10457 13.8954 9 15 9H18.2929C18.6834 9 19 8.68342 19 8.29289C19 8.10536 18.9255 7.9255 18.7929 7.79289L14.2071 3.20711C14.0745 3.0745 13.8946 3 13.7071 3C13.3166 3 13 3.31658 13 3.70711ZM8 11C7.44772 11 7 11.4477 7 12C7 12.5523 7.44772 13 8 13H16C16.5523 13 17 12.5523 17 12C17 11.4477 16.5523 11 16 11H8ZM8 14C7.44772 14 7 14.4477 7 15C7 15.5523 7.44772 16 8 16H16C16.5523 16 17 15.5523 17 15C17 14.4477 16.5523 14 16 14H8ZM7 18C7 17.4477 7.44772 17 8 17H13C13.5523 17 14 17.4477 14 18C14 18.5523 13.5523 19 13 19H8C7.44772 19 7 18.5523 7 18Z" fill="#181818"/>
+</svg>
diff --git a/examples/graphs/common/graphprinting/GraphPrintingExample/documents_fill.svg b/examples/graphs/common/graphprinting/GraphPrintingExample/documents_fill.svg
new file mode 100644 (file)
index 0000000..fc638c5
--- /dev/null
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M4 4V20C4 21.1046 4.89543 22 6 22H18C19.1046 22 20 21.1046 20 20V8.40053C20 7.86033 19.7815 7.3431 19.3942 6.96654L14.8679 2.56601C14.4946 2.20306 13.9944 2 13.4737 2H6C4.89543 2 4 2.89543 4 4ZM8 11C7.44772 11 7 11.4477 7 12C7 12.5523 7.44772 13 8 13H16C16.5523 13 17 12.5523 17 12C17 11.4477 16.5523 11 16 11H8ZM7 15C7 14.4477 7.44772 14 8 14H16C16.5523 14 17 14.4477 17 15C17 15.5523 16.5523 16 16 16H8C7.44772 16 7 15.5523 7 15ZM8 17C7.44772 17 7 17.4477 7 18C7 18.5523 7.44772 19 8 19H13C13.5523 19 14 18.5523 14 18C14 17.4477 13.5523 17 13 17H8Z" fill="#181818"/>
+</svg>
diff --git a/examples/graphs/common/graphprinting/GraphPrintingExample/flatten.svg b/examples/graphs/common/graphprinting/GraphPrintingExample/flatten.svg
new file mode 100644 (file)
index 0000000..5f16abb
--- /dev/null
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M20 4H4V20H20V4ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z" fill="#181818"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M21.9042 7.573C22.1401 8.0724 21.9264 8.66843 21.427 8.90426L3.427 17.4043C2.92759 17.6401 2.33157 17.4264 2.09574 16.927C1.85991 16.4276 2.07358 15.8316 2.57298 15.5958L20.573 7.09576C21.0724 6.85993 21.6684 7.0736 21.9042 7.573Z" fill="#181818"/>
+</svg>
diff --git a/examples/graphs/common/graphprinting/GraphPrintingExample/flatten_square_fill.svg b/examples/graphs/common/graphprinting/GraphPrintingExample/flatten_square_fill.svg
new file mode 100644 (file)
index 0000000..60bf0ab
--- /dev/null
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M4 3H20C20.5523 3 21 3.44772 21 4V20C21 20.5523 20.5523 21 20 21H4C3.44772 21 3 20.5523 3 20V4C3 3.44772 3.44772 3 4 3ZM2 4C2 2.89543 2.89543 2 4 2H20C21.1046 2 22 2.89543 22 4V20C22 21.1046 21.1046 22 20 22H4C2.89543 22 2 21.1046 2 20V4ZM20 4V7.39559L19.5756 7.59454L4 14.8956V4H20ZM4.42443 16.9055L4 17.1044V20H20V9.60441L4.42443 16.9055Z" fill="#181818"/>
+</svg>
diff --git a/examples/graphs/common/graphprinting/GraphPrintingExample/print.svg b/examples/graphs/common/graphprinting/GraphPrintingExample/print.svg
new file mode 100644 (file)
index 0000000..2213a48
--- /dev/null
@@ -0,0 +1,6 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="File / print">
+<path id="Layer01" fill-rule="evenodd" clip-rule="evenodd" d="M7 2C6.44772 2 6 2.44772 6 3V6H5C3.34315 6 2 7.34315 2 9V17C2 17.5523 2.44772 18 3 18H6V16H4V9C4 8.44772 4.44772 8 5 8H7H17H19C19.5523 8 20 8.44772 20 9V16H18V18H21C21.5523 18 22 17.5523 22 17V9C22 7.34315 20.6569 6 19 6H18V3C18 2.44772 17.5523 2 17 2H7ZM16 6V4H8V6H16ZM19 10C19 10.5523 18.5523 11 18 11C17.4477 11 17 10.5523 17 10C17 9.44772 17.4477 9 18 9C18.5523 9 19 9.44772 19 10Z" fill="#181818"/>
+<path id="Layer02" fill-rule="evenodd" clip-rule="evenodd" d="M6 13C6 12.4477 6.44772 12 7 12H17C17.5523 12 18 12.4477 18 13V21C18 21.5523 17.5523 22 17 22H7C6.44772 22 6 21.5523 6 21V13ZM8 14V20H16V14H8ZM8.5 18C8.5 17.7239 8.72386 17.5 9 17.5H15C15.2761 17.5 15.5 17.7239 15.5 18C15.5 18.2761 15.2761 18.5 15 18.5H9C8.72386 18.5 8.5 18.2761 8.5 18ZM9 15.5C8.72386 15.5 8.5 15.7239 8.5 16C8.5 16.2761 8.72386 16.5 9 16.5H15C15.2761 16.5 15.5 16.2761 15.5 16C15.5 15.7239 15.2761 15.5 15 15.5H9Z" fill="#181818"/>
+</g>
+</svg>
diff --git a/examples/graphs/common/graphprinting/GraphPrintingExample/print_fill.svg b/examples/graphs/common/graphprinting/GraphPrintingExample/print_fill.svg
new file mode 100644 (file)
index 0000000..3778bc0
--- /dev/null
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8 6V4H16V6H8ZM6 6V3C6 2.44772 6.44772 2 7 2H17C17.5523 2 18 2.44772 18 3V6H19C20.6569 6 22 7.34315 22 9V17C22 17.5523 21.5523 18 21 18H19V21C19 22.1046 18.1046 23 17 23H7C5.89543 23 5 22.1046 5 21V18H3C2.44772 18 2 17.5523 2 17V9C2 7.34315 3.34315 6 5 6H6ZM6 18V20C6 21.1046 6.89543 22 8 22H16C17.1046 22 18 21.1046 18 20V18V14C18 12.8954 17.1046 12 16 12H8C6.89543 12 6 12.8954 6 14V18ZM18 11C18.5523 11 19 10.5523 19 10C19 9.44772 18.5523 9 18 9C17.4477 9 17 9.44772 17 10C17 10.5523 17.4477 11 18 11ZM17 14C17 13.4477 16.5523 13 16 13H8C7.44772 13 7 13.4477 7 14V20C7 20.5523 7.44772 21 8 21H16C16.5523 21 17 20.5523 17 20V14ZM9 15.5C8.72386 15.5 8.5 15.7239 8.5 16C8.5 16.2761 8.72386 16.5 9 16.5H15C15.2761 16.5 15.5 16.2761 15.5 16C15.5 15.7239 15.2761 15.5 15 15.5H9ZM9 17.5C8.72386 17.5 8.5 17.7239 8.5 18C8.5 18.2761 8.72386 18.5 9 18.5H15C15.2761 18.5 15.5 18.2761 15.5 18C15.5 17.7239 15.2761 17.5 15 17.5H9Z" fill="#181818"/>
+</svg>
diff --git a/examples/graphs/common/graphprinting/GraphPrintingExample/qmldir b/examples/graphs/common/graphprinting/GraphPrintingExample/qmldir
new file mode 100644 (file)
index 0000000..0cd6a5b
--- /dev/null
@@ -0,0 +1,4 @@
+module GraphPrintingExample
+Main 1.0 Main.qml
+Graph2D 1.0 Graph2D.qml
+Graph3D 1.0 Graph3D.qml
diff --git a/examples/graphs/common/graphprinting/doc/graphprinting.rst b/examples/graphs/common/graphprinting/doc/graphprinting.rst
new file mode 100644 (file)
index 0000000..8179a12
--- /dev/null
@@ -0,0 +1,19 @@
+Graph Printing Example
+======================
+
+The Graph Printing example demonstrates how to print or export to PDF 2D and
+3D graphs.
+
+The printing functionality is implemented in the `GraphPrinter` class whose
+slots are invoked from QML, passing an image obtained from
+`QuickItem.grabToImage()`_ . The image is scaled and painted onto either
+a :class:`~PySide6.QtPrintSupport.QPrinter` or a
+:class:`~PySide6.QtGui.QPdfWriter`, which inherit
+:class:`~PySide6.QtGui.QPaintDevice`.
+
+
+.. image:: graphprinting.webp
+   :align: center
+   :alt: Graph Printing example
+
+.. _`QuickItem.grabToImage()`: https://doc.qt.io/qt-6/qquickitem.html#grabToImage
diff --git a/examples/graphs/common/graphprinting/doc/graphprinting.webp b/examples/graphs/common/graphprinting/doc/graphprinting.webp
new file mode 100644 (file)
index 0000000..b0f6131
Binary files /dev/null and b/examples/graphs/common/graphprinting/doc/graphprinting.webp differ
diff --git a/examples/graphs/common/graphprinting/graphprinter.py b/examples/graphs/common/graphprinting/graphprinter.py
new file mode 100644 (file)
index 0000000..a508c3a
--- /dev/null
@@ -0,0 +1,67 @@
+# Copyright (C) 2026 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, QMarginsF, QUrl, Qt, Property, Slot
+from PySide6.QtGui import (QDesktopServices, QImage, QPageSize, QPainter, QPaintDevice,
+                           QPdfWriter)
+from PySide6.QtQml import QmlElement
+from PySide6.QtPrintSupport import QPrinter, QPrinterInfo
+
+QML_IMPORT_NAME = "GraphPrintingExample"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class GraphPrinter(QObject):
+
+    def __init__(self, parent=None):
+        super().__init__(parent)
+
+    @Property(int, constant=True)
+    def maxTextureSize(self):
+        return 4096  # Use 4096 as the minimum
+
+    def paintImage(self, image: QImage, device: QPaintDevice):
+        painter = QPainter(device)
+        viewportSize = painter.viewport().size()
+        imageSize = image.size()
+        print(f"Scaling {imageSize.width()}x{imageSize.height()} to "
+              f"{viewportSize.width()}x{viewportSize.height()}.")
+        finalImage = image.scaled(viewportSize, Qt.AspectRatioMode.KeepAspectRatio)
+        painter.setRenderHint(QPainter.RenderHint.LosslessImageRendering)
+        painter.drawImage(finalImage.rect(), finalImage)
+        painter.end()
+
+    def _generatePDF(self, fileName: str, image: QImage):
+        writer = QPdfWriter(fileName)
+        writer.setResolution(90)
+        writer.setTitle("Graph")
+        writer.setPageSize(QPageSize(image.size()))
+        writer.setPageMargins(QMarginsF(0, 0, 0, 0))
+        writer.newPage()
+        self.paintImage(image, writer)
+
+    @Slot(QUrl, QImage, result=str)
+    def generatePDF(self, path: QUrl, image: QImage):
+        fileName = path.toLocalFile()
+        self._generatePDF(fileName, image)
+
+        QDesktopServices.openUrl(path)
+
+        return fileName
+
+    @Slot(QImage, str, result=str)
+    def print(self, image: QImage, printerName: str):
+        printInfo = QPrinterInfo.printerInfo(printerName)
+        if printInfo.isNull():
+            return f"{printerName} is not a valid printer"
+
+        printer = QPrinter(printInfo, QPrinter.PrinterMode.HighResolution)
+        printer.setOutputFormat(QPrinter.OutputFormat.NativeFormat)
+        self.paintImage(image, printer)
+
+        return f"Printed to {printerName}"
+
+    @Slot(result="QStringList")
+    def getPrinters(self):
+        return QPrinterInfo.availablePrinterNames()
diff --git a/examples/graphs/common/graphprinting/graphprinting.pyproject b/examples/graphs/common/graphprinting/graphprinting.pyproject
new file mode 100644 (file)
index 0000000..1d916e5
--- /dev/null
@@ -0,0 +1,18 @@
+{
+    "files": ["main.py", "graphprinter.py",
+              "GraphPrintingExample/qmldir",
+              "GraphPrintingExample/Main.qml",
+              "GraphPrintingExample/Graph2D.qml",
+              "GraphPrintingExample/Graph3D.qml",
+              "GraphPrintingExample/Main.qml",
+              "GraphPrintingExample/box_left.svg",
+              "GraphPrintingExample/box_left_fill.svg",
+              "GraphPrintingExample/documents.svg",
+              "GraphPrintingExample/documents_fill.svg",
+              "GraphPrintingExample/flatten.svg",
+              "GraphPrintingExample/flatten_square_fill.svg",
+              "GraphPrintingExample/folder.svg",
+              "GraphPrintingExample/folder_fill.svg",
+              "GraphPrintingExample/print.svg",
+              "GraphPrintingExample/print_fill.svg"]
+}
diff --git a/examples/graphs/common/graphprinting/main.py b/examples/graphs/common/graphprinting/main.py
new file mode 100644 (file)
index 0000000..51fea49
--- /dev/null
@@ -0,0 +1,38 @@
+# Copyright (C) 2026 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from pathlib import Path
+
+from PySide6.QtCore import QCoreApplication, QSize, Qt
+from PySide6.QtGui import QGuiApplication, QShortcut, QKeySequence
+from PySide6.QtQuick import QQuickView
+
+from graphprinter import GraphPrinter
+
+
+if __name__ == "__main__":
+    app = QGuiApplication(sys.argv)
+
+    viewer = QQuickView()
+    viewer.setTitle("Graph Printing")
+    graphPrinter = GraphPrinter()
+    viewer.rootContext().setContextProperty("graphPrinter", graphPrinter)
+    viewer.setMinimumSize(QSize(1280, 720))
+    viewer.engine().addImportPath(Path(__file__).parent)
+    viewer.loadFromModule("GraphPrintingExample", "Main")
+    window = viewer.rootObject()
+    if not window:
+        sys.exit(-1)
+    quitKey = QKeySequence(QKeySequence.StandardKey.Quit)
+    if not quitKey.isEmpty():
+        quitShortcut = QShortcut(quitKey, window)
+        quitShortcut.activated.connect(app.quit)
+        quitShortcut.setContext(Qt.ShortcutContext.ApplicationShortcut)
+    viewer.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
+    viewer.setColor(Qt.GlobalColor.white)
+    viewer.show()
+
+    ex = QCoreApplication.exec()
+    del viewer
+    sys.exit(ex)
index cadfe2d09378b9ea35a6446d2f5b11930d0c6e86..9f1088cd74567f1df465fe11014eec70731c387c 100644 (file)
@@ -212,8 +212,7 @@ class AudioTest(QMainWindow):
     def pull_timer_expired(self):
         if self.m_audioSink is not None and self.m_audioSink.state() != QtAudio.State.StoppedState:
             bytes_free = self.m_audioSink.bytesFree()
-            data = self.m_generator.read(bytes_free)
-            if data:
+            if data := self.m_generator.read(bytes_free):
                 self.m_output.write(data)
 
     @Slot()
index 12971b4d9178abb34c11312b966d4761a15b6c48..ad596c766dde60ff15957c2841ca7494722dea23 100644 (file)
@@ -2,16 +2,14 @@
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 from __future__ import annotations
 
-import os
 import sys
-from pathlib import Path
 
 from PySide6.QtMultimedia import (QAudioInput, QCamera, QCameraDevice,
                                   QImageCapture, QMediaCaptureSession,
                                   QMediaDevices, QMediaMetaData,
                                   QMediaRecorder)
 from PySide6.QtWidgets import QDialog, QMainWindow, QMessageBox
-from PySide6.QtGui import QAction, QActionGroup, QIcon, QImage, QPixmap
+from PySide6.QtGui import QAction, QActionGroup, QImage, QPixmap
 from PySide6.QtCore import QDateTime, QDir, QTimer, Qt, Slot, qWarning
 
 from metadatadialog import MetaDataDialog
@@ -46,8 +44,6 @@ class Camera(QMainWindow):
 
         self._ui = Ui_Camera()
         self._ui.setupUi(self)
-        image = Path(__file__).parent / "shutter.svg"
-        self._ui.takeImageButton.setIcon(QIcon(os.fspath(image)))
         if not is_android:
             self._ui.actionAbout_Qt.triggered.connect(qApp.aboutQt)  # noqa: F821
 
@@ -348,8 +344,7 @@ class Camera(QMainWindow):
     def saveMetaData(self):
         data = QMediaMetaData()
         for i in range(0, QMediaMetaData.NumMetaData):
-            val = self.m_metaDataDialog.m_metaDataFields[i].text()
-            if val:
+            if val := self.m_metaDataDialog.m_metaDataFields[i].text():
                 key = QMediaMetaData.Key(i)
                 if key == QMediaMetaData.Key.CoverArtImage:
                     cover_art = QImage(val)
index 9067b1dfadb75984299d67d12bfecb9e2a395526..f3cbe640e84c526e6cbd51baaec8bcd49bed5880 100644 (file)
@@ -8,5 +8,6 @@
               "metadatadialog.py",
               "videosettings.py",
               "videosettings.ui",
-              "videosettings_mobile.ui"]
+              "videosettings_mobile.ui",
+              "camera.qrc"]
 }
diff --git a/examples/multimedia/camera/camera.qrc b/examples/multimedia/camera/camera.qrc
new file mode 100644 (file)
index 0000000..a915eb5
--- /dev/null
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+    <file>images/shutter.svg</file>
+</qresource>
+</RCC>
index 1852c3e9fe228fdbe0a893321187f5ddeebbcbe8..0ebd1b52b6cfcd4819af9e04725b7d3c1e9d4ec2 100644 (file)
           <property name="text">
            <string>Capture Photo</string>
           </property>
+          <property name="icon">
+           <iconset resource="camera.qrc">
+            <normaloff>:/images/shutter.svg</normaloff>:/images/shutter.svg</iconset>
+          </property>
+          <property name="icon">
+           <iconset resource="camera.qrc">
+            <normaloff>:/images/shutter.svg</normaloff>:/images/shutter.svg</iconset>
+          </property>
          </widget>
         </item>
        </layout>
    <container>1</container>
   </customwidget>
  </customwidgets>
- <resources/>
+ <resources>
+  <include location="camera.qrc"/>
+ </resources>
  <connections>
   <connection>
    <sender>recordButton</sender>
index f36722ff25cbcd580b002afecc5c7d8cfa48e6b7..514d8c96e6d6e2b9b5a8e0f44aee90b1e9601f75 100644 (file)
    <container>1</container>
   </customwidget>
  </customwidgets>
- <resources/>
+ <resources>
+  <include location="camera.qrc"/>
+ </resources>
  <connections>
   <connection>
    <sender>recordButton</sender>
diff --git a/examples/multimedia/camera/images/shutter.svg b/examples/multimedia/camera/images/shutter.svg
new file mode 100644 (file)
index 0000000..1849336
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 23.3 19.4" style="enable-background:new 0 0 23.3 19.4;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:none;}
+</style>
+<g>
+       <path class="st0" d="M6.2,4.8H2.4c-0.2,0-0.1-0.1-0.1,0.1V17c0,0.2-0.1,0.8,0.1,0.8h3.9V4.8z"/>
+       <circle class="st0" cx="14" cy="11" r="4.5"/>
+       <path class="st0" d="M20.9,4.8h-1.8c-0.3,0-0.6-0.4-0.8-0.6l-1.7-2.4h-5.3L9.7,4.2C9.5,4.4,9.2,4.8,8.9,4.8H7.2v13h13.7
+               c0.2,0,0.3-0.6,0.3-0.8V4.9C21.2,4.7,21.1,4.8,20.9,4.8z M14,16.4c-3,0-5.5-2.4-5.5-5.5c0-3,2.4-5.5,5.5-5.5c3,0,5.5,2.4,5.5,5.5
+               C19.5,14,17,16.4,14,16.4z"/>
+       <path d="M14,5.5C11,5.5,8.6,8,8.6,11c0,3,2.4,5.5,5.5,5.5c3,0,5.5-2.4,5.5-5.5C19.5,8,17,5.5,14,5.5z M14,15.4
+               c-2.5,0-4.5-2-4.5-4.5c0-2.5,2-4.5,4.5-4.5c2.5,0,4.5,2,4.5,4.5C18.5,13.4,16.5,15.4,14,15.4z"/>
+       <path d="M20.9,2.8h-1.3l-1.7-2.4c-0.2-0.2-0.5-0.6-0.8-0.6h-6.3c-0.3,0-0.6,0.4-0.8,0.6L8.4,2.8h-6c-1.3,0-2.1,0.8-2.1,2.1V17
+               c0,1.3,0.8,2.8,2.1,2.8h18.5c1.3,0,2.3-1.5,2.3-2.8V4.9C23.2,3.6,22.2,2.8,20.9,2.8z M2.2,17V4.9c0-0.2-0.1-0.1,0.1-0.1h3.9v13H2.4
+               C2.2,17.8,2.2,17.2,2.2,17z M21.2,17c0,0.2-0.1,0.8-0.3,0.8H7.2v-13h1.7c0.3,0,0.6-0.4,0.8-0.6l1.7-2.4h5.3l1.7,2.4
+               c0.2,0.2,0.5,0.6,0.8,0.6h1.8c0.2,0,0.3-0.1,0.3,0.1V17z"/>
+</g>
+</svg>
diff --git a/examples/multimedia/camera/rc_camera.py b/examples/multimedia/camera/rc_camera.py
new file mode 100644 (file)
index 0000000..02eb354
--- /dev/null
@@ -0,0 +1,131 @@
+# Resource object code (Python 3)
+# Created by: object code
+# Created by: The Resource Compiler for Qt version 6.10.2
+# WARNING! All changes made in this file will be lost!
+
+from PySide6 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x05\xbe\
+<\
+?xml version=\x221.\
+0\x22 encoding=\x22utf\
+-8\x22?>\x0a<!-- Gener\
+ator: Adobe Illu\
+strator 20.1.0, \
+SVG Export Plug-\
+In . SVG Version\
+: 6.00 Build 0) \
+ -->\x0a<svg versio\
+n=\x221.1\x22 id=\x22Laye\
+r_1\x22 xmlns=\x22http\
+://www.w3.org/20\
+00/svg\x22 xmlns:xl\
+ink=\x22http://www.\
+w3.org/1999/xlin\
+k\x22 x=\x220px\x22 y=\x220p\
+x\x22\x0a\x09 viewBox=\x220 \
+0 23.3 19.4\x22 sty\
+le=\x22enable-backg\
+round:new 0 0 23\
+.3 19.4;\x22 xml:sp\
+ace=\x22preserve\x22>\x0a\
+<style type=\x22tex\
+t/css\x22>\x0a\x09.st0{fi\
+ll:none;}\x0a</styl\
+e>\x0a<g>\x0a\x09<path cl\
+ass=\x22st0\x22 d=\x22M6.\
+2,4.8H2.4c-0.2,0\
+-0.1-0.1-0.1,0.1\
+V17c0,0.2-0.1,0.\
+8,0.1,0.8h3.9V4.\
+8z\x22/>\x0a\x09<circle c\
+lass=\x22st0\x22 cx=\x221\
+4\x22 cy=\x2211\x22 r=\x224.\
+5\x22/>\x0a\x09<path clas\
+s=\x22st0\x22 d=\x22M20.9\
+,4.8h-1.8c-0.3,0\
+-0.6-0.4-0.8-0.6\
+l-1.7-2.4h-5.3L9\
+.7,4.2C9.5,4.4,9\
+.2,4.8,8.9,4.8H7\
+.2v13h13.7\x0a\x09\x09c0.\
+2,0,0.3-0.6,0.3-\
+0.8V4.9C21.2,4.7\
+,21.1,4.8,20.9,4\
+.8z M14,16.4c-3,\
+0-5.5-2.4-5.5-5.\
+5c0-3,2.4-5.5,5.\
+5-5.5c3,0,5.5,2.\
+4,5.5,5.5\x0a\x09\x09C19.\
+5,14,17,16.4,14,\
+16.4z\x22/>\x0a\x09<path \
+d=\x22M14,5.5C11,5.\
+5,8.6,8,8.6,11c0\
+,3,2.4,5.5,5.5,5\
+.5c3,0,5.5-2.4,5\
+.5-5.5C19.5,8,17\
+,5.5,14,5.5z M14\
+,15.4\x0a\x09\x09c-2.5,0-\
+4.5-2-4.5-4.5c0-\
+2.5,2-4.5,4.5-4.\
+5c2.5,0,4.5,2,4.\
+5,4.5C18.5,13.4,\
+16.5,15.4,14,15.\
+4z\x22/>\x0a\x09<path d=\x22\
+M20.9,2.8h-1.3l-\
+1.7-2.4c-0.2-0.2\
+-0.5-0.6-0.8-0.6\
+h-6.3c-0.3,0-0.6\
+,0.4-0.8,0.6L8.4\
+,2.8h-6c-1.3,0-2\
+.1,0.8-2.1,2.1V1\
+7\x0a\x09\x09c0,1.3,0.8,2\
+.8,2.1,2.8h18.5c\
+1.3,0,2.3-1.5,2.\
+3-2.8V4.9C23.2,3\
+.6,22.2,2.8,20.9\
+,2.8z M2.2,17V4.\
+9c0-0.2-0.1-0.1,\
+0.1-0.1h3.9v13H2\
+.4\x0a\x09\x09C2.2,17.8,2\
+.2,17.2,2.2,17z \
+M21.2,17c0,0.2-0\
+.1,0.8-0.3,0.8H7\
+.2v-13h1.7c0.3,0\
+,0.6-0.4,0.8-0.6\
+l1.7-2.4h5.3l1.7\
+,2.4\x0a\x09\x09c0.2,0.2,\
+0.5,0.6,0.8,0.6h\
+1.8c0.2,0,0.3-0.\
+1,0.3,0.1V17z\x22/>\
+\x0a</g>\x0a</svg>\x0a\
+"
+
+qt_resource_name = b"\
+\x00\x06\
+\x07\x03}\xc3\
+\x00i\
+\x00m\x00a\x00g\x00e\x00s\
+\x00\x0b\
+\x0a6\xa3\xa7\
+\x00s\
+\x00h\x00u\x00t\x00t\x00e\x00r\x00.\x00s\x00v\x00g\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x01\x9b\xc6\x15\x833\
+"
+
+def qInitResources():
+    QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+    QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
diff --git a/examples/multimedia/camera/shutter.svg b/examples/multimedia/camera/shutter.svg
deleted file mode 100644 (file)
index 1849336..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-        viewBox="0 0 23.3 19.4" style="enable-background:new 0 0 23.3 19.4;" xml:space="preserve">
-<style type="text/css">
-       .st0{fill:none;}
-</style>
-<g>
-       <path class="st0" d="M6.2,4.8H2.4c-0.2,0-0.1-0.1-0.1,0.1V17c0,0.2-0.1,0.8,0.1,0.8h3.9V4.8z"/>
-       <circle class="st0" cx="14" cy="11" r="4.5"/>
-       <path class="st0" d="M20.9,4.8h-1.8c-0.3,0-0.6-0.4-0.8-0.6l-1.7-2.4h-5.3L9.7,4.2C9.5,4.4,9.2,4.8,8.9,4.8H7.2v13h13.7
-               c0.2,0,0.3-0.6,0.3-0.8V4.9C21.2,4.7,21.1,4.8,20.9,4.8z M14,16.4c-3,0-5.5-2.4-5.5-5.5c0-3,2.4-5.5,5.5-5.5c3,0,5.5,2.4,5.5,5.5
-               C19.5,14,17,16.4,14,16.4z"/>
-       <path d="M14,5.5C11,5.5,8.6,8,8.6,11c0,3,2.4,5.5,5.5,5.5c3,0,5.5-2.4,5.5-5.5C19.5,8,17,5.5,14,5.5z M14,15.4
-               c-2.5,0-4.5-2-4.5-4.5c0-2.5,2-4.5,4.5-4.5c2.5,0,4.5,2,4.5,4.5C18.5,13.4,16.5,15.4,14,15.4z"/>
-       <path d="M20.9,2.8h-1.3l-1.7-2.4c-0.2-0.2-0.5-0.6-0.8-0.6h-6.3c-0.3,0-0.6,0.4-0.8,0.6L8.4,2.8h-6c-1.3,0-2.1,0.8-2.1,2.1V17
-               c0,1.3,0.8,2.8,2.1,2.8h18.5c1.3,0,2.3-1.5,2.3-2.8V4.9C23.2,3.6,22.2,2.8,20.9,2.8z M2.2,17V4.9c0-0.2-0.1-0.1,0.1-0.1h3.9v13H2.4
-               C2.2,17.8,2.2,17.2,2.2,17z M21.2,17c0,0.2-0.1,0.8-0.3,0.8H7.2v-13h1.7c0.3,0,0.6-0.4,0.8-0.6l1.7-2.4h5.3l1.7,2.4
-               c0.2,0.2,0.5,0.6,0.8,0.6h1.8c0.2,0,0.3-0.1,0.3,0.1V17z"/>
-</g>
-</svg>
index ec7dfcec5b42d0471771032eaf3e58458e86713c..985f1a6bf12742f4029b3c54f886a7ffa9699219 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'camera.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.8.0
+## Created by: Qt User Interface Compiler version 6.10.2
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -21,6 +21,7 @@ from PySide6.QtWidgets import (QApplication, QFrame, QGridLayout, QLabel,
     QMainWindow, QMenu, QMenuBar, QPushButton,
     QSizePolicy, QSpacerItem, QStackedWidget, QStatusBar,
     QTabWidget, QWidget)
+import rc_camera
 
 class Ui_Camera(object):
     def setupUi(self, Camera):
@@ -54,6 +55,10 @@ class Ui_Camera(object):
         self.takeImageButton = QPushButton(self.tab_2)
         self.takeImageButton.setObjectName(u"takeImageButton")
         self.takeImageButton.setEnabled(False)
+        icon = QIcon()
+        icon.addFile(u":/images/shutter.svg", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
+        self.takeImageButton.setIcon(icon)
+        self.takeImageButton.setIcon(icon)
 
         self.gridLayout.addWidget(self.takeImageButton, 0, 0, 1, 1)
 
index eff3310afa16c1b62227f6bd65e486798e0389c0..4d931fc7e186eff1ed0b6fe3bdf870e365bb5702 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'camera_mobile.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.8.0
+## Created by: Qt User Interface Compiler version 6.10.2
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -22,6 +22,7 @@ from PySide6.QtWidgets import (QApplication, QFrame, QGridLayout, QHBoxLayout,
     QPushButton, QSizePolicy, QSlider, QSpacerItem,
     QStackedWidget, QStatusBar, QTabWidget, QVBoxLayout,
     QWidget)
+import rc_camera
 
 class Ui_Camera(object):
     def setupUi(self, Camera):
index ed9b01f71e22e4e46a6c920dfe45dd2bfe128df5..fa52906c1a653e01bb7b5457074ecc1aa5fb3345 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'videosettings_mobile.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.8.0
+## Created by: Qt User Interface Compiler version 6.10.2
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
index 498803f60e3f31b3e818a3157fa0de828edbf471..ac36aaf3f65257752d008f1fb8b9cf37392d1706 100644 (file)
@@ -90,8 +90,7 @@ class ScreenCapturePreview(QWidget):
     @Slot(QItemSelection)
     def on_current_screen_selection_changed(self, selection):
         self.clear_error_string()
-        indexes = selection.indexes()
-        if indexes:
+        if indexes := selection.indexes():
             self._screen_capture.setScreen(self._screen_list_model.screen(indexes[0]))
             self.update_active(SourceType.Screen, self.is_active())
             self._window_list_view.clearSelection()
@@ -101,8 +100,7 @@ class ScreenCapturePreview(QWidget):
     @Slot(QItemSelection)
     def on_current_window_selection_changed(self, selection):
         self.clear_error_string()
-        indexes = selection.indexes()
-        if indexes:
+        if indexes := selection.indexes():
             window = self._window_list_model.window(indexes[0])
             if not window.isValid():
                 m = "The window is no longer valid. Update the list of windows?"
index bb19647e6a6a7fb33065aa7ff827b3edfe85952d..f2699043a1f8a6b6a152f4d49a56e4f3159e210d 100644 (file)
@@ -103,8 +103,7 @@ class GSuggestCompletion(QObject):
         self.timer.stop()
         self.popup.hide()
         self.editor.setFocus()
-        item = self.popup.currentItem()
-        if item:
+        if item := self.popup.currentItem():
             self.editor.setText(item.text(0))
             self.editor.returnPressed.emit()
 
diff --git a/examples/pdf/quickpdfviewer/SinglePageModule/Viewer.qml b/examples/pdf/quickpdfviewer/SinglePageModule/Viewer.qml
new file mode 100644 (file)
index 0000000..5342a28
--- /dev/null
@@ -0,0 +1,307 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+import QtQuick.Layouts
+import QtQuick.Pdf
+
+ApplicationWindow {
+    id: root
+    width: 800
+    height: 1024
+    color: "lightgrey"
+    title: document.title
+    visible: true
+    required property url source // for main.py
+    property real scaleStep: Math.sqrt(2)
+
+    header: ToolBar {
+        RowLayout {
+            anchors.fill: parent
+            anchors.rightMargin: 6
+            ToolButton {
+                action: Action {
+                    shortcut: StandardKey.Open
+                    icon.source: "qrc:/pdfviewer/resources/document-open.svg"
+                    onTriggered: fileDialog.open()
+                }
+            }
+            ToolButton {
+                action: Action {
+                    shortcut: StandardKey.ZoomIn
+                    enabled: view.sourceSize.width < 10000
+                    icon.source: "qrc:/pdfviewer/resources/zoom-in.svg"
+                    onTriggered: view.renderScale *= root.scaleStep
+                }
+            }
+            ToolButton {
+                action: Action {
+                    shortcut: StandardKey.ZoomOut
+                    enabled: view.sourceSize.width > 50
+                    icon.source: "qrc:/pdfviewer/resources/zoom-out.svg"
+                    onTriggered: view.renderScale /= root.scaleStep
+                }
+            }
+            ToolButton {
+                action: Action {
+                    icon.source: "qrc:/pdfviewer/resources/zoom-fit-width.svg"
+                    onTriggered: view.scaleToWidth(root.contentItem.width, root.contentItem.height)
+                }
+            }
+            ToolButton {
+                action: Action {
+                    icon.source: "qrc:/pdfviewer/resources/zoom-fit-best.svg"
+                    onTriggered: view.scaleToPage(root.contentItem.width, root.contentItem.height)
+                }
+            }
+            ToolButton {
+                action: Action {
+                    shortcut: "Ctrl+0"
+                    icon.source: "qrc:/pdfviewer/resources/zoom-original.svg"
+                    onTriggered: view.resetScale()
+                }
+            }
+            ToolButton {
+                action: Action {
+                    shortcut: "Ctrl+L"
+                    icon.source: "qrc:/pdfviewer/resources/rotate-left.svg"
+                    onTriggered: view.pageRotation -= 90
+                }
+            }
+            ToolButton {
+                action: Action {
+                    shortcut: "Ctrl+R"
+                    icon.source: "qrc:/pdfviewer/resources/rotate-right.svg"
+                    onTriggered: view.pageRotation += 90
+                }
+            }
+            ToolButton {
+                action: Action {
+                    icon.source: "qrc:/pdfviewer/resources/go-previous-view-page.svg"
+                    enabled: view.backEnabled
+                    onTriggered: view.back()
+                }
+                ToolTip.visible: enabled && hovered
+                ToolTip.delay: 2000
+                ToolTip.text: "go back"
+            }
+            SpinBox {
+                id: currentPageSB
+                from: 1
+                to: document.pageCount
+                editable: true
+                value: view.currentPage + 1
+                onValueModified: view.goToPage(value - 1)
+                Shortcut {
+                    sequence: StandardKey.MoveToPreviousPage
+                    onActivated: view.goToPage(currentPageSB.value - 2)
+                }
+                Shortcut {
+                    sequence: StandardKey.MoveToNextPage
+                    onActivated: view.goToPage(currentPageSB.value)
+                }
+            }
+            ToolButton {
+                action: Action {
+                    icon.source: "qrc:/pdfviewer/resources/go-next-view-page.svg"
+                    enabled: view.forwardEnabled
+                    onTriggered: view.forward()
+                }
+                ToolTip.visible: enabled && hovered
+                ToolTip.delay: 2000
+                ToolTip.text: "go forward"
+            }
+            ToolButton {
+                action: Action {
+                    shortcut: StandardKey.SelectAll
+                    icon.source: "qrc:/pdfviewer/resources/edit-select-all.svg"
+                    onTriggered: view.selectAll()
+                }
+            }
+            ToolButton {
+                action: Action {
+                    shortcut: StandardKey.Copy
+                    icon.source: "qrc:/pdfviewer/resources/edit-copy.svg"
+                    enabled: view.selectedText !== ""
+                    onTriggered: view.copySelectionToClipboard()
+                }
+            }
+            Shortcut {
+                sequences: [ StandardKey.Find ]
+                onActivated: searchField.forceActiveFocus()
+            }
+            Shortcut {
+                sequences: [ StandardKey.Quit ]
+                onActivated: Qt.quit()
+            }
+        }
+    }
+
+    FileDialog {
+        id: fileDialog
+        title: "Open a PDF file"
+        nameFilters: [ "PDF files (*.pdf)" ]
+        onAccepted: document.source = selectedFile
+    }
+
+    Dialog {
+        id: passwordDialog
+        title: "Password"
+        standardButtons: Dialog.Ok | Dialog.Cancel
+        modal: true
+        closePolicy: Popup.CloseOnEscape
+        anchors.centerIn: parent
+        width: 300
+
+        contentItem: TextField {
+            id: passwordField
+            placeholderText: qsTr("Please provide the password")
+            echoMode: TextInput.Password
+            width: parent.width
+            onAccepted: passwordDialog.accept()
+        }
+        onOpened: function() { passwordField.forceActiveFocus() }
+        onAccepted: document.password = passwordField.text
+    }
+
+    Dialog {
+        id: errorDialog
+        title: "Error loading " + document.source
+        standardButtons: Dialog.Close
+        modal: true
+        closePolicy: Popup.CloseOnEscape
+        anchors.centerIn: parent
+        width: 300
+        visible: document.status === PdfDocument.Error
+
+        contentItem: Label {
+            id: errorField
+            text: document.error
+        }
+    }
+
+    PdfScrollablePageView {
+        id: view
+        anchors.fill: parent
+        anchors.leftMargin: searchDrawer.position * searchDrawer.width
+        document: PdfDocument {
+            id: document
+            source: Qt.resolvedUrl(root.source)
+            onPasswordRequired: passwordDialog.open()
+        }
+        searchString: searchField.text
+    }
+
+    Drawer {
+        id: searchDrawer
+        edge: Qt.LeftEdge
+//        modal: false
+//        dim: false // commented out as workaround for QTBUG-83859
+        width: 300
+        y: root.header.height
+        height: view.height
+        clip: true
+        ListView {
+            id: searchResultsList
+            anchors.fill: parent
+            anchors.margins: 2
+            model: view.searchModel
+            currentIndex: view.searchModel.currentResult
+            ScrollBar.vertical: ScrollBar { }
+            delegate: ItemDelegate {
+                id: resultDelegate
+                required property int index
+                required property int page
+                required property string contextBefore
+                required property string contextAfter
+                width: parent ? parent.width : 0
+                RowLayout {
+                    anchors.fill: parent
+                    spacing: 0
+                    Label {
+                        text: "Page " + (resultDelegate.page + 1) + ": "
+                    }
+                    Label {
+                        text: resultDelegate.contextBefore
+                        elide: Text.ElideLeft
+                        horizontalAlignment: Text.AlignRight
+                        Layout.fillWidth: true
+                        Layout.preferredWidth: parent.width / 2
+                    }
+                    Label {
+                        font.bold: true
+                        text: view.searchString
+                        width: implicitWidth
+                    }
+                    Label {
+                        text: resultDelegate.contextAfter
+                        elide: Text.ElideRight
+                        Layout.fillWidth: true
+                        Layout.preferredWidth: parent.width / 2
+                    }
+                }
+                highlighted: ListView.isCurrentItem
+                onClicked: view.searchModel.currentResult = resultDelegate.index
+            }
+        }
+    }
+
+    footer: ToolBar {
+        height: footerRow.implicitHeight
+        RowLayout {
+            id: footerRow
+            anchors.fill: parent
+            ToolButton {
+                action: Action {
+                    icon.source: "qrc:/pdfviewer/resources/go-up-search.svg"
+                    shortcut: StandardKey.FindPrevious
+                    onTriggered: view.searchBack()
+                }
+                ToolTip.visible: enabled && hovered
+                ToolTip.delay: 2000
+                ToolTip.text: "find previous"
+            }
+            TextField {
+                id: searchField
+                placeholderText: "search"
+                Layout.minimumWidth: 150
+                Layout.maximumWidth: 300
+                Layout.fillWidth: true
+                onAccepted: searchDrawer.open()
+                Image {
+                    visible: searchField.text !== ""
+                    source: "qrc:/pdfviewer/resources/edit-clear.svg"
+                    anchors {
+                        right: parent.right
+                        top: parent.top
+                        bottom: parent.bottom
+                        margins: 3
+                        rightMargin: 5
+                    }
+                    TapHandler {
+                        onTapped: searchField.clear()
+                    }
+                }
+            }
+            ToolButton {
+                action: Action {
+                    icon.source: "qrc:/pdfviewer/resources/go-down-search.svg"
+                    shortcut: StandardKey.FindNext
+                    onTriggered: view.searchForward()
+                }
+                ToolTip.visible: enabled && hovered
+                ToolTip.delay: 2000
+                ToolTip.text: "find next"
+            }
+            Label {
+                Layout.fillWidth: true
+                property size implicitPointSize: document.pagePointSize(view.currentPage)
+                text: "page " + (view.currentPage + 1) + " of " + document.pageCount +
+                      " scale " + view.renderScale.toFixed(2) +
+                      " original " + implicitPointSize.width.toFixed(1) + "x" + implicitPointSize.height.toFixed(1) + "pts"
+                visible: document.status === PdfDocument.Ready
+            }
+        }
+    }
+}
diff --git a/examples/pdf/quickpdfviewer/SinglePageModule/qmldir b/examples/pdf/quickpdfviewer/SinglePageModule/qmldir
new file mode 100644 (file)
index 0000000..1f9b6e1
--- /dev/null
@@ -0,0 +1,2 @@
+module SinglePageModule
+Viewer 254.0 Viewer.qml
index 388c590c31fc7a2ac3c4e132dc5022f362f5e841..68cb2128c11fb05f9cbd95f871f76f66032faa7c 100644 (file)
@@ -40,8 +40,8 @@ if __name__ == "__main__":
 
     engine = QQmlApplicationEngine()
     engine.setInitialProperties({"source": url})
-
-    engine.load(QUrl.fromLocalFile(os.fspath(dir / "viewer.qml")))
+    engine.addImportPath(Path(__file__).parent)
+    engine.loadFromModule("SinglePageModule", "Viewer")
     if not engine.rootObjects():
         sys.exit(-1)
 
index 437e25551bcd86fa2092abbed767e1f98bb7a62d..e0198747357889cc113c528cf0f255287e67affc 100644 (file)
@@ -1,3 +1,4 @@
 {
-    "files": ["main.py", "viewer.qml", "viewer.qrc", "resources/test.pdf"]
+    "files": ["main.py", "SinglePageModule/Viewer.qml", "SinglePageModule/qmldir",
+              "viewer.qrc", "resources/test.pdf"]
 }
diff --git a/examples/pdf/quickpdfviewer/viewer.qml b/examples/pdf/quickpdfviewer/viewer.qml
deleted file mode 100644 (file)
index 998c41d..0000000
+++ /dev/null
@@ -1,307 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Dialogs
-import QtQuick.Layouts
-import QtQuick.Pdf
-
-ApplicationWindow {
-    id: root
-    width: 800
-    height: 1024
-    color: "lightgrey"
-    title: document.title
-    visible: true
-    required property url source // for main.py
-    property real scaleStep: Math.sqrt(2)
-
-    header: ToolBar {
-        RowLayout {
-            anchors.fill: parent
-            anchors.rightMargin: 6
-            ToolButton {
-                action: Action {
-                    shortcut: StandardKey.Open
-                    icon.source: "qrc:/pdfviewer/resources/document-open.svg"
-                    onTriggered: fileDialog.open()
-                }
-            }
-            ToolButton {
-                action: Action {
-                    shortcut: StandardKey.ZoomIn
-                    enabled: view.sourceSize.width < 10000
-                    icon.source: "qrc:/pdfviewer/resources/zoom-in.svg"
-                    onTriggered: view.renderScale *= root.scaleStep
-                }
-            }
-            ToolButton {
-                action: Action {
-                    shortcut: StandardKey.ZoomOut
-                    enabled: view.sourceSize.width > 50
-                    icon.source: "qrc:/pdfviewer/resources/zoom-out.svg"
-                    onTriggered: view.renderScale /= root.scaleStep
-                }
-            }
-            ToolButton {
-                action: Action {
-                    icon.source: "qrc:/pdfviewer/resources/zoom-fit-width.svg"
-                    onTriggered: view.scaleToWidth(root.contentItem.width, root.contentItem.height)
-                }
-            }
-            ToolButton {
-                action: Action {
-                    icon.source: "qrc:/pdfviewer/resources/zoom-fit-best.svg"
-                    onTriggered: view.scaleToPage(root.contentItem.width, root.contentItem.height)
-                }
-            }
-            ToolButton {
-                action: Action {
-                    shortcut: "Ctrl+0"
-                    icon.source: "qrc:/pdfviewer/resources/zoom-original.svg"
-                    onTriggered: view.resetScale()
-                }
-            }
-            ToolButton {
-                action: Action {
-                    shortcut: "Ctrl+L"
-                    icon.source: "qrc:/pdfviewer/resources/rotate-left.svg"
-                    onTriggered: view.pageRotation -= 90
-                }
-            }
-            ToolButton {
-                action: Action {
-                    shortcut: "Ctrl+R"
-                    icon.source: "qrc:/pdfviewer/resources/rotate-right.svg"
-                    onTriggered: view.pageRotation += 90
-                }
-            }
-            ToolButton {
-                action: Action {
-                    icon.source: "qrc:/pdfviewer/resources/go-previous-view-page.svg"
-                    enabled: view.backEnabled
-                    onTriggered: view.back()
-                }
-                ToolTip.visible: enabled && hovered
-                ToolTip.delay: 2000
-                ToolTip.text: "go back"
-            }
-            SpinBox {
-                id: currentPageSB
-                from: 1
-                to: document.pageCount
-                editable: true
-                value: view.currentPage + 1
-                onValueModified: view.goToPage(value - 1)
-                Shortcut {
-                    sequence: StandardKey.MoveToPreviousPage
-                    onActivated: view.goToPage(currentPageSB.value - 2)
-                }
-                Shortcut {
-                    sequence: StandardKey.MoveToNextPage
-                    onActivated: view.goToPage(currentPageSB.value)
-                }
-            }
-            ToolButton {
-                action: Action {
-                    icon.source: "qrc:/pdfviewer/resources/go-next-view-page.svg"
-                    enabled: view.forwardEnabled
-                    onTriggered: view.forward()
-                }
-                ToolTip.visible: enabled && hovered
-                ToolTip.delay: 2000
-                ToolTip.text: "go forward"
-            }
-            ToolButton {
-                action: Action {
-                    shortcut: StandardKey.SelectAll
-                    icon.source: "qrc:/pdfviewer/resources/edit-select-all.svg"
-                    onTriggered: view.selectAll()
-                }
-            }
-            ToolButton {
-                action: Action {
-                    shortcut: StandardKey.Copy
-                    icon.source: "qrc:/pdfviewer/resources/edit-copy.svg"
-                    enabled: view.selectedText !== ""
-                    onTriggered: view.copySelectionToClipboard()
-                }
-            }
-            Shortcut {
-                sequence: StandardKey.Find
-                onActivated: searchField.forceActiveFocus()
-            }
-            Shortcut {
-                sequence: StandardKey.Quit
-                onActivated: Qt.quit()
-            }
-        }
-    }
-
-    FileDialog {
-        id: fileDialog
-        title: "Open a PDF file"
-        nameFilters: [ "PDF files (*.pdf)" ]
-        onAccepted: document.source = selectedFile
-    }
-
-    Dialog {
-        id: passwordDialog
-        title: "Password"
-        standardButtons: Dialog.Ok | Dialog.Cancel
-        modal: true
-        closePolicy: Popup.CloseOnEscape
-        anchors.centerIn: parent
-        width: 300
-
-        contentItem: TextField {
-            id: passwordField
-            placeholderText: qsTr("Please provide the password")
-            echoMode: TextInput.Password
-            width: parent.width
-            onAccepted: passwordDialog.accept()
-        }
-        onOpened: function() { passwordField.forceActiveFocus() }
-        onAccepted: document.password = passwordField.text
-    }
-
-    Dialog {
-        id: errorDialog
-        title: "Error loading " + document.source
-        standardButtons: Dialog.Close
-        modal: true
-        closePolicy: Popup.CloseOnEscape
-        anchors.centerIn: parent
-        width: 300
-        visible: document.status === PdfDocument.Error
-
-        contentItem: Label {
-            id: errorField
-            text: document.error
-        }
-    }
-
-    PdfScrollablePageView {
-        id: view
-        anchors.fill: parent
-        anchors.leftMargin: searchDrawer.position * searchDrawer.width
-        document: PdfDocument {
-            id: document
-            source: Qt.resolvedUrl(root.source)
-            onPasswordRequired: passwordDialog.open()
-        }
-        searchString: searchField.text
-    }
-
-    Drawer {
-        id: searchDrawer
-        edge: Qt.LeftEdge
-//        modal: false
-//        dim: false // commented out as workaround for QTBUG-83859
-        width: 300
-        y: root.header.height
-        height: view.height
-        clip: true
-        ListView {
-            id: searchResultsList
-            anchors.fill: parent
-            anchors.margins: 2
-            model: view.searchModel
-            currentIndex: view.searchModel.currentResult
-            ScrollBar.vertical: ScrollBar { }
-            delegate: ItemDelegate {
-                id: resultDelegate
-                required property int index
-                required property int page
-                required property string contextBefore
-                required property string contextAfter
-                width: parent ? parent.width : 0
-                RowLayout {
-                    anchors.fill: parent
-                    spacing: 0
-                    Label {
-                        text: "Page " + (resultDelegate.page + 1) + ": "
-                    }
-                    Label {
-                        text: resultDelegate.contextBefore
-                        elide: Text.ElideLeft
-                        horizontalAlignment: Text.AlignRight
-                        Layout.fillWidth: true
-                        Layout.preferredWidth: parent.width / 2
-                    }
-                    Label {
-                        font.bold: true
-                        text: view.searchString
-                        width: implicitWidth
-                    }
-                    Label {
-                        text: resultDelegate.contextAfter
-                        elide: Text.ElideRight
-                        Layout.fillWidth: true
-                        Layout.preferredWidth: parent.width / 2
-                    }
-                }
-                highlighted: ListView.isCurrentItem
-                onClicked: view.searchModel.currentResult = resultDelegate.index
-            }
-        }
-    }
-
-    footer: ToolBar {
-        height: footerRow.implicitHeight
-        RowLayout {
-            id: footerRow
-            anchors.fill: parent
-            ToolButton {
-                action: Action {
-                    icon.source: "qrc:/pdfviewer/resources/go-up-search.svg"
-                    shortcut: StandardKey.FindPrevious
-                    onTriggered: view.searchBack()
-                }
-                ToolTip.visible: enabled && hovered
-                ToolTip.delay: 2000
-                ToolTip.text: "find previous"
-            }
-            TextField {
-                id: searchField
-                placeholderText: "search"
-                Layout.minimumWidth: 150
-                Layout.maximumWidth: 300
-                Layout.fillWidth: true
-                onAccepted: searchDrawer.open()
-                Image {
-                    visible: searchField.text !== ""
-                    source: "qrc:/pdfviewer/resources/edit-clear.svg"
-                    anchors {
-                        right: parent.right
-                        top: parent.top
-                        bottom: parent.bottom
-                        margins: 3
-                        rightMargin: 5
-                    }
-                    TapHandler {
-                        onTapped: searchField.clear()
-                    }
-                }
-            }
-            ToolButton {
-                action: Action {
-                    icon.source: "qrc:/pdfviewer/resources/go-down-search.svg"
-                    shortcut: StandardKey.FindNext
-                    onTriggered: view.searchForward()
-                }
-                ToolTip.visible: enabled && hovered
-                ToolTip.delay: 2000
-                ToolTip.text: "find next"
-            }
-            Label {
-                Layout.fillWidth: true
-                property size implicitPointSize: document.pagePointSize(view.currentPage)
-                text: "page " + (view.currentPage + 1) + " of " + document.pageCount +
-                      " scale " + view.renderScale.toFixed(2) +
-                      " original " + implicitPointSize.width.toFixed(1) + "x" + implicitPointSize.height.toFixed(1) + "pts"
-                visible: document.status === PdfDocument.Ready
-            }
-        }
-    }
-}
diff --git a/examples/qml/editingmodel/EditingModel/Main.qml b/examples/qml/editingmodel/EditingModel/Main.qml
new file mode 100644 (file)
index 0000000..2318ae8
--- /dev/null
@@ -0,0 +1,106 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Window
+import BaseModel
+
+Window {
+    title: "Moving Rectangle"
+    width: 800
+    height: 480
+    visible: true
+    id: mainWindow
+
+    Column {
+        spacing: 20
+        anchors.fill: parent
+        id: mainColumn
+        Text {
+            padding: 20
+            font.pointSize: 10
+            width: 600
+            wrapMode: Text.Wrap
+            text: "This example shows how to add, remove and move items inside a QML ListView.\n
+It shows and edits data via roles using QAbstractListModel on the Python side.\n
+Use the 'Middle click' on top of a rectangle to remove an item.\n
+'Left click' and drag to move the items."
+        }
+
+        Button {
+            anchors {
+                left: mainColumn.left
+                right: mainColumn.right
+                margins: 30
+            }
+            text: "Reset view"
+            onClicked: lv.model.reset()
+        }
+
+        Button {
+            anchors {
+                left: mainColumn.left
+                right: mainColumn.right
+                margins: 30
+            }
+            text: "Add element"
+            onClicked: lv.model.append()
+        }
+
+        ListView {
+            id: lv
+            anchors {
+                left: mainColumn.left
+                right: mainColumn.right
+                margins: 30
+            }
+
+            height: 200
+            model: BaseModel {}
+            orientation: ListView.Horizontal
+            displaced: Transition {
+                NumberAnimation {
+                    properties: "x,y"
+                    easing.type: Easing.OutQuad
+                }
+            }
+            delegate: DropArea {
+                id: droparea
+                width: ratio * lv.width
+                height: lv.height
+
+                onEntered: function (drag) {
+                    let dragindex = drag.source.modelIndex
+                    if (index === dragindex)
+                        return
+                    lv.model.move(dragindex, index)
+                }
+
+                MovingRectangle {
+                    modelIndex: index
+                    dragParent: lv
+                    sizeParent: droparea
+                }
+            }
+
+            MouseArea {
+                id: lvMousearea
+                anchors.fill: lv
+                z: -1
+            }
+            Rectangle {
+                id: lvBackground
+                anchors.fill: lv
+                anchors.margins: -border.width
+                color: "white"
+                border.color: "black"
+                border.width: 5
+                z: -1
+            }
+            Component.onCompleted: {
+                lv.model.reset()
+            }
+        }
+    }
+}
diff --git a/examples/qml/editingmodel/EditingModel/MovingRectangle.qml b/examples/qml/editingmodel/EditingModel/MovingRectangle.qml
new file mode 100644 (file)
index 0000000..b99a5f4
--- /dev/null
@@ -0,0 +1,78 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+
+import QtQuick
+import QtQuick.Controls
+
+Rectangle {
+    id: root
+    property int modelIndex
+    property Item dragParent
+    property Item sizeParent
+    property alias text: zone.text
+    property alias bgColor: root.color
+
+    anchors {
+        horizontalCenter: parent.horizontalCenter
+        verticalCenter: parent.verticalCenter
+    }
+    color:  backgroundColor
+    anchors.fill: sizeParent
+    border.color: "yellow"
+    border.width: 0
+    TextArea {
+        id: zone
+        anchors.centerIn: parent
+        text: display
+        onTextChanged: model.edit = text
+    }
+
+    MouseArea {
+        id: zoneMouseArea
+        anchors.fill: parent
+
+        acceptedButtons: Qt.MiddleButton
+        onClicked: function(mouse) {
+            if (mouse.button == Qt.MiddleButton)
+                lv.model.remove(index)
+            else
+                mouse.accepted = false
+        }
+    }
+    DragHandler {
+        id: dragHandler
+        xAxis {
+
+            enabled: true
+            minimum: 0
+            maximum: lv.width - droparea.width
+        }
+        yAxis.enabled: false
+        acceptedButtons: Qt.LeftButton
+    }
+    Drag.active: dragHandler.active
+    Drag.source: root
+    Drag.hotSpot.x: width / 2
+
+    states: [
+        State {
+            when: dragHandler.active
+            ParentChange {
+                target: root
+                parent: root.dragParent
+            }
+
+            AnchorChanges {
+                target: root
+                anchors.horizontalCenter: undefined
+                anchors.verticalCenter: undefined
+            }
+            PropertyChanges {
+                target: root
+                opacity: 0.6
+                border.width: 3
+            }
+        }
+    ]
+}
diff --git a/examples/qml/editingmodel/EditingModel/qmldir b/examples/qml/editingmodel/EditingModel/qmldir
new file mode 100644 (file)
index 0000000..1ba3107
--- /dev/null
@@ -0,0 +1,3 @@
+module UsingModel
+Main 1.0 Main.qml
+MovingRectangle 1.0 MovingRectangle.qml
diff --git a/examples/qml/editingmodel/MovingRectangle.qml b/examples/qml/editingmodel/MovingRectangle.qml
deleted file mode 100644 (file)
index b99a5f4..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-
-import QtQuick
-import QtQuick.Controls
-
-Rectangle {
-    id: root
-    property int modelIndex
-    property Item dragParent
-    property Item sizeParent
-    property alias text: zone.text
-    property alias bgColor: root.color
-
-    anchors {
-        horizontalCenter: parent.horizontalCenter
-        verticalCenter: parent.verticalCenter
-    }
-    color:  backgroundColor
-    anchors.fill: sizeParent
-    border.color: "yellow"
-    border.width: 0
-    TextArea {
-        id: zone
-        anchors.centerIn: parent
-        text: display
-        onTextChanged: model.edit = text
-    }
-
-    MouseArea {
-        id: zoneMouseArea
-        anchors.fill: parent
-
-        acceptedButtons: Qt.MiddleButton
-        onClicked: function(mouse) {
-            if (mouse.button == Qt.MiddleButton)
-                lv.model.remove(index)
-            else
-                mouse.accepted = false
-        }
-    }
-    DragHandler {
-        id: dragHandler
-        xAxis {
-
-            enabled: true
-            minimum: 0
-            maximum: lv.width - droparea.width
-        }
-        yAxis.enabled: false
-        acceptedButtons: Qt.LeftButton
-    }
-    Drag.active: dragHandler.active
-    Drag.source: root
-    Drag.hotSpot.x: width / 2
-
-    states: [
-        State {
-            when: dragHandler.active
-            ParentChange {
-                target: root
-                parent: root.dragParent
-            }
-
-            AnchorChanges {
-                target: root
-                anchors.horizontalCenter: undefined
-                anchors.verticalCenter: undefined
-            }
-            PropertyChanges {
-                target: root
-                opacity: 0.6
-                border.width: 3
-            }
-        }
-    ]
-}
index 2b45b23f07e33e571dc417f89201f89c48096477..f045842f5917fb7e47ad0cd17109351c4ea8f663 100644 (file)
@@ -3,14 +3,16 @@ QAbstractListModel in QML
 
 .. tags:: Android
 
-This example shows how to add, remove and move items inside a QML
-ListView, but showing and editing the data via roles using a
-QAbstractListModel from Python.
+This example shows how to add, remove and move items inside a
+`QML ListView`_, but showing and editing the data via roles using a
+:class:`~PySide6.QtCore.QAbstractListModel` from Python.
 
 You can add new elements and reset the view using the two top buttons,
 remove elements by 'middle click' the element, and move the elements
 with a 'left click' plus dragging the item around.
 
 .. image:: qabstractlistmodelqml.png
-   :width: 400
+   :width: 800
    :alt: QAbstractListModel/ListView Screenshot
+
+.. _`QML ListView`: https://doc.qt.io/qt-6/qml-qtquick-listview.html
index cccbd2b0de45911d0554ab872a03c34dde05b9c7..cb2696efbe73645b14df5f2d7d2a61ae67e335d3 100644 (file)
@@ -5,7 +5,6 @@ from __future__ import annotations
 import sys
 from pathlib import Path
 
-from PySide6.QtCore import QUrl
 from PySide6.QtGui import QGuiApplication
 from PySide6.QtQml import QQmlApplicationEngine
 
@@ -14,8 +13,8 @@ from model import BaseModel  # noqa: F401
 if __name__ == "__main__":
     app = QGuiApplication(sys.argv)
     engine = QQmlApplicationEngine()
-    qml_file = Path(__file__).parent / "main.qml"
-    engine.load(QUrl.fromLocalFile(qml_file))
+    engine.addImportPath(Path(__file__).parent)
+    engine.loadFromModule("EditingModel", "Main")
     if not engine.rootObjects():
         sys.exit(-1)
 
index 71272a9735480177c42326c38d049a3bfeb7999f..654a781ffc5874f35b640dc8757c197f7c2044f2 100644 (file)
@@ -1,3 +1,4 @@
 {
-    "files": ["model.py","main.qml","main.py","MovingRectangle.qml"]
+    "files": ["main.py", "model.py", "EditingModel/Main.qml",
+              "EditingModel/MovingRectangle.qml", "EditingModel/qmldir"]
 }
diff --git a/examples/qml/editingmodel/main.qml b/examples/qml/editingmodel/main.qml
deleted file mode 100644 (file)
index 2318ae8..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Window
-import BaseModel
-
-Window {
-    title: "Moving Rectangle"
-    width: 800
-    height: 480
-    visible: true
-    id: mainWindow
-
-    Column {
-        spacing: 20
-        anchors.fill: parent
-        id: mainColumn
-        Text {
-            padding: 20
-            font.pointSize: 10
-            width: 600
-            wrapMode: Text.Wrap
-            text: "This example shows how to add, remove and move items inside a QML ListView.\n
-It shows and edits data via roles using QAbstractListModel on the Python side.\n
-Use the 'Middle click' on top of a rectangle to remove an item.\n
-'Left click' and drag to move the items."
-        }
-
-        Button {
-            anchors {
-                left: mainColumn.left
-                right: mainColumn.right
-                margins: 30
-            }
-            text: "Reset view"
-            onClicked: lv.model.reset()
-        }
-
-        Button {
-            anchors {
-                left: mainColumn.left
-                right: mainColumn.right
-                margins: 30
-            }
-            text: "Add element"
-            onClicked: lv.model.append()
-        }
-
-        ListView {
-            id: lv
-            anchors {
-                left: mainColumn.left
-                right: mainColumn.right
-                margins: 30
-            }
-
-            height: 200
-            model: BaseModel {}
-            orientation: ListView.Horizontal
-            displaced: Transition {
-                NumberAnimation {
-                    properties: "x,y"
-                    easing.type: Easing.OutQuad
-                }
-            }
-            delegate: DropArea {
-                id: droparea
-                width: ratio * lv.width
-                height: lv.height
-
-                onEntered: function (drag) {
-                    let dragindex = drag.source.modelIndex
-                    if (index === dragindex)
-                        return
-                    lv.model.move(dragindex, index)
-                }
-
-                MovingRectangle {
-                    modelIndex: index
-                    dragParent: lv
-                    sizeParent: droparea
-                }
-            }
-
-            MouseArea {
-                id: lvMousearea
-                anchors.fill: lv
-                z: -1
-            }
-            Rectangle {
-                id: lvBackground
-                anchors.fill: lv
-                anchors.margins: -border.width
-                color: "white"
-                border.color: "black"
-                border.width: 5
-                z: -1
-            }
-            Component.onCompleted: {
-                lv.model.reset()
-            }
-        }
-    }
-}
diff --git a/examples/qml/textproperties/TextProperties/Main.qml b/examples/qml/textproperties/TextProperties/Main.qml
new file mode 100644 (file)
index 0000000..c722eb4
--- /dev/null
@@ -0,0 +1,160 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtQuick.Window
+import QtQuick.Controls.Material
+
+import io.qt.textproperties
+
+ApplicationWindow {
+    id: page
+    width: 800
+    height: 400
+    visible: true
+    Material.theme: Material.Dark
+    Material.accent: Material.Red
+
+    Bridge {
+        id: bridge
+    }
+
+    GridLayout {
+        id: grid
+        columns: 2
+        rows: 3
+
+        ColumnLayout {
+            spacing: 2
+            Layout.columnSpan: 1
+            Layout.preferredWidth: 400
+
+            Text {
+                id: leftlabel
+                Layout.alignment: Qt.AlignHCenter
+                color: "white"
+                font.pointSize: 16
+                text: "Qt for Python"
+                Layout.preferredHeight: 100
+                Material.accent: Material.Green
+            }
+
+            RadioButton {
+                id: italic
+                Layout.alignment: Qt.AlignLeft
+                text: "Italic"
+                onToggled: {
+                    leftlabel.font.italic = bridge.getItalic(italic.text)
+                    leftlabel.font.bold = bridge.getBold(italic.text)
+                    leftlabel.font.underline = bridge.getUnderline(italic.text)
+
+                }
+            }
+            RadioButton {
+                id: bold
+                Layout.alignment: Qt.AlignLeft
+                text: "Bold"
+                onToggled: {
+                    leftlabel.font.italic = bridge.getItalic(bold.text)
+                    leftlabel.font.bold = bridge.getBold(bold.text)
+                    leftlabel.font.underline = bridge.getUnderline(bold.text)
+                }
+            }
+            RadioButton {
+                id: underline
+                Layout.alignment: Qt.AlignLeft
+                text: "Underline"
+                onToggled: {
+                    leftlabel.font.italic = bridge.getItalic(underline.text)
+                    leftlabel.font.bold = bridge.getBold(underline.text)
+                    leftlabel.font.underline = bridge.getUnderline(underline.text)
+                }
+            }
+            RadioButton {
+                id: noneradio
+                Layout.alignment: Qt.AlignLeft
+                text: "None"
+                checked: true
+                onToggled: {
+                    leftlabel.font.italic = bridge.getItalic(noneradio.text)
+                    leftlabel.font.bold = bridge.getBold(noneradio.text)
+                    leftlabel.font.underline = bridge.getUnderline(noneradio.text)
+                }
+            }
+        }
+
+        ColumnLayout {
+            id: rightcolumn
+            spacing: 2
+            Layout.columnSpan: 1
+            Layout.preferredWidth: 400
+            Layout.preferredHeight: 400
+            Layout.fillWidth: true
+
+            RowLayout {
+                Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+
+
+                Button {
+                    id: red
+                    text: "Red"
+                    highlighted: true
+                    Material.accent: Material.Red
+                    onClicked: {
+                        leftlabel.color = bridge.getColor(red.text)
+                    }
+                }
+                Button {
+                    id: green
+                    text: "Green"
+                    highlighted: true
+                    Material.accent: Material.Green
+                    onClicked: {
+                        leftlabel.color = bridge.getColor(green.text)
+                    }
+                }
+                Button {
+                    id: blue
+                    text: "Blue"
+                    highlighted: true
+                    Material.accent: Material.Blue
+                    onClicked: {
+                        leftlabel.color = bridge.getColor(blue.text)
+                    }
+                }
+                Button {
+                    id: nonebutton
+                    text: "None"
+                    highlighted: true
+                    Material.accent: Material.BlueGrey
+                    onClicked: {
+                        leftlabel.color = bridge.getColor(nonebutton.text)
+                    }
+                }
+            }
+            RowLayout {
+                Layout.fillWidth: true
+                Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+                Text {
+                    id: rightlabel
+                    color: "white"
+                    Layout.alignment: Qt.AlignLeft
+                    text: "Font size"
+                    Material.accent: Material.White
+                }
+                Slider {
+                    width: rightcolumn.width*0.6
+                    Layout.alignment: Qt.AlignRight
+                    id: slider
+                    value: 0.5
+                    onValueChanged: {
+                        leftlabel.font.pointSize = bridge.getSize(value)
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/examples/qml/textproperties/TextProperties/qmldir b/examples/qml/textproperties/TextProperties/qmldir
new file mode 100644 (file)
index 0000000..2a7c478
--- /dev/null
@@ -0,0 +1,2 @@
+module Bars
+Main 1.0 Main.qml
index 81829dfdccc98738578bea8167bea05705481fc0..c892795475707ed350394ce22a3033258f0eec8f 100644 (file)
@@ -1,8 +1,9 @@
 Text Properties Example
 =======================
 
-A Python application that demonstrates how to load a qml file
-using Material design, to change the look of text.
+A Python application that demonstrates how to load a QML file
+using **Material** design, to change the look of text
+(see :ref:`Styling Qt Quick Controls<Styling-Qt-Quick-Controls>`).
 
 .. image:: textproperties.png
    :width: 400
index 802e9f94b069a2f9e73b9b9af99aac901c128154..cefa0cc69cc9c82e529c672565488d7d794b85bd 100644 (file)
@@ -28,37 +28,24 @@ class Bridge(QObject):
             return "#a5d6a7"
         elif s.lower() == "blue":
             return "#90caf9"
-        else:
-            return "white"
+        return "white"
 
     @Slot(float, result=int)
     def getSize(self, s):
         size = int(s * 34)
-        if size <= 0:
-            return 1
-        else:
-            return size
+        return max(1, size)
 
     @Slot(str, result=bool)
     def getItalic(self, s):
-        if s.lower() == "italic":
-            return True
-        else:
-            return False
+        return s.lower() == "italic"
 
     @Slot(str, result=bool)
     def getBold(self, s):
-        if s.lower() == "bold":
-            return True
-        else:
-            return False
+        return s.lower() == "bold"
 
     @Slot(str, result=bool)
     def getUnderline(self, s):
-        if s.lower() == "underline":
-            return True
-        else:
-            return False
+        return s.lower() == "underline"
 
 
 if __name__ == '__main__':
@@ -66,10 +53,8 @@ if __name__ == '__main__':
     QQuickStyle.setStyle("Material")
     engine = QQmlApplicationEngine()
 
-    # Get the path of the current directory, and then add the name
-    # of the QML file, to load it.
-    qml_file = Path(__file__).parent / 'view.qml'
-    engine.load(qml_file)
+    engine.addImportPath(Path(__file__).parent)
+    engine.loadFromModule("TextProperties", "Main")
 
     if not engine.rootObjects():
         sys.exit(-1)
index e6f087ccef7bb73963a4e6dda088f3372c662c06..39d17ac711fa5fbb86d83b3212c2b70c21bb4a5c 100644 (file)
@@ -1,3 +1,3 @@
 {
-    "files": ["main.py", "view.qml"]
+    "files": ["main.py", "TextProperties/Main.qml", "TextProperties/qmldir"]
 }
diff --git a/examples/qml/textproperties/view.qml b/examples/qml/textproperties/view.qml
deleted file mode 100644 (file)
index c722eb4..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Controls
-import QtQuick.Window
-import QtQuick.Controls.Material
-
-import io.qt.textproperties
-
-ApplicationWindow {
-    id: page
-    width: 800
-    height: 400
-    visible: true
-    Material.theme: Material.Dark
-    Material.accent: Material.Red
-
-    Bridge {
-        id: bridge
-    }
-
-    GridLayout {
-        id: grid
-        columns: 2
-        rows: 3
-
-        ColumnLayout {
-            spacing: 2
-            Layout.columnSpan: 1
-            Layout.preferredWidth: 400
-
-            Text {
-                id: leftlabel
-                Layout.alignment: Qt.AlignHCenter
-                color: "white"
-                font.pointSize: 16
-                text: "Qt for Python"
-                Layout.preferredHeight: 100
-                Material.accent: Material.Green
-            }
-
-            RadioButton {
-                id: italic
-                Layout.alignment: Qt.AlignLeft
-                text: "Italic"
-                onToggled: {
-                    leftlabel.font.italic = bridge.getItalic(italic.text)
-                    leftlabel.font.bold = bridge.getBold(italic.text)
-                    leftlabel.font.underline = bridge.getUnderline(italic.text)
-
-                }
-            }
-            RadioButton {
-                id: bold
-                Layout.alignment: Qt.AlignLeft
-                text: "Bold"
-                onToggled: {
-                    leftlabel.font.italic = bridge.getItalic(bold.text)
-                    leftlabel.font.bold = bridge.getBold(bold.text)
-                    leftlabel.font.underline = bridge.getUnderline(bold.text)
-                }
-            }
-            RadioButton {
-                id: underline
-                Layout.alignment: Qt.AlignLeft
-                text: "Underline"
-                onToggled: {
-                    leftlabel.font.italic = bridge.getItalic(underline.text)
-                    leftlabel.font.bold = bridge.getBold(underline.text)
-                    leftlabel.font.underline = bridge.getUnderline(underline.text)
-                }
-            }
-            RadioButton {
-                id: noneradio
-                Layout.alignment: Qt.AlignLeft
-                text: "None"
-                checked: true
-                onToggled: {
-                    leftlabel.font.italic = bridge.getItalic(noneradio.text)
-                    leftlabel.font.bold = bridge.getBold(noneradio.text)
-                    leftlabel.font.underline = bridge.getUnderline(noneradio.text)
-                }
-            }
-        }
-
-        ColumnLayout {
-            id: rightcolumn
-            spacing: 2
-            Layout.columnSpan: 1
-            Layout.preferredWidth: 400
-            Layout.preferredHeight: 400
-            Layout.fillWidth: true
-
-            RowLayout {
-                Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
-
-
-                Button {
-                    id: red
-                    text: "Red"
-                    highlighted: true
-                    Material.accent: Material.Red
-                    onClicked: {
-                        leftlabel.color = bridge.getColor(red.text)
-                    }
-                }
-                Button {
-                    id: green
-                    text: "Green"
-                    highlighted: true
-                    Material.accent: Material.Green
-                    onClicked: {
-                        leftlabel.color = bridge.getColor(green.text)
-                    }
-                }
-                Button {
-                    id: blue
-                    text: "Blue"
-                    highlighted: true
-                    Material.accent: Material.Blue
-                    onClicked: {
-                        leftlabel.color = bridge.getColor(blue.text)
-                    }
-                }
-                Button {
-                    id: nonebutton
-                    text: "None"
-                    highlighted: true
-                    Material.accent: Material.BlueGrey
-                    onClicked: {
-                        leftlabel.color = bridge.getColor(nonebutton.text)
-                    }
-                }
-            }
-            RowLayout {
-                Layout.fillWidth: true
-                Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
-                Text {
-                    id: rightlabel
-                    color: "white"
-                    Layout.alignment: Qt.AlignLeft
-                    text: "Font size"
-                    Material.accent: Material.White
-                }
-                Slider {
-                    width: rightcolumn.width*0.6
-                    Layout.alignment: Qt.AlignRight
-                    id: slider
-                    value: 0.5
-                    onValueChanged: {
-                        leftlabel.font.pointSize = bridge.getSize(value)
-                    }
-                }
-            }
-        }
-    }
-}
index 4c556c631e0ca51a7f3ac89b3591c77e5296d49c..419d195c5a057ff5d5d8ffe19b2c03c0457ddd42 100644 (file)
@@ -21,8 +21,7 @@ if __name__ == '__main__':
     component = QQmlComponent(engine)
     component.loadFromModule("People", "Main")
 
-    person = component.create()
-    if person:
+    if person := component.create():
         print(f"The person's name is {person.name}")
         print(f"They wear a {person.shoe_size} sized shoe")
     else:
index c914fe7d2284626752c185f55b8cfca6a6d128d4..fa2e53fa5acae2d5c1f64651421508b85932bad5 100644 (file)
@@ -37,8 +37,7 @@ for g in range(party.guestCount()):
     name = guest.name
 
     rsvp_date = None
-    attached = qmlAttachedPropertiesObject(BirthdayParty, guest, False)
-    if attached:
+    if attached := qmlAttachedPropertiesObject(BirthdayParty, guest, False):
         rsvp_date = attached.rsvp.toString()
     if rsvp_date:
         print(f"    {name} RSVP date: {rsvp_date}")
index eb693134a96e3bce77b06a608995667bb21b2970..0efae722f41154b2b08f3bbb00c81da9df6055f9 100644 (file)
@@ -38,8 +38,7 @@ for g in range(party.guestCount()):
     name = guest.name
 
     rsvp_date = None
-    attached = qmlAttachedPropertiesObject(BirthdayParty, guest, False)
-    if attached:
+    if attached := qmlAttachedPropertiesObject(BirthdayParty, guest, False):
         rsvp_date = attached.rsvp.toString()
     if rsvp_date:
         print(f"    {name} RSVP date: {rsvp_date}")
index 45d0fd3f91f606f89112ed4bccf13d1158c28c39..f139d8b05ccc782abfa2c104e252f9fcb0dee80c 100644 (file)
@@ -37,8 +37,7 @@ if __name__ == "__main__":
         name = guest.name
 
         rsvp_date = None
-        attached = qmlAttachedPropertiesObject(BirthdayParty, guest, False)
-        if attached:
+        if attached := qmlAttachedPropertiesObject(BirthdayParty, guest, False):
             rsvp_date = attached.rsvp.toString()
         if rsvp_date:
             print(f"    {name} RSVP date: {rsvp_date}")
index f481be165902da3fb8d8a7a670a09c3042cc97c8..20a6d42f5128cb675e6ca5a36cc1b7621cd3b980 100644 (file)
@@ -22,8 +22,7 @@ if __name__ == '__main__':
     component = QQmlComponent(engine)
     component.loadFromModule("People", "Main")
 
-    party = component.create()
-    if party:
+    if party := component.create():
         print(f"{party.host} is having a birthday!\nThey are inviting:")
         for g in range(party.guestCount()):
             name = party.guest(g).name
diff --git a/examples/qml/usingmodel/UsingModel/Main.qml b/examples/qml/usingmodel/UsingModel/Main.qml
new file mode 100644 (file)
index 0000000..e8b1fb2
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import PersonModel
+
+ListView {
+    width: 100
+    height: 100
+    anchors.fill: parent
+    model: PersonModel
+    delegate: Component {
+        Rectangle {
+            height: 25
+            width: 100
+            Text {
+                text: display + ": " + myrole
+            }
+        }
+    }
+}
diff --git a/examples/qml/usingmodel/UsingModel/qmldir b/examples/qml/usingmodel/UsingModel/qmldir
new file mode 100644 (file)
index 0000000..27b7840
--- /dev/null
@@ -0,0 +1,2 @@
+module UsingModel
+Main 1.0 Main.qml
index 06a1b27b09ad639aa0cd8ce48204e989917ec495..06f442df3e29eaa8d9816db287e9bca5931c4f9a 100644 (file)
@@ -3,9 +3,9 @@ Using Model Example
 
 .. tags:: Android
 
-A Python application that demonstrates how to use a :ref:`QAbstractListModel`
-with QML.
+A Python application that demonstrates how to use a
+:class:`~PySide6.QtCore.QAbstractListModel` with QML.
 
 .. image:: usingmodel.png
-   :width: 400
+   :width: 160
    :alt: Using Model Screenshot
diff --git a/examples/qml/usingmodel/main.py b/examples/qml/usingmodel/main.py
new file mode 100644 (file)
index 0000000..f96d30f
--- /dev/null
@@ -0,0 +1,73 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from dataclasses import dataclass
+from pathlib import Path
+import sys
+from PySide6.QtCore import QAbstractListModel, Qt, QByteArray
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQuick import QQuickView
+from PySide6.QtQml import QmlElement, QmlSingleton
+
+
+QML_IMPORT_NAME = "PersonModel"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@dataclass
+class Person:
+    name: str
+    myrole: str
+
+
+@QmlElement
+@QmlSingleton
+class PersonModel (QAbstractListModel):
+    MyRole = Qt.ItemDataRole.UserRole + 1
+
+    def __init__(self, data, parent=None):
+        super().__init__(parent)
+        self._data = data
+
+    def roleNames(self):
+        roles = {
+            PersonModel.MyRole: QByteArray(b'myrole'),
+            Qt.ItemDataRole.DisplayRole: QByteArray(b'display')
+        }
+        return roles
+
+    def rowCount(self, index):
+        return len(self._data)
+
+    def data(self, index, role):
+        d = self._data[index.row()]
+        if role == Qt.ItemDataRole.DisplayRole:
+            return d.name
+        if role == Qt.ItemDataRole.DecorationRole:
+            return Qt.GlobalColor.black
+        if role == PersonModel.MyRole:
+            return d.myrole
+        return None
+
+    @staticmethod
+    def create(engine):
+        data = [Person('Qt', 'myrole'), Person('PySide', 'role2')]
+        return PersonModel(data)
+
+
+if __name__ == '__main__':
+    app = QGuiApplication(sys.argv)
+    view = QQuickView()
+    view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
+    view.engine().addImportPath(Path(__file__).parent)
+    view.loadFromModule("UsingModel", "Main")
+    if view.status() == QQuickView.Status.Error:
+        sys.exit(-1)
+    view.show()
+
+    r = app.exec()
+    # Deleting the view before it goes out of scope is required to make sure all child QML instances
+    # are destroyed in the correct order.
+    del view
+    sys.exit(r)
diff --git a/examples/qml/usingmodel/usingmodel.py b/examples/qml/usingmodel/usingmodel.py
deleted file mode 100644 (file)
index e73eafe..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-from __future__ import annotations
-
-import os
-from dataclasses import dataclass
-from pathlib import Path
-import sys
-from PySide6.QtCore import QAbstractListModel, Qt, QUrl, QByteArray
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtQuick import QQuickView
-from PySide6.QtQml import QmlElement, QmlSingleton
-
-
-QML_IMPORT_NAME = "PersonModel"
-QML_IMPORT_MAJOR_VERSION = 1
-
-
-@dataclass
-class Person:
-    name: str
-    myrole: str
-
-
-@QmlElement
-@QmlSingleton
-class PersonModel (QAbstractListModel):
-    MyRole = Qt.ItemDataRole.UserRole + 1
-
-    def __init__(self, data, parent=None):
-        super().__init__(parent)
-        self._data = data
-
-    def roleNames(self):
-        roles = {
-            PersonModel.MyRole: QByteArray(b'myrole'),
-            Qt.ItemDataRole.DisplayRole: QByteArray(b'display')
-        }
-        return roles
-
-    def rowCount(self, index):
-        return len(self._data)
-
-    def data(self, index, role):
-        d = self._data[index.row()]
-        if role == Qt.ItemDataRole.DisplayRole:
-            return d.name
-        if role == Qt.ItemDataRole.DecorationRole:
-            return Qt.GlobalColor.black
-        if role == PersonModel.MyRole:
-            return d.myrole
-        return None
-
-    @staticmethod
-    def create(engine):
-        data = [Person('Qt', 'myrole'), Person('PySide', 'role2')]
-        return PersonModel(data)
-
-
-if __name__ == '__main__':
-    app = QGuiApplication(sys.argv)
-    view = QQuickView()
-    view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
-
-    qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
-    view.setSource(QUrl.fromLocalFile(qml_file))
-    if view.status() == QQuickView.Status.Error:
-        sys.exit(-1)
-    view.show()
-
-    r = app.exec()
-    # Deleting the view before it goes out of scope is required to make sure all child QML instances
-    # are destroyed in the correct order.
-    del view
-    sys.exit(r)
index 600cdb4092b30e9868cf58cec5de8d477a6a3626..7b45eb7dfd859235a11f130853580c6111936d1c 100644 (file)
@@ -1,3 +1,3 @@
 {
-    "files": ["view.qml", "usingmodel.py"]
+    "files": ["main.py", "UsingModel/Main.qml", "UsingModel/qmldir"]
 }
diff --git a/examples/qml/usingmodel/view.qml b/examples/qml/usingmodel/view.qml
deleted file mode 100644 (file)
index e8b1fb2..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import PersonModel
-
-ListView {
-    width: 100
-    height: 100
-    anchors.fill: parent
-    model: PersonModel
-    delegate: Component {
-        Rectangle {
-            height: 25
-            width: 100
-            Text {
-                text: display + ": " + myrole
-            }
-        }
-    }
-}
diff --git a/examples/quick/models/objectlistmodel/ObjectListModel/Main.qml b/examples/quick/models/objectlistmodel/ObjectListModel/Main.qml
new file mode 100644 (file)
index 0000000..3cb072d
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+ListView {
+    id: listview
+    width: 200; height: 320
+    required model
+    ScrollBar.vertical: ScrollBar { }
+
+    delegate: Rectangle {
+        width: listview.width; height: 25
+
+        required color
+        required property string name
+
+        Text { text: parent.name }
+    }
+}
diff --git a/examples/quick/models/objectlistmodel/ObjectListModel/qmldir b/examples/quick/models/objectlistmodel/ObjectListModel/qmldir
new file mode 100644 (file)
index 0000000..6d2da4d
--- /dev/null
@@ -0,0 +1,2 @@
+module ObjectListModel
+Main 1.0 Main.qml
index a4af62706f0de399ab3f59cdb254272f5d54f434..6d2504d4e2531540ddd6b1d1b2738fc86909269a 100644 (file)
@@ -3,12 +3,12 @@ Object List Model Example
 
 .. tags:: Android
 
-A list of QObject values can also be used as a model.
+A list of :class:`~PySide6.QtCore.QObject` values can also be used as a model.
 A list[QObject,] provides the properties of the objects in the list as roles.
 
 The following application creates a DataObject class with Property values
 that will be accessible as named roles when a list[DataObject,] is exposed to QML:
 
 .. image:: objectlistmodel.png
-   :width: 400
+   :width: 200
    :alt: Object List Model Screenshot
diff --git a/examples/quick/models/objectlistmodel/main.py b/examples/quick/models/objectlistmodel/main.py
new file mode 100644 (file)
index 0000000..17d754c
--- /dev/null
@@ -0,0 +1,63 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from pathlib import Path
+import sys
+from PySide6.QtCore import QObject, Property, Signal
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQuick import QQuickView
+
+# This example illustrates exposing a list of QObjects as a model in QML
+
+
+class DataObject(QObject):
+
+    nameChanged = Signal()
+    colorChanged = Signal()
+
+    def __init__(self, name, color, parent=None):
+        super().__init__(parent)
+        self._name = name
+        self._color = color
+
+    def name(self):
+        return self._name
+
+    def setName(self, name):
+        if name != self._name:
+            self._name = name
+            self.nameChanged.emit()
+
+    def color(self):
+        return self._color
+
+    def setColor(self, color):
+        if color != self._color:
+            self._color = color
+            self.colorChanged.emit()
+
+    name = Property(str, name, setName, notify=nameChanged)
+    color = Property(str, color, setColor, notify=colorChanged)
+
+
+if __name__ == '__main__':
+    app = QGuiApplication(sys.argv)
+
+    dataList = [DataObject("Item 1", "red"),
+                DataObject("Item 2", "green"),
+                DataObject("Item 3", "blue"),
+                DataObject("Item 4", "yellow")]
+
+    view = QQuickView()
+    view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
+    view.setInitialProperties({"model": dataList})
+
+    qml_file = Path(__file__).parent / "view.qml"
+    view.engine().addImportPath(Path(__file__).parent)
+    view.loadFromModule("ObjectListModel", "Main")
+    view.show()
+
+    r = app.exec()
+    del view
+    sys.exit(r)
diff --git a/examples/quick/models/objectlistmodel/objectlistmodel.py b/examples/quick/models/objectlistmodel/objectlistmodel.py
deleted file mode 100644 (file)
index 0f0da3e..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-from __future__ import annotations
-
-from pathlib import Path
-import sys
-from PySide6.QtCore import QObject, QUrl, Property, Signal
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtQuick import QQuickView
-
-# This example illustrates exposing a list of QObjects as a model in QML
-
-
-class DataObject(QObject):
-
-    nameChanged = Signal()
-    colorChanged = Signal()
-
-    def __init__(self, name, color, parent=None):
-        super().__init__(parent)
-        self._name = name
-        self._color = color
-
-    def name(self):
-        return self._name
-
-    def setName(self, name):
-        if name != self._name:
-            self._name = name
-            self.nameChanged.emit()
-
-    def color(self):
-        return self._color
-
-    def setColor(self, color):
-        if color != self._color:
-            self._color = color
-            self.colorChanged.emit()
-
-    name = Property(str, name, setName, notify=nameChanged)
-    color = Property(str, color, setColor, notify=colorChanged)
-
-
-if __name__ == '__main__':
-    app = QGuiApplication(sys.argv)
-
-    dataList = [DataObject("Item 1", "red"),
-                DataObject("Item 2", "green"),
-                DataObject("Item 3", "blue"),
-                DataObject("Item 4", "yellow")]
-
-    view = QQuickView()
-    view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
-    view.setInitialProperties({"model": dataList})
-
-    qml_file = Path(__file__).parent / "view.qml"
-    view.setSource(QUrl.fromLocalFile(qml_file))
-    view.show()
-
-    r = app.exec()
-    del view
-    sys.exit(r)
index 556e399f4f28c8d13c1d5405b89921a44dc96bc6..7670cc865bd525be7ef815c4c2f03ac01799610b 100644 (file)
@@ -1,3 +1,3 @@
 {
-    "files": ["objectlistmodel.py", "view.qml"]
+    "files": ["main.py", "ObjectListModel/Main.qml", "ObjectListModel/qmldir"]
 }
diff --git a/examples/quick/models/objectlistmodel/view.qml b/examples/quick/models/objectlistmodel/view.qml
deleted file mode 100644 (file)
index 3cb072d..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-
-ListView {
-    id: listview
-    width: 200; height: 320
-    required model
-    ScrollBar.vertical: ScrollBar { }
-
-    delegate: Rectangle {
-        width: listview.width; height: 25
-
-        required color
-        required property string name
-
-        Text { text: parent.name }
-    }
-}
index a7660087dce95b94a0a6f35c6f0c2d6979fdfd84..74f28d5a311b6f4a6b407d90bb837a31c22a0522 100644 (file)
@@ -29,8 +29,7 @@ if __name__ == "__main__":
 
     settings = QSettings()
     if not os.environ.get("QT_QUICK_CONTROLS_STYLE"):
-        style_name = settings.value("style")
-        if style_name:
+        if style_name := settings.value("style"):
             QQuickStyle.setStyle(style_name)
 
     engine = QQmlApplicationEngine()
index 4b699038ddb66bb420e5ae59e9eec467825329eb..1decfc69ec361008cce229789cefa74598dc0af3 100644 (file)
@@ -76,6 +76,7 @@ ApplicationWindow {
             anchors.leftMargin: !window.portraitMode ? drawer.width : undefined
 
             ToolButton {
+                Accessible.name: qsTr("Toggle drawer")
                 action: navigateBackAction
                 visible: window.portraitMode
             }
@@ -92,6 +93,7 @@ ApplicationWindow {
 
             ToolButton {
                 action: optionsMenuAction
+                Accessible.name: qsTr("Open options menu")
 
                 Menu {
                     id: optionsMenu
index 2ccd1fa93c8b3d4d0ffd2c6a2e37ddf0ab08167a..2f6d636e1f0028580217a67e2c8cbdbe763ee536 100644 (file)
@@ -23,6 +23,7 @@ ScrollablePage {
             enabled: !GalleryConfig.disabled
             value: 0.5
             anchors.horizontalCenter: parent.horizontalCenter
+            Accessible.name: qsTr("demo dial")
         }
     }
 }
index 2a3f7158e154e750255cb122982585ee3316c4e0..48c0e2e2cd9a910d38d4ed9f511903f2ff8a1be2 100644 (file)
@@ -23,11 +23,13 @@ ScrollablePage {
             id: bar
             value: 0.5
             anchors.horizontalCenter: parent.horizontalCenter
+            Accessible.name: qsTr("demo progressbar")
         }
 
         ProgressBar {
             indeterminate: true
             anchors.horizontalCenter: parent.horizontalCenter
+            Accessible.name: qsTr("indeterminate progressbar")
         }
     }
 }
index 83dced76d55e5b10bd1612d3bc4e132990f89259..7f73ce17b0c0e8a995b0a1277b77e80701618e2c 100644 (file)
@@ -23,6 +23,7 @@ ScrollablePage {
             first.value: 0.25
             second.value: 0.75
             anchors.horizontalCenter: parent.horizontalCenter
+            Accessible.name: qsTr("demo range slider")
         }
 
         RangeSlider {
@@ -31,6 +32,7 @@ ScrollablePage {
             first.value: 0.25
             second.value: 0.75
             anchors.horizontalCenter: parent.horizontalCenter
+            Accessible.name: qsTr("vertical demo slider")
         }
     }
 }
index b7d83fdf307e3792d54512009406668a900f3a79..97e169e8730dd4780f89f7604627098cede85c35 100644 (file)
@@ -22,6 +22,7 @@ ScrollablePage {
             enabled: !GalleryConfig.disabled
             value: 0.5
             anchors.horizontalCenter: parent.horizontalCenter
+            Accessible.name: qsTr("demo slider")
         }
 
         Slider {
@@ -29,6 +30,7 @@ ScrollablePage {
             orientation: Qt.Vertical
             value: 0.5
             anchors.horizontalCenter: parent.horizontalCenter
+            Accessible.name: qsTr("vertical demo slider")
         }
     }
 }
index 4536e8a9d89a90aa1b2d97542702af1553b560fb..6c99c89e333d66ffac8b1f9f38a0d7e806ca44c5 100644 (file)
@@ -24,6 +24,7 @@ ScrollablePage {
             value: 50
             anchors.horizontalCenter: parent.horizontalCenter
             editable: true
+            Accessible.name: qsTr("demo spinbox")
         }
     }
 }
index d4f3ca0f7b76f24ce14f09ac9ae2f443c6ead98d..c7d3aef7d18b93706dcd2df4520eaa403f8fcaaa 100644 (file)
@@ -25,6 +25,7 @@ ScrollablePage {
 
             wrapMode: TextArea.Wrap
             text: qsTr("TextArea\n...\n...\n...")
+            Accessible.name: qsTr("demo text area")
         }
     }
 }
index ba61145ec9e87aef5fe3c15c8ff109e1b72bc005..5ec946f9395bae63d0d7f4000e1f02c218296d72 100644 (file)
@@ -22,6 +22,7 @@ ScrollablePage {
             enabled: !GalleryConfig.disabled
             placeholderText: qsTr("TextField")
             anchors.horizontalCenter: parent.horizontalCenter
+            Accessible.name: qsTr("demo textfield")
         }
     }
 }
index c7107ae2e77a72765ddf1d2cf034e17dfcd42380..f092308b1f67f472228f40e19b4e9efb37f8435d 100644 (file)
 # Resource object code (Python 3)
 # Created by: object code
-# Created by: The Resource Compiler for Qt version 6.10.0
+# Created by: The Resource Compiler for Qt version 6.10.2
 # WARNING! All changes made in this file will be lost!
 
 from PySide6 import QtCore
 
 qt_resource_data = b"\
-\x00\x00\x0b\xbc\
+\x00\x00\x0b\xe1\
 \x00\
-\x004\xd3x\x9c\xbd\x1b]s\xdb\xb8\xf1]\xbf\x02\xa7\
-\xbe\xc8\x97\x88\x8a\x9d&\x0f\xba\xa67\xb6\x9c\x0fO\x9d\
-&\xb1\x9dKg2y\x80HHBM\x114\x00\xda\
-V\x12\xff\xf7\xee\x82\x04\xc5\x0f\x80\x96\xe9\xbbb&\x16\
-I,v\x17\x8b\xc5~\x01\x99L\xc8L\xa4\x1b\xc9\x97\
-+MF\xb3=r\xf0\xec`\x9f\x5c\xac\x18\xf9\xa4\xa1\
-g\x9d\xd2dCNu\x14\x0c&\x13r\xfe\xf1\xf8?\
-\xe3S\x1e\xb2D\xb1\xf1I\xc4\x12\xcd\x17\x9c\xc9))\
-\xbe\x9d\xb1\xc5\xf8\x93\x1e\xc3\xb05\x93!\xa71\xf9p\
-F\x8e\xce\x8f\xc7\xcf\xc7\xb3\x98f\x8a\x0d\x06\xa9\xa4\xcb\
-55\x98E\x02\x08\x8e\xd8\x8a^s\x018\x8eD\x96\
-D\x83\x01\x87\x0e\xa9\x81\xfaLH\xb6}\xfb\x94\xf1\xf0\
-\xb2\xf1\x1a\x9c\xd2\x8d\xc8\xb4j~\x9e\x89DK\x11\xab\
-\x12\xd90\x18\x12\xaa\xc8a\x9a\x0e\x06\xf0'\xe6!\xd5\
-\x5c$_x\x12\x89\x1b\xf2c@\xa0\xf1hJn\xcc\
-\x07\xf3z\xc3#\xbd\x9a\x92\xe7/\x9f\x99\xd7\x15C\x01\
-M\xc9\x8b\x83\xfc\xfd\x9a+>\x8f\xd9\x94h\x991\xf3\
-Es\x8d\xefW\xeaB\x8e\x86 ;\xc3\x0a\xb1\xac\x0c\
-\xf7\x06\x06j2\xf9\x85|\x15\x92\xc3\xcc\x0d\x0b\xdf\xcc\
-W\xc9h$\x92xCR)R&\xf5\x86\xcc\x85\x88\
-\x09\xf2.)\xd7\xefE\x04\x98\x7f\xa9\x0c\x9b\xadXx\
-y$n\x83\x10\x1fXD~\xfe,\x98\x0f\x0c\xe3\xe4\
-\x1f\xf65g\xdcC\xdb|^dI\x88\xaf0\xc78\
-\x1d\xed\x15\xe2\xc0\x163M\x22\xae\xd2\x98nx\xb2,\
-\xa6B^\x91\x98+\xfd\x07g7A\x98I\x09\xd8N\
-\x92\x88\xdd\x92_^\xbd\x22\xe3\xfd\xda\xe0\xa2\xbf\x18\xf9\
-o\xbaf0\xba\x85\xb1\x1c\x82\xed\xf7-\xf65L;\
-\x0e\x96L\x8f\x9c\x04\xf7\x02#\xf2@\x8bSq\xc3\xe4\
-\x8c*\x06\xdcO\xc9pX\xe3!\x93\xc8\xf2p\xa5u\
-\xaa\xa6\x93I$\xc2\xe0J\x07\x5cL\xae\xf4\xf8\xe5d\
-X#\xfe\x84\x8c\xba\xb93\x0c\x0e\xaf\xd6\xf1\xf8J_\
-\xe1\x02\x8f\xc3b\x81\x0f\xc6C\x18\xee\x98\xf0\x13\xd0\xbe\
-\x95^\xc7\xc3&\x22\xe0\xb4\x8d\x04P\xc3\xb43\x98\x96\
-\x19\xb3\xf7[9\xea\x93\x0e@7\x92\xcf2~}\xab\
-\x99Lh\x1coF0\xb9=\x03q7(\x14\x09\x10\
-J\xd0\x87R\x91\xae\xa9$\xf3\x8c\xc7 \xb3s\xbd\x89\
-\x99\xca\x01\xcf\x99\xd60GUYm\xdc\x00\xaa\xf8\x5c\
-~,\xf1(-\xa1\x03~\x00G\x95\xe2\xf9\x0a\x944\
-\xcct\x05\x91\x02&X\x1225%_\x87\xafU8\
-|J\x86G4\xbc\x1c~+AXBa\xf7 A\
-\x0d\x1dfi#\x96\x82\xde\xfe\x93l5H$\x87\xa0\
-\x98\xd7T#`\x02Vb\x09\x8f\x88\xe8\xd0\xe8k\x00\
-\x1c-\x97L\x8e\xf6\x1e\xc0\xcf\xb9\xa6IDe\xf4/\
-\xb6\x09\xde\x81\xbe\xe3J\xc1\x82\xa9on\xaa\xe5&\xc2\
-\x9dQ%\x93\xb3\xd0\x90^\x9b\xc7m7,q\x90\x80\
->8\xa7\x8cJ5G\x09\xa1RD\x92\x82:\x0f+\
-\x0c]\xe4\x13E\x86~\xd4\xb4\x88/\xc8\xc8\x81n\xaf\
-\x01f\x84P\x82\xa5\xc2\xce\xa5\xda\xdc{\xba\xb6\xa3\xcd\
-\xf4\x09\x8b\x15s\xe0\xcf\xb96*\xda\xc0~7\xa8?\
-uIP\xa4\xf8Q\xbdgI\xe6\x17\xe0p\x0d\xdd\x1e\
-\xf9T\x10Ty)h\xae\xc0\xce\xa2\xbb\x02\x1f\x10\x5c\
-\x80\x85=\x82\xcd\xb1e\xe0L\xdc\xe4\x0e\xa51=\x95\
-\xd2\x10t\x7fJ\x0a\xdbo\x1bMBP6\x15,x\
-\x1cOIJQlN\x80\x98-\xf4{*\x97<\x01\
-+^\xa8T\xd5\xb6\xc3\xf2\x17\xe2\xcb\x8d\xf7\x94\x80'\
-d\x0b\x9e\xb0hPChx\xce\xb4\xae\xc9\xad\xa4f\
-\x04\xd6\xa9\x85\xb6\x95\xde\xcb\xc1Lc\xedj\xaf\xa7t\
-\xceb\x07i\x5c:c\x8c\x0d@\xab[\xb3[p\x9d\
--\x0d\xd3l\x0d3o\x1bw\xfc\x0e\xde\x1a\x7f\x8fY\
-\xccp.`\xeb\x01\x07\xb1\xee\xf5-\x18?&7\xc3\
-\xb6\x1a/`?\x07)\xbfe\xf19\xff\xceZK\x86\
-\x8d\xc5\x1c\xdd\xa9a5x\x8d/g\xa5\x8b\xac6X\
-:\xfe\x1d\xd0\xd1\xf80\xe6\xcb\x04\x94\x0e&\x01V\xd8\
-\xbc\xbd\x9b\xc1+\x93m\xc9\x82\xb1\x84\xe8\xc25\xe4\x0f\
-\xcf\x90\x5c\xe7\x8c\x16}\xc9\x83\x8e2\xa6\xf0,\xc3N\
-j\xd0\xdeJ-P\xecs`\xc0\xd6\xd8\x8cN\x98[\
-\xab\xf4\x85\xda\x8e\xf3\xa0\xc9\x09\x0b\xea\x95\xa8\x85\x90\xeb\
-\x0f\xb0Wq\x1f\x98\x1dz!\xd2\x5c\xf6\xce1-\x0b\
-\xd1Bj\xf4*W\x09\xeb\xd0\x1c:a[\xcdPX\
-Ow\x0cQ\xaaX\xba\xec\x96mw\x8fg\x0e=\xcd\
-\xae\x8c\xb5]\xce_\xc0\xd0\xe1\x1cTnW\x8e(\x02\
-\xf7\x91\xd3\xdd\xc0\xfdV\xf3\x03\xc7\xc6\xf25\xfc@n\
-\x0e\xb7jQD\xe3\xef\xa9^\x05k\x9e\x8c\xaa\xb1\xee\
-\xd3z\xa8\xbbG&\xe49\xf9\x95\x1c\x94\x83m\xec\xde\
-\x8e\x88\xb1A\xb8E\xe3nK\xc8q\xdf\xe2\xbe\xbav\
-\x9bL\xb4bM7\xfc\x0a\xa2\xe1}\x0cG\x17\x14\x1c\
-\xe66\x9e\x12\x8a\xe7\xfb\xd3\x8d\xe7\x19\x8c\xd8z\xdc\xd2\
-N\xbb\xbc\xc6V:\xa7\x85\x05mF\x07\xd1\xd6\xe8\xd6\
-w\xd8B\x84\x99r\xd8\x99\xaa\xef\x9f6]\xbf\xd3\xdd\
-\xd5 L\xc0>5\xec \x83.O\x81}\xafc\x86\
-\x96\x91\xfc\xa8\xe7LG\x99\xda\x00e\xcc\xcd\x84\x84\xc8\
-\x97(\x91\xc9\x10\xfd\xfd\x95\x0c\xa7\x93\x94.\x99\x9a\xd4\
-\x80>\xc2\xa7\x00\x22\xe6\xa1C\xf7:\x09\xa1\xe9\xf4S\
-\xc0\xde\x9e\xa8m^\xe6Cn\xfb\xfb\xa2\x17\xeb\xb9\xe8\
-B_\xf4\xf7D\x0f\xde\x96n\xba\xa5S\x01\xe9K\x04\
-\x0c\x89\x17;\xf4=\x02\xadXv!\x16\xcb\xfeb1\
-A\x88\xea\x10\x8a\x01\xe8\x89\xff\x8d\x84\xb0\xd6\x87\xdbt\
-\xf6D\xfcV\x8a,\xedP\x17\xdb\xdf\x13=\xbap\x88\
-\xa0}\xd8\x8b\xee\xbe\xc8!\xf2Z\xbd\x95<\xf2\xa2\xb7\
-\x00=\x09\xe0\xb0{\xcdM\x0d\xa8/!)\x96\x92)\
-\xd5!\xaa\x0aHO\x22g4\xe2\xa2{\xebV@z\
-\x13I\x96\xec\x1c#f\xefL* =\x89\x9c\x87R\
-\xc4q\x87\xb0J\x80G\x11\xb8w\xe9\x1b`}\x891\
-*\xc3\xd5\x1b\xceb\xaf\x22W@\xfa\x12\xe9\x5c\x92\xc7\
-\xadF\xca\x93\x0e\x0bRt\xf7F\x1es\x13\x9d\xf8\xd1\
-\x17\x00}\x09\xd8\xa0\xccK\xc0\x02\xf4%p\xc3S\xd6\
-I\xc0\x02\xf4'\xa0\xc3U\x07v\xe8\xed\x89\xfa\x82\xce\
-;vY\xde\xdb\x1fu\xdc)\x96\x12\xa0/\x01\xc8e\
-\x0e%\xa3^\xfcE\xff#\xd0w\xee\xd9\x12\xa0/\x81\
-\xbc\xf8\xe4E\x9fw?\x02\xf9\x05O\xbb\x90Cw_\
-\xe4\x92u\xafl\xd1\xdf\x17}\xb6\x06\xcd\xf0\x0b&\xef\
-\xf6!o\xd4I\xa2\x22.\x9b\xd6jI\x9e\xfa\x95\x05\
-F\xd0\x16@\x91\x7f\xdaD+\xb8\xc6?\xeejG\x9e\
-f\x9bI\xb5KJ\x90y\xc6\x98}bZ]\x22\xe3\
-j\xb6\xad|\xb5K!\xed\x9a>\xe4\xa3\xf0\x0fr\xb4\
-\x1d`\xb1\xfeo\xf2\xb2\x1d`\x8b\x1a\xbf\x9bw/x\
-\xbeNm\xbeE2\x8b9\x1eN5\xab\xd6\xb6\xf9\xaa\
-\xd7\xde\xaa\x84d:\x93\x8e:\x166_\xfd\xda-'\
-l\x95\x8ax\xa6V\xa3|\x1an\xe2\xc8\xa9#\x03\xf7\
-sZ\x94s\xc3X\xe0\xa1\xd4\xbd\x85\x91\xdak#\xf0\
-\x08lIq\xda\xec\x81\xbd\xe3\xa9\xa9\x94n\xady\xbc\
-c\xbfo)v\x16\xb1\xff\xd4\x026O\xb8\x864\x0c\
-\x95|J>\xd2\xa4\xb9\x11\x91\xc1\x14>\xd7\xa5q\xb2\
-\x86\xbd\xee\xd9\xb2\x90\xd2\x09\xdfVEL\x01\xbd\xa6<\
-FWc\x0a\xabdR)\x0a\xd9f\x8bCu\xf8w\
-\xe6\xabs\x80\x95Ih\xaa\xb9'\x89\xb3\xf2_\x05\xb4\
-\xeb\x97\x97\x7f?,\x16\x8a\x01\xbd\xf1\x8bve\x1a\xd7\
- ?\xeb5\xb3\x0e>BN\xc0\xe45;T)\x0b\
-\xc1\xdf\xb4i\x946\x92\xaf\x8d}\xbc\xd2c\x14J\x90\
-&\xcba\x97\x8e\xf9\x0a\xf9\xd5\x1aa\xeb\x0c\x1bw\xfe\
-5\xc4\x92\x8aP,\x9f\x12\xb1 \xf6\xe0\x92\xe8\x15\xd5\
-$\xa4\x09\x993\x92)\xb0\x12Z\x98c\xc7\x08@\xd6\
-i\xcc\xc0\xec\x9a*\xda\x82\x860\x9e'\xc4b\x0f\x1c\
-UH+\xb9\xb5Q9\xe5\xac\xe2[\x18-\xd2\x5c\x11\
-\x82\xb9\x80\xd4\xa6m\xba\xab:\x5c\x16\xac\xf1\xc5\x0b)\
-\xadF\x18P\xe9<\x1c\xb0\xb09\xd1)\xa1R\xc2\x9e\
-\x00fv;F\xc8\x0f\x1f\x1ez\x92P\x19\xe5;L\
-\xb8\x914\xcdU(\x07\xfe\x02\xef]\x8a\xd0\xb5\xbd\xcc\
-\x9c\xeeU\xb9|\xe6-\x85\xab\x0aiw\xd1[q\x16\
-\xb0\x9e%}\xc0\xf1U\xfd\xc9\x96\x9cM5\xc8s\xf4\
-\x9dw\x96]\xb7E\xd5Y\xe2\xed\x94Q\xad\xf0l\x0f\
-<\xb0\xe2|\xb0\xd5\xe2MmD\xad\xe8\x0c\x80/\xb7\
-\x80E\xe5\xb9V\x85uUf\x1b\x09ey\xceQ\x02\
-\xa8\xe2P;O\xeeaxQ\xac\xffpI~\xda\xe7\
-\x19\x88\xb8\x12\x04\xe0)w\x08\x1e\xb7\xed\x9d\xad\x1c\x02\
-s\xd4\x0f\x1e\xd4\xfc\xe2]\x93\xe2j\x04\xc6\xbe\xb5\x11\
-\xc5i\x1c\x98\x89\x05_\x22\x949\xdb\xcfoz\xe0\xa3\
-\xb5\x1f\xcdK+N\xb2\x05\xbbM\xcfyW\xe1\xfc\x8c\
-\xfd\x17\xcc\xa1\x83s\xcbh#\x04(\xbf\x9b\x87\x93V\
-@p/\xe9\xf21\xcco\x0b\xe4.l&\xe2l\x9d\
-8O\x8c\xab\xfa\x94\x83\xd5\x09V\x0e\x94k\x1d\xbe\x13\
-\xe8\xda\xa0\xfdg\xed\x08\xc8g\xcf\xb1\xd5N\xc9P\x02\
-S\x87\xc9\xbdk\xe3\xb4\xc5\xe4\x8e\x13B+Y'@\
--\x5c\xdd\x8a\xbeu\xae`[qzPl\x98\xfa\x9d\
-\x15\x17|y\x85,\x80H\xb3p2\xdeX\x13\xdb\x96\
-\x07P\x0a\x08N\xa2Q]\xd7\x9f\xe2Q-l],\
-\xc6\xdc\xb2\xe8\xdc\x84\xb7\xfe\x18/\x0f_K\x94\xf9\xf5\
-'?86\x97b\xb6\xf5\xd16\xf79\xdf\x0e\xc7\xc5\
-\xed\xc1w\xbb\xeb\x19\xaek(b!\xcfa\x9b\xae\x1d\
-\xb2\xc7{\x80b\xcdJ\x0f\x9eO\x03\xa3\x03\x0e\xaeN\
-\xf3\xef\x0c\xa2\x01\x06\xc1\xba\x89Z\xf85\x83\xd4J\x81\
-\xcb\x22j\xa3\xf0h\x7f\x99A\x0c\x11Cl\xe8D-\
-\xc0\xef\x99\xf1\x05tJa]5\x0bZ\xb0\xe5\x15\xa2\
-\xaf\xc37q\x06R\xfd\xc2\x93\xcf'\xcf\xf1\xb2\xd1\x9b\
-Lq\x91\xe0\x13\xffp\x9e\xdf>R<\x1c~\x0bx\
-\x12\xc6\x19p3j\x9a\x0a\xb4i\xed\xa5\xb3\x06\xabc\
-\x07\xd0L\x8b\xd9VZN\xb8\xc2\xdcy\x16\x0a[\xed\
-h\x160:7h\x0f\xee\xc0)\x5c\xde\xc7]\x95\xf6\
-1\xc0\x13\xf4\xa2\x7f\x16\x03&\xd9}\x08\x07\xa7\xc6M\
-\xee\xccB\xee\xf1\xcc9\x86\x87\x0bv\x0bK\xae\xcc\xc9\
-\xadW\xfcs\xeb7\xabz\x0f^\x0a\xc2WP\x0e\xe7\
-\x90\xe2\x0a&+|\xeel\x85u\xefN\xeb\x83w\x11\
-\x95A\xfd\x9b\xdfB\x99z\x1e\x19\x85U\xdc\xae\xdb\x5c\
-\xd5\x16R\xc5\x9az8\xed\x1caH\x1980B`\
-\xf3>'\x97\x89\xb8qO\xb4\xda\xe6\x92\xd1K?\xf7\
-%3\x0d\xb5{\x183\xa8\x83\x7f\x1a'M\xfd{\x18\
-+\xa7\xce\xd0\xff\x81\xbc\xb8\xad86\xa0`\xec\xd0;\
-p\x90\xa0n[.\xd15\xf8\xb7\xcb\xfd\xf7,j\xaf\
-\x1d\xbb\xd4\x5c\xf2i_*\xee\xcc\x0a_\x1b\xb3\x0b^\
-(\x89THS\xd76-\xed]\xfd\xea\x83m\x0f\xbf\
-\xeft\xcf\x14<a\xa6\x9f/w\xc0\xda9\xed\xe3\x1c\
-\xa8z\x9f\xfb\xf1\xf3\xda%\x0b?c\x10\xddK]\x96\
-\xdf\x5c\xf2F\xcd\x81\x84\xeco\xec\xef\xfb\xec\xe0E;\
-\x0f\x13\x18:\xea\xcd\xd4\x13!c\xe4\xe2\x88\x91\xc9\xef\
-d?\xc0\xfb(\xcf\x82v\xfa\xfd\xff\xcdjw\x8cy\
-*`\xef\x8a\xb2\x8ec\x09\xeaO])a\xe5\xe2\xd3\
-\xe3\x92\xb6\xe6u+\x08\x82\xfd\x99d5\x91le\x8f\
-e\xe7\xa3nD\x15y\x8c\x15\x92\x99g\x9e\xa7\xd8\xdb\
-Q%h\xfe\xd9\x91\xdfT\x06\xed\x96\xdc\xf8\xb4\xbd\x98\
-I\xf5\x96Y\xbdn\xd7\xb9?\x8a\xff\xa9\xd2\xa8T\xe5\
-w\xe8\xb1\xa6\x0f\x9e_*\x13P&x\x83t\xc9\x12\
-&\x8d\xb5\xc32\x95\xdc\x16\xa5\xb6\xa5\xac9\xc5\xfa\x95\
-\xe8.Q\xdd[g\xc9\xf5\xa2~\x19u\xff\xa0\x8f5\
-x\x9c|N\x12S\x81\xa3\x92+\x98\x92\x16\x0ea\xed\
-?u|l[\x11lO\xc8\x90J\x886\x12\xf0\x1b\
-\x11\x88O,\xc8\x9a.\x13\xae!\xaa&\x8ac\x12&\
-\x9f\xe6^\x17\x1f\xc0I\x80\x13\x808^\xfeu2\xac\
-?\xdd\x0d\xee\x06\xff\x03\xeeC\x8b\x0c\
+\x005Ex\x9c\xbd\x1b]W\xdb:\xf2=\xbfB7\
+\xfb\x12n\x1b\xa7\xd0m\x1f\xd2\xed\xde\x03\xa1\x1f\x9c\xa5\
+K\x0b\xf4v\xcf\xe9\xe9\x83b+\x89\x16\xc72\x92\x0c\
+\xa4-\xff}gd\xcb\xf1\x87d\xc0\xf4\xae\xce)\xb1\
+\xad\xd1\xcch4\x9a/\xa9\x93\x09\x99\x89t#\xf9r\
+\xa5\xc9h\xb6C\xf6\x9e\xed\xed\x92\xf3\x15#\x9f4\xf4\
+\xacS\x9al\xc8\xb1\x8e\x82\xc1dB\xce>\x1e\xfeg\
+|\xccC\x96(6>\x8aX\xa2\xf9\x8239%\xc5\
+\xb7S\xb6\x18\x7f\xd2c\x18\xb6f2\xe44&'\xa7\
+\xe4\xe0\xecp\xfc|<\x8bi\xa6\xd8`\x90J\xba\x5c\
+S\x83Y$\x80\xe0\x80\xad\xe8\x15\x17\x80\xe3@dI\
+4\x18p\xe8\x90\x1a\xa8\xcf\x84d\xdb\xb7O\x19\x0f/\
+\x1a\xaf\xc11\xdd\x88L\xab\xe6\xe7\x99H\xb4\x14\xb1*\
+\x91\x0d\x83!\xa1\x8a\xec\xa7\xe9`\x00\x7fb\x1eR\xcd\
+E\xf2\x85'\x91\xb8&?\x06\x04\x1a\x8f\xa6\xe4\xda|\
+0\xaf\xd7<\xd2\xab)y\xfe\xf2\x99y]1\x14\xd0\
+\x94\xbc\xd8\xcb\xdf\xaf\xb8\xe2\xf3\x98M\x89\x96\x193_\
+4\xd7\xf8~\xa9\xce\xe5h\x08\xb23\xac\x10\xcb\xcap\
+g`\xa0&\x93\xdf\xc8W!9\xcc\xdc\xb0\xf0\xcd|\
+\x95\x8cF\x22\x897$\x95\x22eRo\xc8\x5c\x88\x98\
+ \xef\x92r\xfdAD\x80\xf9\xb7\xca\xb0\xd9\x8a\x85\x17\
+\x07\xe2&\x08\xf1\x81E\xe4\xe7\xcf\x82\xf9\xc00N\xfe\
+a_s\xc6=\xb4\xcd\xe7E\x96\x84\xf8\x0as\x8c\xd3\
+\xd1N!\x0el1\xd3$\xe2*\x8d\xe9\x86'\xcbb\
+*\xe45\x89\xb9\xd2\x7frv\x1d\x84\x99\x94\x80\xed(\
+\x89\xd8\x0d\xf9\xed\xf5k2\xde\xad\x0d.\xfa\x8b\x91\xff\
+\xa6k\x06\xa3[\x18\xcb!\xd8\xfe\xd8b_\xc3\xb4\xe3\
+`\xc9\xf4\xc8Ip'0\x22\x0f\xb48\x16\xd7L\xce\
+\xa8b\xc0\xfd\x94\x0c\x875\x1e2\x89,\x0fWZ\xa7\
+j:\x99D\x22\x0c.u\xc0\xc5\xe4R\x8f_N\x86\
+5\xe2O\xc8\xa8\x9b;\xc3\xe0\xf0r\x1d\x8f/\xf5%\
+.\xf08,\x16xo<\x84\xe1\x8e\x09?\x01\xed[\
+\xe9u<l\x22\x02N\xdbH\x005L;\x83i\x99\
+1;\xaf\xcaQ\x9ft\x00\xba\x91|\x96\xf1\x9b\x1b\xcd\
+dB\xe3x3\x82\xc9\xed\x18\x88\xdbA\xa1H\x80P\
+\x82>\x94\x8atE%\x99g<\x06\x99\x9d\xe9M\xcc\
+T\x0ex\xc6\xb4\x869\xaa\xcaj\xe3\x06P\xc5\xe7\xf2\
+c\x89Gi\x09\x1d\xf0\x038\xaa\x14\xcfV\xa0\xa4a\
+\xa6+\x88\x140\xc1\x92\x90\xa9)\xf9:|\xa3\xc2\xe1\
+S2<\xa0\xe1\xc5\xf0[\x09\xc2\x12\x0a\xbb\x07\x09j\
+\xe80K\x1b\xb1\x14\xf4\xf6\x9fd\xabA\x22\xd9\x07\xc5\
+\xbc\xa2\x1a\x01\x13\xb0\x12KxDD\xfbF_\x03\xe0\
+h\xb9dr\xb4\xf3\x00~\xce4M\x22*\xa3\x7f\xb1\
+M\xf0\x1e\xf4\x1dW\x0a\x16L}sS-7\x11\xee\
+\x8c*\x99\x9c\x85\x86\xf4\xda<n\xbba\x89\x83\x04\xf4\
+\xc19eT\xaa9J\x08\x95\x22\x92\x14\xd4yXa\
+\xe8<\x9f(2\xf4\xa3\xa6E|AF\x0et;\x0d\
+0#\x84\x12,\x15v.\xd5\xe6\xde\xd3\xb5\x1dm\xa6\
+OX\xac\x98\x03\x7f\xce\xb5Q\xd1\x06\xf6\xdbA\xfd\xa9\
+K\x82\x22\xc5\x8f\xea\x03K2\xbf\x00\x87k\xe8\xf6\xc8\
+\xa7\x82\xa0\xcaKAs\x05v\x16\xdd\x15\xf8\x80\xe0\x1c\
+,\xec\x01l\x8e-\x03\xa7\xe2:w(\x8d\xe9\xa9\x94\
+\x86\xa0\xfbSR\xd8~\xdbh\x12\x82\xb2\xa9`\xc1\xe3\
+xJR\x8abs\x02\xc4l\xa1?P\xb9\xe4\x09X\
+\xf1B\xa5\xaa\xb6\x1d\x96\xbf\x10_n\xbc\xa7\x04<!\
+[\xf0\x84E\x83\x1aB\xc3s\xa6uMn\xb6\xed\x87\
+\xa0\xe2\xc6+\x15r\xca]\xd1\xb9X.cV\x10\x18\
+\xb6\x17\x9e\x1a9w*\xafm\xa5\xd3s\xcc\xa1\xb1\xe4\
+\xb5\xd7c:g\xb1\x83c\x5cqc\xc3\x0d@\xab[\
+\xb3\x1b\xf0\xb8-\xc5\xd4l\x0d\x02k\xfb\x04\xfc\x0eN\
+\x1e\x7f\x0fY\xccp.\xe0\x22\x00\x07\xb1\xa2x\x076\
+\x93\xc9\x8dC\x08\x0b0\x03A\xcaoX|\xc6\xbf\xb3\
+\xd6Jcc1G/lX\x0d\xde\xe0\xcbi\xe9Y\
+\xab\x0dV\x9c\x7f\x07t4\xde\x8f\xf92\x01]\x85I\
+\x80\xf16o\xefg\xf0\xcad[\xb2`c!(q\
+\x0d\xf9\xd33$WU\xa3|_\xf2X\xa5\x0cE<\
+\xcb\xd0\xa9=V\x0d\xfc;\xd06\x8f\x9e\x9d\xc0n\xb3\
+\xa3\x89\xd9\xa1;\x83\xd6XD\xeb \x8e\xad\xb1\xfd\x9d\
+07v\x9b\x15\x1be\x9c\x87iNX\xd0\xccD-\
+\x84\x5c\x9f\x80u\xc0\x9dgl\xc2\xb9H\xf3es\x8e\
+i\xd9\xa4\x16R\xa3\x92\xf9\x84\xad\x0bu\xa8\x93m5\
+\xd3d}\xeb!\xc4\xc5b\xe9\xb2\x94\xb6\xdd>\x9e9\
+\xf4m\xf7e\xac\xed\xe4\xfe\x02\x86\xf6\xe7\xa0\xad\xf7\xe5\
+\x88\x22p\x1f9\xdd\x0e\xdco5\xcfshLa\xc3\
+\xf3\xe4\xf6q\xab\x16E\xfc\xff\x81\xeaU\xb0\xe6\xc9\xa8\
+\x1a]?\xad\x07\xd7;dB\x9e\x93\xdf\xc9^9\xd8\
+f\x0b\xed\x18\x1c\x1b\x04x4\xee6\xa2\x1c\xb7<n\
+\xc9+\xb7\xb5E\x03\xd8t\xfc\xaf!\xfe\xde\xc5\x00x\
+A\xc1Eo#8\xa1x\xbe\xb5\xddx\x9e\xc1\x88\xad\
+\x8f/M\xbc\xcbOm\xa5s\x5c\x18\xdff<\x12m\
+\xedu}\x87-D\x98)\x87\x89\xaaF\x1b\xd3f\xb0\
+\xe1t\xb05\x08\x93\x22L\x0d;\xc8\xa0\xcb\xc9`\xdf\
+\x9b\x98\xa1Q%?\xeaY\xdaA\xa66@\x19\xb3A\
+\x01\xae\xf1\x15Q\x22\x93!F\x18\x972\x9cNR\xba\
+djR\x03\xfa\x08\x9f\x02\x88\xd1\x87\x0e\xdd\xeb$\x84\
+V\xd7O\x01{{\xa2\xb6\x99\xa0\x0f\xb9\xed\xef\x8b^\
+\xac\xe7\xa2\x0b}\xd1\xdf\x13=8j\xba\xe9\x96N\x05\
+\xa4/\x110$^\xec\xd0\xf7\x08\xb4b\xd9\x85X,\
+\xfb\x8b\xc5\xc4/\xaaC(\x06\xa0'\xfe\xb7\x12\x1c\xb7\
+\x0f\xb7\xe9\xec\x89\xf8\x9d\x14Y\xda\xa1.\xb6\xbf'z\
+t\xe1\x10\xb3\xfb\xb0\x17\xdd}\x91C\xd0\xb6z'y\
+\xe4Eo\x01z\x12\xc0aw\x9a\x9b\x1aP_BR\
+,%\xc4h\x1d\xa2\xaa\x80\xf4$rJ#.\xba\xb7\
+n\x05\xa47\x91d\xc9\xce0\xd8\xf6\xce\xa4\x02\xd2\x93\
+\xc8Y(E\x1cw\x08\xab\x04x\x14\x81;\x97\xbe\x01\
+\xd6\x97\x18\xa32\x5c\xbd\xe5,\xf6*r\x05\xa4/\x91\
+\xce%y\xdcj\xa4<\xe9\xb0 Ewo\xe417\
+\xd1\x89\x1f}\x01\xd0\x97\x80\x0d\xca\xbc\x04,@_\x02\
+\xd7<e\x9d\x04,@\x7f\x02:\x5cu`\x87\xde\x9e\
+\xa8\xcf\xe9\xbcc\x97\xe5\xbd\xfdQ\xc7\x9db)\x01\xfa\
+\x12\x80\x5cf_2\xea\xc5_\xf4?\x02}\xe7\x9e-\
+\x01\xfa\x12\xc8\xcb]^\xf4y\xf7#\x90\x9f\xf3\xb4\x0b\
+9t\xf7E.Y\xf7\xca\x16\xfd}\xd1gk\xd0\x0c\
+\xbf`\xf2n\x1f\xf2F\x89%*\xe2\xb2i\xad\x0c\xe5\
+)}Y`\x04m\x01\x14\xf9\xa7M\xb4\x82+\xfc\xe3\
+\xaev\xe4i\xb6\x99T\xbb\x1a\x05\x99g\x8c\xd9'\xa6\
+\xd5%2\xaef\xdb\xa2Y\xbb\x14\xd2>E\x80|\x14\
+\xfeA\x8ev\x0fX<q0y\xd9=`\x8bS\x05\
+7\xef^\xf0|\x9d\xda|\x8bd\x16s<\x0ek\xd6\
+\xc9m\xf3\xd5\xcb\xbdU\x09\xc9t&\x13w\xb1\xc8W\
+1w\xcb\x09[\xa5\x06\x9f\xa9\xd5(\x9f\x86\x9b8r\
+\xea\xc8\xc0\xfd\x9c\x16\x05\xe40\x16x\x0cvga\xa4\
+\xf6\xda\x08<\x02[\x8d\x9c6{`\xefxj*\xa5\
+[k\x1e(\xd9\xef[\x8a\x9de\xf3_Z2\xe7\x09\
+\xd7\x90\x86\xa1\x92O\xc9G\x9a47\x222\x98\xc2\xe7\
+\xba4\x8e\xd6\xb0\xd7=[\x16R:\xe1\xdb\xaa\x88)\
+\xa0W\x94\xc7\xe8jLM\x96L*E!\xdblq\
+\xa8\x0e\xff\xde|u\x0e\xb02\x09M!\xf8(q\x9e\
+5T\x01\xed\xfa\xe5\x95\xe3\x93\xc5B1\xa07~\xd1\
+.j\xe3\x1a\xe4\xa7\xcbf\xd6\xc1G\xc8\x09\x98\xbcb\
+\xfb*e!\xf8\x9b6\x8d\xd2F\xf2\xb5\xb1\x8f\x97z\
+\x8cB\x09\xd2d9\xec\xd21\xdf\x19@\xb5F\xd8:\
+5\xc7\x9d\x7f\x05\xb1\xa4\x22\x14\xcb\xa7D,\x88=*\
+%zE5\x09iB\xe6\x8cd\x0a\xac\x84\x16\xe6\xa0\
+3\x02\x90u\x1a30\xbb\xa6\x8a\xb6\xa0!\x8c\xe7\x09\
+\xb1\xd8\x03\xd7!H!\xb9\xb5Q9\xe5<\x00\xb00\
+Z\xa4\xb9\x22\x04s\x01\xa9M\xdbtWu\xb8,X\
+\xe3\x8b\x17RZ\x8d0\xa0\xd2y\xae`as\xa2S\
+B\xa5\x84=\x01\xcc\xdc\xef\x04\x22?\xb7x\xe8!D\
+e\x94\xef\x1c\xe2Z\xd24W\xa1\x1c\xf8\x0b\xbcw)\
+B\xd7\xf62s\xbaS\xe5\xf2\x99\xb7\x14\xae*\xa4\xfb\
+\x8b\xde\x8a\xb3\x80\xf5,\xe9\x03N\xbe\xeaO\xb6\xe4l\
+\xaaA\x9e\xc3\xf6\xbc\xb3\xec\xba)\xaa\xce\x12\xef\xc3\x8c\
+j\x85g{\xe0\x81\x15\xe7\xbd\xad\x16oj#jE\
+g\x00|\xb9\x05,*\xcf\xb5*\xac\xab2\xdbH(\
+\xcbs\x8e\x12@\x15\xc7\xe8yr\x0f\xc3\x8bb\xfd\xc9\
+\x05\xf9i\x9fg \xe2J\x10\x80\xe7\xea!x\xdc\xb6\
+w\xb6r\x08\xcc\xe5\x02\xf0\xa0\xe6\x17o\xb7\x14\x971\
+0\xf6\xad\x8d(\x0e\xf2\xc0L,\xf8\x12\xa1\xccm\x82\
+\xfcn\x09>Z\xfb\xd1\xbc&\xe3$[\xb0\xdb\xf4\x9c\
+\xb7\x15\xceO\xd9\x7f\xc1\x1c:8\xb7\x8c6B\x80\xf2\
+\xbby8j\x05\x04w\x92.\x1f\xc3\xfc~B\xee\xc2\
+f\x22\xce\xd6\x89\xf3\x8c\xba\xaaO9X\x9d`\xe5\x08\
+\xbb\xd6\xe1;\xf3\xae\x0d\xda}\xd6\x8e\x80|\xf6\x1c[\
+\xed\x94\x0c%0u\x98\xdc\xdb6N[L\xee8!\
+\xb4\x92u\x02\xd4\xc2\xd5\xad\xe8[\xe7\x0a\xb6\x15\xa7\x07\
+\xc5\x86\xa9\xdf\x92q\xc1\x97\x97\xd6\x02\x884\x0b'\xe3\
+\x8d5\xb1my\x00\xa5\x80\xe0$\x1a\xd5u\xfd)\x9e\
+\xf2\xc2\xd6\xc5b\xcc\x0d\x8b\xceLx\xeb\x8f\xf1\xf2\xf0\
+\xb5D\x99_\xb8\xf2\x83cs)f[\x1fms\x9f\
+\xf3\xdd\xe3\xa4\xb9=\xf8\xf6\xfez\x86\xeb\x1a\x8aX\xc8\
+3\xd8\xa6k\x87\xec\xf1\xe6\xa1X\xb3\xd2\x83\xe7\xd3\xc0\
+\xe8\x80\x83\xab\xd3\xfc;\x83h\x80A\xb0n\xa2\x16~\
+\xc5 \xb5R\xe0\xb2\x88\xda(\xbc\x15\xb0\xcc \x86\x88\
+!6t\xa2\x16\xe0\xf7\xcc\xf8\x02:\xa5\xb0\xae\x9a\x05\
+-\xd8\xf2\xd2\xd2\xd7\xe1\xdb8\x03\xa9~\xe1\xc9\xe7\xa3\
+\xe7x\xbd\xe9m\xa6\xb8H\xf0\x89\x9f\x9c\xe5\xf7\x9d\x14\
+\x0f\x87\xdf\x02\x9e\x84q\x06\xdc\x8c\x9a\xa6\x02mZ{\
+\xe9\xac\xc1\xea\xd8\x014\xd3b\xb6\x95\x96\x13\xae0w\
+\x9e\x85\xc2V;\x9a\x05\x8c\xce\x0d\xda\x83;p\x0a\x17\
+wqW\xa5}\x08\xf0\x04\xbd\xe8\xafb\xc0$\xbb\x0f\
+\xe1\xe0\xd8\xb8\xc9{\xb3\x90{<s\x8e\xe1\xe1\x82\xdd\
+\xc0\x92+sr\xeb\x15\xff\xdc\xfa\xcd\xaa\xde\x83\x97\x82\
+\xf0\x15\x94\xc39\xa4\xb8\xf4\xc9\x0a\x9f;[a\xdd\xbb\
+\xd3\xfa\xe0\xedGeP\xbf\xf2[(S\xcf#\xa3\xb0\
+\x8a\xdbu\x7f\xac\xdaB\xaaXS\x0f\xa7\x9d#\x0c)\
+\x03\x07F\x08l\xde\xe7\xe4\x22\x11\xd7\xee\x89V\xdb\x5c\
+2z\xe1\xe7\xbed\xa6\xa1v\x0fc\x06u\xf0\x97q\
+\xd2\xd4\xbf\x87\xb1r\xec\x0c\xfd\x1f\xc8\x8b\xdb\x8ac\x03\
+\x0a\xc6\x0e\xbd\x07\x07\x09\xea\xb6\xe5\x12]\x83\x7f\xbb\xdc\
+}\xcf\xa2\xf6\xda\xb1K\xcd%\x9f\xf65\xe6\xce\xac\xf0\
+\x8d1\xbb\xe0\x85\x92H\x854um\xd3\xd2\xde\xd5\xaf\
+>\xd8\xf6\xf0\xabRwL\xc1\x13f\xfa\xf9r\x07\xac\
+\x9d\xd3>\xcc\x81\xaa7\xc8\x1f?\xaf\xfbd\xe1\xa7\x0c\
+\xa2{\xa9\xcb\xf2\x9bK\xde\xa89\x90\x90\xfd\x8d\xfd}\
+\x97\xed\xbdh\xe7a\x02CG\xbd\x99z\x22d\x8c\x5c\
+\x1c12\xf9\x83\xec\x06x\x1f\xe5Y\xd0N\xbf\xff\xbf\
+Y\xed=c\x9e\x0a\xd8\xfb\xa2\xac\xe3X\x82\xfaSW\
+JX\xb9\xf8\xf4\xb8\xa4\xady\xdd\x0a\x82`\x7f&Y\
+M$[\xd9c\xd9\xf9\xa8\x1bQE\x1ec\x85d\xe6\
+\x99\xe7)\xf6vT\x09\x9a\x7fv\xe47\x95A\xf7K\
+n|\xda^\xcc\xa4z\xcb\xac^\xb7\xeb\xdc\x1f\xc5\xff\
+\x8diT\xaa\xf2[\xfbX\xd3\x07\xcf/\x95\x09(\x13\
+\xbc|\xbad\x09\x93\xc6\xdaa\x99Jn\x8bR\xdbR\
+\xd6\x9cb\xfdJt\x97\xa8\xee\xac\xb3\xe4zQ\xbf\xc7\
+\xba\xbb\xd7\xc7\x1a<N>G\x89\xa9\xc0Q\xc9\x15L\
+I\x0b\x87\xb0v\x9f:>\xb6\xad\x08\xb6'dH%\
+D\x1b\x09\xf8\x8d\x08\xc4'\x16dM\x97\x09\xd7\x10U\
+\x13\xc51\x09\x93Os\xaf\x8b\x0f\xe0$\xc0\x09@\x1c\
+/\xff:\x19\xd6\x9fn\x07\xb7\x83\xff\x01f\x8c\xabf\
+\
 \x00\x00\x00`\
 [\
 Material]\x0aPrimar\
@@ -222,7 +225,7 @@ odule App\x0asingle\
 ton GalleryConfi\
 g 1.0 pages/Gall\
 eryConfig.qml\x0a\
-\x00\x00\x03*\
+\x00\x00\x03\x5c\
 /\
 / Copyright (C) \
 2017 The Qt Comp\
@@ -273,8 +276,11 @@ s.horizontalCent\
 er: parent.horiz\
 ontalCenter\x0a    \
         editable\
-: true\x0a        }\
-\x0a    }\x0a}\x0a\
+: true\x0a         \
+   Accessible.na\
+me: qsTr(\x22demo s\
+pinbox\x22)\x0a       \
+ }\x0a    }\x0a}\x0a\
 \x00\x00\x02\x8a\
 /\
 / Copyright (C) \
@@ -494,7 +500,7 @@ Indicator.vertic\
 al: ScrollIndica\
 tor { }\x0a    }\x0a}\x0a\
 \
-\x00\x00\x03U\
+\x00\x00\x03\xca\
 /\
 / Copyright (C) \
 2017 The Qt Comp\
@@ -540,16 +546,23 @@ ressBar {\x0a      \
  anchors.horizon\
 talCenter: paren\
 t.horizontalCent\
-er\x0a        }\x0a\x0a  \
-      ProgressBa\
-r {\x0a            \
-indeterminate: t\
-rue\x0a            \
-anchors.horizont\
-alCenter: parent\
-.horizontalCente\
-r\x0a        }\x0a    \
-}\x0a}\x0a\
+er\x0a            A\
+ccessible.name: \
+qsTr(\x22demo progr\
+essbar\x22)\x0a       \
+ }\x0a\x0a        Prog\
+ressBar {\x0a      \
+      indetermin\
+ate: true\x0a      \
+      anchors.ho\
+rizontalCenter: \
+parent.horizonta\
+lCenter\x0a        \
+    Accessible.n\
+ame: qsTr(\x22indet\
+erminate progres\
+sbar\x22)\x0a        }\
+\x0a    }\x0a}\x0a\
 \x00\x00\x06\x87\
 /\
 / Copyright (C) \
@@ -657,7 +670,7 @@ ntalCenter: pare\
 nt.horizontalCen\
 ter\x0a        }\x0a  \
   }\x0a}\x0a\
-\x00\x00\x02\xd4\
+\x00\x00\x03\x08\
 /\
 / Copyright (C) \
 2017 The Qt Comp\
@@ -703,8 +716,11 @@ talCenter\x0a\x0a     \
            text:\
  qsTr(\x22TextArea\x5c\
 n...\x5cn...\x5cn...\x22)\
-\x0a        }\x0a    }\
-\x0a}\x0a\
+\x0a            Acc\
+essible.name: qs\
+Tr(\x22demo text ar\
+ea\x22)\x0a        }\x0a \
+   }\x0a}\x0a\
 \x00\x00\x02\xec\
 /\
 / Copyright (C) \
@@ -792,7 +808,7 @@ JN\xa1\xe7I-2Ca\xf4KHN\x1cn]\
 \xe8\xd5\xed\xf5\xb0\xa1\xf9\xb7S\xfe\xffL\x9b\x7f\xc1o\
 o;\xf1\xb3\xe7\xec\xe5\x7f9\x1fC\xa3_\xc4\x97s\
 {s\xee\xcby\x8c\xfe\x02O\xe4\x17\xf2\
-\x00\x00\x03H\
+\x00\x00\x03\xb3\
 /\
 / Copyright (C) \
 2017 The Qt Comp\
@@ -833,20 +849,27 @@ fig.disabled\x0a   \
 anchors.horizont\
 alCenter: parent\
 .horizontalCente\
-r\x0a        }\x0a\x0a   \
-     Slider {\x0a  \
-          enable\
-d: !GalleryConfi\
-g.disabled\x0a     \
-       orientati\
-on: Qt.Vertical\x0a\
-            valu\
-e: 0.5\x0a         \
-   anchors.horiz\
-ontalCenter: par\
-ent.horizontalCe\
-nter\x0a        }\x0a \
-   }\x0a}\x0a\
+r\x0a            Ac\
+cessible.name: q\
+sTr(\x22demo slider\
+\x22)\x0a        }\x0a\x0a  \
+      Slider {\x0a \
+           enabl\
+ed: !GalleryConf\
+ig.disabled\x0a    \
+        orientat\
+ion: Qt.Vertical\
+\x0a            val\
+ue: 0.5\x0a        \
+    anchors.hori\
+zontalCenter: pa\
+rent.horizontalC\
+enter\x0a          \
+  Accessible.nam\
+e: qsTr(\x22vertica\
+l demo slider\x22)\x0a\
+        }\x0a    }\x0a\
+}\x0a\
 \x00\x00\x02x\
 /\
 / Copyright (C) \
@@ -1530,7 +1553,7 @@ bled\x0a           \
 d: true\x0a        \
     }\x0a        }\x0a\
     }\x0a}\x0a\
-\x00\x00\x02\x84\
+\x00\x00\x02\xb8\
 /\
 / Copyright (C) \
 2017 The Qt Comp\
@@ -1571,8 +1594,11 @@ placeholderText:\
 nchors.horizonta\
 lCenter: parent.\
 horizontalCenter\
-\x0a        }\x0a    }\
-\x0a}\x0a\
+\x0a            Acc\
+essible.name: qs\
+Tr(\x22demo textfie\
+ld\x22)\x0a        }\x0a \
+   }\x0a}\x0a\
 \x00\x00\x06S\
 /\
 / Copyright (C) \
@@ -1758,7 +1784,7 @@ k\x7f\x06\x1d\xea\x93\x91`\xbfA\xd8Ih\xdc>\xfe\
 c\x12z}\x8c\xef\xe5\xc1J\xff\x0e|G7\xe5)\
 \xfbu\xeb-\xaa\xafa7\x8c\x9fi\x22\xd9G\xc5\xbf\
 \x07\xef\xc1\xfb\x17\xe7\x1f\xdf\x92\
-\x00\x00\x02\xfb\
+\x00\x00\x03*\
 /\
 / Copyright (C) \
 2017 The Qt Comp\
@@ -1807,7 +1833,10 @@ alue: 0.5\x0a      \
 rizontalCenter: \
 parent.horizonta\
 lCenter\x0a        \
-}\x0a    }\x0a}\x0a\
+    Accessible.n\
+ame: qsTr(\x22demo \
+dial\x22)\x0a        }\
+\x0a    }\x0a}\x0a\
 \x00\x00\x05-\
 /\
 / Copyright (C) \
@@ -2150,7 +2179,7 @@ heckBox.checked \
 ysOn : ScrollBar\
 .AsNeeded\x0a    }\x0a\
 }\x0a\
-\x00\x00\x03\xbf\
+\x00\x00\x040\
 /\
 / Copyright (C) \
 2017 The Qt Comp\
@@ -2196,22 +2225,29 @@ ond.value: 0.75\x0a\
 ors.horizontalCe\
 nter: parent.hor\
 izontalCenter\x0a  \
-      }\x0a\x0a       \
- RangeSlider {\x0a \
-           enabl\
-ed: !GalleryConf\
-ig.disabled\x0a    \
-        orientat\
-ion: Qt.Vertical\
-\x0a            fir\
-st.value: 0.25\x0a \
-           secon\
-d.value: 0.75\x0a  \
-          anchor\
-s.horizontalCent\
-er: parent.horiz\
-ontalCenter\x0a    \
-    }\x0a    }\x0a}\x0a\
+          Access\
+ible.name: qsTr(\
+\x22demo range slid\
+er\x22)\x0a        }\x0a\x0a\
+        RangeSli\
+der {\x0a          \
+  enabled: !Gall\
+eryConfig.disabl\
+ed\x0a            o\
+rientation: Qt.V\
+ertical\x0a        \
+    first.value:\
+ 0.25\x0a          \
+  second.value: \
+0.75\x0a           \
+ anchors.horizon\
+talCenter: paren\
+t.horizontalCent\
+er\x0a            A\
+ccessible.name: \
+qsTr(\x22vertical d\
+emo slider\x22)\x0a   \
+     }\x0a    }\x0a}\x0a\
 \x00\x00\x05b\
 /\
 / Copyright (C) \
@@ -5166,112 +5202,112 @@ qt_resource_struct = b"\
 \x00\x00\x00\x00\x00\x00\x00\x00\
 \x00\x00\x00\x90\x00\x02\x00\x00\x00\x0c\x00\x00\x00\x0a\
 \x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x0c\xbc\
-\x00\x00\x01\x99{\x95\xd3\xc8\
-\x00\x00\x00,\x00\x00\x00\x00\x00\x01\x00\x00\x0b\xc0\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x00t\x00\x00\x00\x00\x00\x01\x00\x00\x0c$\
-\x00\x00\x01\x99\xa0\x15<\x1b\
+\x00\x00\x00\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x0c\xe1\
+\x00\x00\x01\x9b\xc6<\xa6)\
+\x00\x00\x00,\x00\x00\x00\x00\x00\x01\x00\x00\x0b\xe5\
+\x00\x00\x01\x99-^\x9a:\
+\x00\x00\x00t\x00\x00\x00\x00\x00\x01\x00\x00\x0cI\
+\x00\x00\x01\x9b\xc6<(c\
 \x00\x00\x00\x5c\x00\x02\x00\x00\x00\x01\x00\x00\x00\x09\
 \x00\x00\x00\x00\x00\x00\x00\x00\
 \x00\x00\x00\x10\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x00t\x00\x00\x00\x00\x00\x01\x00\x01\x1d|\
-\x00\x00\x01\x99\xa0\x15<\x1b\
-\x00\x00\x070\x00\x00\x00\x00\x00\x01\x00\x01\x09\xe6\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x07l\x00\x00\x00\x00\x00\x01\x00\x01\x19\xca\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x06r\x00\x00\x00\x00\x00\x01\x00\x00\xd0@\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x07P\x00\x00\x00\x00\x00\x01\x00\x01\x0e\xa5\
-\x00\x00\x01\x87,\x09\xa2X\
-\x00\x00\x066\x00\x00\x00\x00\x00\x01\x00\x00\xa9M\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x06\x1e\x00\x00\x00\x00\x00\x01\x00\x00\xa8\x0a\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x07\x0e\x00\x00\x00\x00\x00\x01\x00\x00\xf6\x9a\
-\x00\x00\x01\x87,\x09\xa2X\
-\x00\x00\x06\xce\x00\x00\x00\x00\x00\x01\x00\x00\xd8`\
-\x00\x00\x01\x87,\x09\xa2X\
-\x00\x00\x06P\x00\x00\x00\x00\x00\x01\x00\x00\xab\x1b\
-\x00\x00\x01\x87,\x09\xa2X\
-\x00\x00\x06\xf0\x00\x00\x00\x00\x00\x01\x00\x00\xf4\xba\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x06\x92\x00\x00\x00\x00\x00\x01\x00\x00\xd2\xff\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x06\xb0\x00\x00\x00\x00\x00\x01\x00\x00\xd5\xf9\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x04\xd0\x00\x00\x00\x00\x00\x01\x00\x00\x81\x99\
-\x00\x00\x01\x99\xa0\x15<\x1d\
-\x00\x00\x03\xf8\x00\x01\x00\x00\x00\x01\x00\x00d\xea\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x04\xa8\x00\x00\x00\x00\x00\x01\x00\x00|B\
-\x00\x00\x01\x99\xa0\x15<\x1d\
-\x00\x00\x02|\x00\x00\x00\x00\x00\x01\x00\x002\x85\
-\x00\x00\x01\x99\xa0\x15<\x1e\
-\x00\x00\x02Z\x00\x00\x00\x00\x00\x01\x00\x00/9\
-\x00\x00\x01\x99\xa0\x15<\x1d\
-\x00\x00\x00\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x10-\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x02\xa0\x00\x00\x00\x00\x00\x01\x00\x005\x01\
-\x00\x00\x01\x99\xa0\x15<\x1e\
-\x00\x00\x03\x16\x00\x00\x00\x00\x00\x01\x00\x00K\xab\
-\x00\x00\x01\x99\xa0\x15<\x1e\
-\x00\x00\x04\x80\x00\x00\x00\x00\x00\x01\x00\x00v\xcd\
-\x00\x00\x01\x99\xa0\x15<\x1d\
-\x00\x00\x05\xa4\x00\x00\x00\x00\x00\x01\x00\x00\x99[\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x03`\x00\x00\x00\x00\x00\x01\x00\x00W\x8a\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x02\xee\x00\x00\x00\x00\x00\x01\x00\x00C\xe3\
-\x00\x00\x01\x99\xa0\x15<\x1d\
-\x00\x00\x01j\x00\x00\x00\x00\x00\x01\x00\x00\x1bG\
-\x00\x00\x01\x99\xa0\x15<\x1d\
-\x00\x00\x05\xca\x00\x00\x00\x00\x00\x01\x00\x00\x9d3\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x03\xd2\x00\x00\x00\x00\x00\x01\x00\x00^\x93\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x01\xec\x00\x00\x00\x00\x00\x01\x00\x00'0\
-\x00\x00\x01\x99\xa0\x15<\x1e\
-\x00\x00\x05\xf6\x00\x00\x00\x00\x00\x01\x00\x00\xa0%\
-\x00\x00\x01\x99\xa0\x15<\x1d\
-\x00\x00\x00\xc4\x00\x00\x00\x00\x00\x01\x00\x00\x0c\xff\
-\x00\x00\x01\x99\xa0\x15<\x1d\
-\x00\x00\x01\xc0\x00\x00\x00\x00\x00\x01\x00\x00 \xa5\
-\x00\x00\x01\x99\xa0\x15<\x1d\
-\x00\x00\x05\x82\x00\x01\x00\x00\x00\x01\x00\x00\x93\x8a\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x01\x18\x00\x00\x00\x00\x00\x01\x00\x00\x12\xbb\
-\x00\x00\x01\x99\xa0\x15<\x1e\
-\x00\x00\x05N\x00\x00\x00\x00\x00\x01\x00\x00\x8f\x07\
-\x00\x00\x01\x99\xa0\x15<\x1d\
-\x00\x00\x03\x88\x00\x00\x00\x00\x00\x01\x00\x00XN\
-\x00\x00\x01\x99\xa0\x15<\x1d\
-\x00\x00\x026\x00\x01\x00\x00\x00\x01\x00\x00,\xf8\
-\x00\x00\x01\x99\xa0\x15<\x1e\
-\x00\x00\x02\x12\x00\x00\x00\x00\x00\x01\x00\x00*\x08\
-\x00\x00\x01\x99\xa0\x15<\x1e\
-\x00\x00\x04<\x00\x00\x00\x00\x00\x01\x00\x00l\xd7\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x04\x5c\x00\x00\x00\x00\x00\x01\x00\x00r\x08\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x01\x94\x00\x00\x00\x00\x00\x01\x00\x00\x1dL\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x04\x1e\x00\x00\x00\x00\x00\x01\x00\x00i\xd8\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x03>\x00\x01\x00\x00\x00\x01\x00\x00UB\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x01:\x00\x00\x00\x00\x00\x01\x00\x00\x18w\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x05\x22\x00\x00\x00\x00\x00\x01\x00\x00\x8a\xc2\
-\x00\x00\x01\x99\xa0\x15<\x1d\
-\x00\x00\x02\xc6\x00\x01\x00\x00\x00\x01\x00\x00@\xb9\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x04\xfc\x00\x00\x00\x00\x00\x01\x00\x00\x85\x5c\
-\x00\x00\x01\x99\xa0\x15<\x1c\
-\x00\x00\x03\xaa\x00\x00\x00\x00\x00\x01\x00\x00\x5c\x0b\
-\x00\x00\x01\x99\xa0\x15<\x1e\
+\x00\x00\x01\x9b\xc6D\x84W\
+\x00\x00\x00t\x00\x00\x00\x00\x00\x01\x00\x01\x1f\xbb\
+\x00\x00\x01\x975l\xc7\xef\
+\x00\x00\x070\x00\x00\x00\x00\x00\x01\x00\x01\x0c%\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x07l\x00\x00\x00\x00\x00\x01\x00\x01\x1c\x09\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x06r\x00\x00\x00\x00\x00\x01\x00\x00\xd2\x7f\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x07P\x00\x00\x00\x00\x00\x01\x00\x01\x10\xe4\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x066\x00\x00\x00\x00\x00\x01\x00\x00\xab\x8c\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x06\x1e\x00\x00\x00\x00\x00\x01\x00\x00\xaaI\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x07\x0e\x00\x00\x00\x00\x00\x01\x00\x00\xf8\xd9\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x06\xce\x00\x00\x00\x00\x00\x01\x00\x00\xda\x9f\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x06P\x00\x00\x00\x00\x00\x01\x00\x00\xadZ\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x06\xf0\x00\x00\x00\x00\x00\x01\x00\x00\xf6\xf9\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x06\x92\x00\x00\x00\x00\x00\x01\x00\x00\xd5>\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x06\xb0\x00\x00\x00\x00\x00\x01\x00\x00\xd88\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x04\xd0\x00\x00\x00\x00\x00\x01\x00\x00\x83g\
+\x00\x00\x01\x9b\xc6CC\xeb\
+\x00\x00\x03\xf8\x00\x01\x00\x00\x00\x01\x00\x00f\x89\
+\x00\x00\x01\x99.H\x8e\xfe\
+\x00\x00\x04\xa8\x00\x00\x00\x00\x00\x01\x00\x00~\x10\
+\x00\x00\x01\x99.H\x8e\xff\
+\x00\x00\x02|\x00\x00\x00\x00\x00\x01\x00\x003\xf0\
+\x00\x00\x01\x99.H\x8f\x00\
+\x00\x00\x02Z\x00\x00\x00\x00\x00\x01\x00\x0009\
+\x00\x00\x01\x9b\xc6CC\xeb\
+\x00\x00\x00\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x10\x84\
+\x00\x00\x01\x99.H\x8e\xfe\
+\x00\x00\x02\xa0\x00\x00\x00\x00\x00\x01\x00\x006l\
+\x00\x00\x01\x99.H\x8f\x00\
+\x00\x00\x03\x16\x00\x00\x00\x00\x00\x01\x00\x00M\x16\
+\x00\x00\x01\x99.H\x8e\xff\
+\x00\x00\x04\x80\x00\x00\x00\x00\x00\x01\x00\x00x\x9b\
+\x00\x00\x01\x99.H\x8e\xff\
+\x00\x00\x05\xa4\x00\x00\x00\x00\x00\x01\x00\x00\x9b\x9a\
+\x00\x00\x01\x99.H\x8e\xfd\
+\x00\x00\x03`\x00\x00\x00\x00\x00\x01\x00\x00X\xf5\
+\x00\x00\x01\x99.H\x8e\xfe\
+\x00\x00\x02\xee\x00\x00\x00\x00\x00\x01\x00\x00EN\
+\x00\x00\x01\x99.H\x8e\xff\
+\x00\x00\x01j\x00\x00\x00\x00\x00\x01\x00\x00\x1b\x9e\
+\x00\x00\x01\x99-e\xb2 \
+\x00\x00\x05\xca\x00\x00\x00\x00\x00\x01\x00\x00\x9fr\
+\x00\x00\x01\x99.H\x8e\xfd\
+\x00\x00\x03\xd2\x00\x00\x00\x00\x00\x01\x00\x00`2\
+\x00\x00\x01\x99.H\x8e\xfd\
+\x00\x00\x01\xec\x00\x00\x00\x00\x00\x01\x00\x00'\xfc\
+\x00\x00\x01\x9b\xc6CC\xeb\
+\x00\x00\x05\xf6\x00\x00\x00\x00\x00\x01\x00\x00\xa2d\
+\x00\x00\x01\x99.H\x8e\xff\
+\x00\x00\x00\xc4\x00\x00\x00\x00\x00\x01\x00\x00\x0d$\
+\x00\x00\x01\x9b\xc6F\x1b\xb0\
+\x00\x00\x01\xc0\x00\x00\x00\x00\x00\x01\x00\x00!q\
+\x00\x00\x01\x99.H\x8e\xff\
+\x00\x00\x05\x82\x00\x01\x00\x00\x00\x01\x00\x00\x95\xc9\
+\x00\x00\x01\x99.H\x8e\xfe\
+\x00\x00\x01\x18\x00\x00\x00\x00\x00\x01\x00\x00\x13\x12\
+\x00\x00\x01\x99.H\x8e\xff\
+\x00\x00\x05N\x00\x00\x00\x00\x00\x01\x00\x00\x91F\
+\x00\x00\x01\x99.H\x8e\xff\
+\x00\x00\x03\x88\x00\x00\x00\x00\x00\x01\x00\x00Y\xb9\
+\x00\x00\x01\x99.H\x8e\xff\
+\x00\x00\x026\x00\x01\x00\x00\x00\x01\x00\x00-\xf8\
+\x00\x00\x01\x99.H\x8f\x00\
+\x00\x00\x02\x12\x00\x00\x00\x00\x00\x01\x00\x00+\x08\
+\x00\x00\x01\x99.H\x8f\x00\
+\x00\x00\x04<\x00\x00\x00\x00\x00\x01\x00\x00n\xa5\
+\x00\x00\x01\x99.H\x8e\xfe\
+\x00\x00\x04\x5c\x00\x00\x00\x00\x00\x01\x00\x00s\xd6\
+\x00\x00\x01\x99.H\x8e\xfe\
+\x00\x00\x01\x94\x00\x00\x00\x00\x00\x01\x00\x00\x1d\xa3\
+\x00\x00\x01\x9b\xc6CC\xeb\
+\x00\x00\x04\x1e\x00\x00\x00\x00\x00\x01\x00\x00kw\
+\x00\x00\x01\x9b\xc6CC\xeb\
+\x00\x00\x03>\x00\x01\x00\x00\x00\x01\x00\x00V\xad\
+\x00\x00\x01\x99.H\x8e\xfd\
+\x00\x00\x01:\x00\x00\x00\x00\x00\x01\x00\x00\x18\xce\
+\x00\x00\x01\x99.H\x8e\xfd\
+\x00\x00\x05\x22\x00\x00\x00\x00\x00\x01\x00\x00\x8d\x01\
+\x00\x00\x01\x99.H\x8e\xff\
+\x00\x00\x02\xc6\x00\x01\x00\x00\x00\x01\x00\x00B$\
+\x00\x00\x01\x99.H\x8e\xfe\
+\x00\x00\x04\xfc\x00\x00\x00\x00\x00\x01\x00\x00\x87\x9b\
+\x00\x00\x01\x99.H\x8e\xfe\
+\x00\x00\x03\xaa\x00\x00\x00\x00\x00\x01\x00\x00]v\
+\x00\x00\x01\x9b\xc6CC\xeb\
 \x00\x00\x07\x8c\x00\x02\x00\x00\x00\x05\x00\x00\x00:\
 \x00\x00\x00\x00\x00\x00\x00\x00\
 \x00\x00\x07\xb4\x00\x02\x00\x00\x00\x03\x00\x00\x00H\
@@ -5282,32 +5318,32 @@ qt_resource_struct = b"\
 \x00\x00\x00\x00\x00\x00\x00\x00\
 \x00\x00\x07\xc4\x00\x02\x00\x00\x00\x03\x00\x00\x00?\
 \x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x07\xd8\x00\x00\x00\x00\x00\x01\x00\x01\x1e?\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x08\x08\x00\x00\x00\x00\x00\x01\x00\x01!\x7f\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x088\x00\x00\x00\x00\x00\x01\x00\x01#\x00\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x08\x1e\x00\x00\x00\x00\x00\x01\x00\x01\x22y\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x08\x08\x00\x00\x00\x00\x00\x01\x00\x01\x1fM\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x088\x00\x00\x00\x00\x00\x01\x00\x01 \xba\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x08\x1e\x00\x00\x00\x00\x00\x01\x00\x01 4\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x08\x08\x00\x00\x00\x00\x00\x01\x00\x01%w\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x088\x00\x00\x00\x00\x00\x01\x00\x01&\xb5\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x08\x1e\x00\x00\x00\x00\x00\x01\x00\x01&3\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x08\x08\x00\x00\x00\x00\x00\x01\x00\x01#\xe3\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x088\x00\x00\x00\x00\x00\x01\x00\x01$\xf8\
-\x00\x00\x01\x86\xe4%hu\
-\x00\x00\x08\x1e\x00\x00\x00\x00\x00\x01\x00\x01$y\
-\x00\x00\x01\x86\xe4%hu\
+\x00\x00\x07\xd8\x00\x00\x00\x00\x00\x01\x00\x01 ~\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x08\x08\x00\x00\x00\x00\x00\x01\x00\x01#\xbe\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x088\x00\x00\x00\x00\x00\x01\x00\x01%?\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x08\x1e\x00\x00\x00\x00\x00\x01\x00\x01$\xb8\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x08\x08\x00\x00\x00\x00\x00\x01\x00\x01!\x8c\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x088\x00\x00\x00\x00\x00\x01\x00\x01\x22\xf9\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x08\x1e\x00\x00\x00\x00\x00\x01\x00\x01\x22s\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x08\x08\x00\x00\x00\x00\x00\x01\x00\x01'\xb6\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x088\x00\x00\x00\x00\x00\x01\x00\x01(\xf4\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x08\x1e\x00\x00\x00\x00\x00\x01\x00\x01(r\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x08\x08\x00\x00\x00\x00\x00\x01\x00\x01&\x22\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x088\x00\x00\x00\x00\x00\x01\x00\x01'7\
+\x00\x00\x01\x975l\xc7\xf0\
+\x00\x00\x08\x1e\x00\x00\x00\x00\x00\x01\x00\x01&\xb8\
+\x00\x00\x01\x975l\xc7\xf0\
 "
 
 def qInitResources():
index 125073d296c6230033442589e9d0620e6fb81f05..7195b7e8ce611b2365a92123136830c8700d6548 100644 (file)
@@ -44,9 +44,8 @@ if __name__ == '__main__':
 
     truck.arrive()
     truck.printAvailableFlavors()
-    result = truck.deliver()
 
-    if result:
+    if result := truck.deliver():
         print("All the kids got some icecream!")
     else:
         print("Aww, someone didn't get the flavor they wanted...")
index 98af12eb54aef41483d6e005413e0c7920d0cd0c..47d7dff53acc3c6e7cdc5dad0270c0fa12019c69 100644 (file)
@@ -17,8 +17,6 @@ public:
 
     void testFunction1();
 
-    static constexpr auto TEST = QLatin1StringView("test");
-
 private Q_SLOTS:
     void slotCursorChanged();
     void slotRunScript();
diff --git a/examples/webenginequick/nanobrowser/ApplicationRoot.qml b/examples/webenginequick/nanobrowser/ApplicationRoot.qml
deleted file mode 100644 (file)
index ec57162..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-pragma ComponentBehavior: Bound
-
-import QtQuick
-import QtWebEngine
-
-QtObject {
-    id: root
-
-    property WebEngineProfilePrototype defaultProfilePrototype : WebEngineProfilePrototype {
-        storageName: "Profile"
-        Component.onCompleted: {
-            let fullVersionList = root.defaultProfilePrototype.instance().clientHints.fullVersionList;
-            fullVersionList["QuickNanoBrowser"] = "1.0";
-            root.defaultProfilePrototype.instance().clientHints.fullVersionList = fullVersionList;
-        }
-    }
-
-    property WebEngineProfilePrototype otrPrototype : WebEngineProfilePrototype {
-    }
-
-    property Component browserWindowComponent: BrowserWindow {
-        applicationRoot: root
-    }
-    property Component browserDialogComponent: BrowserDialog {
-        onClosing: destroy()
-    }
-    function createWindow(profile) {
-        var newWindow = browserWindowComponent.createObject(root) as BrowserWindow;
-        newWindow.currentWebView.profile = profile;
-        profile.downloadRequested.connect(newWindow.onDownloadRequested);
-        return newWindow;
-    }
-    function createDialog(profile) {
-        var newDialog = browserDialogComponent.createObject(root) as BrowserDialog;
-        newDialog.currentWebView.profile = profile;
-        return newDialog;
-    }
-    function load(url) {
-        var browserWindow = createWindow(root.defaultProfilePrototype.instance());
-        browserWindow.currentWebView.url = url;
-    }
-}
diff --git a/examples/webenginequick/nanobrowser/BrowserDialog.qml b/examples/webenginequick/nanobrowser/BrowserDialog.qml
deleted file mode 100644 (file)
index 7af347e..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Window
-import QtWebEngine
-
-Window {
-    id: window
-    property alias currentWebView: webView
-    flags: Qt.Dialog
-    width: 800
-    height: 600
-    visible: true
-    onClosing: destroy()
-    WebEngineView {
-        id: webView
-        anchors.fill: parent
-
-        onGeometryChangeRequested: function(geometry) {
-            window.x = geometry.x
-            window.y = geometry.y
-            window.width = geometry.width
-            window.height = geometry.height
-        }
-    }
-}
diff --git a/examples/webenginequick/nanobrowser/BrowserUtils/ApplicationRoot.qml b/examples/webenginequick/nanobrowser/BrowserUtils/ApplicationRoot.qml
new file mode 100644 (file)
index 0000000..ec57162
--- /dev/null
@@ -0,0 +1,45 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtWebEngine
+
+QtObject {
+    id: root
+
+    property WebEngineProfilePrototype defaultProfilePrototype : WebEngineProfilePrototype {
+        storageName: "Profile"
+        Component.onCompleted: {
+            let fullVersionList = root.defaultProfilePrototype.instance().clientHints.fullVersionList;
+            fullVersionList["QuickNanoBrowser"] = "1.0";
+            root.defaultProfilePrototype.instance().clientHints.fullVersionList = fullVersionList;
+        }
+    }
+
+    property WebEngineProfilePrototype otrPrototype : WebEngineProfilePrototype {
+    }
+
+    property Component browserWindowComponent: BrowserWindow {
+        applicationRoot: root
+    }
+    property Component browserDialogComponent: BrowserDialog {
+        onClosing: destroy()
+    }
+    function createWindow(profile) {
+        var newWindow = browserWindowComponent.createObject(root) as BrowserWindow;
+        newWindow.currentWebView.profile = profile;
+        profile.downloadRequested.connect(newWindow.onDownloadRequested);
+        return newWindow;
+    }
+    function createDialog(profile) {
+        var newDialog = browserDialogComponent.createObject(root) as BrowserDialog;
+        newDialog.currentWebView.profile = profile;
+        return newDialog;
+    }
+    function load(url) {
+        var browserWindow = createWindow(root.defaultProfilePrototype.instance());
+        browserWindow.currentWebView.url = url;
+    }
+}
diff --git a/examples/webenginequick/nanobrowser/BrowserUtils/BrowserDialog.qml b/examples/webenginequick/nanobrowser/BrowserUtils/BrowserDialog.qml
new file mode 100644 (file)
index 0000000..7af347e
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Window
+import QtWebEngine
+
+Window {
+    id: window
+    property alias currentWebView: webView
+    flags: Qt.Dialog
+    width: 800
+    height: 600
+    visible: true
+    onClosing: destroy()
+    WebEngineView {
+        id: webView
+        anchors.fill: parent
+
+        onGeometryChangeRequested: function(geometry) {
+            window.x = geometry.x
+            window.y = geometry.y
+            window.width = geometry.width
+            window.height = geometry.height
+        }
+    }
+}
diff --git a/examples/webenginequick/nanobrowser/BrowserUtils/BrowserWindow.qml b/examples/webenginequick/nanobrowser/BrowserUtils/BrowserWindow.qml
new file mode 100644 (file)
index 0000000..c4e28b8
--- /dev/null
@@ -0,0 +1,948 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtCore
+import QtQml
+import QtQuick
+import QtQuick.Controls.Fusion
+import QtQuick.Dialogs
+import QtQuick.Layouts
+import QtQuick.Window
+import QtWebEngine
+import BrowserUtils
+
+ApplicationWindow {
+    id: win
+    required property QtObject applicationRoot
+    property WebEngineView currentWebView: tabBar.currentIndex < tabBar.count ? tabLayout.children[tabBar.currentIndex] : null
+    property int previousVisibility: Window.Windowed
+    property int createdTabs: 0
+    property bool lastTabClosing: false
+
+    width: 1300
+    height: 900
+    visible: true
+    title: win.currentWebView?.title ?? ""
+
+    // Make sure the Qt.WindowFullscreenButtonHint is set on OS X.
+    Component.onCompleted: flags = flags | Qt.WindowFullscreenButtonHint
+
+    onCurrentWebViewChanged: {
+        findBar.reset();
+    }
+
+    // When using style "mac", ToolButtons are not supposed to accept focus.
+    property bool platformIsMac: Qt.platform.os === "osx"
+
+    Settings {
+        id : appSettings
+        property alias autoLoadImages: loadImages.checked
+        property alias javaScriptEnabled: javaScriptEnabled.checked
+        property alias errorPageEnabled: errorPageEnabled.checked
+        property alias pluginsEnabled: pluginsEnabled.checked
+        property alias fullScreenSupportEnabled: fullScreenSupportEnabled.checked
+        property alias autoLoadIconsForPage: autoLoadIconsForPage.checked
+        property alias touchIconsEnabled: touchIconsEnabled.checked
+        property alias webRTCPublicInterfacesOnly : webRTCPublicInterfacesOnly.checked
+        property alias devToolsEnabled: devToolsEnabled.checked
+        property alias pdfViewerEnabled: pdfViewerEnabled.checked
+        property int imageAnimationPolicy: WebEngineSettings.ImageAnimationPolicy.Allow
+        property alias javascriptCanAccessClipboard: javascriptCanAccessClipboard.checked
+        property alias javascriptCanPaste: javascriptCanPaste.checked
+    }
+
+    Action {
+        shortcut: "Ctrl+D"
+        onTriggered: {
+            downloadView.visible = !downloadView.visible;
+        }
+    }
+    Action {
+        id: focus
+        shortcut: "Ctrl+L"
+        onTriggered: {
+            addressBar.forceActiveFocus();
+            addressBar.selectAll();
+        }
+    }
+    Action {
+        shortcut: StandardKey.Refresh
+        onTriggered: {
+            if (win.currentWebView)
+                win.currentWebView.reload();
+        }
+    }
+    Action {
+        shortcut: StandardKey.AddTab
+        onTriggered: {
+            tabBar.createTab(tabBar.count !== 0
+                             ? win.currentWebView.profile
+                             : (win.applicationRoot as ApplicationRoot).defaultProfilePrototype.instance());
+            addressBar.forceActiveFocus();
+            addressBar.selectAll();
+        }
+    }
+    Action {
+        shortcut: StandardKey.Close
+        onTriggered: {
+            win.currentWebView.triggerWebAction(WebEngineView.RequestClose);
+        }
+    }
+    Action {
+        shortcut: StandardKey.Quit
+        onTriggered: win.close()
+    }
+    Action {
+        shortcut: "Escape"
+        onTriggered: {
+            if (win.currentWebView.state === "FullScreen") {
+                win.visibility = win.previousVisibility;
+                fullScreenNotification.hide();
+                win.currentWebView.triggerWebAction(WebEngineView.ExitFullScreen);
+            }
+
+            if (findBar.visible)
+                findBar.visible = false;
+        }
+    }
+    Action {
+        shortcut: "Ctrl+0"
+        onTriggered: win.currentWebView.zoomFactor = 1.0
+    }
+    Action {
+        shortcut: StandardKey.ZoomOut
+        onTriggered: win.currentWebView.zoomFactor -= 0.1
+    }
+    Action {
+        shortcut: StandardKey.ZoomIn
+        onTriggered: win.currentWebView.zoomFactor += 0.1
+    }
+
+    Action {
+        shortcut: StandardKey.Copy
+        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Copy)
+    }
+    Action {
+        shortcut: StandardKey.Cut
+        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Cut)
+    }
+    Action {
+        shortcut: StandardKey.Paste
+        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Paste)
+    }
+    Action {
+        shortcut: "Shift+"+StandardKey.Paste
+        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.PasteAndMatchStyle)
+    }
+    Action {
+        shortcut: StandardKey.SelectAll
+        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.SelectAll)
+    }
+    Action {
+        shortcut: StandardKey.Undo
+        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Undo)
+    }
+    Action {
+        shortcut: StandardKey.Redo
+        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Redo)
+    }
+    Action {
+        shortcut: StandardKey.Back
+        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Back)
+    }
+    Action {
+        shortcut: StandardKey.Forward
+        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Forward)
+    }
+    Action {
+        shortcut: StandardKey.Find
+        onTriggered: {
+            if (!findBar.visible)
+                findBar.visible = true;
+        }
+    }
+    Action {
+        shortcut: StandardKey.FindNext
+        onTriggered: findBar.findNext()
+    }
+    Action {
+        shortcut: StandardKey.FindPrevious
+        onTriggered: findBar.findPrevious()
+    }
+
+    menuBar: ToolBar {
+        id: navigationBar
+        RowLayout {
+            anchors.fill: parent
+            ToolButton {
+                enabled: win.currentWebView?.canGoBack || win.currentWebView?.canGoForward
+                onClicked: historyMenu.open()
+                text: qsTr("â–¼")
+                Menu {
+                    id: historyMenu
+                    Instantiator {
+                        model: win.currentWebView?.history?.items
+                        MenuItem {
+                            required property var model
+                            text: model.title
+                            onTriggered: win.currentWebView.goBackOrForward(model.offset)
+                            checkable: !enabled
+                            checked: !enabled
+                            enabled: model.offset
+                        }
+
+                        onObjectAdded: function(index, object) {
+                            historyMenu.insertItem(index, object)
+                        }
+                        onObjectRemoved: function(index, object) {
+                            historyMenu.removeItem(object)
+                        }
+                    }
+                }
+            }
+
+            ToolButton {
+                id: backButton
+                icon.source: "qrc:///icons/3rdparty/go-previous.png"
+                onClicked: win.currentWebView.goBack()
+                enabled: win.currentWebView?.canGoBack ?? false
+                activeFocusOnTab: !win.platformIsMac
+            }
+            ToolButton {
+                id: forwardButton
+                icon.source: "qrc:///icons/3rdparty/go-next.png"
+                onClicked: win.currentWebView.goForward()
+                enabled: win.currentWebView?.canGoForward ?? false
+                activeFocusOnTab: !win.platformIsMac
+            }
+            ToolButton {
+                id: reloadButton
+                icon.source: win.currentWebView?.loading
+                             ? "qrc:///icons/3rdparty/process-stop.png"
+                             : "qrc:///icons/3rdparty/view-refresh.png"
+                onClicked: win.currentWebView?.loading ? win.currentWebView.stop() : win.currentWebView.reload()
+                activeFocusOnTab: !win.platformIsMac
+            }
+            TextField {
+                id: addressBar
+                Image {
+                    anchors.verticalCenter: addressBar.verticalCenter;
+                    x: 5
+                    z: 2
+                    id: faviconImage
+                    width: 16; height: 16
+                    sourceSize: Qt.size(width, height)
+                    source: win.currentWebView?.icon ? win.currentWebView.icon : ''
+                }
+                MouseArea {
+                    id: textFieldMouseArea
+                    acceptedButtons: Qt.RightButton
+                    anchors.fill: parent
+                    onClicked: {
+                        var textSelectionStartPos = addressBar.selectionStart;
+                        var textSelectionEndPos = addressBar.selectionEnd;
+                        textFieldContextMenu.open();
+                        addressBar.select(textSelectionStartPos, textSelectionEndPos);
+                    }
+                    Menu {
+                        id: textFieldContextMenu
+                        x: textFieldMouseArea.mouseX
+                        y: textFieldMouseArea.mouseY
+                        MenuItem {
+                            text: qsTr("Cut")
+                            onTriggered: addressBar.cut()
+                            enabled: addressBar.selectedText.length > 0
+                        }
+                        MenuItem {
+                            text: qsTr("Copy")
+                            onTriggered: addressBar.copy()
+                            enabled: addressBar.selectedText.length > 0
+                        }
+                        MenuItem {
+                            text: qsTr("Paste")
+                            onTriggered: addressBar.paste()
+                            enabled: addressBar.canPaste
+                        }
+                        MenuItem {
+                            text: qsTr("Delete")
+                            onTriggered: addressBar.text = qsTr("")
+                            enabled: addressBar.selectedText.length > 0
+                        }
+                        MenuSeparator {}
+                        MenuItem {
+                            text: qsTr("Select All")
+                            onTriggered: addressBar.selectAll()
+                            enabled: addressBar.text.length > 0
+                        }
+                    }
+                }
+                leftPadding: 26
+                focus: true
+                Layout.fillWidth: true
+                Binding on text {
+                    when: win.currentWebView
+                    value: win.currentWebView.url
+                }
+                onAccepted: win.currentWebView.url = Utils.fromUserInput(text)
+                selectByMouse: true
+            }
+            ToolButton {
+                id: settingsMenuButton
+                text: qsTr("â‹®")
+                onClicked: settingsMenu.open()
+                Menu {
+                    id: settingsMenu
+                    y: settingsMenuButton.height
+                    MenuItem {
+                        id: loadImages
+                        text: "Autoload images"
+                        checkable: true
+                        checked: WebEngine.settings.autoLoadImages
+                    }
+                    MenuItem {
+                        id: javaScriptEnabled
+                        text: "JavaScript On"
+                        checkable: true
+                        checked: WebEngine.settings.javascriptEnabled
+                    }
+                    MenuItem {
+                        id: errorPageEnabled
+                        text: "ErrorPage On"
+                        checkable: true
+                        checked: WebEngine.settings.errorPageEnabled
+                    }
+                    MenuItem {
+                        id: pluginsEnabled
+                        text: "Plugins On"
+                        checkable: true
+                        checked: true
+                    }
+                    MenuItem {
+                        id: fullScreenSupportEnabled
+                        text: "FullScreen On"
+                        checkable: true
+                        checked: WebEngine.settings.fullScreenSupportEnabled
+                    }
+                    MenuItem {
+                        id: offTheRecordEnabled
+                        text: "Off The Record"
+                        checkable: true
+                        checked: win.currentWebView?.profile === (win.applicationRoot as ApplicationRoot).otrPrototype.instance()
+                        onToggled: function() {
+                            if (win.currentWebView) {
+                                win.currentWebView.profile = offTheRecordEnabled.checked
+                                        ? (win.applicationRoot as ApplicationRoot).otrPrototype.instance()
+                                        : (win.applicationRoot as ApplicationRoot).defaultProfilePrototype.instance();
+                            }
+                        }
+                    }
+                    MenuItem {
+                        id: httpDiskCacheEnabled
+                        text: "HTTP Disk Cache"
+                        checkable: !win.currentWebView?.profile?.offTheRecord ?? false
+                        checked: win.currentWebView?.profile.httpCacheType === WebEngineProfile.DiskHttpCache
+                        onToggled: function() {
+                            if (win.currentWebView) {
+                                win.currentWebView.profile.httpCacheType = httpDiskCacheEnabled.checked
+                                        ? WebEngineProfile.DiskHttpCache
+                                        : WebEngineProfile.MemoryHttpCache;
+                            }
+                        }
+                    }
+                    MenuItem {
+                        id: autoLoadIconsForPage
+                        text: "Icons On"
+                        checkable: true
+                        checked: WebEngine.settings.autoLoadIconsForPage
+                    }
+                    MenuItem {
+                        id: touchIconsEnabled
+                        text: "Touch Icons On"
+                        checkable: true
+                        checked: WebEngine.settings.touchIconsEnabled
+                        enabled: autoLoadIconsForPage.checked
+                    }
+                    MenuItem {
+                        id: webRTCPublicInterfacesOnly
+                        text: "WebRTC Public Interfaces Only"
+                        checkable: true
+                        checked: WebEngine.settings.webRTCPublicInterfacesOnly
+                    }
+                    MenuItem {
+                        id: devToolsEnabled
+                        text: "Open DevTools"
+                        checkable: true
+                        checked: false
+                    }
+                    MenuItem {
+                        id: pdfViewerEnabled
+                        text: "PDF Viewer Enabled"
+                        checkable: true
+                        checked: WebEngine.settings.pdfViewerEnabled
+                    }
+                    Menu {
+                        id: imageAnimationPolicy
+                        title: "Image Animation Policy"
+
+                        MenuItem {
+                            id: disableImageAnimation
+                            text: "Disable All Image Animation"
+                            checkable: true
+                            autoExclusive: true
+                            checked: WebEngine.settings.imageAnimationPolicy === WebEngineSettings.ImageAnimationPolicy.Disallow
+                            onTriggered: {
+                                appSettings.imageAnimationPolicy = WebEngineSettings.ImageAnimationPolicy.Disallow
+                            }
+                        }
+
+                        MenuItem {
+                            id: allowImageAnimation
+                            text: "Allow All Animated Images"
+                            checkable: true
+                            autoExclusive: true
+                            checked: WebEngine.settings.imageAnimationPolicy === WebEngineSettings.ImageAnimationPolicy.Allow
+                            onTriggered : {
+                                appSettings.imageAnimationPolicy = WebEngineSettings.ImageAnimationPolicy.Allow
+                            }
+                        }
+
+                        MenuItem {
+                            id: animateImageOnce
+                            text: "Animate Image Once"
+                            checkable: true
+                            autoExclusive: true
+                            checked: WebEngine.settings.imageAnimationPolicy === WebEngineSettings.ImageAnimationPolicy.AnimateOnce
+                            onTriggered : {
+                                appSettings.imageAnimationPolicy = WebEngineSettings.ImageAnimationPolicy.AnimateOnce
+                            }
+                        }
+                    }
+
+                    MenuItem {
+                        id: javascriptCanAccessClipboard
+                        text: "JavaScript can access clipboard"
+                        checkable: true
+                        checked: WebEngine.settings.javascriptCanAccessClipboard
+                    }
+                    MenuItem {
+                        id: javascriptCanPaste
+                        text: "JavaScript can paste"
+                        checkable: true
+                        checked: WebEngine.settings.javascriptCanPaste
+                    }
+                }
+            }
+        }
+        ProgressBar {
+            id: progressBar
+            height: 3
+            anchors {
+                left: parent.left
+                top: parent.bottom
+                right: parent.right
+                leftMargin: parent.anchors.leftMargin
+                rightMargin: parent.anchors.rightMargin
+            }
+            background: Item {}
+            z: -2
+            from: 0
+            to: 100
+            value: (win.currentWebView?.loadProgress < 100) ? win.currentWebView.loadProgress : 0
+        }
+    }
+
+    StackLayout {
+        id: tabLayout
+        currentIndex: tabBar.currentIndex
+
+        anchors.top: tabBar.bottom
+        anchors.bottom: devToolsView.top
+        anchors.left: parent.left
+        anchors.right: parent.right
+    }
+
+    Component {
+        id: tabButtonComponent
+
+        TabButton {
+            id: tabButton
+            property color frameColor: "#999999"
+            property color fillColor: "#eeeeee"
+            property color nonSelectedColor: "#dddddd"
+            property string tabTitle: "New Tab"
+
+            contentItem: Rectangle {
+                id: tabRectangle
+                color: tabButton.down ? tabButton.fillColor : tabButton.nonSelectedColor
+                border.width: 1
+                border.color: tabButton.frameColor
+                implicitWidth: Math.max(text.width + 30, 80)
+                implicitHeight: Math.max(text.height + 10, 20)
+                Rectangle { height: 1 ; width: parent.width ; color: tabButton.frameColor}
+                Rectangle { height: parent.height ; width: 1; color: tabButton.frameColor}
+                Rectangle { x: parent.width - 2; height: parent.height ; width: 1; color: tabButton.frameColor}
+                Text {
+                    id: text
+                    anchors.left: parent.left
+                    anchors.verticalCenter: parent.verticalCenter
+                    anchors.leftMargin: 6
+                    text: tabButton.tabTitle
+                    elide: Text.ElideRight
+                    color: tabButton.down ? "black" : tabButton.frameColor
+                    width: parent.width - button.background.width
+                }
+                Button {
+                    id: button
+                    anchors.right: parent.right
+                    anchors.verticalCenter: parent.verticalCenter
+                    anchors.rightMargin: 4
+                    height: 12
+                    background: Rectangle {
+                        implicitWidth: 12
+                        implicitHeight: 12
+                        color: button.hovered ? "#cccccc" : tabRectangle.color
+                        Text {text: "x"; anchors.centerIn: parent; color: "gray"}
+                    }
+                    onClicked: tabButton.closeTab()
+                }
+            }
+
+            onClicked: addressBar.text = (tabLayout.itemAt(TabBar.index) as WebEngineView).url;
+            function closeTab() {
+                tabBar.tryCloseView(TabBar.index);
+            }
+        }
+    }
+
+    TabBar {
+        id: tabBar
+        anchors.top: parent.top
+        anchors.left: parent.left
+        anchors.right: parent.right
+        Component.onCompleted: createTab((win.applicationRoot as ApplicationRoot).defaultProfilePrototype.instance())
+
+        function createTab(profile, focusOnNewTab = true, url = undefined) {
+            var webview = tabComponent.createObject(tabLayout, {index: tabBar.count , profile: profile});
+            var newTabButton = tabButtonComponent.createObject(tabBar, {tabTitle: Qt.binding(function () { return webview.title; })});
+            tabBar.addItem(newTabButton);
+            if (focusOnNewTab) {
+                tabBar.setCurrentIndex(tabBar.count - 1);
+            }
+            if (url !== undefined) {
+                webview.url = url;
+            }
+            return webview;
+        }
+
+        function tryCloseView(index) {
+            tabLayout.children[index].triggerWebAction(WebEngineView.RequestClose);
+        }
+
+        function removeView(index) {
+            if (tabBar.count > 1) {
+                tabBar.removeItem(tabBar.itemAt(index));
+                tabLayout.children[index].destroy();
+            } else {
+                win.lastTabClosing = true;
+                win.close();
+            }
+        }
+
+        Component {
+            id: tabComponent
+            WebEngineView {
+                id: webEngineView
+                property int index;
+                focus: true
+
+                onLinkHovered: function(hoveredUrl) {
+                    if (hoveredUrl === "")
+                        hideStatusText.start();
+                    else {
+                        statusText.text = hoveredUrl;
+                        statusBubble.visible = true;
+                        hideStatusText.stop();
+                    }
+                }
+
+                states: [
+                    State {
+                        name: "FullScreen"
+                        PropertyChanges {
+                            target: tabBar
+                            visible: false
+                            height: 0
+                        }
+                        PropertyChanges {
+                            target: navigationBar
+                            visible: false
+                        }
+                    }
+                ]
+                settings.localContentCanAccessRemoteUrls: true
+                settings.localContentCanAccessFileUrls: false
+                settings.autoLoadImages: appSettings.autoLoadImages
+                settings.javascriptEnabled: appSettings.javaScriptEnabled
+                settings.errorPageEnabled: appSettings.errorPageEnabled
+                settings.pluginsEnabled: appSettings.pluginsEnabled
+                settings.fullScreenSupportEnabled: appSettings.fullScreenSupportEnabled
+                settings.autoLoadIconsForPage: appSettings.autoLoadIconsForPage
+                settings.touchIconsEnabled: appSettings.touchIconsEnabled
+                settings.webRTCPublicInterfacesOnly: appSettings.webRTCPublicInterfacesOnly
+                settings.pdfViewerEnabled: appSettings.pdfViewerEnabled
+                settings.imageAnimationPolicy: appSettings.imageAnimationPolicy
+                settings.screenCaptureEnabled: true
+                settings.javascriptCanAccessClipboard: appSettings.javascriptCanAccessClipboard
+                settings.javascriptCanPaste: appSettings.javascriptCanPaste
+
+                onWindowCloseRequested: function() {
+                    tabBar.removeView(webEngineView.index);
+                }
+
+                onCertificateError: function(error) {
+                    if (!error.isMainFrame) {
+                        error.rejectCertificate();
+                        return;
+                    }
+
+                    error.defer();
+                    sslDialog.enqueue(error);
+                }
+
+                onNewWindowRequested: function(request) {
+                    if (!request.userInitiated)
+                        console.warn("Blocked a popup window.");
+                    else if (request.destination === WebEngineNewWindowRequest.InNewTab) {
+                        var tab = tabBar.createTab(win.currentWebView.profile, true, request.requestedUrl);
+                        tab.acceptAsNewWindow(request);
+                    } else if (request.destination === WebEngineNewWindowRequest.InNewBackgroundTab) {
+                        var backgroundTab = tabBar.createTab(win.currentWebView.profile, false);
+                        backgroundTab.acceptAsNewWindow(request);
+                    } else if (request.destination === WebEngineNewWindowRequest.InNewDialog) {
+                        var dialog = (win.applicationRoot as ApplicationRoot).createDialog(win.currentWebView.profile);
+                        dialog.win.currentWebView.acceptAsNewWindow(request);
+                    } else {
+                        var window = (win.applicationRoot as ApplicationRoot).createWindow(win.currentWebView.profile);
+                        window.win.currentWebView.acceptAsNewWindow(request);
+                    }
+                }
+
+                onFullScreenRequested: function(request) {
+                    if (request.toggleOn) {
+                        webEngineView.state = "FullScreen";
+                        win.previousVisibility = win.visibility;
+                        win.showFullScreen();
+                        fullScreenNotification.show();
+                    } else {
+                        webEngineView.state = "";
+                        win.visibility = win.previousVisibility;
+                        fullScreenNotification.hide();
+                    }
+                    request.accept();
+                }
+
+                onRegisterProtocolHandlerRequested: function(request) {
+                    console.log("accepting registerProtocolHandler request for "
+                                + request.scheme + " from " + request.origin);
+                    request.accept();
+                }
+
+                onDesktopMediaRequested: function(request) {
+                    // select the primary screen
+                    request.selectScreen(request.screensModel.index(0, 0));
+                }
+
+                onRenderProcessTerminated: function(terminationStatus, exitCode) {
+                    var status = "";
+                    switch (terminationStatus) {
+                    case WebEngineView.NormalTerminationStatus:
+                        status = "(normal exit)";
+                        break;
+                    case WebEngineView.AbnormalTerminationStatus:
+                        status = "(abnormal exit)";
+                        break;
+                    case WebEngineView.CrashedTerminationStatus:
+                        status = "(crashed)";
+                        break;
+                    case WebEngineView.KilledTerminationStatus:
+                        status = "(killed)";
+                        break;
+                    }
+
+                    print("Render process exited with code " + exitCode + " " + status);
+                    reloadTimer.running = true;
+                }
+
+                onSelectClientCertificate: function(selection) {
+                    selection.certificates[0].select();
+                }
+
+                onFindTextFinished: function(result) {
+                    if (!findBar.visible)
+                        findBar.visible = true;
+
+                    findBar.numberOfMatches = result.numberOfMatches;
+                    findBar.activeMatch = result.activeMatch;
+                }
+
+                onLoadingChanged: function(loadRequest) {
+                    if (loadRequest.status === WebEngineView.LoadStartedStatus)
+                        findBar.reset();
+                }
+
+                onPermissionRequested: function(permission) {
+                    permissionDialog.permission = permission;
+                    permissionDialog.visible = true;
+                }
+                onWebAuthUxRequested: function(request) {
+                    webAuthDialog.init(request);
+                }
+
+                Timer {
+                    id: reloadTimer
+                    interval: 0
+                    running: false
+                    repeat: false
+                    onTriggered: win.currentWebView.reload()
+                }
+            }
+        }
+    }
+    WebEngineView {
+        id: devToolsView
+        visible: devToolsEnabled.checked
+        height: visible ? 400 : 0
+        inspectedView: visible && tabBar.currentIndex < tabBar.count ? tabLayout.children[tabBar.currentIndex] : null
+        anchors.left: parent.left
+        anchors.right: parent.right
+        anchors.bottom: parent.bottom
+        onNewWindowRequested: function(request) {
+            var tab = tabBar.createTab(win.currentWebView.profile);
+            request.openIn(tab);
+        }
+
+        Timer {
+            id: hideTimer
+            interval: 0
+            running: false
+            repeat: false
+            onTriggered: devToolsEnabled.checked = false
+        }
+        onWindowCloseRequested: function() {
+            // Delay hiding for keep the inspectedView set to receive the ACK message of close.
+            hideTimer.running = true;
+        }
+    }
+    Dialog {
+        id: sslDialog
+        anchors.centerIn: parent
+        contentWidth: Math.max(mainTextForSSLDialog.width, detailedTextForSSLDialog.width)
+        contentHeight: mainTextForSSLDialog.height + detailedTextForSSLDialog.height
+        property var certErrors: []
+        // fixme: icon!
+        // icon: StandardIcon.Warning
+        standardButtons: Dialog.No | Dialog.Yes
+        title: "Server's certificate not trusted"
+        contentItem: Item {
+            Label {
+                id: mainTextForSSLDialog
+                text: "Do you wish to continue?"
+            }
+            Text {
+                id: detailedTextForSSLDialog
+                anchors.top: mainTextForSSLDialog.bottom
+                text: "If you wish so, you may continue with an unverified certificate.\n" +
+                      "Accepting an unverified certificate means\n" +
+                      "you may not be connected with the host you tried to connect to.\n" +
+                      "Do you wish to override the security check and continue?"
+            }
+        }
+
+        onAccepted: {
+            certErrors.shift().acceptCertificate();
+            presentError();
+        }
+        onRejected: reject()
+
+        function reject(){
+            certErrors.shift().rejectCertificate();
+            presentError();
+        }
+        function enqueue(error){
+            certErrors.push(error);
+            presentError();
+        }
+        function presentError(){
+            visible = certErrors.length > 0
+        }
+    }
+    Dialog {
+        id: permissionDialog
+        anchors.centerIn: parent
+        width: Math.min(win.width, win.height) / 3 * 2
+        contentWidth: mainTextForPermissionDialog.width
+        contentHeight: mainTextForPermissionDialog.height
+        standardButtons: Dialog.No | Dialog.Yes
+        title: "Permission Request"
+
+        property var permission;
+
+        contentItem: Item {
+            Label {
+                id: mainTextForPermissionDialog
+            }
+        }
+
+        onAccepted: permission.grant()
+        onRejected: permission.deny()
+        onVisibleChanged: {
+            if (visible) {
+                mainTextForPermissionDialog.text = questionForPermissionType();
+                width = contentWidth + 20;
+            }
+        }
+
+        function questionForPermissionType() {
+            var question = "Allow " + permission.origin + " to "
+
+            switch (permission.permissionType) {
+            case WebEnginePermission.PermissionType.Geolocation:
+                question += "access your location information?";
+                break;
+            case WebEnginePermission.PermissionType.MediaAudioCapture:
+                question += "access your microphone?";
+                break;
+            case WebEnginePermission.PermissionType.MediaVideoCapture:
+                question += "access your webcam?";
+                break;
+            case WebEnginePermission.PermissionType.MediaAudioVideoCapture:
+                question += "access your microphone and webcam?";
+                break;
+            case WebEnginePermission.PermissionType.MouseLock:
+                question += "lock your mouse cursor?";
+                break;
+            case WebEnginePermission.PermissionType.DesktopVideoCapture:
+                question += "capture video of your desktop?";
+                break;
+            case WebEnginePermission.PermissionType.DesktopAudioVideoCapture:
+                question += "capture audio and video of your desktop?";
+                break;
+            case WebEnginePermission.PermissionType.Notifications:
+                question += "show notification on your desktop?";
+                break;
+            case WebEnginePermission.PermissionType.ClipboardReadWrite:
+                question += "read from and write to your clipboard?";
+                break;
+            case WebEnginePermission.PermissionType.LocalFontsAccess:
+                question += "access the fonts stored on your machine?";
+                break;
+            default:
+                question += "access unknown or unsupported permission type [" + permission.permissionType + "] ?";
+                break;
+            }
+
+            return question;
+        }
+    }
+
+    FullScreenNotification {
+        id: fullScreenNotification
+    }
+
+    DownloadView {
+        id: downloadView
+        visible: false
+        anchors.fill: parent
+    }
+
+    WebAuthDialog {
+        id: webAuthDialog
+        visible: false
+    }
+
+    MessageDialog {
+        id: downloadAcceptDialog
+        property var downloadRequest: downloadView.pendingDownloadRequest
+        title: "Download requested"
+        text: downloadRequest ? downloadRequest.suggestedFileName : ""
+        buttons: Dialog.No | Dialog.Yes
+        onAccepted: {
+            downloadView.visible = true;
+            downloadView.append(downloadRequest);
+            downloadRequest.accept();
+        }
+        onRejected: {
+            downloadRequest.cancel();
+        }
+        onButtonClicked: {
+            visible = false;
+        }
+        visible: false
+    }
+
+    function onDownloadRequested(download) {
+        downloadView.pendingDownloadRequest = download;
+        downloadAcceptDialog.visible = true;
+    }
+
+    FindBar {
+        id: findBar
+        visible: false
+        anchors.right: parent.right
+        anchors.rightMargin: 10
+        anchors.top: parent.top
+
+        onFindNext: {
+            if (text)
+                win.currentWebView?.findText(text);
+            else if (!visible)
+                visible = true;
+        }
+        onFindPrevious: {
+            if (text)
+                win.currentWebView?.findText(text, WebEngineView.FindBackward);
+            else if (!visible)
+                visible = true;
+        }
+    }
+
+
+    Rectangle {
+        id: statusBubble
+        color: "oldlace"
+        property int padding: 8
+        visible: false
+
+        anchors.left: parent.left
+        anchors.bottom: parent.bottom
+        width: statusText.paintedWidth + padding
+        height: statusText.paintedHeight + padding
+
+        Text {
+            id: statusText
+            anchors.centerIn: statusBubble
+            elide: Qt.ElideMiddle
+
+            Timer {
+                id: hideStatusText
+                interval: 750
+                onTriggered: {
+                    statusText.text = "";
+                    statusBubble.visible = false;
+                }
+            }
+        }
+    }
+
+    onClosing: function(closeEvent) {
+       if (lastTabClosing) {
+           return;
+       }
+       closeEvent.accepted = false
+       for (var i = 0; i < tabBar.count; i++)  {
+           tabBar.tryCloseView(i);
+       }
+    }
+}
diff --git a/examples/webenginequick/nanobrowser/BrowserUtils/DownloadView.qml b/examples/webenginequick/nanobrowser/BrowserUtils/DownloadView.qml
new file mode 100644 (file)
index 0000000..463d146
--- /dev/null
@@ -0,0 +1,137 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Controls.Fusion
+
+Rectangle {
+    id: downloadView
+    color: "lightgray"
+    property var pendingDownloadRequest: null
+
+    ListModel {
+        id: downloadModel
+        property var downloads: []
+    }
+
+    function append(download) {
+        downloadModel.append(download);
+        downloadModel.downloads.push(download);
+    }
+
+    Component {
+        id: downloadItemDelegate
+
+        Rectangle {
+            id: downloadItem
+            width: listView.width
+            height: childrenRect.height
+            anchors.margins: 10
+            radius: 3
+            color: "transparent"
+            border.color: "black"
+
+            required property int index
+
+            Rectangle {
+                id: progressBar
+
+                property real progress: {
+                    let d = downloadModel.downloads[downloadItem.index]
+                    return d ? d.receivedBytes / d.totalBytes : 0
+                }
+
+                radius: 3
+                color: width === listView.width ? "green" : "#2b74c7"
+                width: listView.width * progress
+                height: cancelButton.height
+
+                Behavior on width {
+                    SmoothedAnimation { duration: 100 }
+                }
+            }
+            Rectangle {
+                anchors {
+                    left: parent.left
+                    right: parent.right
+                    leftMargin: 20
+                }
+                Label {
+                    id: label
+                    text: {
+                        let d = downloadModel.downloads[downloadItem.index]
+                        return d ? d.downloadDirectory + "/" + d.downloadFileName : qsTr("")
+                    }
+                    anchors {
+                        verticalCenter: cancelButton.verticalCenter
+                        left: parent.left
+                        right: cancelButton.left
+                    }
+                }
+                Button {
+                    id: cancelButton
+                    anchors.right: parent.right
+                    icon.source: "qrc:///icons/3rdparty/process-stop.png"
+                    onClicked: {
+                        var download = downloadModel.downloads[downloadItem.index];
+
+                        download.cancel();
+
+                        downloadModel.downloads = downloadModel.downloads.filter(function (el) {
+                            return el.id !== download.id;
+                        });
+                        downloadModel.remove(downloadItem.index);
+                    }
+                }
+            }
+        }
+
+    }
+    ListView {
+        id: listView
+        anchors {
+            topMargin: 10
+            top: parent.top
+            bottom: parent.bottom
+            horizontalCenter: parent.horizontalCenter
+        }
+        width: parent.width - 20
+        spacing: 5
+
+        model: downloadModel
+        delegate: downloadItemDelegate
+
+        Text {
+            visible: !listView.count
+            horizontalAlignment: Text.AlignHCenter
+            height: 30
+            anchors {
+                top: parent.top
+                left: parent.left
+                right: parent.right
+            }
+            font.pixelSize: 20
+            text: "No active downloads."
+        }
+
+        Rectangle {
+            color: "gray"
+            anchors {
+                bottom: parent.bottom
+                left: parent.left
+                right: parent.right
+            }
+            height: 30
+            Button {
+                id: okButton
+                text: "OK"
+                anchors.centerIn: parent
+                onClicked: {
+                    downloadView.visible = false;
+                }
+            }
+        }
+    }
+}
diff --git a/examples/webenginequick/nanobrowser/BrowserUtils/FindBar.qml b/examples/webenginequick/nanobrowser/BrowserUtils/FindBar.qml
new file mode 100644 (file)
index 0000000..013f28e
--- /dev/null
@@ -0,0 +1,110 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls.Fusion
+import QtQuick.Layouts
+
+Rectangle {
+    id: root
+
+    property int numberOfMatches: 0
+    property int activeMatch: 0
+    property alias text: findTextField.text
+
+    function reset() {
+        numberOfMatches = 0;
+        activeMatch = 0;
+        visible = false;
+    }
+
+    signal findNext()
+    signal findPrevious()
+
+    width: 250
+    height: 35
+    radius: 2
+
+    border.width: 1
+    border.color: "black"
+    color: "white"
+
+    onVisibleChanged: {
+        if (visible)
+            findTextField.forceActiveFocus();
+    }
+
+
+    RowLayout {
+        anchors.fill: parent
+        anchors.topMargin: 5
+        anchors.bottomMargin: 5
+        anchors.leftMargin: 10
+        anchors.rightMargin: 10
+
+        spacing: 5
+
+        Rectangle {
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+
+            TextField {
+                id: findTextField
+                anchors.fill: parent
+                color: "black"
+                background: Rectangle {
+                    color: "transparent"
+                }
+
+                onAccepted: root.findNext()
+                onTextChanged: root.findNext()
+                onActiveFocusChanged: activeFocus ? selectAll() : deselect()
+            }
+        }
+
+        Label {
+            text: root.activeMatch + "/" + root.numberOfMatches
+            visible: findTextField.text !== ""
+            color: "black"
+        }
+
+        Rectangle {
+            border.width: 1
+            border.color: "#dddddd"
+            Layout.preferredWidth: 2
+            Layout.preferredHeight: parent.height
+        }
+
+        ToolButton {
+            id: findBtnLeft
+            text: "<"
+            enabled: root.numberOfMatches > 0
+            onClicked: root.findPrevious()
+            contentItem: Text {
+                color: "black"
+                text: findBtnLeft.text
+            }
+        }
+
+        ToolButton {
+            id: findBtnRight
+            text: ">"
+            enabled: root.numberOfMatches > 0
+            onClicked: root.findNext()
+            contentItem: Text {
+                color: "black"
+                text: findBtnRight.text
+            }
+        }
+
+        ToolButton {
+            id: findBtnClose
+            text: "x"
+            onClicked: root.visible = false
+            contentItem: Text {
+                color: "black"
+                text: findBtnClose.text
+            }
+        }
+    }
+}
diff --git a/examples/webenginequick/nanobrowser/BrowserUtils/FullScreenNotification.qml b/examples/webenginequick/nanobrowser/BrowserUtils/FullScreenNotification.qml
new file mode 100644 (file)
index 0000000..cdf154c
--- /dev/null
@@ -0,0 +1,62 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Rectangle {
+    id: fullScreenNotification
+    width: 500
+    height: 40
+    color: "white"
+    radius: 7
+
+    visible: false
+    opacity: 0
+
+    function show() {
+        visible = true;
+        opacity = 1;
+        reset.start();
+    }
+
+    function hide() {
+        reset.stop();
+        opacity = 0;
+    }
+
+    Behavior on opacity {
+        NumberAnimation {
+            duration: 750
+            onStopped: {
+                if (fullScreenNotification.opacity === 0)
+                    fullScreenNotification.visible = false;
+            }
+        }
+    }
+
+    Timer {
+        id: reset
+        interval: 5000
+        onTriggered: fullScreenNotification.hide()
+    }
+
+    anchors.horizontalCenter: parent.horizontalCenter
+    y: 125
+
+    Text {
+        id: message
+        width: parent.width
+
+        anchors.horizontalCenter: parent.horizontalCenter
+        anchors.verticalCenter: parent.verticalCenter
+
+        horizontalAlignment: Text.AlignHCenter
+        verticalAlignment: Text.AlignVCenter
+
+        wrapMode: Text.WordWrap
+        elide: Text.ElideNone
+        clip: true
+
+        text: qsTr("You are now in fullscreen mode. Press ESC to quit!")
+    }
+}
diff --git a/examples/webenginequick/nanobrowser/BrowserUtils/WebAuthDialog.qml b/examples/webenginequick/nanobrowser/BrowserUtils/WebAuthDialog.qml
new file mode 100644 (file)
index 0000000..8d5cf59
--- /dev/null
@@ -0,0 +1,285 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtWebEngine
+
+Dialog {
+    id: webAuthDialog
+    anchors.centerIn: parent
+    width: Math.min(parent.parent.width, parent.parent.height) / 3 * 2
+    contentWidth: verticalLayout.width +10;
+    contentHeight: verticalLayout.height +10;
+    standardButtons: Dialog.Cancel | Dialog.Apply
+    title: "WebAuth Request"
+
+    property var selectAccount;
+    property var authrequest: null;
+
+    Connections {
+        id: webauthConnection
+        ignoreUnknownSignals: true
+        function onStateChanged(state) {
+            webAuthDialog.setupUI(state);
+        }
+    }
+
+    onApplied: {
+        switch (webAuthDialog.authrequest.state) {
+            case WebEngineWebAuthUxRequest.WebAuthUxState.CollectPin:
+                webAuthDialog.authrequest.setPin(pinEdit.text);
+                break;
+            case WebEngineWebAuthUxRequest.WebAuthUxState.SelectAccount:
+                webAuthDialog.authrequest.setSelectedAccount(webAuthDialog.selectAccount);
+                break;
+             default:
+                 break;
+        }
+    }
+
+    onRejected: {
+        webAuthDialog.authrequest.cancel();
+    }
+
+    function init(request) {
+        pinLabel.visible = false;
+        pinEdit.visible = false;
+        confirmPinLabel.visible = false;
+        confirmPinEdit.visible = false;
+        selectAccountModel.clear();
+        webAuthDialog.authrequest = request;
+        webauthConnection.target = request;
+        setupUI(webAuthDialog.authrequest.state)
+        webAuthDialog.visible = true;
+        pinEntryError.visible = false;
+    }
+
+    function setupUI(state) {
+        switch (state) {
+        case WebEngineWebAuthUxRequest.WebAuthUxState.SelectAccount:
+            setupSelectAccountUI();
+            break;
+        case WebEngineWebAuthUxRequest.WebAuthUxState.CollectPin:
+            setupCollectPin();
+            break;
+        case WebEngineWebAuthUxRequest.WebAuthUxState.FinishTokenCollection:
+            setupFinishCollectToken();
+            break;
+        case WebEngineWebAuthUxRequest.WebAuthUxState.RequestFailed:
+            setupErrorUI();
+            break;
+        case WebEngineWebAuthUxRequest.WebAuthUxState.Completed:
+            webAuthDialog.close();
+            break;
+        }
+    }
+
+    ButtonGroup {
+        id : selectAccount;
+        exclusive: true;
+    }
+
+    ListModel {
+        id: selectAccountModel
+
+    }
+    contentItem: Item {
+        ColumnLayout  {
+            id : verticalLayout
+            spacing : 10
+
+            Label {
+                id: heading
+                text: "";
+            }
+
+            Label {
+                id: description
+                text: "";
+            }
+
+            Row {
+                spacing : 10
+                Label {
+                    id: pinLabel
+                    text: "PIN";
+                }
+                TextInput {
+                    id: pinEdit
+                    text: "EnterPin"
+                    enabled: true
+                    focus: true
+                    color: "white"
+                    layer.sourceRect: Qt.rect(0, 0, 20, 20)
+                }
+            }
+
+            Row {
+                spacing : 10
+                Label {
+                    id: confirmPinLabel
+                    text: "Confirm PIN";
+                }
+                TextEdit {
+                    id: confirmPinEdit
+                    text: ""
+                }
+            }
+
+            Label {
+                id: pinEntryError
+                text: "";
+            }
+
+            Repeater {
+                id : selectAccountRepeater
+                model: selectAccountModel
+                Column {
+                    id: selectAccountRepeaterColumn
+                    required property string modelData
+                    spacing : 5
+                    RadioButton {
+                        text: selectAccountRepeaterColumn.modelData
+                        ButtonGroup.group : webAuthDialog.selectAccount;
+                        onClicked: function(){
+                            webAuthDialog.selectAccount = text;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    function setupSelectAccountUI() {
+        webAuthDialog.selectAccount = "";
+        heading.text = "Choose a passkey";
+        description.text = "Which passkey do you want to use for " + webAuthDialog.authrequest.relyingPartyId;
+
+        selectAccountModel.clear();
+        var userNames = webAuthDialog.authrequest.userNames;
+        for (var i = 0; i < userNames.length; i++) {
+            selectAccountModel.append( {"name" : userNames[i]});
+        }
+        pinLabel.visible = false;
+        pinEdit.visible = false;
+        confirmPinLabel.visible = false;
+        confirmPinEdit.visible = false;
+        pinEntryError.visible = false;
+        standardButton(Dialog.Apply).visible = true;
+        standardButton(Dialog.Cancel).visible = true;
+        standardButton(Dialog.Cancel).text ="Cancel"
+    }
+
+    function setupCollectPin() {
+        var requestInfo = webAuthDialog.authrequest.pinRequest;
+
+        pinEdit.clear();
+
+        if (requestInfo.reason === WebEngineWebAuthUxRequest.PinEntryReason.Challenge) {
+            heading.text = "PIN required";
+            description.text = "Enter the PIN for your security key";
+            pinLabel.visible = true;
+            pinEdit.visible = true;
+            confirmPinLabel.visible = false;
+            confirmPinEdit.visible = false;
+        } else if (requestInfo.reason === WebEngineWebAuthUxRequest.PinEntryReason.Set) {
+            heading.text = "Set PIN ";
+            description.text = "Set new PIN for your security key";
+            pinLabel.visible = true;
+            pinEdit.visible = true;
+            confirmPinLabel.visible = true;
+            confirmPinEdit.visible = true;
+        }
+        pinEntryError.text = getPINErrorDetails() + " " + requestInfo.remainingAttempts + " attempts reamining";
+        pinEntryError.visible = true;
+        selectAccountModel.clear();
+        standardButton(Dialog.Cancel).visible = true;
+        standardButton(Dialog.Cancel).text ="Cancel"
+        standardButton(Dialog.Apply).visible = true;
+    }
+
+    function getPINErrorDetails() {
+        var requestInfo = webAuthDialog.authrequest.pinRequest;
+        switch (requestInfo.error) {
+        case WebEngineWebAuthUxRequest.PinEntryError.NoError:
+            return "";
+        case WebEngineWebAuthUxRequest.PinEntryError.TooShort:
+            return "Too short";
+        case WebEngineWebAuthUxRequest.PinEntryError.InternalUvLocked:
+            return "Internal Uv locked";
+        case WebEngineWebAuthUxRequest.PinEntryError.WrongPin:
+            return "Wrong PIN";
+        case WebEngineWebAuthUxRequest.PinEntryError.InvalidCharacters:
+            return "Invalid characters";
+        case WebEngineWebAuthUxRequest.PinEntryError.SameAsCurrentPin:
+            return "Same as current PIN";
+        }
+    }
+
+    function getRequestFailureResaon() {
+        var requestFailureReason = webAuthDialog.authrequest.requestFailureReason;
+        switch (requestFailureReason) {
+        case WebEngineWebAuthUxRequest.RequestFailureReason.Timeout:
+            return " Request Timeout";
+        case WebEngineWebAuthUxRequest.RequestFailureReason.KeyNotRegistered:
+            return "Key not registered";
+        case WebEngineWebAuthUxRequest.RequestFailureReason.KeyAlreadyRegistered:
+            return "You already registered this device. You don't have to register it again\n"
+                     + "Try again with different key or device.";
+        case WebEngineWebAuthUxRequest.RequestFailureReason.SoftPinBlock:
+            return "The security key is locked because the wrong PIN was entered too many times.\n"
+                    + "To unlock it, remove and reinsert it.";
+        case WebEngineWebAuthUxRequest.RequestFailureReason.HardPinBlock:
+            return "The security key is locked because the wrong PIN was entered too many times.\n"
+                    + "You'll need to reset the security key.";
+        case WebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorRemovedDuringPinEntry:
+            return "Authenticator removed during verification. Please reinsert and try again";
+        case WebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorMissingResidentKeys:
+            return "Authenticator doesn't have resident key support";
+        case WebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorMissingUserVerification:
+            return "Authenticator missing user verification";
+        case WebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorMissingLargeBlob:
+            return "Authenticator missing Large Blob support";
+        case WebEngineWebAuthUxRequest.RequestFailureReason.NoCommonAlgorithms:
+            return "No common Algorithms";
+        case WebEngineWebAuthUxRequest.RequestFailureReason.StorageFull:
+            return "Storage full";
+        case WebEngineWebAuthUxRequest.RequestFailureReason.UserConsentDenied:
+            return "User consent denied";
+        case WebEngineWebAuthUxRequest.RequestFailureReason.WinUserCancelled:
+            return "User cancelled request";
+        }
+    }
+
+    function setupFinishCollectToken() {
+        heading.text = "Use your security key with " + webAuthDialog.authrequest.relyingPartyId;
+        description.text = "Touch your security key again to complete the request.";
+        pinLabel.visible = false;
+        pinEdit.visible = false;
+        confirmPinLabel.visible = false;
+        confirmPinEdit.visible = false;
+        selectAccountModel.clear();
+        pinEntryError.visible = false;
+        standardButton(Dialog.Apply).visible = false;
+        standardButton(Dialog.Cancel).visible = true;
+        standardButton(Dialog.Cancel).text ="Cancel"
+    }
+
+    function setupErrorUI() {
+        heading.text = "Something went wrong";
+        description.text = getRequestFailureResaon();
+        pinLabel.visible = false;
+        pinEdit.visible = false;
+        confirmPinLabel.visible = false;
+        confirmPinEdit.visible = false;
+        selectAccountModel.clear();
+        pinEntryError.visible = false;
+        standardButton(Dialog.Apply).visible = false;
+        standardButton(Dialog.Cancel).visible = true;
+        standardButton(Dialog.Cancel).text ="Close"
+    }
+}
diff --git a/examples/webenginequick/nanobrowser/BrowserUtils/qmldir b/examples/webenginequick/nanobrowser/BrowserUtils/qmldir
new file mode 100644 (file)
index 0000000..d4d0abd
--- /dev/null
@@ -0,0 +1,9 @@
+module BrowserUtils
+ApplicationRoot 254.0 ApplicationRoot.qml
+BrowserDialog 254.0 BrowserDialog.qml
+BrowserWindow 254.0 BrowserWindow.qml
+DownloadView 254.0 DownloadView.qml
+FindBar 254.0 FindBar.qml
+FullScreenNotification 254.0 FullScreenNotification.qml
+WebAuthDialog 254.0 WebAuthDialog.qml
+depends QtQuick
diff --git a/examples/webenginequick/nanobrowser/BrowserWindow.qml b/examples/webenginequick/nanobrowser/BrowserWindow.qml
deleted file mode 100644 (file)
index 474968b..0000000
+++ /dev/null
@@ -1,948 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-pragma ComponentBehavior: Bound
-
-import QtCore
-import QtQml
-import QtQuick
-import QtQuick.Controls.Fusion
-import QtQuick.Dialogs
-import QtQuick.Layouts
-import QtQuick.Window
-import QtWebEngine
-import BrowserUtils
-
-ApplicationWindow {
-    id: win
-    required property QtObject applicationRoot
-    property WebEngineView currentWebView: tabBar.currentIndex < tabBar.count ? tabLayout.children[tabBar.currentIndex] : null
-    property int previousVisibility: Window.Windowed
-    property int createdTabs: 0
-    property bool lastTabClosing: false
-
-    width: 1300
-    height: 900
-    visible: true
-    title: win.currentWebView?.title ?? ""
-
-    // Make sure the Qt.WindowFullscreenButtonHint is set on OS X.
-    Component.onCompleted: flags = flags | Qt.WindowFullscreenButtonHint
-
-    onCurrentWebViewChanged: {
-        findBar.reset();
-    }
-
-    // When using style "mac", ToolButtons are not supposed to accept focus.
-    property bool platformIsMac: Qt.platform.os === "osx"
-
-    Settings {
-        id : appSettings
-        property alias autoLoadImages: loadImages.checked
-        property alias javaScriptEnabled: javaScriptEnabled.checked
-        property alias errorPageEnabled: errorPageEnabled.checked
-        property alias pluginsEnabled: pluginsEnabled.checked
-        property alias fullScreenSupportEnabled: fullScreenSupportEnabled.checked
-        property alias autoLoadIconsForPage: autoLoadIconsForPage.checked
-        property alias touchIconsEnabled: touchIconsEnabled.checked
-        property alias webRTCPublicInterfacesOnly : webRTCPublicInterfacesOnly.checked
-        property alias devToolsEnabled: devToolsEnabled.checked
-        property alias pdfViewerEnabled: pdfViewerEnabled.checked
-        property int imageAnimationPolicy: WebEngineSettings.ImageAnimationPolicy.Allow
-        property alias javascriptCanAccessClipboard: javascriptCanAccessClipboard.checked
-        property alias javascriptCanPaste: javascriptCanPaste.checked
-    }
-
-    Action {
-        shortcut: "Ctrl+D"
-        onTriggered: {
-            downloadView.visible = !downloadView.visible;
-        }
-    }
-    Action {
-        id: focus
-        shortcut: "Ctrl+L"
-        onTriggered: {
-            addressBar.forceActiveFocus();
-            addressBar.selectAll();
-        }
-    }
-    Action {
-        shortcut: StandardKey.Refresh
-        onTriggered: {
-            if (win.currentWebView)
-                win.currentWebView.reload();
-        }
-    }
-    Action {
-        shortcut: StandardKey.AddTab
-        onTriggered: {
-            tabBar.createTab(tabBar.count !== 0
-                             ? win.currentWebView.profile
-                             : (win.applicationRoot as ApplicationRoot).defaultProfilePrototype.instance());
-            addressBar.forceActiveFocus();
-            addressBar.selectAll();
-        }
-    }
-    Action {
-        shortcut: StandardKey.Close
-        onTriggered: {
-            win.currentWebView.triggerWebAction(WebEngineView.RequestClose);
-        }
-    }
-    Action {
-        shortcut: StandardKey.Quit
-        onTriggered: win.close()
-    }
-    Action {
-        shortcut: "Escape"
-        onTriggered: {
-            if (win.currentWebView.state === "FullScreen") {
-                win.visibility = win.previousVisibility;
-                fullScreenNotification.hide();
-                win.currentWebView.triggerWebAction(WebEngineView.ExitFullScreen);
-            }
-
-            if (findBar.visible)
-                findBar.visible = false;
-        }
-    }
-    Action {
-        shortcut: "Ctrl+0"
-        onTriggered: win.currentWebView.zoomFactor = 1.0
-    }
-    Action {
-        shortcut: StandardKey.ZoomOut
-        onTriggered: win.currentWebView.zoomFactor -= 0.1
-    }
-    Action {
-        shortcut: StandardKey.ZoomIn
-        onTriggered: win.currentWebView.zoomFactor += 0.1
-    }
-
-    Action {
-        shortcut: StandardKey.Copy
-        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Copy)
-    }
-    Action {
-        shortcut: StandardKey.Cut
-        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Cut)
-    }
-    Action {
-        shortcut: StandardKey.Paste
-        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Paste)
-    }
-    Action {
-        shortcut: "Shift+"+StandardKey.Paste
-        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.PasteAndMatchStyle)
-    }
-    Action {
-        shortcut: StandardKey.SelectAll
-        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.SelectAll)
-    }
-    Action {
-        shortcut: StandardKey.Undo
-        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Undo)
-    }
-    Action {
-        shortcut: StandardKey.Redo
-        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Redo)
-    }
-    Action {
-        shortcut: StandardKey.Back
-        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Back)
-    }
-    Action {
-        shortcut: StandardKey.Forward
-        onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Forward)
-    }
-    Action {
-        shortcut: StandardKey.Find
-        onTriggered: {
-            if (!findBar.visible)
-                findBar.visible = true;
-        }
-    }
-    Action {
-        shortcut: StandardKey.FindNext
-        onTriggered: findBar.findNext()
-    }
-    Action {
-        shortcut: StandardKey.FindPrevious
-        onTriggered: findBar.findPrevious()
-    }
-
-    menuBar: ToolBar {
-        id: navigationBar
-        RowLayout {
-            anchors.fill: parent
-            ToolButton {
-                enabled: win.currentWebView?.canGoBack || win.currentWebView?.canGoForward
-                onClicked: historyMenu.open()
-                text: qsTr("â–¼")
-                Menu {
-                    id: historyMenu
-                    Instantiator {
-                        model: win.currentWebView?.history?.items
-                        MenuItem {
-                            required property var model
-                            text: model.title
-                            onTriggered: win.currentWebView.goBackOrForward(model.offset)
-                            checkable: !enabled
-                            checked: !enabled
-                            enabled: model.offset
-                        }
-
-                        onObjectAdded: function(index, object) {
-                            historyMenu.insertItem(index, object)
-                        }
-                        onObjectRemoved: function(index, object) {
-                            historyMenu.removeItem(object)
-                        }
-                    }
-                }
-            }
-
-            ToolButton {
-                id: backButton
-                icon.source: "icons/3rdparty/go-previous.png"
-                onClicked: win.currentWebView.goBack()
-                enabled: win.currentWebView?.canGoBack ?? false
-                activeFocusOnTab: !win.platformIsMac
-            }
-            ToolButton {
-                id: forwardButton
-                icon.source: "icons/3rdparty/go-next.png"
-                onClicked: win.currentWebView.goForward()
-                enabled: win.currentWebView?.canGoForward ?? false
-                activeFocusOnTab: !win.platformIsMac
-            }
-            ToolButton {
-                id: reloadButton
-                icon.source: win.currentWebView?.loading
-                             ? "icons/3rdparty/process-stop.png"
-                             : "icons/3rdparty/view-refresh.png"
-                onClicked: win.currentWebView?.loading ? win.currentWebView.stop() : win.currentWebView.reload()
-                activeFocusOnTab: !win.platformIsMac
-            }
-            TextField {
-                id: addressBar
-                Image {
-                    anchors.verticalCenter: addressBar.verticalCenter;
-                    x: 5
-                    z: 2
-                    id: faviconImage
-                    width: 16; height: 16
-                    sourceSize: Qt.size(width, height)
-                    source: win.currentWebView?.icon ? win.currentWebView.icon : ''
-                }
-                MouseArea {
-                    id: textFieldMouseArea
-                    acceptedButtons: Qt.RightButton
-                    anchors.fill: parent
-                    onClicked: {
-                        var textSelectionStartPos = addressBar.selectionStart;
-                        var textSelectionEndPos = addressBar.selectionEnd;
-                        textFieldContextMenu.open();
-                        addressBar.select(textSelectionStartPos, textSelectionEndPos);
-                    }
-                    Menu {
-                        id: textFieldContextMenu
-                        x: textFieldMouseArea.mouseX
-                        y: textFieldMouseArea.mouseY
-                        MenuItem {
-                            text: qsTr("Cut")
-                            onTriggered: addressBar.cut()
-                            enabled: addressBar.selectedText.length > 0
-                        }
-                        MenuItem {
-                            text: qsTr("Copy")
-                            onTriggered: addressBar.copy()
-                            enabled: addressBar.selectedText.length > 0
-                        }
-                        MenuItem {
-                            text: qsTr("Paste")
-                            onTriggered: addressBar.paste()
-                            enabled: addressBar.canPaste
-                        }
-                        MenuItem {
-                            text: qsTr("Delete")
-                            onTriggered: addressBar.text = qsTr("")
-                            enabled: addressBar.selectedText.length > 0
-                        }
-                        MenuSeparator {}
-                        MenuItem {
-                            text: qsTr("Select All")
-                            onTriggered: addressBar.selectAll()
-                            enabled: addressBar.text.length > 0
-                        }
-                    }
-                }
-                leftPadding: 26
-                focus: true
-                Layout.fillWidth: true
-                Binding on text {
-                    when: win.currentWebView
-                    value: win.currentWebView.url
-                }
-                onAccepted: win.currentWebView.url = Utils.fromUserInput(text)
-                selectByMouse: true
-            }
-            ToolButton {
-                id: settingsMenuButton
-                text: qsTr("â‹®")
-                onClicked: settingsMenu.open()
-                Menu {
-                    id: settingsMenu
-                    y: settingsMenuButton.height
-                    MenuItem {
-                        id: loadImages
-                        text: "Autoload images"
-                        checkable: true
-                        checked: WebEngine.settings.autoLoadImages
-                    }
-                    MenuItem {
-                        id: javaScriptEnabled
-                        text: "JavaScript On"
-                        checkable: true
-                        checked: WebEngine.settings.javascriptEnabled
-                    }
-                    MenuItem {
-                        id: errorPageEnabled
-                        text: "ErrorPage On"
-                        checkable: true
-                        checked: WebEngine.settings.errorPageEnabled
-                    }
-                    MenuItem {
-                        id: pluginsEnabled
-                        text: "Plugins On"
-                        checkable: true
-                        checked: true
-                    }
-                    MenuItem {
-                        id: fullScreenSupportEnabled
-                        text: "FullScreen On"
-                        checkable: true
-                        checked: WebEngine.settings.fullScreenSupportEnabled
-                    }
-                    MenuItem {
-                        id: offTheRecordEnabled
-                        text: "Off The Record"
-                        checkable: true
-                        checked: win.currentWebView?.profile === (win.applicationRoot as ApplicationRoot).otrPrototype.instance()
-                        onToggled: function() {
-                            if (win.currentWebView) {
-                                win.currentWebView.profile = offTheRecordEnabled.checked
-                                        ? (win.applicationRoot as ApplicationRoot).otrPrototype.instance()
-                                        : (win.applicationRoot as ApplicationRoot).defaultProfilePrototype.instance();
-                            }
-                        }
-                    }
-                    MenuItem {
-                        id: httpDiskCacheEnabled
-                        text: "HTTP Disk Cache"
-                        checkable: !win.currentWebView?.profile?.offTheRecord ?? false
-                        checked: win.currentWebView?.profile.httpCacheType === WebEngineProfile.DiskHttpCache
-                        onToggled: function() {
-                            if (win.currentWebView) {
-                                win.currentWebView.profile.httpCacheType = httpDiskCacheEnabled.checked
-                                        ? WebEngineProfile.DiskHttpCache
-                                        : WebEngineProfile.MemoryHttpCache;
-                            }
-                        }
-                    }
-                    MenuItem {
-                        id: autoLoadIconsForPage
-                        text: "Icons On"
-                        checkable: true
-                        checked: WebEngine.settings.autoLoadIconsForPage
-                    }
-                    MenuItem {
-                        id: touchIconsEnabled
-                        text: "Touch Icons On"
-                        checkable: true
-                        checked: WebEngine.settings.touchIconsEnabled
-                        enabled: autoLoadIconsForPage.checked
-                    }
-                    MenuItem {
-                        id: webRTCPublicInterfacesOnly
-                        text: "WebRTC Public Interfaces Only"
-                        checkable: true
-                        checked: WebEngine.settings.webRTCPublicInterfacesOnly
-                    }
-                    MenuItem {
-                        id: devToolsEnabled
-                        text: "Open DevTools"
-                        checkable: true
-                        checked: false
-                    }
-                    MenuItem {
-                        id: pdfViewerEnabled
-                        text: "PDF Viewer Enabled"
-                        checkable: true
-                        checked: WebEngine.settings.pdfViewerEnabled
-                    }
-                    Menu {
-                        id: imageAnimationPolicy
-                        title: "Image Animation Policy"
-
-                        MenuItem {
-                            id: disableImageAnimation
-                            text: "Disable All Image Animation"
-                            checkable: true
-                            autoExclusive: true
-                            checked: WebEngine.settings.imageAnimationPolicy === WebEngineSettings.ImageAnimationPolicy.Disallow
-                            onTriggered: {
-                                appSettings.imageAnimationPolicy = WebEngineSettings.ImageAnimationPolicy.Disallow
-                            }
-                        }
-
-                        MenuItem {
-                            id: allowImageAnimation
-                            text: "Allow All Animated Images"
-                            checkable: true
-                            autoExclusive: true
-                            checked: WebEngine.settings.imageAnimationPolicy === WebEngineSettings.ImageAnimationPolicy.Allow
-                            onTriggered : {
-                                appSettings.imageAnimationPolicy = WebEngineSettings.ImageAnimationPolicy.Allow
-                            }
-                        }
-
-                        MenuItem {
-                            id: animateImageOnce
-                            text: "Animate Image Once"
-                            checkable: true
-                            autoExclusive: true
-                            checked: WebEngine.settings.imageAnimationPolicy === WebEngineSettings.ImageAnimationPolicy.AnimateOnce
-                            onTriggered : {
-                                appSettings.imageAnimationPolicy = WebEngineSettings.ImageAnimationPolicy.AnimateOnce
-                            }
-                        }
-                    }
-
-                    MenuItem {
-                        id: javascriptCanAccessClipboard
-                        text: "JavaScript can access clipboard"
-                        checkable: true
-                        checked: WebEngine.settings.javascriptCanAccessClipboard
-                    }
-                    MenuItem {
-                        id: javascriptCanPaste
-                        text: "JavaScript can paste"
-                        checkable: true
-                        checked: WebEngine.settings.javascriptCanPaste
-                    }
-                }
-            }
-        }
-        ProgressBar {
-            id: progressBar
-            height: 3
-            anchors {
-                left: parent.left
-                top: parent.bottom
-                right: parent.right
-                leftMargin: parent.anchors.leftMargin
-                rightMargin: parent.anchors.rightMargin
-            }
-            background: Item {}
-            z: -2
-            from: 0
-            to: 100
-            value: (win.currentWebView?.loadProgress < 100) ? win.currentWebView.loadProgress : 0
-        }
-    }
-
-    StackLayout {
-        id: tabLayout
-        currentIndex: tabBar.currentIndex
-
-        anchors.top: tabBar.bottom
-        anchors.bottom: devToolsView.top
-        anchors.left: parent.left
-        anchors.right: parent.right
-    }
-
-    Component {
-        id: tabButtonComponent
-
-        TabButton {
-            id: tabButton
-            property color frameColor: "#999999"
-            property color fillColor: "#eeeeee"
-            property color nonSelectedColor: "#dddddd"
-            property string tabTitle: "New Tab"
-
-            contentItem: Rectangle {
-                id: tabRectangle
-                color: tabButton.down ? tabButton.fillColor : tabButton.nonSelectedColor
-                border.width: 1
-                border.color: tabButton.frameColor
-                implicitWidth: Math.max(text.width + 30, 80)
-                implicitHeight: Math.max(text.height + 10, 20)
-                Rectangle { height: 1 ; width: parent.width ; color: tabButton.frameColor}
-                Rectangle { height: parent.height ; width: 1; color: tabButton.frameColor}
-                Rectangle { x: parent.width - 2; height: parent.height ; width: 1; color: tabButton.frameColor}
-                Text {
-                    id: text
-                    anchors.left: parent.left
-                    anchors.verticalCenter: parent.verticalCenter
-                    anchors.leftMargin: 6
-                    text: tabButton.tabTitle
-                    elide: Text.ElideRight
-                    color: tabButton.down ? "black" : tabButton.frameColor
-                    width: parent.width - button.background.width
-                }
-                Button {
-                    id: button
-                    anchors.right: parent.right
-                    anchors.verticalCenter: parent.verticalCenter
-                    anchors.rightMargin: 4
-                    height: 12
-                    background: Rectangle {
-                        implicitWidth: 12
-                        implicitHeight: 12
-                        color: button.hovered ? "#cccccc" : tabRectangle.color
-                        Text {text: "x"; anchors.centerIn: parent; color: "gray"}
-                    }
-                    onClicked: tabButton.closeTab()
-                }
-            }
-
-            onClicked: addressBar.text = (tabLayout.itemAt(TabBar.index) as WebEngineView).url;
-            function closeTab() {
-                tabBar.tryCloseView(TabBar.index);
-            }
-        }
-    }
-
-    TabBar {
-        id: tabBar
-        anchors.top: parent.top
-        anchors.left: parent.left
-        anchors.right: parent.right
-        Component.onCompleted: createTab((win.applicationRoot as ApplicationRoot).defaultProfilePrototype.instance())
-
-        function createTab(profile, focusOnNewTab = true, url = undefined) {
-            var webview = tabComponent.createObject(tabLayout, {index: tabBar.count , profile: profile});
-            var newTabButton = tabButtonComponent.createObject(tabBar, {tabTitle: Qt.binding(function () { return webview.title; })});
-            tabBar.addItem(newTabButton);
-            if (focusOnNewTab) {
-                tabBar.setCurrentIndex(tabBar.count - 1);
-            }
-            if (url !== undefined) {
-                webview.url = url;
-            }
-            return webview;
-        }
-
-        function tryCloseView(index) {
-            tabLayout.children[index].triggerWebAction(WebEngineView.RequestClose);
-        }
-
-        function removeView(index) {
-            if (tabBar.count > 1) {
-                tabBar.removeItem(tabBar.itemAt(index));
-                tabLayout.children[index].destroy();
-            } else {
-                win.lastTabClosing = true;
-                win.close();
-            }
-        }
-
-        Component {
-            id: tabComponent
-            WebEngineView {
-                id: webEngineView
-                property int index;
-                focus: true
-
-                onLinkHovered: function(hoveredUrl) {
-                    if (hoveredUrl === "")
-                        hideStatusText.start();
-                    else {
-                        statusText.text = hoveredUrl;
-                        statusBubble.visible = true;
-                        hideStatusText.stop();
-                    }
-                }
-
-                states: [
-                    State {
-                        name: "FullScreen"
-                        PropertyChanges {
-                            target: tabBar
-                            visible: false
-                            height: 0
-                        }
-                        PropertyChanges {
-                            target: navigationBar
-                            visible: false
-                        }
-                    }
-                ]
-                settings.localContentCanAccessRemoteUrls: true
-                settings.localContentCanAccessFileUrls: false
-                settings.autoLoadImages: appSettings.autoLoadImages
-                settings.javascriptEnabled: appSettings.javaScriptEnabled
-                settings.errorPageEnabled: appSettings.errorPageEnabled
-                settings.pluginsEnabled: appSettings.pluginsEnabled
-                settings.fullScreenSupportEnabled: appSettings.fullScreenSupportEnabled
-                settings.autoLoadIconsForPage: appSettings.autoLoadIconsForPage
-                settings.touchIconsEnabled: appSettings.touchIconsEnabled
-                settings.webRTCPublicInterfacesOnly: appSettings.webRTCPublicInterfacesOnly
-                settings.pdfViewerEnabled: appSettings.pdfViewerEnabled
-                settings.imageAnimationPolicy: appSettings.imageAnimationPolicy
-                settings.screenCaptureEnabled: true
-                settings.javascriptCanAccessClipboard: appSettings.javascriptCanAccessClipboard
-                settings.javascriptCanPaste: appSettings.javascriptCanPaste
-
-                onWindowCloseRequested: function() {
-                    tabBar.removeView(webEngineView.index);
-                }
-
-                onCertificateError: function(error) {
-                    if (!error.isMainFrame) {
-                        error.rejectCertificate();
-                        return;
-                    }
-
-                    error.defer();
-                    sslDialog.enqueue(error);
-                }
-
-                onNewWindowRequested: function(request) {
-                    if (!request.userInitiated)
-                        console.warn("Blocked a popup window.");
-                    else if (request.destination === WebEngineNewWindowRequest.InNewTab) {
-                        var tab = tabBar.createTab(win.currentWebView.profile, true, request.requestedUrl);
-                        tab.acceptAsNewWindow(request);
-                    } else if (request.destination === WebEngineNewWindowRequest.InNewBackgroundTab) {
-                        var backgroundTab = tabBar.createTab(win.currentWebView.profile, false);
-                        backgroundTab.acceptAsNewWindow(request);
-                    } else if (request.destination === WebEngineNewWindowRequest.InNewDialog) {
-                        var dialog = (win.applicationRoot as ApplicationRoot).createDialog(win.currentWebView.profile);
-                        dialog.win.currentWebView.acceptAsNewWindow(request);
-                    } else {
-                        var window = (win.applicationRoot as ApplicationRoot).createWindow(win.currentWebView.profile);
-                        window.win.currentWebView.acceptAsNewWindow(request);
-                    }
-                }
-
-                onFullScreenRequested: function(request) {
-                    if (request.toggleOn) {
-                        webEngineView.state = "FullScreen";
-                        win.previousVisibility = win.visibility;
-                        win.showFullScreen();
-                        fullScreenNotification.show();
-                    } else {
-                        webEngineView.state = "";
-                        win.visibility = win.previousVisibility;
-                        fullScreenNotification.hide();
-                    }
-                    request.accept();
-                }
-
-                onRegisterProtocolHandlerRequested: function(request) {
-                    console.log("accepting registerProtocolHandler request for "
-                                + request.scheme + " from " + request.origin);
-                    request.accept();
-                }
-
-                onDesktopMediaRequested: function(request) {
-                    // select the primary screen
-                    request.selectScreen(request.screensModel.index(0, 0));
-                }
-
-                onRenderProcessTerminated: function(terminationStatus, exitCode) {
-                    var status = "";
-                    switch (terminationStatus) {
-                    case WebEngineView.NormalTerminationStatus:
-                        status = "(normal exit)";
-                        break;
-                    case WebEngineView.AbnormalTerminationStatus:
-                        status = "(abnormal exit)";
-                        break;
-                    case WebEngineView.CrashedTerminationStatus:
-                        status = "(crashed)";
-                        break;
-                    case WebEngineView.KilledTerminationStatus:
-                        status = "(killed)";
-                        break;
-                    }
-
-                    print("Render process exited with code " + exitCode + " " + status);
-                    reloadTimer.running = true;
-                }
-
-                onSelectClientCertificate: function(selection) {
-                    selection.certificates[0].select();
-                }
-
-                onFindTextFinished: function(result) {
-                    if (!findBar.visible)
-                        findBar.visible = true;
-
-                    findBar.numberOfMatches = result.numberOfMatches;
-                    findBar.activeMatch = result.activeMatch;
-                }
-
-                onLoadingChanged: function(loadRequest) {
-                    if (loadRequest.status === WebEngineView.LoadStartedStatus)
-                        findBar.reset();
-                }
-
-                onPermissionRequested: function(permission) {
-                    permissionDialog.permission = permission;
-                    permissionDialog.visible = true;
-                }
-                onWebAuthUxRequested: function(request) {
-                    webAuthDialog.init(request);
-                }
-
-                Timer {
-                    id: reloadTimer
-                    interval: 0
-                    running: false
-                    repeat: false
-                    onTriggered: win.currentWebView.reload()
-                }
-            }
-        }
-    }
-    WebEngineView {
-        id: devToolsView
-        visible: devToolsEnabled.checked
-        height: visible ? 400 : 0
-        inspectedView: visible && tabBar.currentIndex < tabBar.count ? tabLayout.children[tabBar.currentIndex] : null
-        anchors.left: parent.left
-        anchors.right: parent.right
-        anchors.bottom: parent.bottom
-        onNewWindowRequested: function(request) {
-            var tab = tabBar.createTab(win.currentWebView.profile);
-            request.openIn(tab);
-        }
-
-        Timer {
-            id: hideTimer
-            interval: 0
-            running: false
-            repeat: false
-            onTriggered: devToolsEnabled.checked = false
-        }
-        onWindowCloseRequested: function() {
-            // Delay hiding for keep the inspectedView set to receive the ACK message of close.
-            hideTimer.running = true;
-        }
-    }
-    Dialog {
-        id: sslDialog
-        anchors.centerIn: parent
-        contentWidth: Math.max(mainTextForSSLDialog.width, detailedTextForSSLDialog.width)
-        contentHeight: mainTextForSSLDialog.height + detailedTextForSSLDialog.height
-        property var certErrors: []
-        // fixme: icon!
-        // icon: StandardIcon.Warning
-        standardButtons: Dialog.No | Dialog.Yes
-        title: "Server's certificate not trusted"
-        contentItem: Item {
-            Label {
-                id: mainTextForSSLDialog
-                text: "Do you wish to continue?"
-            }
-            Text {
-                id: detailedTextForSSLDialog
-                anchors.top: mainTextForSSLDialog.bottom
-                text: "If you wish so, you may continue with an unverified certificate.\n" +
-                      "Accepting an unverified certificate means\n" +
-                      "you may not be connected with the host you tried to connect to.\n" +
-                      "Do you wish to override the security check and continue?"
-            }
-        }
-
-        onAccepted: {
-            certErrors.shift().acceptCertificate();
-            presentError();
-        }
-        onRejected: reject()
-
-        function reject(){
-            certErrors.shift().rejectCertificate();
-            presentError();
-        }
-        function enqueue(error){
-            certErrors.push(error);
-            presentError();
-        }
-        function presentError(){
-            visible = certErrors.length > 0
-        }
-    }
-    Dialog {
-        id: permissionDialog
-        anchors.centerIn: parent
-        width: Math.min(win.width, win.height) / 3 * 2
-        contentWidth: mainTextForPermissionDialog.width
-        contentHeight: mainTextForPermissionDialog.height
-        standardButtons: Dialog.No | Dialog.Yes
-        title: "Permission Request"
-
-        property var permission;
-
-        contentItem: Item {
-            Label {
-                id: mainTextForPermissionDialog
-            }
-        }
-
-        onAccepted: permission.grant()
-        onRejected: permission.deny()
-        onVisibleChanged: {
-            if (visible) {
-                mainTextForPermissionDialog.text = questionForPermissionType();
-                width = contentWidth + 20;
-            }
-        }
-
-        function questionForPermissionType() {
-            var question = "Allow " + permission.origin + " to "
-
-            switch (permission.permissionType) {
-            case WebEnginePermission.PermissionType.Geolocation:
-                question += "access your location information?";
-                break;
-            case WebEnginePermission.PermissionType.MediaAudioCapture:
-                question += "access your microphone?";
-                break;
-            case WebEnginePermission.PermissionType.MediaVideoCapture:
-                question += "access your webcam?";
-                break;
-            case WebEnginePermission.PermissionType.MediaAudioVideoCapture:
-                question += "access your microphone and webcam?";
-                break;
-            case WebEnginePermission.PermissionType.MouseLock:
-                question += "lock your mouse cursor?";
-                break;
-            case WebEnginePermission.PermissionType.DesktopVideoCapture:
-                question += "capture video of your desktop?";
-                break;
-            case WebEnginePermission.PermissionType.DesktopAudioVideoCapture:
-                question += "capture audio and video of your desktop?";
-                break;
-            case WebEnginePermission.PermissionType.Notifications:
-                question += "show notification on your desktop?";
-                break;
-            case WebEnginePermission.PermissionType.ClipboardReadWrite:
-                question += "read from and write to your clipboard?";
-                break;
-            case WebEnginePermission.PermissionType.LocalFontsAccess:
-                question += "access the fonts stored on your machine?";
-                break;
-            default:
-                question += "access unknown or unsupported permission type [" + permission.permissionType + "] ?";
-                break;
-            }
-
-            return question;
-        }
-    }
-
-    FullScreenNotification {
-        id: fullScreenNotification
-    }
-
-    DownloadView {
-        id: downloadView
-        visible: false
-        anchors.fill: parent
-    }
-
-    WebAuthDialog {
-        id: webAuthDialog
-        visible: false
-    }
-
-    MessageDialog {
-        id: downloadAcceptDialog
-        property var downloadRequest: downloadView.pendingDownloadRequest
-        title: "Download requested"
-        text: downloadRequest ? downloadRequest.suggestedFileName : ""
-        buttons: Dialog.No | Dialog.Yes
-        onAccepted: {
-            downloadView.visible = true;
-            downloadView.append(downloadRequest);
-            downloadRequest.accept();
-        }
-        onRejected: {
-            downloadRequest.cancel();
-        }
-        onButtonClicked: {
-            visible = false;
-        }
-        visible: false
-    }
-
-    function onDownloadRequested(download) {
-        downloadView.pendingDownloadRequest = download;
-        downloadAcceptDialog.visible = true;
-    }
-
-    FindBar {
-        id: findBar
-        visible: false
-        anchors.right: parent.right
-        anchors.rightMargin: 10
-        anchors.top: parent.top
-
-        onFindNext: {
-            if (text)
-                win.currentWebView?.findText(text);
-            else if (!visible)
-                visible = true;
-        }
-        onFindPrevious: {
-            if (text)
-                win.currentWebView?.findText(text, WebEngineView.FindBackward);
-            else if (!visible)
-                visible = true;
-        }
-    }
-
-
-    Rectangle {
-        id: statusBubble
-        color: "oldlace"
-        property int padding: 8
-        visible: false
-
-        anchors.left: parent.left
-        anchors.bottom: parent.bottom
-        width: statusText.paintedWidth + padding
-        height: statusText.paintedHeight + padding
-
-        Text {
-            id: statusText
-            anchors.centerIn: statusBubble
-            elide: Qt.ElideMiddle
-
-            Timer {
-                id: hideStatusText
-                interval: 750
-                onTriggered: {
-                    statusText.text = "";
-                    statusBubble.visible = false;
-                }
-            }
-        }
-    }
-
-    onClosing: function(closeEvent) {
-       if (lastTabClosing) {
-           return;
-       }
-       closeEvent.accepted = false
-       for (var i = 0; i < tabBar.count; i++)  {
-           tabBar.tryCloseView(i);
-       }
-    }
-}
diff --git a/examples/webenginequick/nanobrowser/DownloadView.qml b/examples/webenginequick/nanobrowser/DownloadView.qml
deleted file mode 100644 (file)
index ef0c7f5..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-pragma ComponentBehavior: Bound
-
-import QtQuick
-import QtQuick.Controls.Fusion
-
-Rectangle {
-    id: downloadView
-    color: "lightgray"
-    property var pendingDownloadRequest: null
-
-    ListModel {
-        id: downloadModel
-        property var downloads: []
-    }
-
-    function append(download) {
-        downloadModel.append(download);
-        downloadModel.downloads.push(download);
-    }
-
-    Component {
-        id: downloadItemDelegate
-
-        Rectangle {
-            id: downloadItem
-            width: listView.width
-            height: childrenRect.height
-            anchors.margins: 10
-            radius: 3
-            color: "transparent"
-            border.color: "black"
-
-            required property int index
-
-            Rectangle {
-                id: progressBar
-
-                property real progress: {
-                    let d = downloadModel.downloads[downloadItem.index]
-                    return d ? d.receivedBytes / d.totalBytes : 0
-                }
-
-                radius: 3
-                color: width === listView.width ? "green" : "#2b74c7"
-                width: listView.width * progress
-                height: cancelButton.height
-
-                Behavior on width {
-                    SmoothedAnimation { duration: 100 }
-                }
-            }
-            Rectangle {
-                anchors {
-                    left: parent.left
-                    right: parent.right
-                    leftMargin: 20
-                }
-                Label {
-                    id: label
-                    text: {
-                        let d = downloadModel.downloads[downloadItem.index]
-                        return d ? d.downloadDirectory + "/" + d.downloadFileName : qsTr("")
-                    }
-                    anchors {
-                        verticalCenter: cancelButton.verticalCenter
-                        left: parent.left
-                        right: cancelButton.left
-                    }
-                }
-                Button {
-                    id: cancelButton
-                    anchors.right: parent.right
-                    icon.source: "icons/3rdparty/process-stop.png"
-                    onClicked: {
-                        var download = downloadModel.downloads[downloadItem.index];
-
-                        download.cancel();
-
-                        downloadModel.downloads = downloadModel.downloads.filter(function (el) {
-                            return el.id !== download.id;
-                        });
-                        downloadModel.remove(downloadItem.index);
-                    }
-                }
-            }
-        }
-
-    }
-    ListView {
-        id: listView
-        anchors {
-            topMargin: 10
-            top: parent.top
-            bottom: parent.bottom
-            horizontalCenter: parent.horizontalCenter
-        }
-        width: parent.width - 20
-        spacing: 5
-
-        model: downloadModel
-        delegate: downloadItemDelegate
-
-        Text {
-            visible: !listView.count
-            horizontalAlignment: Text.AlignHCenter
-            height: 30
-            anchors {
-                top: parent.top
-                left: parent.left
-                right: parent.right
-            }
-            font.pixelSize: 20
-            text: "No active downloads."
-        }
-
-        Rectangle {
-            color: "gray"
-            anchors {
-                bottom: parent.bottom
-                left: parent.left
-                right: parent.right
-            }
-            height: 30
-            Button {
-                id: okButton
-                text: "OK"
-                anchors.centerIn: parent
-                onClicked: {
-                    downloadView.visible = false;
-                }
-            }
-        }
-    }
-}
diff --git a/examples/webenginequick/nanobrowser/FindBar.qml b/examples/webenginequick/nanobrowser/FindBar.qml
deleted file mode 100644 (file)
index 013f28e..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls.Fusion
-import QtQuick.Layouts
-
-Rectangle {
-    id: root
-
-    property int numberOfMatches: 0
-    property int activeMatch: 0
-    property alias text: findTextField.text
-
-    function reset() {
-        numberOfMatches = 0;
-        activeMatch = 0;
-        visible = false;
-    }
-
-    signal findNext()
-    signal findPrevious()
-
-    width: 250
-    height: 35
-    radius: 2
-
-    border.width: 1
-    border.color: "black"
-    color: "white"
-
-    onVisibleChanged: {
-        if (visible)
-            findTextField.forceActiveFocus();
-    }
-
-
-    RowLayout {
-        anchors.fill: parent
-        anchors.topMargin: 5
-        anchors.bottomMargin: 5
-        anchors.leftMargin: 10
-        anchors.rightMargin: 10
-
-        spacing: 5
-
-        Rectangle {
-            Layout.fillWidth: true
-            Layout.fillHeight: true
-
-            TextField {
-                id: findTextField
-                anchors.fill: parent
-                color: "black"
-                background: Rectangle {
-                    color: "transparent"
-                }
-
-                onAccepted: root.findNext()
-                onTextChanged: root.findNext()
-                onActiveFocusChanged: activeFocus ? selectAll() : deselect()
-            }
-        }
-
-        Label {
-            text: root.activeMatch + "/" + root.numberOfMatches
-            visible: findTextField.text !== ""
-            color: "black"
-        }
-
-        Rectangle {
-            border.width: 1
-            border.color: "#dddddd"
-            Layout.preferredWidth: 2
-            Layout.preferredHeight: parent.height
-        }
-
-        ToolButton {
-            id: findBtnLeft
-            text: "<"
-            enabled: root.numberOfMatches > 0
-            onClicked: root.findPrevious()
-            contentItem: Text {
-                color: "black"
-                text: findBtnLeft.text
-            }
-        }
-
-        ToolButton {
-            id: findBtnRight
-            text: ">"
-            enabled: root.numberOfMatches > 0
-            onClicked: root.findNext()
-            contentItem: Text {
-                color: "black"
-                text: findBtnRight.text
-            }
-        }
-
-        ToolButton {
-            id: findBtnClose
-            text: "x"
-            onClicked: root.visible = false
-            contentItem: Text {
-                color: "black"
-                text: findBtnClose.text
-            }
-        }
-    }
-}
diff --git a/examples/webenginequick/nanobrowser/FullScreenNotification.qml b/examples/webenginequick/nanobrowser/FullScreenNotification.qml
deleted file mode 100644 (file)
index cdf154c..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Rectangle {
-    id: fullScreenNotification
-    width: 500
-    height: 40
-    color: "white"
-    radius: 7
-
-    visible: false
-    opacity: 0
-
-    function show() {
-        visible = true;
-        opacity = 1;
-        reset.start();
-    }
-
-    function hide() {
-        reset.stop();
-        opacity = 0;
-    }
-
-    Behavior on opacity {
-        NumberAnimation {
-            duration: 750
-            onStopped: {
-                if (fullScreenNotification.opacity === 0)
-                    fullScreenNotification.visible = false;
-            }
-        }
-    }
-
-    Timer {
-        id: reset
-        interval: 5000
-        onTriggered: fullScreenNotification.hide()
-    }
-
-    anchors.horizontalCenter: parent.horizontalCenter
-    y: 125
-
-    Text {
-        id: message
-        width: parent.width
-
-        anchors.horizontalCenter: parent.horizontalCenter
-        anchors.verticalCenter: parent.verticalCenter
-
-        horizontalAlignment: Text.AlignHCenter
-        verticalAlignment: Text.AlignVCenter
-
-        wrapMode: Text.WordWrap
-        elide: Text.ElideNone
-        clip: true
-
-        text: qsTr("You are now in fullscreen mode. Press ESC to quit!")
-    }
-}
diff --git a/examples/webenginequick/nanobrowser/WebAuthDialog.qml b/examples/webenginequick/nanobrowser/WebAuthDialog.qml
deleted file mode 100644 (file)
index 8d5cf59..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-pragma ComponentBehavior: Bound
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtWebEngine
-
-Dialog {
-    id: webAuthDialog
-    anchors.centerIn: parent
-    width: Math.min(parent.parent.width, parent.parent.height) / 3 * 2
-    contentWidth: verticalLayout.width +10;
-    contentHeight: verticalLayout.height +10;
-    standardButtons: Dialog.Cancel | Dialog.Apply
-    title: "WebAuth Request"
-
-    property var selectAccount;
-    property var authrequest: null;
-
-    Connections {
-        id: webauthConnection
-        ignoreUnknownSignals: true
-        function onStateChanged(state) {
-            webAuthDialog.setupUI(state);
-        }
-    }
-
-    onApplied: {
-        switch (webAuthDialog.authrequest.state) {
-            case WebEngineWebAuthUxRequest.WebAuthUxState.CollectPin:
-                webAuthDialog.authrequest.setPin(pinEdit.text);
-                break;
-            case WebEngineWebAuthUxRequest.WebAuthUxState.SelectAccount:
-                webAuthDialog.authrequest.setSelectedAccount(webAuthDialog.selectAccount);
-                break;
-             default:
-                 break;
-        }
-    }
-
-    onRejected: {
-        webAuthDialog.authrequest.cancel();
-    }
-
-    function init(request) {
-        pinLabel.visible = false;
-        pinEdit.visible = false;
-        confirmPinLabel.visible = false;
-        confirmPinEdit.visible = false;
-        selectAccountModel.clear();
-        webAuthDialog.authrequest = request;
-        webauthConnection.target = request;
-        setupUI(webAuthDialog.authrequest.state)
-        webAuthDialog.visible = true;
-        pinEntryError.visible = false;
-    }
-
-    function setupUI(state) {
-        switch (state) {
-        case WebEngineWebAuthUxRequest.WebAuthUxState.SelectAccount:
-            setupSelectAccountUI();
-            break;
-        case WebEngineWebAuthUxRequest.WebAuthUxState.CollectPin:
-            setupCollectPin();
-            break;
-        case WebEngineWebAuthUxRequest.WebAuthUxState.FinishTokenCollection:
-            setupFinishCollectToken();
-            break;
-        case WebEngineWebAuthUxRequest.WebAuthUxState.RequestFailed:
-            setupErrorUI();
-            break;
-        case WebEngineWebAuthUxRequest.WebAuthUxState.Completed:
-            webAuthDialog.close();
-            break;
-        }
-    }
-
-    ButtonGroup {
-        id : selectAccount;
-        exclusive: true;
-    }
-
-    ListModel {
-        id: selectAccountModel
-
-    }
-    contentItem: Item {
-        ColumnLayout  {
-            id : verticalLayout
-            spacing : 10
-
-            Label {
-                id: heading
-                text: "";
-            }
-
-            Label {
-                id: description
-                text: "";
-            }
-
-            Row {
-                spacing : 10
-                Label {
-                    id: pinLabel
-                    text: "PIN";
-                }
-                TextInput {
-                    id: pinEdit
-                    text: "EnterPin"
-                    enabled: true
-                    focus: true
-                    color: "white"
-                    layer.sourceRect: Qt.rect(0, 0, 20, 20)
-                }
-            }
-
-            Row {
-                spacing : 10
-                Label {
-                    id: confirmPinLabel
-                    text: "Confirm PIN";
-                }
-                TextEdit {
-                    id: confirmPinEdit
-                    text: ""
-                }
-            }
-
-            Label {
-                id: pinEntryError
-                text: "";
-            }
-
-            Repeater {
-                id : selectAccountRepeater
-                model: selectAccountModel
-                Column {
-                    id: selectAccountRepeaterColumn
-                    required property string modelData
-                    spacing : 5
-                    RadioButton {
-                        text: selectAccountRepeaterColumn.modelData
-                        ButtonGroup.group : webAuthDialog.selectAccount;
-                        onClicked: function(){
-                            webAuthDialog.selectAccount = text;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    function setupSelectAccountUI() {
-        webAuthDialog.selectAccount = "";
-        heading.text = "Choose a passkey";
-        description.text = "Which passkey do you want to use for " + webAuthDialog.authrequest.relyingPartyId;
-
-        selectAccountModel.clear();
-        var userNames = webAuthDialog.authrequest.userNames;
-        for (var i = 0; i < userNames.length; i++) {
-            selectAccountModel.append( {"name" : userNames[i]});
-        }
-        pinLabel.visible = false;
-        pinEdit.visible = false;
-        confirmPinLabel.visible = false;
-        confirmPinEdit.visible = false;
-        pinEntryError.visible = false;
-        standardButton(Dialog.Apply).visible = true;
-        standardButton(Dialog.Cancel).visible = true;
-        standardButton(Dialog.Cancel).text ="Cancel"
-    }
-
-    function setupCollectPin() {
-        var requestInfo = webAuthDialog.authrequest.pinRequest;
-
-        pinEdit.clear();
-
-        if (requestInfo.reason === WebEngineWebAuthUxRequest.PinEntryReason.Challenge) {
-            heading.text = "PIN required";
-            description.text = "Enter the PIN for your security key";
-            pinLabel.visible = true;
-            pinEdit.visible = true;
-            confirmPinLabel.visible = false;
-            confirmPinEdit.visible = false;
-        } else if (requestInfo.reason === WebEngineWebAuthUxRequest.PinEntryReason.Set) {
-            heading.text = "Set PIN ";
-            description.text = "Set new PIN for your security key";
-            pinLabel.visible = true;
-            pinEdit.visible = true;
-            confirmPinLabel.visible = true;
-            confirmPinEdit.visible = true;
-        }
-        pinEntryError.text = getPINErrorDetails() + " " + requestInfo.remainingAttempts + " attempts reamining";
-        pinEntryError.visible = true;
-        selectAccountModel.clear();
-        standardButton(Dialog.Cancel).visible = true;
-        standardButton(Dialog.Cancel).text ="Cancel"
-        standardButton(Dialog.Apply).visible = true;
-    }
-
-    function getPINErrorDetails() {
-        var requestInfo = webAuthDialog.authrequest.pinRequest;
-        switch (requestInfo.error) {
-        case WebEngineWebAuthUxRequest.PinEntryError.NoError:
-            return "";
-        case WebEngineWebAuthUxRequest.PinEntryError.TooShort:
-            return "Too short";
-        case WebEngineWebAuthUxRequest.PinEntryError.InternalUvLocked:
-            return "Internal Uv locked";
-        case WebEngineWebAuthUxRequest.PinEntryError.WrongPin:
-            return "Wrong PIN";
-        case WebEngineWebAuthUxRequest.PinEntryError.InvalidCharacters:
-            return "Invalid characters";
-        case WebEngineWebAuthUxRequest.PinEntryError.SameAsCurrentPin:
-            return "Same as current PIN";
-        }
-    }
-
-    function getRequestFailureResaon() {
-        var requestFailureReason = webAuthDialog.authrequest.requestFailureReason;
-        switch (requestFailureReason) {
-        case WebEngineWebAuthUxRequest.RequestFailureReason.Timeout:
-            return " Request Timeout";
-        case WebEngineWebAuthUxRequest.RequestFailureReason.KeyNotRegistered:
-            return "Key not registered";
-        case WebEngineWebAuthUxRequest.RequestFailureReason.KeyAlreadyRegistered:
-            return "You already registered this device. You don't have to register it again\n"
-                     + "Try again with different key or device.";
-        case WebEngineWebAuthUxRequest.RequestFailureReason.SoftPinBlock:
-            return "The security key is locked because the wrong PIN was entered too many times.\n"
-                    + "To unlock it, remove and reinsert it.";
-        case WebEngineWebAuthUxRequest.RequestFailureReason.HardPinBlock:
-            return "The security key is locked because the wrong PIN was entered too many times.\n"
-                    + "You'll need to reset the security key.";
-        case WebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorRemovedDuringPinEntry:
-            return "Authenticator removed during verification. Please reinsert and try again";
-        case WebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorMissingResidentKeys:
-            return "Authenticator doesn't have resident key support";
-        case WebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorMissingUserVerification:
-            return "Authenticator missing user verification";
-        case WebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorMissingLargeBlob:
-            return "Authenticator missing Large Blob support";
-        case WebEngineWebAuthUxRequest.RequestFailureReason.NoCommonAlgorithms:
-            return "No common Algorithms";
-        case WebEngineWebAuthUxRequest.RequestFailureReason.StorageFull:
-            return "Storage full";
-        case WebEngineWebAuthUxRequest.RequestFailureReason.UserConsentDenied:
-            return "User consent denied";
-        case WebEngineWebAuthUxRequest.RequestFailureReason.WinUserCancelled:
-            return "User cancelled request";
-        }
-    }
-
-    function setupFinishCollectToken() {
-        heading.text = "Use your security key with " + webAuthDialog.authrequest.relyingPartyId;
-        description.text = "Touch your security key again to complete the request.";
-        pinLabel.visible = false;
-        pinEdit.visible = false;
-        confirmPinLabel.visible = false;
-        confirmPinEdit.visible = false;
-        selectAccountModel.clear();
-        pinEntryError.visible = false;
-        standardButton(Dialog.Apply).visible = false;
-        standardButton(Dialog.Cancel).visible = true;
-        standardButton(Dialog.Cancel).text ="Cancel"
-    }
-
-    function setupErrorUI() {
-        heading.text = "Something went wrong";
-        description.text = getRequestFailureResaon();
-        pinLabel.visible = false;
-        pinEdit.visible = false;
-        confirmPinLabel.visible = false;
-        confirmPinEdit.visible = false;
-        selectAccountModel.clear();
-        pinEntryError.visible = false;
-        standardButton(Dialog.Apply).visible = false;
-        standardButton(Dialog.Cancel).visible = true;
-        standardButton(Dialog.Cancel).text ="Close"
-    }
-}
diff --git a/examples/webenginequick/nanobrowser/doc/nanobrowser.png b/examples/webenginequick/nanobrowser/doc/nanobrowser.png
deleted file mode 100644 (file)
index 3dddb05..0000000
Binary files a/examples/webenginequick/nanobrowser/doc/nanobrowser.png and /dev/null differ
index 8509850729265dea0c14ee4e4954e0e85c3d4655..a05211f2fa600ce66e90321b5c4b6e6f6b9a5e47 100644 (file)
@@ -1,8 +1,10 @@
 Nano Browser Example
 ====================
 
-A web browser implemented using the WebEngineView QML type.
+A web browser implemented using the `WebEngineView`_ QML type.
 
-.. image:: nanobrowser.png
-   :width: 400
+.. image:: nanobrowser.webp
+   :width: 645
    :alt: Nano Browser Screenshot
+
+.. _`WebEngineView`: https://doc.qt.io/qt-6/qml-qtwebengine-webengineview.html
diff --git a/examples/webenginequick/nanobrowser/doc/nanobrowser.webp b/examples/webenginequick/nanobrowser/doc/nanobrowser.webp
new file mode 100644 (file)
index 0000000..076154f
Binary files /dev/null and b/examples/webenginequick/nanobrowser/doc/nanobrowser.webp differ
diff --git a/examples/webenginequick/nanobrowser/main.py b/examples/webenginequick/nanobrowser/main.py
new file mode 100644 (file)
index 0000000..df0dd5e
--- /dev/null
@@ -0,0 +1,72 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+"""PySide6 WebEngine QtQuick 2 Example"""
+
+import sys
+from argparse import ArgumentParser, RawTextHelpFormatter
+from pathlib import Path
+
+from PySide6.QtCore import (QCoreApplication, QFileInfo, QMetaObject, QObject,
+                            QUrl, Slot, Q_ARG)
+from PySide6.QtQml import QQmlApplicationEngine, QmlElement, QmlSingleton
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtWebEngineQuick import QtWebEngineQuick
+
+import rc_resources  # noqa: F401
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "BrowserUtils"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+def url_from_user_input(user_input):
+    file_info = QFileInfo(user_input)
+    if file_info.exists():
+        return QUrl.fromLocalFile(file_info.absoluteFilePath())
+    return QUrl.fromUserInput(user_input)
+
+
+@QmlElement
+@QmlSingleton
+class Utils(QObject):
+
+    @Slot(str, result=QUrl)
+    def fromUserInput(self, user_input):
+        return url_from_user_input(user_input)
+
+
+if __name__ == '__main__':
+    QCoreApplication.setApplicationName("Quick Nano Browser")
+    QCoreApplication.setOrganizationName("QtProject")
+
+    QtWebEngineQuick.initialize()
+
+    argument_parser = ArgumentParser(description="Quick Nano Browser",
+                                     formatter_class=RawTextHelpFormatter)
+    argument_parser.add_argument("--single-process", "-s", action="store_true",
+                                 help="Run in single process mode (trouble shooting)")
+    argument_parser.add_argument("url", help="The URL to open",
+                                 nargs='?', type=str)
+    options = argument_parser.parse_args()
+
+    url = url_from_user_input(options.url) if options.url else QUrl("chrome://qt")
+
+    app_args = sys.argv
+    if options.single_process:
+        app_args.extend(["--webEngineArgs", "--single-process"])
+    app = QGuiApplication(app_args)
+    engine = QQmlApplicationEngine()
+    engine.addImportPath(Path(__file__).parent)
+    engine.loadFromModule("BrowserUtils", "ApplicationRoot")
+    if not engine.rootObjects():
+        sys.exit(-1)
+
+    QMetaObject.invokeMethod(engine.rootObjects()[0], "load", Q_ARG("QVariant", url))
+
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
index 392748d6828a9d38af083f48fd98f97aabbd1a42..fb03fd6c6db800023846efa265499bf49739c252 100644 (file)
@@ -1,6 +1,7 @@
 {
-    "files": ["quicknanobrowser.py", "ApplicationRoot.qml",
-              "BrowserDialog.qml", "BrowserWindow.qml", "DownloadView.qml",
-              "FindBar.qml", "FullScreenNotification.qml", "WebAuthDialog.qml",
-              "resources.qrc"]
+    "files": ["main.py", "BrowserUtils/ApplicationRoot.qml",
+              "BrowserUtils/BrowserDialog.qml", "BrowserUtils/BrowserWindow.qml",
+              "BrowserUtils/DownloadView.qml", "BrowserUtils/FindBar.qml",
+              "BrowserUtils/FullScreenNotification.qml", "BrowserUtils/WebAuthDialog.qml",
+              "BrowserUtils/qmldir", "resources.qrc"]
 }
index d4d0abdccb8a72ec3f5b19ac2d218d435b41398b..036b2e22307d9307c540dee245b45098ceced9dc 100644 (file)
@@ -1,4 +1,5 @@
 module BrowserUtils
+prefer :/qt/qml/BrowserUtils/
 ApplicationRoot 254.0 ApplicationRoot.qml
 BrowserDialog 254.0 BrowserDialog.qml
 BrowserWindow 254.0 BrowserWindow.qml
diff --git a/examples/webenginequick/nanobrowser/quicknanobrowser.py b/examples/webenginequick/nanobrowser/quicknanobrowser.py
deleted file mode 100644 (file)
index f68cc2e..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-from __future__ import annotations
-
-"""PySide6 WebEngine QtQuick 2 Example"""
-
-import os
-import sys
-from argparse import ArgumentParser, RawTextHelpFormatter
-from pathlib import Path
-
-from PySide6.QtCore import (QCoreApplication, QFileInfo, QMetaObject, QObject,
-                            QUrl, Slot, Q_ARG)
-from PySide6.QtQml import QQmlApplicationEngine, QmlElement, QmlSingleton
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtWebEngineQuick import QtWebEngineQuick
-
-import rc_resources  # noqa: F401
-
-
-# To be used on the @QmlElement decorator
-# (QML_IMPORT_MINOR_VERSION is optional)
-QML_IMPORT_NAME = "BrowserUtils"
-QML_IMPORT_MAJOR_VERSION = 1
-
-
-def url_from_user_input(user_input):
-    file_info = QFileInfo(user_input)
-    if file_info.exists():
-        return QUrl.fromLocalFile(file_info.absoluteFilePath())
-    return QUrl.fromUserInput(user_input)
-
-
-@QmlElement
-@QmlSingleton
-class Utils(QObject):
-
-    @Slot(str, result=QUrl)
-    def fromUserInput(self, user_input):
-        return url_from_user_input(user_input)
-
-
-if __name__ == '__main__':
-    QCoreApplication.setApplicationName("Quick Nano Browser")
-    QCoreApplication.setOrganizationName("QtProject")
-
-    QtWebEngineQuick.initialize()
-
-    argument_parser = ArgumentParser(description="Quick Nano Browser",
-                                     formatter_class=RawTextHelpFormatter)
-    argument_parser.add_argument("--single-process", "-s", action="store_true",
-                                 help="Run in single process mode (trouble shooting)")
-    argument_parser.add_argument("url", help="The URL to open",
-                                 nargs='?', type=str)
-    options = argument_parser.parse_args()
-
-    url = url_from_user_input(options.url) if options.url else QUrl("chrome://qt")
-
-    app_args = sys.argv
-    if options.single_process:
-        app_args.extend(["--webEngineArgs", "--single-process"])
-    app = QGuiApplication(app_args)
-    engine = QQmlApplicationEngine()
-    qml_file = os.fspath(Path(__file__).resolve().parent / 'ApplicationRoot.qml')
-    engine.load(QUrl.fromLocalFile(qml_file))
-    if not engine.rootObjects():
-        sys.exit(-1)
-
-    QMetaObject.invokeMethod(engine.rootObjects()[0], "load", Q_ARG("QVariant", url))
-
-    exit_code = app.exec()
-    del engine
-    sys.exit(exit_code)
index b517c39a055e3e0ae37db8600bea494c00e498cc..d831f0c0f1d869eb66892b0495c9a07d7a7fc66e 100644 (file)
@@ -1,6 +1,6 @@
 # Resource object code (Python 3)
 # Created by: object code
-# Created by: The Resource Compiler for Qt version 6.10.0
+# Created by: The Resource Compiler for Qt version 6.10.2
 # WARNING! All changes made in this file will be lost!
 
 from PySide6 import QtCore
@@ -306,6 +306,10 @@ qt_resource_name = b"\
 \x00o\xa6S\
 \x00i\
 \x00c\x00o\x00n\x00s\
+\x00\x08\
+\x08\xb6\x8e\xf9\
+\x003\
+\x00r\x00d\x00p\x00a\x00r\x00t\x00y\
 \x00\x0b\
 \x0c+\x1f\xc7\
 \x00g\
@@ -327,15 +331,17 @@ qt_resource_name = b"\
 qt_resource_struct = b"\
 \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
 \x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x02\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
 \x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00,\x00\x00\x00\x00\x00\x01\x00\x00\x03\xa6\
+\x00\x00\x00\x10\x00\x02\x00\x00\x00\x04\x00\x00\x00\x03\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00B\x00\x00\x00\x00\x00\x01\x00\x00\x03\xa6\
 \x00\x00\x01\x975l\xc7\xfb\
-\x00\x00\x00R\x00\x00\x00\x00\x00\x01\x00\x00\x08\xfe\
+\x00\x00\x00h\x00\x00\x00\x00\x00\x01\x00\x00\x08\xfe\
 \x00\x00\x01\x975l\xc7\xfb\
-\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x00&\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
 \x00\x00\x01\x975l\xc7\xfb\
-\x00\x00\x00x\x00\x00\x00\x00\x00\x01\x00\x00\x0d\xfa\
+\x00\x00\x00\x8e\x00\x00\x00\x00\x00\x01\x00\x00\x0d\xfa\
 \x00\x00\x01\x975l\xc7\xfb\
 "
 
index 829285ec0d907d63a0543b9168de402fcd1b87b8..f44bc2bf25530a499e391ae5f67e66e6838ba8fe 100644 (file)
@@ -1,8 +1,8 @@
 <RCC>
-    <qresource prefix="/icons">
-        <file alias="go-next.png">icons/3rdparty/go-next.png</file>
-        <file alias="go-previous.png">icons/3rdparty/go-previous.png</file>
-        <file alias="process-stop.png">icons/3rdparty/process-stop.png</file>
-        <file alias="view-refresh.png">icons/3rdparty/view-refresh.png</file>
+    <qresource>
+        <file>icons/3rdparty/go-next.png</file>
+        <file>icons/3rdparty/go-previous.png</file>
+        <file>icons/3rdparty/process-stop.png</file>
+        <file>icons/3rdparty/view-refresh.png</file>
     </qresource>
 </RCC>
index ad28304ef93722579b0e050405b52572f770b21f..1d27fcbc70e8e1436533329def798300daf0366c 100644 (file)
@@ -204,20 +204,17 @@ class BrowserWindow(QMainWindow):
 
     @Slot()
     def _zoom_in(self):
-        tab = self.current_tab()
-        if tab:
+        if tab := self.current_tab():
             tab.setZoomFactor(tab.zoomFactor() + 0.1)
 
     @Slot()
     def _zoom_out(self):
-        tab = self.current_tab()
-        if tab:
+        if tab := self.current_tab():
             tab.setZoomFactor(tab.zoomFactor() - 0.1)
 
     @Slot()
     def _reset_zoom(self):
-        tab = self.current_tab()
-        if tab:
+        if tab := self.current_tab():
             tab.setZoomFactor(1)
 
     @Slot()
@@ -476,8 +473,7 @@ class BrowserWindow(QMainWindow):
             self._progress_bar.setValue(0)
 
     def handle_show_window_triggered(self):
-        action = self.sender()
-        if action:
+        if action := self.sender():
             offset = action.data()
             window = self._browser.windows()[offset]
             window.activateWindow()
index 4a5a933bb234bbec0a6515cefbc89688f440e19f..3e931b86987e465e28da129b25f1b37f240bf369 100644 (file)
@@ -196,8 +196,7 @@ class TabWidget(QTabWidget):
             self.close_tab(i)
 
     def close_tab(self, index):
-        view = self.web_view(index)
-        if view:
+        if view := self.web_view(index):
             has_focus = view.hasFocus()
             self.removeTab(index)
             if has_focus and self.count() > 0:
@@ -207,20 +206,17 @@ class TabWidget(QTabWidget):
             view.deleteLater()
 
     def clone_tab(self, index):
-        view = self.web_view(index)
-        if view:
+        if view := self.web_view(index):
             tab = self.create_tab()
             tab.setUrl(view.url())
 
     def set_url(self, url):
-        view = self.current_web_view()
-        if view:
+        if view := self.current_web_view():
             view.setUrl(url)
             view.setFocus()
 
     def trigger_web_page_action(self, action):
-        web_view = self.current_web_view()
-        if web_view:
+        if web_view := self.current_web_view():
             web_view.triggerPageAction(action)
             web_view.setFocus()
 
@@ -237,6 +233,5 @@ class TabWidget(QTabWidget):
         self.setCurrentIndex(next)
 
     def reload_tab(self, index):
-        view = self.web_view(index)
-        if view:
+        if view := self.web_view(index):
             view.reload()
index 5b5ed1733835a6757a5199ea9ebc4037160d981e..68e0186f1e44898cb7d2121e37f79c11933ab7e3 100644 (file)
@@ -46,8 +46,7 @@ class WebPopupWindow(QWidget):
         return self._view
 
     def handle_geometry_change_requested(self, newGeometry):
-        window = self.windowHandle()
-        if window:
+        if window := self.windowHandle():
             self.setGeometry(newGeometry.marginsRemoved(window.frameMargins()))
         self.show()
         self._view.setFocus()
index f647c62607ae7e05a3c833ef20c10213a9e571f3..e30a00278ce4dfc0fe40fa4dccab2b7029148676 100644 (file)
@@ -21,6 +21,7 @@ ApplicationWindow {
             ToolButton {
                 id: backButton
                 icon.source: "qrc:/left-32.png"
+                icon.color: "transparent"
                 onClicked: webView.goBack()
                 enabled: webView.canGoBack
                 Layout.preferredWidth: navigationBar.height
@@ -29,6 +30,7 @@ ApplicationWindow {
             ToolButton {
                 id: forwardButton
                 icon.source: "qrc:/right-32.png"
+                icon.color: "transparent"
                 onClicked: webView.goForward()
                 enabled: webView.canGoForward
                 Layout.preferredWidth: navigationBar.height
@@ -37,6 +39,7 @@ ApplicationWindow {
             ToolButton {
                 id: reloadButton
                 icon.source: webView.loading ? "qrc:/stop-32.png" : "qrc:/refresh-32.png"
+                icon.color: "transparent"
                 onClicked: webView.loading ? webView.stop() : webView.reload()
                 Layout.preferredWidth: navigationBar.height
             }
index f3604716a18d6f99c9d676bc845506d12b0c58de..49e81fd5ec87228d40dc588dee547a1c0c242315 100644 (file)
@@ -5,5 +5,6 @@
         <file alias="stop-32.png">images/stop-32.png</file>
         <file alias="refresh-32.png">images/refresh-32.png</file>
         <file alias="right-32.png">images/right-32.png</file>
+        <file alias="settings-32.png">images/settings-32.png</file>
     </qresource>
 </RCC>
index 104eadba1884d450e42e9fe6efd4232f32a71db0..39db3b0291dfa7824762b494790bd4e27d80efee 100644 (file)
@@ -1,6 +1,6 @@
 # Resource object code (Python 3)
 # Created by: object code
-# Created by: The Resource Compiler for Qt version 6.7.2
+# Created by: The Resource Compiler for Qt version 6.10.2
 # WARNING! All changes made in this file will be lost!
 
 from PySide6 import QtCore
@@ -186,85 +186,458 @@ C\xaa]\x15\x0d\xf4\x5c{\x8d\xddV\xb0\x8cb\x00v\
 \xc1~\x98\xc8\xe5X\x00\xaa\x80\xa6|;$\x03p\x93\
 \xdc|.\x1c\xe8\xc7\xe9\xbe\xdb_E\x87\x0e'\xe81\
 \xfc\xef\x00\x00\x00\x00IEND\xaeB`\x82\
-\x00\x00\x04\xcf\
-\x00\
-\x00\x141x\xda\xc5X\xddS\xe36\x10\x7f\xf7_\xa1\
-\xe6\xe1&\xe9L\x1c>\xae\xd3\x99\xb4\xf4\x06B\xb9\xa3\
-\x93\x9b\x03\x02\xe5\xfa(l%VQ$#\xc9\x04z\
-\xbd\xff\xbd+\xc9vl\xd9\x0e\xa1\xf4\xa8\x9f\xa2\xdd\xf5\
-\xee\xea\xb7\x9f\xceh\x84&\x22}\x94t\x91h\xd4\x9f\
-\x0c\xd0\xde\xce\xee\x8f\xe82!\xe8\x5c\x03g\x99b\xfe\
-\x88\xa6:\x0e\x83\xd1\x08\xcd\xce\x8e?\x0f\xa74\x22\x5c\
-\x91\xe1iL\xb8\xa6sJ\xe4\x18\xe5\xb4\x0b2\x1f\x9e\
-\xeb!\xbc\xb6$2\xa2\x98\xa1O\x17\xe8hv<\xdc\
-\x1fN\x18\xce\x14\x09\x02\xbaL\x85\xd4\xa0\xfc<\xa3\xd1\
-\xadw\x0c'\x82k)\x98Z\xd3\xaf\xc9\xcd\xef\x94\xac\
-|\xc1)~\x14\x99VA\x10\x1c\xa6)\xa3\x11\xd6T\
-\xf0k\xcac\xb1B_\x02\x04\x0f\x8d\xc7he\x09\xf6\
-xO\x15\xbdad\x8c\xb4\xcc\x88\xa5<\x8c\x11\xe5T\
-\x83\x97\x9f\xed\xf9\xb1<\xffa\xcf+\x1a\xeb\xa4\xa4]\
-\x9b\x93\xa5'\xc4`U2>\xd8\xa3\xe5h\xaa\x8d\x85\
-\x95s9\xb4\xc7\xc0r\x96\x84gG\x18\x80\xba\x14\x82\
-\xc1\x8f\xdc\xc7\xc2O\x8e\xef\xe9\xc2^\x01x%\xe7B\
-\xac\xdc5+\xd2\xe6\xc1<J\x84T\xe1\x9c26F\
-)\x96\x10\x87\x9a\x80JqD\xf9b\x8cv\x82\x1a\xdd\
-\x1a\xcf\xb4\x16\xdc\xd3X\xf8q\x83\xa3['\xd0dG\
-\x82\x87Jd2\x82\x1b\xf6\xeed4\x1e12\xd7\xc3\
-\xfd\xbd0\xe5\x8b^C^\xf0\x09\x84\xe5\x96\xc4k<\
-\x16\xe2\x08\xf4\xf7\x07\x0dY\xc21\x84\xa6\x22\x19a\xfe\
-\xde\x0a7D\x1d a*\xc9\x9cHI\xe2k\x17\xa4\
-\x1a\x80a\xb2\x0eI\xf1|}&\x12s!WX\xc6\
-\xdb\x83a\x0b\xe8yh\x9c8\x1b[\x03\x92\xcb\xffo\
-\x98H\xc2\x04\xde\x0a\x92\xc2m#\x0f\x89\x88\xde\xe5 \
-)-\xd2\x02#T\x22G\xe6\x92\xa8\xe49\xd8\xad\xf5\
-\x16\x14\xa3\xb9?@k\x11\xe7l\x0b\xb6\xff\x11Z\xa7\
-\x9a,\xd1\x97.m?4\xc0%\x0f\xfa\x84\x12\x16\xb7\
-`\x9b\xeb0\xe5\x9c\xbf^\xf6(?\x04\x99dVK\
-\x93\xc9\xd3L\x7f$:\x11\xf1\x07\xca\xb5\x1aC\xa7\x0c\
-O\x97\xc9\x95d\x93\x04K\x1ci\x22\xd5'\xce\x1e\xd1\
-\xdf9\xe7\xcc\xba<\x15+\xe8\xd4X5\xcdi\xf0x\
-\x0d'\x18n\x09\xcca\x14\x91TW#\x03r\xe8\x00\
-e\x9a2\xe8OR,\xaf\x14\x91\xa7\xc6\xb7\xbe\xd1\xe7\
-E\xe3\x85\x90>\x95\xaf\xd0o\xdas\xd5]\xedN]\
-\xca~\xef\xbd\xe8\x0d6\xa5\x5cS\xb5y\x00\xc2\x0a\xe2\
-a\x04\x03\x8f\xea\x96dk\x91MhL:$\x9f\x06\
-\xb1\x88\x7f\xd8\x82\xa6\x05\xf4%\xf5\xad\x88\xd6PUj\
-\xfb\xa6W\xbc\xb1M\xed\xb6\x03Yh8\x96\x18\x121\
-\xb43\x17\xee\xdeo\xa5\xff\x82v\x06P\xf3;\xa8\x98\
-\xec9\xfd{\xb4;z\xfb\x04\x18\xafV\xfa\xbb;U\
-\xcb\x95\x9fgR,\xa0\xd1\xa9\xfa\xf8/\xd1Osv\
-\x9d\x93\xcf\xfa\x16\xf4\xcc\xf4-\xa6\x7fh\x0e\xcd4\x17\
-i)p# \xa6\xcb\x86\x88t\xabL.$\x1b7\
-/\x0c}\xc4rAy\xcd\x9c#\xb5k\xf4\xc4+\xb4\
-`S\x88\xf2\xcdj\xbfN\xfd\xcb\xf6\xb2\x94a\x0dC\
-y\x19\x0a\x85\x0e\x0e\x0eP\x0f\xf3X\x0a\x1a\xf7 !\
-\x86\xbb\x90\x11\xc3\xbd\xfakf\x97YH\x91q\xc0\xd6\
-E\xcc\xb3Vn\x84\x9e\xf6\xef\x8cv*T\x0f\xbdy\
-\xd3\xca\x83\xdc\x93\xda\xcbuS\xa6f\xdf\xf2\x02`\xd2\
-\xc1#\xdec\x96y#\xb2\xc8\x0c\xf4\xb3\x11\xafL\xb5\
-\x1a\xb3\xaa\xde]%\xcfKw\xbb\xa0\xad\x94]\xf9\x04\
-\xfe\xe6\xb8)\xec\x13\xc1\xb2%o]<\xa7\xf8\x86\xb0\
-\x96Tt\xfd\xb4\xf7\x1b\xbe\xc7\xb3H\xd2\xd4\x03\xa7\x8e\
-\xfb$!\xb0d\x8a\x87\x8e&\xf4g\xa9\xe4W\xb7\x04\
-\x15\xf2]V\xf3]\xa9\xb5\xf7\x98Wg\x1ak\x023\
-\x90/\xaas\xaa\x00(l\xd83\xdd'*_\x84\x5c\
-39`5\x91x\xb0\xe1^O\x803\x15\x11|\x0e\
-\xc1\x9a\x22\xf1\x82\xbc\x00\x1ff\xf4\xcc\x9c\x9a\xd7A\xa8\
-\xc5\xe27\xc2\xe8\x901\xf8|\x83=\x88 \x0c\x9b\x85\
-R/\xc0\x09\x1b]'\xa0\xea\xd0jz\x1d\xa8<\xa3\
-\xdf4\x95`\x1ak\xa8^\x04\x9f\x069Z\x0e\xb9\xab\
-\x8b\xa9zi~M\x9c\xee\x09\xe6\xee\x22\xe6J\xb0I\
-\xaaWL\xb8.\x17\xfe\x0d\xa6\xb5n\x99\xff\xa1\xe05\
-\xcc\xdc\x89\x92\x06\x8bV\xf9\x85\x7fU\xd9}\xbd\x0e\xea\
-\xad)\xb51\x5c\x88v\x0f\xea\xe2\x9f\x84\x9c\xe7m\x1e\
-\x82O\xddgN\x89\xd4<\xe3\x91YS\xfaf.\x5c\
-\x90\xbb\x8c(=\xf0\xe2H\xe7\xa8\xca\x0ea5\x11r\
-\xa6%\xe8i\xee\x8b\x90CJ0\xe2\x84:_\xfb)\
-hY\x84\xcc_C\x82\x1b\xb7!\xa8\xf0\x9b\x11\xdd\xdc\
-\xf2:\x9byX\x0d!\xda\xd0\x8c\xdfU\xc2\x8b\xec\xa8\
-\xbe\xe2\x91;\xd6,mh\x8b\x1d\xb6\xda\xda\xda\xb6\xd6\
-67\x97\x0e\x83~sx\xd6\xd5\xb6\xac\xc8Mw\xed\
-\xac\xa8m\x1c)J\xe8k\xf0\x0f\x96\x1c\x9e\xf1\
+\x00\x00\x17;\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
+\x00\x00\x07\xc3zTXtRaw prof\
+ile type exif\x00\x00x\
+\xda\xadX[v\xec\xba\x0a\xfc\xd7(\xce\x10\xac\x07\x02\
+\x0d\x07=X\xeb\xce\xe0\x0e\xff\x14\xb2\xdd\xe9\xbcvr\
+\x92m\xa7-[\x96\x05\xa2\xa0@\x09\xeb\xff\xff\xb3\xf0\
+\x0f\x8e\xc4\xb5\x84B,\xb5\xd5z\xe0(\xad\xb4\xa4\xb8\
+\x91\xe3<t_\xe3Q\xf6u\x1f\xe9z\x85\xe7W\xfd\
+\xe1\xf1\x22\xa1+\xa3\xcd\xe7\xa3\xd4k\xfc\xdd\x1f\x1f\x13\
+\x9c\x8d\xe2\x8e\x9e&\x92q\xbd\xe8\xaf_\xb4r\xcd/\
+o&\xba\x04e\xd7\xc8\xb5\x98\xd7D\xed\x9a(\xa7\xf3\
+E\xbc&\xd0sYGm\xc2\xcfK\xe8\xebl\xe7\xbd\
+\x129\x7f\xc1/\x99\xcf\xa5\xdf\x93\xbc}.\x0c\xebM\
+BgNi\xe5\x98\x0f\x5cS.\xa7\x02\xd9\x7f)d\
+\xc5M\xc55\xe6\x8a\x81\x18\x84\xfb\xb4{J\xbe\x97\x0a\
+\x83|d\xa7\xe3I\xab\xf0\x16\x95\xc7]\xfc\xa4\xff\x0d\
+(\xb9\x9e\xfd\x01\x1d\xaf\x8dY\x1f\xed\x87\xfd\x91>6\
+~\xd8&~\x92\x9c\xc7C\xf2\xab~iG{\xbb\x9c\
+\xfbg6%\x98\xadsuZ*LZ\xafE\xddK\
+\xd9w\x18\xd8a\xf2\xbc?\xab8\x19?\xc2=\xef\xd3\
+EH\x80\xf7\x0e\xa03\x8fqt\x9c#\xb6\x98`q\
+\x8b%\xce\xa8\xd1\xe2\xda\xed\x88\x03*\x96\xb4\x12\xa3M\
+i\xa4\xbc\xfb$sji\xe4#\x00\xa7\xe2g\xb4\xc4\
+\xb9\xe5\x99\x05\x98\x0d\xc0\x9b\xd1\x9b\x1e\xba\xc4-\xb7m\
+q#\x0a\x04\xcf\x88\x91)b\xb2x\xc2\x9f\xfe\xce\xf9\
+\xe9Df\xee\xf21\x1e\xf2\xb0\x15\xf4J\xee\xa8P\xc3\
+\x91\xf3+F\x01\x90h\xb7\x1f\xd16\xf0}\xbe=\x1c\
+\xd7\x0c\x04i\x9bY\xb0@=\xfa9E\xa7x\xf9\x96\
+\xfbQ\xde@g\x0c$\xb4gXD\x9e\xd7\x040\x11\
+d\x13\x94\x89\x19\x08\x1c5f\x8a5\x1e\x9c\x12\xc7\x08\
+;\x0a\xf0QL$\x08\x9a\xd4\x01A$J\x13Z\xa6\
+\x92\x11-\x9c$\xb9l|\xc3q\x8fM\x94\xcenp\
+\x96\x87\x0f\xc2\x88\x01M\xcb\x0a\xac\x9c\xd8\xe0?\x5c\x04\
+>\xa4\x94\xa9\x10Q%&\xa1FZs-\x95j\xad\
+\x5c\x9d\xfc\x943\x17&\xae\xcc,\xdcX%K\x11\x92\
+*,\x12\xa4\x89\xb6\xd42\xc8\x91Zm\xdc\xa4\xb5\xa6\
+\x0a\xa1\x8a\x99\x15_+\x06\xa8\xf6\xd4s/\x9dz\xed\
+\xdc\xa5\xb7\xae\x03\xee3\xca\xa0Q\x07\x0f\x09\xa3\x0d\x9d\
+i\xe6\x09\x9e\x98u\xf2\x94\xd9\xa6\xae\xb8\xe0J\xab,\
+Zu\xf1\x92\xd5\x96\x1a\x5c\xcd\xb2\x15#\xab\xc6&\xd6\
+L\x1f\xa8\xc5p\xc2\xfa\xee\xfc>j\xf1F-m\xa4\
+| ?P\xc3\xa7\xcc\xf7\x14\xd1\xe9\x84\x1c3 \x96\
+J\x04\xe2\xec\x08\xc0\xa1\x93cvH,%\x05\x87\xce\
+1;ZBTP\x82\x96\xe4\xe0\xcc\xe8\x88\x01\xc1\xb2\
+b\x22\x8b\x0f\xec^\x90{\x85[(\xe5W\xb8\xa5\x1b\
+\xb9\xe0\xd0\xfd\x0d\xe4\x82C\xf7\x09r\xefq\xfb\x00\xb5\
+\xe9\xd9f\x1c9l\x84<\x0c\xdd\xa8GF\xf8a\xc0\
+\x12M\xa2\x9e\xd4\xben\xfb\x5c\xa9\x1f\x1a0\xffl\x04\
+\x85\xe7\x98\xda`l,m\x16;\xd8\x87\xcc\xd5\x1a\x89\
+M\xce\xd6\x95i0b\xd0:\x99\x80\xee\x18\xc9\xbec\
+\x19Um\xf5\xbc\x02lg\x06\xebCY\xdb\xac*\x89\
+eQ\xa45\xb8,\xcd>\x08\xac\x87\xd5\xda\xf4\x11#\
+\x83\x9b\xdbrP\xb8\x03\x9c=\xa4\xd9\x0c3\xb7Dr\
+t\x9e\x0a\x06\xb2\xe3Vv\xc4\xd5\xabF\xe8\xab\xd3`\
+`YE]\xef6;\x1d\xba\xd7\xc4\xf0\xac}\x07\xfe\
+\xd7p\xdf|\xdab\xcd\xea\xf6\xb0c~\xf2\xd0V\xeb\
+\x14\xe0\xa8\x04ua\x9ceqJ\x8f\x5c\x97P\xd2:\
+\xc90\xb6\x98vnm\x09[\x1d\xbe4\x98+z+\
+\x87+\xa66c\xe1I}\x04L\x02\xb70,S\x9c\
+]\x93`b\xa0\x8d\x95\xeb\x5c\x15\xc6\x81m\xe7\xb0\xc1\
+0M,\xd0\xb3[\xa1hT\x0d\xa9f\xc1\x1e\x0b\xce\
+\x91\xcd\xc2\xae<\x22\xdc\x19\xf2e\x8bw\xe1\x13\xc6\xdf\
+\xa2\xb7`&\x9a\xcfo\xa1\xda\xfd\xb6t \x02\xff\xd6\
+0l)d\xae<\x8c\xa5C\x5c\x1e\xb9\x8a\x1a',\
+\xac\x0b\xd2\xd2\xcb\xb3\x1c\x1f<\x9c\x0e\xd5\x03\x0c\xd6\xd1\
+\xb1\x0d\xf7'\x9b\x7f\xd5\x86\x9f~(\xb7N\xfdT%\
+\xfc^\x97\xb3\x0d\xbf\xd0\xe5\xb2\x8fq\xd3\xd1\x03\xc2\x1e\
+\x0d\x08y5\xdb\x0e\x8f\xe8\x85\xcf\x82\x13\xdcI\xdcW\
+\xba\xd66\xcb*\x1d\xfe\xb5P\x15\x92Q[d\xac\xb3\
+&\xb8\x11\xb9OL\x0e;\x96P\x9c\x18z\x9f\xe2\xcd\
+\xbdA\x85\xca\x84\xbbU\xcc\x1aW\xd9\xc1v\x86Zu\
+\xe8\xd4u\x82\xe4\x91\xf3\xe2\x84\xa5A\xd2\xdc\xda\xae\x0d\
+\xf9\x0f\x9f\x82vB\xd6\x1c\x1d\xfaZ\xcasP{\xc5\
+\x04\x97f\xf1R\x0ca\xe0D\x827l#>V\x06\
+\x02\x08\x17I\xf4'\x92\x10DG\xd1\xd1\xfc\xba\xa2\xb6\
+y\xac\x01N\xa9\x06q\xf0\xcft\xd2\x9aUP o\
+\xe3\xc3\x95\xe1\xd9\x08b\x18\x88\xeaL\xf0\xfb\xaa\x04\xaa\
+\x80\xabS\xce\xa9\xf5\xe2\x11\x0de\x9a\xebD^v?\
+Z\xaf\xbbA\xcd\x05\x8cm\xe0u\xd9\xf0\x1b\x1e\x84\xbf\
+\x8d\xfc\xaaM\xa5\x22'H\xedc\xe4\xb5\xbcX\x09.\
+@\xea\x96C^\xeb/\x10\x08D\xc6\x85\x9c\x02\x0e\x1c\
+\xd8<\xe5\xc9u\x1e\x88\xf3Y]8\xd2E%RP\
+(\x82_0\x06K\x92\x126a\xd6,\xc3zbZ\
+G\xca\xbc\xaa5'.gA\x10R\x96\xef\xe4\x80\xf0\
+\xddd\xf1\xa7\xb6a\x89!y\xb2\x00E\xa7\x85\x94\xe7\
+~)V\x18Uq\xe7d\xc3\x91\xf0QU\xfb\x14\xaf\
+G2\x10\xec\xa3\xf55\x1dBr\xfc\x1d\x10\xb8mx\
+\xb6\xcfo\xda\xf0\xf5@\x10\xb8E\x85\xdfd\xb4C\x17\
+(\x9e\x09\xae;\xc0\xb10\xbcY\x01\x105\x9co\xe1\
+H\xbe\xd1\x9a\xd8zU\xeb\x8c\xdc<K\xef{&&\
+d\xc3\xf7~\xf4\xb6\x0d_\x0d\xf8^\x1bK\x80\x9d\xf9\
+\xc5\xa4\xf0\xf9\x89\x84\xcd97D\x15J\x86y\xbc\x80\
+\x81\x0c\x0e\xb5\x9bUp\xca\xc0.\x08I\x1e\xe1\x99\xce\
+B! \xbb\x82 \xdf91\x8a\x18D\x99z\x81\xe3\
+yvc\x83B\xc0\x8e\xf5\x89)\xc3;\xdb\xf2\x98y\
+\x87\x11.\x9e}&\xb6E{~\xech\xde:\x10\xf6\
+Mv\x80\x15\x99Q\x8d \xdbO;\x1d\xa81P\xd0\
+\x1d\xe4\xc7\x0e\xe9\xb3(A\xbdQ2J\x00TX\xbb\
+\x04 B\x18O\xef\x13,\x0b\x08\xd6\x8e:\xdbz\xfe\
+\x0b\x0b;\xce\xa0\xfd\xc1\xc2\xc0\xe2\xd8{{p\xdav\
+\xed\x1e@p\xa8\x97\xfaBU\xd8,6\xc2:P\xbd\
+T9\xb5XD\x1dz\xa5Y\xe9,*\x90z\xa5t\
+wH\xd2\x08\x1d\xf1\xa9u|k\xa1]\xa5\xc3\xce\xfd\
+\xa8\x1b\xda\xcc\x9e0\xb4F\x99\xa3\x1a\x08\x16\x15\x1c\xcc\
+sQ\x00\xd2I\xfc\x90\xa8\xc2\x8f\x92\xa2\x9e\x15\xcbP\
+/\xd9\x18h0\x87z\xf4\x888\xae !\xc6\x1ft\
+s\xba\xc5\x89*\x7f8\xf77\xf0\xd6\x18^\xb8u\xd7\
+\xac\xef*\x09%\xce\x8a\xdd\x99\xaf\xf9\xbf\x8d\xc0|!\
+.\xec\xf7\xfc\xf9\x97\x0c\x10~\xf8!A\xa1\x8d\x85+\
+\x06\xf5V\xa0r\xea\x06\x9d~EI\xe1\xe7\x0a\xbd\x0e\
+\xff\x00\xf5N\x9dv\xea\xf8\xf3C\xb1\xe2{B\xd3\xb5\
+\xcb\xd2\xb2\x06\xc1\xa9\xc0\xb8;e7\x8f\x15\x14\xaf\xc3\
+Z\xdc\xc5\xeb\xdfK\xd9\xcd}\xc4\x0b\xd4\xb8\x0b\x02\x14\
+\xaf\x83P<\xdb\xf2\xba\xb8xU\xbbK\xf6-\xfb*\
+cPh\xb9\xf0]7\xdf\xc2\xb3\xbe\x0c\xb9\x94\xbb\xde\
+#\xd5!?2U6\x9a(\x8b\x1b|\x11\xae\xd6\xb5\
+y\xa6=\xcd\xc6\x1cG\xdd\xdet>\xc3\x02\xcf\x0f\xff\
+\x1d5B\xf9\x7fFtE\x8bp\xf6M^\x85\xef#\
+\x95t\xe4hS\x98\x1d\xa5\xc2\xe6\xb3\x042V\xc4*\
+6A]\x026w\xd3\xa0,R\x086\x8fp2\xcf\
+\xf9(\xbc\x11\x0bcu\xe4\xfbv\xd7\xf6\xfb_R\xc8\
+I\xd8\x8a\xca\xa4k\xe7\xb07\x0e0\x7f\x0b\xbes\xd8\
+[@P<\xaa\xc8\x15\xa1Z\xf1\x1d\x1bz=/\x14\
+\x88\xc5\xc0l\xd2\x1e\xdaF\xf2\x1dR\x5cP\x0c|\x81\
+\x82\x0d5E@\xf6M\xd8N\x9e,\x08\xee\xf9*\x83\
+\x019\xec\x14\x81\xd2\xbf\xe7\x93p\xb4\xfa4\x07=\x00\
+\x00\x01\x85iCCPICC profi\
+le\x00\x00x\x9c}\x91=H\xc3@\x18\x86\xdf\xa6\
+\xd6\x16\xa9(\xd8A\xc4!Cu\xb2 *\xe2\xa8U\
+(B\x85P+\xb4\xea`r\xe9\x1f4iHZ\x5c\
+\x1c\x05\xd7\x82\x83?\x8bU\x07\x17g]\x1d\x5c\x05A\
+\xf0\x07\xc4\xd1\xc9I\xd1EJ\xfc.)\xb4\x88\xf1\x8e\
+\xe3\x1e\xde\xfb\xde\x97\xbb\xef\x00\xa1Qf\x9a\xd55\x0e\
+hz\xd5L%\xe2b&\xbb*\x06_\xd1\x8d~\x84\
+h\x06df\x19s\x92\x94\x84\xe7\xf8\xba\x87\x8f\xefw\
+1\x9e\xe5]\xf7\xe7\xe8Us\x16\x03|\x22\xf1,3\
+\xcc*\xf1\x06\xf1\xf4f\xd5\xe0\xbcO\x1caEY%\
+>'\x1e3\xe9\x82\xc4\x8f\x5cW\x5c~\xe3\x5cpX\
+\xe0\x99\x113\x9d\x9a'\x8e\x10\x8b\x85\x0eV:\x98\x15\
+M\x8dx\x8a8\xaaj:\xe5\x0b\x19\x97U\xce[\x9c\
+\xb5r\x8d\xb5\xee\xc9_\x18\xce\xe9+\xcb\x5c\xa75\x8c\
+\x04\x16\xb1\x04\x09\x22\x14\xd4PB\x19U\xc4h\xd7I\
+\xb1\x90\xa2\xf3\xb8\x87\x7f\xc8\xf1K\xe4R\xc8U\x02#\
+\xc7\x02*\xd0 ;~\xf0?\xf8\xdd[+?9\xe1\
+&\x85\xe3@\xe0\xc5\xb6?F\x80\xe0.\xd0\xac\xdb\xf6\
+\xf7\xb1m7O\x00\xff3p\xa5\xb7\xfd\x95\x060\xf3\
+Iz\xbd\xadE\x8f\x80\xbem\xe0\xe2\xba\xad){\xc0\
+\xe5\x0e0\xf8d\xc8\xa6\xecH~ZB>\x0f\xbc\x9f\
+\xd17e\x81\x81[\xa0g\xcd\xed[\xeb\x1c\xa7\x0f@\
+\x9az\x95\xbc\x01\x0e\x0e\x81\xd1\x02e\xaf{\xbc;\xd4\
+\xd9\xb7\x7fkZ\xfd\xfb\x01\xda\xddrj\xb6}\xf5\xc0\
+\x00\x00\x0d\x1ciTXtXML:com.\
+adobe.xmp\x00\x00\x00\x00\x00<?\
+xpacket begin=\x22\xef\
+\xbb\xbf\x22 id=\x22W5M0MpCe\
+hiHzreSzNTczkc9d\
+\x22?>\x0a<x:xmpmeta x\
+mlns:x=\x22adobe:ns\
+:meta/\x22 x:xmptk=\
+\x22XMP Core 4.4.0-\
+Exiv2\x22>\x0a <rdf:RD\
+F xmlns:rdf=\x22htt\
+p://www.w3.org/1\
+999/02/22-rdf-sy\
+ntax-ns#\x22>\x0a  <rd\
+f:Description rd\
+f:about=\x22\x22\x0a    x\
+mlns:xmpMM=\x22http\
+://ns.adobe.com/\
+xap/1.0/mm/\x22\x0a   \
+ xmlns:stEvt=\x22ht\
+tp://ns.adobe.co\
+m/xap/1.0/sType/\
+ResourceEvent#\x22\x0a\
+    xmlns:dc=\x22ht\
+tp://purl.org/dc\
+/elements/1.1/\x22\x0a\
+    xmlns:GIMP=\x22\
+http://www.gimp.\
+org/xmp/\x22\x0a    xm\
+lns:tiff=\x22http:/\
+/ns.adobe.com/ti\
+ff/1.0/\x22\x0a    xml\
+ns:xmp=\x22http://n\
+s.adobe.com/xap/\
+1.0/\x22\x0a   xmpMM:D\
+ocumentID=\x22gimp:\
+docid:gimp:30715\
+408-8d32-4ae9-83\
+e2-d50b65d75fbc\x22\
+\x0a   xmpMM:Instan\
+ceID=\x22xmp.iid:f2\
+632b44-ef61-4bb8\
+-8c52-9a17f84e9f\
+25\x22\x0a   xmpMM:Ori\
+ginalDocumentID=\
+\x22xmp.did:91237de\
+5-bc1c-4a00-96b5\
+-5db5026b7be8\x22\x0a \
+  dc:Format=\x22ima\
+ge/png\x22\x0a   GIMP:\
+API=\x222.0\x22\x0a   GIM\
+P:Platform=\x22Mac \
+OS\x22\x0a   GIMP:Time\
+Stamp=\x22165536800\
+5115995\x22\x0a   GIMP\
+:Version=\x222.10.2\
+4\x22\x0a   tiff:Orien\
+tation=\x221\x22\x0a   xm\
+p:CreatorTool=\x22G\
+IMP 2.10\x22>\x0a   <x\
+mpMM:History>\x0a  \
+  <rdf:Seq>\x0a    \
+ <rdf:li\x0a      s\
+tEvt:action=\x22sav\
+ed\x22\x0a      stEvt:\
+changed=\x22/\x22\x0a    \
+  stEvt:instance\
+ID=\x22xmp.iid:182d\
+9bb3-865a-4e04-b\
+6ed-1e094e3b47d4\
+\x22\x0a      stEvt:so\
+ftwareAgent=\x22Gim\
+p 2.10 (Mac OS)\x22\
+\x0a      stEvt:whe\
+n=\x222022-06-16T10\
+:26:45+02:00\x22/>\x0a\
+    </rdf:Seq>\x0a \
+  </xmpMM:Histor\
+y>\x0a  </rdf:Descr\
+iption>\x0a </rdf:R\
+DF>\x0a</x:xmpmeta>\
+\x0a               \
+                \
+                \
+                \
+                \
+                \
+     \x0a          \
+                \
+                \
+                \
+                \
+                \
+          \x0a     \
+                \
+                \
+                \
+                \
+                \
+               \x0a\
+                \
+                \
+                \
+                \
+                \
+                \
+    \x0a           \
+                \
+                \
+                \
+                \
+                \
+         \x0a      \
+                \
+                \
+                \
+                \
+                \
+              \x0a \
+                \
+                \
+                \
+                \
+                \
+                \
+   \x0a            \
+                \
+                \
+                \
+                \
+                \
+        \x0a       \
+                \
+                \
+                \
+                \
+                \
+             \x0a  \
+                \
+                \
+                \
+                \
+                \
+                \
+  \x0a             \
+                \
+                \
+                \
+                \
+                \
+       \x0a        \
+                \
+                \
+                \
+                \
+                \
+            \x0a   \
+                \
+                \
+                \
+                \
+                \
+                \
+ \x0a              \
+                \
+                \
+                \
+                \
+                \
+      \x0a         \
+                \
+                \
+                \
+                \
+                \
+           \x0a    \
+                \
+                \
+                \
+                \
+                \
+                \
+\x0a               \
+                \
+                \
+                \
+                \
+                \
+     \x0a          \
+                \
+                \
+                \
+                \
+                \
+          \x0a     \
+                \
+                \
+                \
+                \
+                \
+               \x0a\
+                \
+                \
+                \
+                \
+                \
+                \
+    \x0a           \
+                \
+\x0a<?xpacket end=\x22\
+w\x22?>=L\x95\x03\x00\x00\x00\x06bKGD\
+\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pH\
+Ys\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\
+\x00\x00\x07tIME\x07\xe6\x06\x10\x08\x1a-(q\
+\x07\x16\x00\x00\x00@IDATX\xc3\xed\xd4\xb1\x0d\
+\x00 \x0c\x03\xc1\xc0\xc8\x19$+\xc3\x00\xd0\x22\x8a\xdc\
+\x97ilE\xf2G\xa0;\xe3v\xcc\xcc\xf5\x22\xac\xaa\
+\x8e\xbc\xf9\xfb\x03\x0aL3\x00\x11\xf1\x00\x11\x81\x88\x88\
+H\x013@{6\x18\xac\x0c\x1c\xa5\x14\xd1\xbc\x00\x00\
+\x00\x00IEND\xaeB`\x82\
+\x00\x00\x04\xba\
+(\
+\xb5/\xfd`]\x13\x85%\x00Vp\x8c%\xf0vt\
+\x94\xf0Z\x1e0\x91V\x10R\x93\xedo\xfe\x84\xc0v\
+>6\xd31Us\xf6\x80*\x80\x81\x97l\x22\xe3\xc0\
+E\x05\x84\x00\x7f\x00\x86\x00\xb0\x9b#%w\x10\x1a+\
+\xf6D\xdb\xef\xab\xe6\x9f\x92\xc2\xce\xc6\x1d\xd9R\xa26\
+3\xf39\xc9\xad)^\x0f\xee}\xf7\x16f\xd9\xc3\xf3\
+<\xf1\xb5.q\x99M\xb7\xa3\xa3\x1e\x11\xb6?\xf7\x14\
+\x85\xbc\xd56\xde;\xc3\xf6y4j\x0eN\x90\x01\x14\
+\xb1\x81HMFB\xb7\xcf\x15\x83~\x03\xaaT\x93\x92\
+\xebV\xfb\xac\x94\x05\xa4\x91\xf5m\x06\xfa\x88\xd0\xd8<\
+\x11\xf5\x99,a\x97a\xbf\x91zqd\x02\x01\xa6 \
+/\x93%\xff\xb2\xd6K\xb2\x9er` \xf1y+\xa8\
+\xbe^1^\xcb\x18\xbav\x92\x14\xd4\x9dx\x12E8\
+IL\xdf\x22/\x96+\xaaW\x9aY\x7ftH\x8b\xd7\
+5T\x9f\xa0\xdcm\xdeU\x8a^\xd1\xf5\xd4zP\xea\
+\xae\x95z\x16Q\xb0{\xb0\xe3X/\x16\x88\xb6(\x85\
+\x13wd\xe7\xef\x0d\xfbU,\xce\xa2\x02\x03\xe8+\x85\
+\x03\xd2s\x91JJ\xfa\x5c\xeb\x18q\xbf\x90\xe7:\xdc\
+\xff\xda\x83I\xc9\xe2}.\x8b\xcf\xea.K\x09\xc2\xad\
+\xa2\xe8}=v[k\xac\x1dq\x1c\x0b Q\xac\xb5\
+[\x05PJI\x00\xa5CV\xa8m\xa7\xd8U\x92\x81\
+\x81!cG\x98w\xf5\x0ef&\x06\x12\xaf\xe5\x0e\x03\
+\xeb\xff\xeed\x1de1\xaa[\xed\x1a\x18\x82\x83\x01\x82\
+\x14HL:\xe4\x05\x93\x0d\x96-r\xdd\x9cGD\x8b\
+\xf7_c\xa4\xd4^\x17\xcb/\x86\xf5\x8b.\x95\xa6\xa8\
+X\x9c#\x8a\xb4e\xb1p\x8d\x1c\xac\xbd\x8a\x22\xdc\xd7\
+\x92!)\xfdL\xdf{\xf8\xe8\x9e\xeb\xd6\x19\x0d\x94,\
+\x81\x9c\x11]\xff9w\xd7\x04\x9dsa\x16\xd1o\xa4\
+\x14O\xcacN:d\xf4\x0a\xb9\xb9\x1f\x05\x85\x1d\xb9\
+\x9f\xff\xc6\xdb]\xa3\xac\xf6\xf2Q5\x11\xd5@\x0b\xf7\
+\xfec\xad\xcapC\xb7\xbf\xbc)A\xd7<\xa0s\xd8\
+MS\xcch8\xec^>\xebs\xbf\x87\x09\x17\xa3\xbe\
+\xe7R\xe1V\xb95\x84\x0f\x1b\xa8\x96\xf3\xfe\x98\xd0\xbd\
+\xce\x5c(}\x91\xc5\xee\x9a\xf4\xddoT\x02\x84\xa4\xf5\
+\x9a\xb2\xf5\xb7q#\xee\xa2\x02\xb5\xe32\x8ci\xb0\xa7\
+g!5\xe8F\xfa\x9dw\x0b\x08\xc8J\xec\x81\x09\xa8\
+\xd1%B32#\x22)HRH\x07!\x08\x82p\
+,\xea\xbc:\x0f\xa2h@Jq\x18\x870 \xc2\x8c\
+\xa2D\xc0\xc8\xc8\x042\xa1\x88L\xaa\xa0\xb0\xd8FS\
+\xc0\xe8\xc8\xb0\xcaOr\xe5\xd4`*\xc6=\xed\x0c\x86\
+O\xb1d\xd5\xe9\x1b\x88\x0a\xea\xafqws\xddl\xc4\
+\xc5\x1c\xc8\xbcb,y\xe8\x84,=\xe9\xf6#\xa1\xfb\
+NI*\x8c!9\xd7a@\x85fD\xc2\x16\x8a\x10\
+\xc8x\x99\xbd3@\x0b\xd5\xa8S\xd7>\xe8\xf8\x1a\xe7\
+=\xc1\xba\x00\xc7\xda\xf8\xc2\xb54,\xaax\xe8\xf2n\
+;\xe2\x8e&\xbfeY1}\xd2\x95\xec\x1aT\x81$\
+\xa1\xadE\x8d9\xe9w\xcb\xea\x98B\x8f\xf2\x8a\xb9\xbe\
+:\xe1\xc5\x05?\x5c(4\x9f\x1c\xf2\x1e\x87%Vr\
+\xd6\x09\xbe\xd6f\x94P\xad\xbcX8\x84\x18\xc1\x15$\
+\xaa\xd9\xca\x9b\xdb\x03a\xbeq\x017\x04r\x92\x19\x91\
+\xa4\x92dU\x11\xcf\xfb\x8fK\x95\x1e9\xfd\xa9\x82L\
+\x82\x87\xd4YD\x1c\x85\xb9\xfe\xd3c\x16H$S\xc9\
+\xc6\xf1<\xfa\xd2P?{\xed\xea\x13\xad\xac\xa5\xf5\x17\
+\xdecS\x8aG'\xf2\x01M\xd4\x03e\x81\x0e\x19\x0d\
+\xea^\x05#\x122\x907>J\xaa\xde\x08\xac:s\
+\xcf2\x88\xc3\xf9Az\x15\xcb\x83Z\x96\xa6\xb0b\xd3\
+\xac\x9e\xb9\x06M\x89\x9f\xc548\xad\xef)\x89\xad\x88\
+e+\xc0\xbce{Q\x14\x05\x1a\xb6>\xcc\xaa&\x15\
+\xd4\x05\x06\xbe\x7f\xd0\xf9\xbf\x91\x82dj\x83Z\xecY\
+t\x02I\x12 \x94 \xc4\xd7\x1fh\xfaO-\x84\xe8\
+\xc2]\x09\xa0\xcd\xb2\xfc\x8b\xf5\x0e\xb8`\xc0\x04oz\
+\x0b\xd5\xe9d\xea*j@\xa7\x92\x16u\xd1\x00\xbe\x22\
+e\x93\x16\xdc\xf9\xe4\xac\xc3\x18\xe1\x84\x93\x87\xae\xd5N\
+.\xbc\xc5\x11\xca\xec\xa6\xb0V\x08#\x1bL\xb7\xe2s\
+\xeeW\x859?\xf8g\xa9\x8fk\x80\x929\xdb\xec\x84\
+\xea5p5)\x03[i\x1a\xb9\x00\x81\x02\xe7\xa3\xe2\
+\xa2\x22\x1ev\x84\xe7lA\x89\x92Z\x9f\x19\xd8\xc3|\
+aXp\x01@\x1a\xa7\xb3\xaa\x8d\xba\xc8R\xc0\x0c\xa7\
+\x9b\x9e\x80H\xd8 i\xd3>\x9f\x0b\x22\x10j\xe7\x98\
+#\x03\xaa\x13\x1a\xdcd\x7f\xc3\x95*m\xdc\x04>\x8f\
+8\x09\xc0\x5c\xd7\x9e\xff\xa5D\xc3\xdf=\x9c \x8c\xd5\
+\x0c\xc1\xfa?\xd9\xf4q\xbe\x16\xe9AJ,>\xb7\x8a\
+\x02 \xae\xf5O\xc4\xf5f\xc4\x00`\x8c\xec\xe1?$\
+\x0d\x91\xcdUt\xcf\xcc\xdb\x97\x85S\xb2\xac\xd3lJ\
+N^\xc3\x89\x86b\xb1r\xfbDq\x9e\xc1\xd9\xd7\xc2\
+eW\xa0Zp\xd2\xf2\xf6\x0f\
 \x00\x00\x036\
 \x89\
 PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -334,6 +707,10 @@ qt_resource_name = b"\
 \x00\x80-\xa7\
 \x00l\
 \x00e\x00f\x00t\x00-\x003\x002\x00.\x00p\x00n\x00g\
+\x00\x0f\
+\x07\x04,\xa7\
+\x00s\
+\x00e\x00t\x00t\x00i\x00n\x00g\x00s\x00-\x003\x002\x00.\x00p\x00n\x00g\
 \x00\x08\
 \x08\x01Z\x5c\
 \x00m\
@@ -345,18 +722,20 @@ qt_resource_name = b"\
 "
 
 qt_resource_struct = b"\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x06\x00\x00\x00\x01\
 \x00\x00\x00\x00\x00\x00\x00\x00\
 \x00\x00\x00>\x00\x00\x00\x00\x00\x01\x00\x00\x07\x9c\
-\x00\x00\x01\x91\x08\xc1\x9ai\
+\x00\x00\x01\x975l\xc7\xfe\
 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
-\x00\x00\x01\x91\x08\xc1\x9ai\
+\x00\x00\x01\x975l\xc7\xfe\
+\x00\x00\x00Z\x00\x00\x00\x00\x00\x01\x00\x00\x0a\xdf\
+\x00\x00\x01\x9b\xc6\x0ai\xb3\
 \x00\x00\x00\x1c\x00\x00\x00\x00\x00\x01\x00\x00\x02\x83\
-\x00\x00\x01\x91\x08\xc1\x9ai\
-\x00\x00\x00Z\x00\x01\x00\x00\x00\x01\x00\x00\x0a\xdf\
-\x00\x00\x01\x91\x08\xc1\x9ai\
-\x00\x00\x00p\x00\x00\x00\x00\x00\x01\x00\x00\x0f\xb2\
-\x00\x00\x01\x91\x08\xc1\x9ai\
+\x00\x00\x01\x975l\xc7\xfe\
+\x00\x00\x00~\x00\x04\x00\x00\x00\x01\x00\x00\x22\x1e\
+\x00\x00\x01\x9b\xc6\x06\x8a\x02\
+\x00\x00\x00\x94\x00\x00\x00\x00\x00\x01\x00\x00&\xdc\
+\x00\x00\x01\x975l\xc7\xfe\
 "
 
 def qInitResources():
index fba1f71b07ab4d7aac079155ea0ec6f20715cef1..bbeacfc8846bf5bae4ca2c00fd5965e27c254030 100644 (file)
@@ -8,9 +8,9 @@ import sys
 
 from PySide6.QtCore import (QDir, QPoint, QRect, QStandardPaths, Qt, QTimer,
                             Slot)
-from PySide6.QtGui import QGuiApplication, QImageWriter
+from PySide6.QtGui import QImageWriter
 from PySide6.QtWidgets import (QApplication, QCheckBox, QDialog, QFileDialog,
-                               QGridLayout, QGroupBox, QHBoxLayout, QLabel,
+                               QFrame, QGridLayout, QGroupBox, QHBoxLayout, QLabel,
                                QMessageBox, QPushButton, QSizePolicy, QSpinBox,
                                QVBoxLayout, QWidget)
 
@@ -29,6 +29,7 @@ class Screenshot(QWidget):
         self.screenshot_label.setMinimumSize(
             screen_geometry.width() / 8, screen_geometry.height() / 8
         )
+        self.screenshot_label.setFrameShape(QFrame.Shape.Box)
 
         main_layout = QVBoxLayout(self)
         main_layout.addWidget(self.screenshot_label)
@@ -53,9 +54,9 @@ class Screenshot(QWidget):
         self.new_screenshot_button = QPushButton("New Screenshot", self)
         self.new_screenshot_button.clicked.connect(self.new_screenshot)
         buttons_layout.addWidget(self.new_screenshot_button)
-        save_screenshot_button = QPushButton("Save Screenshot", self)
-        save_screenshot_button.clicked.connect(self.save_screenshot)
-        buttons_layout.addWidget(save_screenshot_button)
+        self.save_screenshot_button = QPushButton("Save Screenshot", self)
+        self.save_screenshot_button.clicked.connect(self.save_screenshot)
+        buttons_layout.addWidget(self.save_screenshot_button)
         quit_screenshot_button = QPushButton("Quit", self)
         quit_screenshot_button.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_Q)
         quit_screenshot_button.clicked.connect(self.close)
@@ -70,10 +71,11 @@ class Screenshot(QWidget):
         self.resize(300, 200)
 
     def resizeEvent(self, event):
-        scaled_size = self.original_pixmap.size()
-        scaled_size.scale(self.screenshot_label.size(), Qt.AspectRatioMode.KeepAspectRatio)
-        if scaled_size != self.screenshot_label.pixmap().size():
-            self.update_screenshot_label()
+        if not self.original_pixmap.isNull():
+            scaled_size = self.original_pixmap.size()
+            scaled_size.scale(self.screenshot_label.size(), Qt.AspectRatioMode.KeepAspectRatio)
+            if scaled_size != self.screenshot_label.pixmap().size():
+                self.update_screenshot_label()
 
     @Slot()
     def new_screenshot(self):
@@ -115,17 +117,10 @@ class Screenshot(QWidget):
             )
 
     def shoot_screen(self):
-        screen = QGuiApplication.primaryScreen()
-        window = self.windowHandle()
-        if window:
-            screen = window.screen()
-        if not screen:
-            return
-
         if self.delay_spinbox.value() != 0:
             QApplication.beep()
 
-        self.original_pixmap = screen.grabWindow(0)
+        self.original_pixmap = self.screen().grabWindow(0)
         self.update_screenshot_label()
 
         self.new_screenshot_button.setDisabled(False)
@@ -141,13 +136,18 @@ class Screenshot(QWidget):
             self.hide_this_window_checkbox.setDisabled(False)
 
     def update_screenshot_label(self):
-        self.screenshot_label.setPixmap(
-            self.original_pixmap.scaled(
-                self.screenshot_label.size(),
-                Qt.AspectRatioMode.KeepAspectRatio,
-                Qt.TransformationMode.SmoothTransformation,
+        if self.original_pixmap.isNull():
+            self.save_screenshot_button.setEnabled(False)
+            self.screenshot_label.setText(f'Grabbing "{self.screen().name()}" failed.')
+        else:
+            self.save_screenshot_button.setEnabled(True)
+            self.screenshot_label.setPixmap(
+                self.original_pixmap.scaled(
+                    self.screenshot_label.size(),
+                    Qt.AspectRatioMode.KeepAspectRatio,
+                    Qt.TransformationMode.SmoothTransformation,
+                )
             )
-        )
 
 
 if __name__ == "__main__":
index d7014313f75074f10764ba5553b2d763b3961978..1fcd5ae31dec53515a11c7795c368201f206c01f 100644 (file)
@@ -95,8 +95,7 @@ class ClassWizard(QWizard):
                 return class_str
             module = class_str[0:dot]
             class_name = class_str[dot + 1:]
-            class_list = module_imports.get(module)
-            if class_list:
+            if class_list := module_imports.get(module):
                 if class_name not in class_list:
                     class_list.append(class_name)
             else:
@@ -114,8 +113,7 @@ class ClassWizard(QWizard):
         if base_class:
             base_class = add_import(base_class)
 
-        signals = self.field('signals')
-        if signals:
+        if signals := self.field('signals'):
             add_import('PySide6.QtCore.Signal')
 
         property_types = []
@@ -130,8 +128,7 @@ class ClassWizard(QWizard):
         if property_names:
             add_import('PySide6.QtCore.Property')
 
-        signals = self.field('signals')
-        if signals:
+        if signals := self.field('signals'):
             add_import('PySide6.QtCore.Signal')
 
         property_types = []
@@ -160,8 +157,7 @@ class ClassWizard(QWizard):
         if base_class:
             block += f'({base_class})'
         block += ':\n'
-        description = self.field('description')
-        if description:
+        if description := self.field('description'):
             block += f'    """{description}"""\n'
 
         if signals:
index f7239ae3cab9ffd8b4a3ef11718f209a318b57f0..8f3445f070c4be993730a34324b9235fd5ea059c 100644 (file)
@@ -133,8 +133,7 @@ class ListChooser(QGroupBox):
 
     @Slot()
     def _add(self):
-        new_item = self._create_new_item()
-        if new_item:
+        if new_item := self._create_new_item():
             self._list.addItem(new_item)
 
     def _create_new_item(self):
index 541aab305de6ce16d2666d0cd284d4ea45cc4eee..fa773cfc8289ff1d610f9fe3392f6210d56dec69 100644 (file)
@@ -286,9 +286,8 @@ class Dialog(QDialog):
         options_value = self._font_options.value()
         options = QFontDialog.FontDialogOptions(options_value)
 
-        description = self._font_label.text()
         default_font = QFont()
-        if description:
+        if description := self._font_label.text():
             default_font.fromString(description)
 
         ok, font = QFontDialog.getFont(default_font, self, "Select Font", options)
index 6710a2365779a67f17cef99f61d3f2bfd5c400cd..a1cd75643e725b116d1e9851d24f8b1353f294e6 100644 (file)
@@ -44,8 +44,7 @@ if __name__ == '__main__':
 
     src_dir = Path(__file__).resolve().parent
     try:
-        translation = gettext.translation('example', localedir=src_dir / 'locales')
-        if translation:
+        if translation := gettext.translation('example', localedir=src_dir / 'locales'):
             translation.install()
             _ = translation.gettext
             ngettext = translation.ngettext
index e33220e8c4545c4f0d7772329171a3b8c14f6070..6bf98cdd07d4c23f0b51aa93814f962befc9860c 100644 (file)
@@ -106,8 +106,7 @@ class MainWindow(QMainWindow):
         model: QAbstractItemModel = self.view.model()
         column: int = self.view.selectionModel().currentIndex().column()
 
-        changed: bool = model.insertColumn(column + 1)
-        if changed:
+        if model.insertColumn(column + 1):
             model.setHeaderData(column + 1, Qt.Orientation.Horizontal, "[No header]",
                                 Qt.ItemDataRole.EditRole)
 
index 98e4d4752283bf1addf8912ea15f6b13a5464404..dd7c8a51b7d421564770763956b58b9935d9a577 100644 (file)
@@ -59,8 +59,7 @@ class TreeModel(QAbstractItemModel):
         if not parent_item:
             return QModelIndex()
 
-        child_item: TreeItem = parent_item.child(row)
-        if child_item:
+        if child_item := parent_item.child(row):
             return self.createIndex(row, column, child_item)
         return QModelIndex()
 
@@ -89,8 +88,7 @@ class TreeModel(QAbstractItemModel):
         if not index.isValid():
             return QModelIndex()
 
-        child_item: TreeItem = self.get_item(index)
-        if child_item:
+        if child_item := self.get_item(index):
             parent_item: TreeItem = child_item.parent()
         else:
             parent_item = None
index 88357ae14e075f2ab7d2513f10abf3bebb5e8c8d..34dfb5ac7ad54a780e675abe072670e4df9f3f22 100644 (file)
@@ -341,9 +341,7 @@ class SpreadSheet(QMainWindow):
             col_first = self._table.column(first)
             col_last = self._table.column(last)
 
-        current = self._table.currentItem()
-
-        if current:
+        if current := self._table.currentItem():
             row_cur = self._table.row(current)
             col_cur = self._table.column(current)
 
@@ -364,8 +362,7 @@ class SpreadSheet(QMainWindow):
         cell2 = "C2"
         out = "C3"
 
-        current = self._table.currentItem()
-        if current:
+        if self._table.currentItem():
             out = SpreadSheetItem.encode_pos(self._table.currentRow(), self._table.currentColumn())
 
         if self.run_input_dialog(title, "Cell 1", "Cell 2", op, "Output to:", cell1, cell2, out):
index a6d81223f75a4bf0aeffb57f82ed9ceb2e260534..d2fcec4d210f84023454a8f6fc530c3282e26b33 100644 (file)
@@ -57,11 +57,9 @@ class SpreadSheetDelegate(QStyledItemDelegate):
 
     def set_model_data(self, editor: QWidget,
                        model: QAbstractItemModel, index: QModelIndex) -> None:
-        edit = QLineEdit(editor)
-        if edit:
+        if edit := QLineEdit(editor):
             model.setData(index, edit.text())
             return
 
-        dateEditor = QDateTimeEdit(editor)
-        if dateEditor:
+        if dateEditor := QDateTimeEdit(editor):
             model.setData(index, dateEditor.date().toString("dd/M/yyyy"))
index db3abe34a97ebcb406afb2c64a4e6740a436b5ed..e5edfc8d889364c717f5ee4f2b05690c99c8d3ba 100644 (file)
@@ -165,8 +165,7 @@ class MainWindow(QMainWindow):
     def open(self):
         file_name, _ = QFileDialog.getOpenFileName(self)
         if file_name:
-            existing = self.find_mdi_child(file_name)
-            if existing:
+            if existing := self.find_mdi_child(file_name):
                 self._mdi_area.setActiveSubWindow(existing)
             else:
                 self.load(file_name)
@@ -399,8 +398,7 @@ class MainWindow(QMainWindow):
         settings.setValue('geometry', self.saveGeometry())
 
     def active_mdi_child(self):
-        active_sub_window = self._mdi_area.activeSubWindow()
-        if active_sub_window:
+        if active_sub_window := self._mdi_area.activeSubWindow():
             return active_sub_window.widget()
         return None
 
index 4b009b30dc309ec569035c8416bab5b06788d029..4b638887ce0fff81128521050658c72cd0cfa5d1 100644 (file)
@@ -178,10 +178,7 @@ class MainWindow(QMainWindow):
 
     @Slot()
     def on_color_clicked(self):
-
-        color = QColorDialog.getColor(self.color, self)
-
-        if color:
+        if color := QColorDialog.getColor(self.color, self):
             self.set_color(color)
 
     def set_color(self, color: QColor = Qt.GlobalColor.black):
index 8cd330a72354156f9b0e2d2745000f88231cab5e..20ced2c5d74fbf9fd3ad026efacf885031fa2031 100644 (file)
@@ -188,8 +188,7 @@ class TextEdit(QMainWindow):
         self._action_paste.setShortcut(QKeySequence.StandardKey.Paste)
         tb.addAction(self._action_paste)
 
-        md = QGuiApplication.clipboard().mimeData()
-        if md:
+        if md := QGuiApplication.clipboard().mimeData():
             self._action_paste.setEnabled(md.hasText())
 
     def setup_text_actions(self):
@@ -648,9 +647,8 @@ class TextEdit(QMainWindow):
     @Slot()
     def cursor_position_changed(self):
         self.alignment_changed(self._text_edit.alignment())
-        list = self._text_edit.textCursor().currentList()
-        if list:
-            style = list.format().style()
+        if current_list := self._text_edit.textCursor().currentList():
+            style = current_list.format().style()
             if style == QTextListFormat.ListDisc:
                 self._combo_style.setCurrentIndex(1)
             elif style == QTextListFormat.ListCircle:
index 3f6190b1d710516cd07bef6a18feaff9f17df991..0f1f45acd14b15835eae1c4334c7ca5985f22391 100644 (file)
@@ -46,7 +46,8 @@ HELP_EXTRA_MODULES = dedent("""
 
 # plugins to be removed from the --include-qt-plugins option because these plugins
 # don't exist in site-package under PySide6/Qt/plugins
-PLUGINS_TO_REMOVE = ["accessiblebridge", "platforms/darwin", "networkaccess", "scenegraph"]
+PLUGINS_TO_REMOVE = ["accessiblebridge", "platforms/darwin", "networkaccess",
+                     "scenegraph", "wayland-inputdevice-integration"]
 
 
 def get_all_pyside_modules():
index 762e76f3163636a7dd71af02e82115d53378920d..edc8813a25112bf23b6407e060c6a67ceb3bc9e3 100644 (file)
@@ -5,7 +5,7 @@ from __future__ import annotations
 import sys
 import os
 from pathlib import Path
-from argparse import ArgumentParser, RawTextHelpFormatter
+from argparse import ArgumentParser, Namespace, RawTextHelpFormatter
 
 from project_lib import (QmlProjectData, check_qml_decorators, is_python_file, migrate_pyproject,
                          QMLDIR_FILE, MOD_CMD, METATYPES_JSON_SUFFIX, SHADER_SUFFIXES,
@@ -186,10 +186,11 @@ class Project:
 
         self._regenerate_qmldir()
 
-    def run(self) -> int:
+    def run(self, args: list) -> int:
         """Runs the project"""
         self.build()
         cmd = [sys.executable, str(self.project.main_file)]
+        cmd.extend(args)
         return run_command(cmd, cwd=self.project.project_file.parent)
 
     def _clean_file(self, source: Path):
@@ -266,11 +267,13 @@ class Project:
                 run_command(cmd, cwd=project_dir)
 
 
-def main(mode: str = None, dry_run: bool = False, quiet: bool = False, force: bool = False,
-         qml_module: bool = None, project_dir: str = None, project_path: str = None,
-         legacy_pyproject: bool = False):
-    cl_options = ClOptions(dry_run=dry_run, quiet=quiet,  # noqa: F841
-                           force=force, qml_module=qml_module)
+def main(args: Namespace):
+    mode = args.mode
+    project_dir = getattr(args, "project_dir", None)
+    project_path = getattr(args, "project_path", None)
+    legacy_pyproject = getattr(args, "legacy_pyproject", False)
+    cl_options = ClOptions(dry_run=args.dry_run, quiet=args.quiet,  # noqa: F841
+                           force=args.force, qml_module=args.qml_module)
 
     if new_project_type := NewProjectTypes.find_by_command(mode):
         if not project_dir:
@@ -301,7 +304,7 @@ def main(mode: str = None, dry_run: bool = False, quiet: bool = False, force: bo
     if mode == "build":
         project.build()
     elif mode == "run":
-        sys.exit(project.run())
+        sys.exit(project.run(args.arguments))
     elif mode == "clean":
         project.clean()
     elif mode == "qmllint":
@@ -340,9 +343,7 @@ if __name__ == "__main__":
     for op_mode, op_help in OPERATION_HELP.items():
         op_parser = subparsers.add_parser(op_mode, help=op_help)
         op_parser.add_argument("project_path", nargs="?", type=str, help="Path to the project file")
+        if op_mode == "run":
+            op_parser.add_argument('arguments', nargs='*', help="Arguments")
 
-    args = parser.parse_args()
-
-    main(args.mode, args.dry_run, args.quiet, args.force, args.qml_module,
-         getattr(args, "project_dir", None), getattr(args, "project_path", None),
-         getattr(args, "legacy_pyproject", None))
+    main(parser.parse_args())
index ffe43a7b9f1ee05dae684624061febceeb2efdf1..60d72e848581ed875fb95ceb5954fd4d82c9871e 100644 (file)
@@ -1,5 +1,5 @@
 set(pyside_MAJOR_VERSION "6")
 set(pyside_MINOR_VERSION "10")
-set(pyside_MICRO_VERSION "2")
+set(pyside_MICRO_VERSION "3")
 set(pyside_PRE_RELEASE_VERSION_TYPE "")
 set(pyside_PRE_RELEASE_VERSION "")
index b2a4fda85942d7b7ed76c05556cdf7e7972fbc03..f5fc048ab3d45eeb415351ac33b25ff58821389e 100644 (file)
@@ -5,6 +5,7 @@ project(QtHttpServer)
 
 set(QtHttpServer_SRC
     ${QtHttpServer_GEN_DIR}/qabstracthttpserver_wrapper.cpp
+    ${QtHttpServer_GEN_DIR}/qhttpserverconfiguration_wrapper.cpp
     ${QtHttpServer_GEN_DIR}/qfuturehttpserverresponse_wrapper.cpp
     ${QtHttpServer_GEN_DIR}/qhttpserver_wrapper.cpp
     ${QtHttpServer_GEN_DIR}/qhttpserverresponder_wrapper.cpp
index 66b2ff9eafae96f29f408c3dbb62476cb9c28cc2..f923d1940bf8ba73e93b6f0d32276fb9a4d3793b 100644 (file)
@@ -26,6 +26,7 @@
                      snippet="qhttpserver-addafterrequesthandler"/>
       </add-function>
   </object-type>
+  <value-type name="QHttpServerConfiguration" since="6.9"/>
   <object-type name="QHttpServerResponder">
       <enum-type name="StatusCode"/>
   </object-type>
index 3c15f4ee4d8e87b9a5903c1331175756668f781a..ddaa5e421983df8880d5a805ea64292c76faa90d 100644 (file)
@@ -52,7 +52,7 @@
     <enum-type name="SavePageFormat"/>
   </object-type>
 
-  <object-type name="QWebEngineExtensionInfo" since="6.10"/>
+  <value-type name="QWebEngineExtensionInfo" since="6.10"/>
   <object-type name="QWebEngineExtensionManager" since="6.10"/>
 
   <value-type name="QWebEngineFileSystemAccessRequest">
index a20143726a8fa0950aeab66dddc8c59d6ef0934f..2b8a42d0fd7a4239db5c1dbac0fd729996da2b02 100644 (file)
@@ -28,10 +28,10 @@ you might get them with ``brew``, and on **Windows** you can download the instal
 website.
 
 * **Python**: 3.9+ `[official Python website] <https://www.python.org/downloads/>`_
-* **Qt:** 6.8+ `[online installer] <https://download.qt.io/official_releases/online_installers/>`_
+* **Qt:** 6.10+ `[online installer] <https://download.qt.io/official_releases/online_installers/>`_
 * **CMake:** 3.18+ `[official CMake website] <https://cmake.org/download/>`_
 * **Git:** 2.0+. `[official Git website] <https://git-scm.com/downloads>`_
-* **libclang:** The libclang library, recommended: version 18+ for 6.8+.
+* **libclang:** The libclang library, recommended: version 18+ for 6.10+.
   Prebuilt versions for each OS can be `downloaded here`_.
 * Check the `Supported Platforms of Qt`_
 
@@ -128,7 +128,7 @@ Using the ``--build-tests`` option will enable us to run all the auto tests insi
   python testrunner.py test > testlog.txt
 
 .. note:: On Windows, don't forget to have qtpaths in your path
-   (:command:`set PATH=C:\\\Path\\\to\\\Qt\\\6.4.0\\\msvc2019_64\\\bin;%PATH%`)
+   (:command:`set PATH=C:\\\Path\\\to\\\Qt\\\6.10.0\\\msvc2022_64\\\bin;%PATH%`)
 
 You can also run a specific test (for example ``qpainter_test``) by running::
 
@@ -177,12 +177,12 @@ First and foremost, you need to have access to the target device because you
 need to copy several system files (sysroot). We recommend a Linux OS that has
 the latest Qt versions, like `Manjaro ARM`_ or `Archlinux ARM`_.
 
-* (target) Install Qt 6.3+ on the system using the package manager.
-* (host) Install Qt 6.3+ on the system using the package manager or Qt
+* (target) Install Qt 6.10+ on the system using the package manager.
+* (host) Install Qt 6.10+ on the system using the package manager or Qt
   Installer.
 * (target, host) Install the library and development packages that provide
   C++ headers, linkers, libraries, and compilers.
-* (target, host) Install Python interpreter v3.7 or later
+* (target, host) Install Python interpreter v3.9 or later
 * (target, host) Install CMake 3.17+
 
 After installing these prerequisites, copy the ``target`` sysroot to your
@@ -354,7 +354,7 @@ An example of the ``setup.py`` invocation might look like the following:
     python setup.py bdist_wheel \
         --parallel=8 --ignore-git --reuse-build --standalone --limited-api=yes \
         --cmake-toolchain-file=/opt/toolchain-aarch64.cmake \
-        --qt-host-path=/opt/Qt/6.3.0/gcc_64 \
+        --qt-host-path=/opt/Qt/6.10.0/gcc_64 \
         --plat-name=linux_aarch64
 
 Depending on the target platform, you could use ``linux_armv7``,
@@ -369,8 +369,8 @@ directory, for example:
 
 .. code-block:: bash
 
-    PySide6-6.3.0-6.3.0-cp36-abi3-manylinux2014_aarch64.whl
-    shiboken6-6.3.0-6.3.0-cp36-abi3-manylinux2014_aarch64.whl
+    PySide6-6.10.0-cp310-abi3-manylinux_2_42_aarch64.whl
+    shiboken6-6.10.0cp310-abi3-manylinux_2_42_aarch64.whl
 
 
 Troubleshooting
@@ -536,7 +536,7 @@ A set of tools can be found under the ``tools/`` directory inside the ``pyside-s
 * ``create_changelog.py``: Script used to create the CHANGELOG that you can find in the ``dist/``
   directory. Usage::
 
-    python create_changelog.py -r 6.0.1 -v v6.0.0..6.0 -t bug-fix
+    python create_changelog.py -r 6.10.1
 
 * ``debug_windows.py``: This script can be used to find out why PySide modules
   fail to load with various DLL errors like Missing DLL or Missing symbol in DLL.
@@ -558,7 +558,7 @@ A set of tools can be found under the ``tools/`` directory inside the ``pyside-s
   regarding available modules and classses. This content is displayed in our `wiki page`_,
   and can be used as follows::
 
-    python missing_bindings.py --qt-version 6.0.1 -w all
+    python missing_bindings.py --qt-version 6.10.0 -w all
 
 .. note:: The script relies on BeautifulSoup to parse the content and generate a list of the
    missing bindings.
index 80d56f443ce1aa276f59985902efc6a267f8aff5..4ef0df9abe0236f42ed9670a1f29a1380f2e9136 100644 (file)
@@ -56,9 +56,9 @@ Cloning the official repository can be done by::
 
     git clone https://code.qt.io/pyside/pyside-setup
 
-Checking out the version that we want to build, for example 6.8::
+Checking out the version that we want to build, for example 6.10::
 
-    cd pyside-setup && git checkout 6.8
+    cd pyside-setup && git checkout 6.10
 
 Install the general dependencies::
 
@@ -69,7 +69,7 @@ For building the documentation::
     pip install -r requirements-doc.txt
 
 .. note:: Keep in mind you need to use the same version as your Qt installation.
-          Additionally, :command:`git checkout -b 6.8 --track origin/6.8` could be a better option
+          Additionally, :command:`git checkout -b 6.10 --track origin/6.10` could be a better option
           in case you want to work on it.
 
 .. note:: With `uv`_, use `uv pip install ...`
@@ -82,15 +82,15 @@ uses `create_wheels.py`_ to create the wheels. Once the wheels are created, you
 wheels using the `pip` command.
 
 Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
-for example, :command:`/opt/Qt/6.8.0/gcc_64/bin/qtpaths`.
+for example, :command:`/opt/Qt/6.10.0/gcc_64/bin/qtpaths`.
 
 Build can take a few minutes, so it is recommended to use more than one CPU core::
 
-    python setup.py build --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+    python setup.py build --qtpaths=/opt/Qt/6.10.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
 
 With `uv`_, the build command becomes::
 
-    uv run setup.py build --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+    uv run setup.py build --qtpaths=/opt/Qt/6.10.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
 
 To create the wheels, just run::
 
index 0e10d6cfeb950f659de7ace0aad24774ae6ad251..051eb6cf953ad61de8aedcffff061e390b90c732 100644 (file)
@@ -60,9 +60,9 @@ Cloning the official repository can be done by::
 
     git clone https://code.qt.io/pyside/pyside-setup
 
-Checking out the version that we want to build, for example, 6.8::
+Checking out the version that we want to build, for example, 6.10::
 
-    cd pyside-setup && git checkout 6.8
+    cd pyside-setup && git checkout 6.10
 
 Install the general dependencies::
 
@@ -80,15 +80,15 @@ Building PySide
 ~~~~~~~~~~~~~~~
 
 Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
-for example, ``/opt/Qt/6.8.0/gcc_64/bin/qtpaths``.
+for example, ``/opt/Qt/6.10.0/gcc_64/bin/qtpaths``.
 
 Build can take a few minutes, so it is recommended to use more than one CPU core::
 
-    python setup.py build --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+    python setup.py build --qtpaths=/opt/Qt/6.10.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
 
 With `uv`_, this command becomes::
 
-    uv run setup.py build --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+    uv run setup.py build --qtpaths=/opt/Qt/6.10.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
 
 
 Installing PySide
index 0732953db3ae01ebc946d32177f6df39c6ddd0ac..c4ca5ab4ec62125485c2a9ff31fc41fd64fd34f0 100644 (file)
@@ -77,9 +77,9 @@ Cloning the official repository can be done by::
 
     git clone https://code.qt.io/pyside/pyside-setup
 
-Checking out the version that we want to build, for example, 6.8::
+Checking out the version that we want to build, for example, 6.10::
 
-    cd pyside-setup && git checkout 6.8
+    cd pyside-setup && git checkout 6.10
 
 Install the general dependencies::
 
@@ -97,7 +97,7 @@ Building PySide
 ~~~~~~~~~~~~~~~
 
 Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
-for example, ``C:\Qt\6.8.0\msvc2019_64\bin\qtpaths.exe``.
+for example, ``C:\Qt\6.10.0\msvc2022_64\bin\qtpaths.exe``.
 
 Build can take a few minutes, so it is recommended to use more than one CPU core::
 
index d3062629d45323ee605e41fb044eeba804797f2e..de3da9327934f189d6f0a2d685078b58ea233da6 100644 (file)
@@ -107,7 +107,8 @@ be installed into a virtual environment rather than the default interpreter.
 A ``requirements.txt`` file will be provided alongside the wheels, in order to
 simplify the installation step::
 
-   pip install -r /path/to/Qt/QtForPython/6.6.0/requirements.txt
+   cd /path/to/Qt/QtForPython/6.10.0/
+   pip install --no-index --find-links=. -r requirements.txt
 
 Complementary to the wheels, you will be able to download the sources
 as well.
@@ -133,8 +134,7 @@ There are two options that will list a different set of packages:
   Additionally, here is where you can find the *Qt for automation (M2M
   Protocols)* packages.
 
-* **Qt for Python (Commercial LTS)** where you will find the 5.15.x LTS and 6.2.x
-  LTS releases.
+* **Qt for Python (Commercial LTS)** where you will find the 6.8.x LTS releases.
 
 Once you select any of those, you will be able to select the version of the
 packages you want to download for the **Qt for Python Commercial wheels** packages:
@@ -170,7 +170,7 @@ Alternatively for Windows, you can specify the following command which includes
 the version, and assumes that you are running it on the same directory where
 the wheels are::
 
-    pip install --no-index --find-links=. PySide6==6.4.0.commercial
+    pip install --no-index --find-links=. PySide6==6.10.0.commercial
 
 .. note:: As described in the :ref:`package_details` page, the dependency
    of the packages requires you to first install the ``shiboken6``
index 7d2064c73030fb4be9c270b4d3c0c4a93592aa71..0ce9717af1adf8ec7c95a49dc473960640e1b7f5 100644 (file)
@@ -55,16 +55,16 @@ directory as a directory called ``.pyside6-android-deploy``. ``pyside6-android-d
 automatically detect the NDK and SDK from this cache directory.
 
 If you want to try to download the NDK and SDK manually, you can do so from the following steps
-(for Qt 6.8):
+(for Qt 6.10):
 
 1. Download the sdkmanager using the instructions provided in the `Android Studio
    documentation <https://developer.android.com/studio/command-line/sdkmanager>`_.
 
-2. Using the sdkmanager download the following packages (for Qt 6.8)::
+2. Using the sdkmanager download the following packages (for Qt 6.10)::
 
    "platform-tools", "platforms;android-34", "build-tools;35.0.0"
 
-  and install the NDK using the following command (for Qt 6.8)::
+  and install the NDK using the following command (for Qt 6.10)::
 
    "ndk;26.1.10909125"
 
@@ -268,10 +268,10 @@ to cross-compile Qt for Python Android wheels.
 
     git clone https://code.qt.io/pyside/pyside-setup
 
-#. Check out the version that you want to build, for example 6.7. The version checked out has
+#. Check out the version that you want to build, for example 6.10. The version checked out has
    to correspond to the Qt version downloaded in Step 1::
 
-    cd pyside-setup && git checkout 6.7
+    cd pyside-setup && git checkout 6.10
 
 #. Installing the dependencies::
 
@@ -280,10 +280,10 @@ to cross-compile Qt for Python Android wheels.
 
 #. Run the cross-compilation Python script.::
 
-    python tools/cross_compile_android/main.py --plat-name=aarch64 --qt-install-path=/opt/Qt/6.7.0
+    python tools/cross_compile_android/main.py --plat-name=aarch64 --qt-install-path=/opt/Qt/6.10.0
     --auto-accept-license --skip-update
 
-   *--qt-install-path* refers to the path where Qt 6.7.0 is installed. *--auto-accept-license* and
+   *--qt-install-path* refers to the path where Qt 6.10.0 is installed. *--auto-accept-license* and
    *--skip-update* are required for downloading and installing Android NDK and SDK if not already
    specified through command line options or if they don't already exist in the
    ``pyside6-android-deploy`` cache. Use --help to see all the other available options::
index 1161f6d53e1f6935fc51555aebb1224ff81dd1c4..bba12183c3eeeaa74a0316f0e64c74a9419c37d3 100644 (file)
@@ -39,10 +39,10 @@ kept for `self`.
 
 ## Issues
 
-- [Receiver Leak PYSIDE-1057](https://bugreports.qt.io/browse/PYSIDE-1057)
-- [Partial function receiver Leak PYSIDE-2793](https://bugreports.qt.io/browse/PYSIDE-2793)
+- [Receiver Leak PYSIDE-1057](https://qt-project.atlassian.net/browse/PYSIDE-1057)
+- [Partial function receiver Leak PYSIDE-2793](https://qt-project.atlassian.net/browse/PYSIDE-2793)
 - Various issues related to threading and object deletion, solved by workarounds
-  ([PYSIDE-2646](https://bugreports.qt.io/browse/PYSIDE-2646))
+  ([PYSIDE-2646](https://qt-project.atlassian.net/browse/PYSIDE-2646))
 - Complicated code, hard to maintain
 - Disconnect does not work for `QObject.connect()` with context argument; it also
   leaks methods
index 1aa385008fa72ecc3d84fcac156c9cd6b52847df..4db151bbc8dee3b6a81d69b81e28e920a77b78c0 100644 (file)
@@ -14,8 +14,9 @@ QML applications with Python code.
 .. note:: A set of Qt Quick-based UI controls is also available to create user interfaces.
           See :mod:`PySide6.QtQuickControls2` for more information.
 
-If you're new to QML and Qt Quick, please see :ref:`QML-Applications` for an
-introduction to writing QML applications.
+If you're new to QML and Qt Quick, please see
+:ref:`Getting started with Qt Quick applications <Getting-started-with-Qt-Quick-applications>`
+for an introduction to writing QML applications.
 
 Using the Module
 ^^^^^^^^^^^^^^^^
index bf6d19ab308cef04d91c50b8c03ca375c08bd811..55e1e7d94a05b6e40392b454a1e42e105a74aa35 100644 (file)
@@ -5,16 +5,17 @@ Binding Generation: What Is Shiboken?
 
 When you install ``PySide6`` you might have notice that also ``Shiboken6``
 is installed as a dependency::
+https://download.qt.io/snapshots/ci/pyside/6.10/latest/pyside6/shiboken6-6.10.0a1.dev1771851518-cp39-abi3-manylinux_2_34_x86_64.whl
 
     $ pip install pyside6
     Collecting pyside6
-      Downloading PySide6-6.0.0-6.0.0-cp36.cp37.cp38.cp39-abi3-manylinux1_x86_64.whl (170.5 MB)
+      Downloading PySide6-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl (170.5 MB)
          |████████████████████████████████| 170.5 MB 42 kB/s
-    Collecting shiboken6==6.0.0
-      Downloading shiboken6-6.0.0-6.0.0-cp36.cp37.cp38.cp39-abi3-manylinux1_x86_64.whl (964 kB)
+    Collecting shiboken6==6.10.0
+      Downloading shiboken6-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl (964 kB)
          |████████████████████████████████| 964 kB 29.3 MB/s
     Installing collected packages: shiboken6, pyside6
-    Successfully installed pyside6-6.0.0 shiboken6-6.0.0
+    Successfully installed pyside6-6.10.0 shiboken6-6.10.0
 
 That installed package is also called **Shiboken Module**, and it contains
 some utilities for PySide to properly work.
index 9f4b97f8e53166e8173ea1f9622661d429aa9891..ef2d2c05b5f89306cd52af072b16f7320364b02e 100644 (file)
@@ -15,7 +15,7 @@ Requirements
 
 Before you can install |project|, first you must install the following software:
 
-* `Official <https://www.python.org/downloads/>`_ Python 3.8+
+* `Official <https://www.python.org/downloads/>`_ Python 3.9+
 * We **highly** recommend using a virtual environment, such as
   `venv <https://docs.python.org/3/library/venv.html>`_ or
   `virtualenv <https://virtualenv.pypa.io/en/latest>`_
@@ -61,14 +61,14 @@ Installation
 
         pip install pyside6
 
-  * For a specific version, like 6.4.1::
+  * For a specific version, like 6.10.1::
 
-        pip install pyside6==6.4.1
+        pip install pyside6==6.10.1
 
   * It is also possible to install a specific snapshot from our servers.
     To do so, you can use the following command::
 
-      pip install --index-url=https://download.qt.io/snapshots/ci/pyside/6.4/latest pyside6 --trusted-host download.qt.io
+      pip install --index-url=https://download.qt.io/snapshots/ci/pyside/6.10/latest pyside6 --trusted-host download.qt.io
 
 * **Test your installation**
 
index 8c614978af148be4e8e6a5cec23c6b5e188caf81..a6f7b2cd5f471d6b4232b4c2e504da2a00257edf 100644 (file)
@@ -29,7 +29,7 @@ PySide2 applications.
 This project is available under the LGPLv3/GPLv3 and the `Qt commercial license`_.
 
 .. _Qt: https://doc.qt.io
-.. _`Qt commercial license`: https://www.qt.io/licensing/`
+.. _`Qt commercial license`: https://www.qt.io/licensing/
 
 Quick Start
 ===========
index d3e4f7bb04bd6eb60cd9eed8e67fe3733181c3e9..46b34a8e7e5d94ccf46d711f36c1f446fd2db370 100644 (file)
@@ -16,8 +16,8 @@ Qt Modules Supported by Qt for Python
 
     .. grid-item-card:: :mod:`Qt Charts <PySide6.QtCharts>`
 
-        UI Components for displaying visually pleasing charts,
-        driven by static or dynamic data models.
+        Deprecated since Qt 6.10 in favor of :mod:`Qt Graphs <PySide6.QtGraphs>`.
+        UI Components for displaying charts.
 
     .. grid-item-card:: :mod:`Qt Concurrent <PySide6.QtConcurrent>`
 
@@ -30,7 +30,8 @@ Qt Modules Supported by Qt for Python
 
     .. grid-item-card:: :mod:`Qt Data Visualization <PySide6.QtDataVisualization>`
 
-        UI Components for creating stunning 3D data visualizations.
+        Deprecated since Qt 6.10 in favor of :mod:`Qt Graphs <PySide6.QtGraphs>`.
+        UI Components for creating 3D data visualizations.
 
     .. grid-item-card:: :mod:`Qt D-Bus <PySide6.QtDBus>`
 
index 1a46cb0323f8e9ad782bfe10dc77e67e09eac878..a030a4351daacfced0d4459b1ee0f082d3e485bb 100644 (file)
@@ -47,7 +47,7 @@ Third-party Licenses
 
 The licenses for the third-party sources used by Qt itself are listed
 in
-`Qt documentation <https://doc.qt.io/qt-5/licenses-used-in-qt.html>`_.
+`Qt documentation <https://doc.qt.io/qt-6/licenses-used-in-qt.html>`_.
 The following table lists parts of Qt for Python that incorporates
 code licensed under third-party opensource licenses:
 
index c6913f3638213e9cc386d8a69c4511562d7ea94f..4b65b06a7e33edaa39f896b8db8da9487d7a09d9 100644 (file)
@@ -75,7 +75,8 @@ It is also possible to specify a directory containing the project file.
     (see :ref:`tutorial_uifiles`, :ref:`tutorial_qrcfiles`).
 
 *run*
-    Builds the project and runs the main.
+    Builds the project and runs the main. Additional command line arguments
+    can be passed following the project file argument.
 
 *deploy*
     Deploys the application (see see :ref:`pyside6-deploy`).
index 2666f854feee1f14c882dcfd78db3180668bc410..32025f007afb0eda55ca3f0ffe338ddeff887998 100644 (file)
@@ -177,4 +177,4 @@ Run the application by calling ``python main.py`` to checkout the new icon-set:
 .. image:: player-new.png
    :alt: New Multimedia Player Qt Example
 
-.. _`Qt Resource System`: https://doc.qt.io/qt-5/resources.html
+.. _`Qt Resource System`: https://doc.qt.io/qt-6/resources.html
index c1f56df263082765204cf08e6e3d05d04b39b5b0..30aa3c2296dbb7df1bf949d18a562420324bc368 100644 (file)
@@ -8,10 +8,10 @@ Due to the nature of Qt, :class:`~PySide6.QtCore.QObject`\s
 require a way to communicate, and that's the reason for this mechanism to
 be a **central feature of Qt**.
 
-In simple terms, you can understand **Signal and Slots** in the same way you
+
+In simple terms, you can understand **Signals and Slots** in the same way you
 interact with the lights in your house. When you move the light switch
-(signal) you get a result which may be that your light bulbs are switched
-on/off (slot).
+(signal), the result is that your light bulbs are switched on or off (slot).
 
 While developing interfaces, you can get a real example by the effect of
 clicking a button: the 'click' will be the signal, and the slot will be what
@@ -51,9 +51,9 @@ has a ``clicked()`` signal and :class:`~PySide6.QtWidgets.QLineEdit`
 (single line input field) has a slot named ``clear()``.
 So, a text input field with a button to clear the text
 could be implemented by placing a :class:`~PySide6.QtWidgets.QToolButton`
-to the right of the ``QLineEdit`` and connecting its ``clicked()`` signal to the slot
-``clear()``. This is done using the :meth:`~PySide6.QtCore.Signal.connect`
-method of the signal:
+to the right of the ``QLineEdit`` and connecting its ``clicked()`` signal to
+the slot ``clear()``. This is done using the
+:meth:`~PySide6.QtCore.Signal.connect` method of the signal:
 
 .. code-block:: python
 
@@ -130,7 +130,6 @@ as the variable that it is being assigned to.
 
 .. code-block:: python
 
-    # TODO
     signal5 = Signal(int, name='rangeChanged')
     # ...
     rangeChanged.emit(...)
@@ -179,8 +178,8 @@ due to the method being added to the ``QMetaObject`` when creating the connectio
 This is particularly important for ``QObject`` classes registered with QML, where
 missing decorators can introduce bugs.
 
-Missing decorators can be diagnosed by setting activating warnings of the
-logging category ``qt.pyside.libpyside``; for example by setting the
+Missing decorators can be diagnosed by activating warnings of the
+logging category ``qt.pyside.libpyside``; for example, by setting the
 environment variable:
 
 .. code-block:: bash
@@ -211,9 +210,10 @@ main thread.
 Overloading Signals and Slots with Different Types
 --------------------------------------------------
 
-It is actually possible to use signals and slots of the same name with different
-parameter type lists. This is legacy from Qt 5 and not recommended for new code.
-In Qt 6, signals have distinct names for different types.
+
+It is possible to use signals and slots of the same name with different
+parameter type lists. This is legacy from Qt 5 and is not recommended for new code.
+In Qt 6, signals should have distinct names for different types.
 
 The following example uses two handlers for a Signal and a Slot to showcase
 the different functionality.
@@ -272,9 +272,10 @@ strings passed through the ``SIGNAL()`` and/or ``SLOT()`` functions:
     button.connect(SIGNAL("clicked(Qt::MouseButton)"),
                    action_handler, SLOT("action1(Qt::MouseButton)"))
 
+
 This is not normally recommended; it is only needed
 for a few cases where signals are only accessible via ``QMetaObject``
-(``QAxObject``, ``QAxWidget``, ``QDBusInterface`` or ``QWizardPage::registerField()``):
+(``QAxObject``, ``QAxWidget``, ``QDBusInterface``, or ``QWizardPage::registerField()``):
 
 .. code-block:: python
 
index 6d17f03972082c26ebcfdde8b27deb4d46217109..335d3c3b6b86e96d271065d499972701270e9cee 100644 (file)
@@ -5,20 +5,19 @@ Using ``.ui`` files from Designer or QtCreator with ``QUiLoader`` and ``pyside6-
 *************************************************************************************
 
 This page describes the use of
-`Qt Widgets Designer <https://doc.qt.io/qt-6/qtdesigner-manual.html>`_ to create
+`Qt Widgets Designer`_ to create
 graphical interfaces based on Qt Widgets for your Qt for Python project.
 *Qt Widgets Designer* is a graphical UI design tool which is available as a
 standalone binary (``pyside6-designer``) or embedded into the
-`Qt Creator IDE <https://doc.qt.io/qtcreator>`_. Its use within *Qt Creator*
-is described at
-`Using Qt Widgets Designer <https://doc.qt.io/qtcreator/creator-using-qt-designer.html>`_.
+`Qt Creator IDE`_. Its use within *Qt Creator* is described at
+`Using Qt Widgets Designer`_.
 
 .. image:: uifiles.png
    :alt: Designer and the equivalent code
 
 The designs are stored in ``.ui`` files, which is an XML-based format. It will
 be converted to Python or C++ code populating a widget instance at project build
-time by the `pyside6-uic <https://doc.qt.io/qt-6/uic.html>`_ tool.
+time by the `pyside6-uic`_ tool.
 
 To create a new Qt Design Form in *Qt Creator*, choose
 ``File/New File Or Project`` and "Main Window" for template. Save it as
@@ -140,6 +139,32 @@ file:
   You must run ``pyside6-uic`` again every time you make changes
   to the **UI file**.
 
+Resources
+---------
+
+To avoid path resolution issues for image files used in icons, they can be
+embedded into Qt resource files (see `Qt Resource System`_,
+`Editing Resources with Qt Widgets Designer`_ and :ref:`tutorial_qrcfiles`).
+
+When building the project, the tool ``pyside6-rcc`` generates a Python file
+loading them from the ``.qrc`` file. It is typically named ``rc_<name>.py``
+and is imported by the Python file generated by ``pyside6-uic``. For this
+to work, the resource file and the ``.ui`` file should be in the same
+directory.
+
+If that is not the case, the import statement generated by ``pyside6-uic``
+can be tailored by a number of command line options
+(see ``pyside6-uic --help``):
+
+.. code-block:: text
+
+    --from-imports                  Python: generate imports relative to '.'
+    --absolute-imports              Python: generate absolute imports
+    --python-paths <pathlist>       Python paths for --absolute-imports.
+
+Typically, this will be a combination of ``--absolute-imports`` and
+``--python-paths``, passing the project directory to the latter.
+
 Option B: Loading it directly
 =============================
 
@@ -337,3 +362,10 @@ Troubleshooting the Qt Widgets Designer Plugin
   only after *Qt Widgets Designer* has terminated.
 - When building Qt for Python, be sure to set the ``--standalone`` option
   for the plugin to be properly installed.
+
+.. _`Qt Resource System`: https://doc.qt.io/qt-6/resources.html
+.. _`Qt Creator IDE`: https://doc.qt.io/qtcreator
+.. _`pyside6-uic`: https://doc.qt.io/qt-6/uic.html
+.. _`Qt Widgets Designer`: https://doc.qt.io/qt-6/qtdesigner-manual.html
+.. _`Editing Resources with Qt Widgets Designer`: https://doc.qt.io/qt-6/designer-resources.html
+.. _`Using Qt Widgets Designer`: https://doc.qt.io/qtcreator/creator-using-qt-designer.html
index 8f9e3ae960671cc129afdfc0ebaaf516b65a61e7..08bb7a01a93bf79f41dfba21548b805bbb7607d0 100644 (file)
@@ -80,8 +80,8 @@ More examples can be found in the `Qt Style Sheet Examples`_ documentation
 page.
 
 
-.. _`Qt Style Sheets Reference`: https://doc.qt.io/qt-5/stylesheet-reference.html
-.. _`Qt Style Sheet Examples`: https://doc.qt.io/qt-5/stylesheet-examples.html
+.. _`Qt Style Sheets Reference`: https://doc.qt.io/qt-6/stylesheet-reference.html
+.. _`Qt Style Sheet Examples`: https://doc.qt.io/qt-6/stylesheet-examples.html
 
 Qt Style Sheets
 ---------------
index 9073e679a294c200c090bb6ba30d07c3ea137718..637082a49afedc80c8c1da614fef390344a3a756 100644 (file)
@@ -5,7 +5,7 @@ Chapter 3 - Create an empty QMainWindow
 ==========================================
 
 You can now think of presenting your data in a UI. A
-class:`~PySide6.QtWidgets.QMainWindow` provides a
+:class:`~PySide6.QtWidgets.QMainWindow` provides a
 convenient structure for GUI applications, such as a menu bar and status bar.
 The following image shows the layout that QMainWindow offers out-of-the box:
 
@@ -25,8 +25,8 @@ the resolution you currently have. In the following snippet, you will see how
 window size is defined based on available screen width (80%) and height (70%).
 
 .. note:: You can achieve a similar structure using other Qt elements like
-   class:`~PySide6.QtWidgets.QMenuBar`, class:`~PySide6.QtWidgets.QWidget`,
-   and class:`~PySide6.QtWidgets.QStatusBar`. Refer the QMainWindow layout for
+   :class:`~PySide6.QtWidgets.QMenuBar`, class:`~PySide6.QtWidgets.QWidget`,
+   and :class:`~PySide6.QtWidgets.QStatusBar`. Refer the QMainWindow layout for
    guidance.
 
 .. literalinclude:: datavisualize3/main_window.py
index b3041349c4f2705e6e607c3007c4d81695d7f368..3d8f20ab4eff7f5731ed8b8964ceea1ef044e308 100644 (file)
@@ -9,16 +9,16 @@ interface. Usually, a QWidget is used to display data in most data-driven
 applications. Use a table view to display your data.
 
 The first step is to add a horizontal layout with just a
-class:`~PySide6.QtWidgets.QTableView`. You can create a QTableView object
-and place it inside a class:`~PySide6.QtWidgets.QHBoxLayout`. Once the
+:class:`~PySide6.QtWidgets.QTableView`. You can create a QTableView object
+and place it inside a :class:`~PySide6.QtWidgets.QHBoxLayout`. Once the
 QWidget is properly built, pass the object to the QMainWindow as its central
 widget.
 
 Remember that a QTableView needs a model to display information. In this case,
-you can use a class:`~PySide6.QtCore.QAbstractTableModel` instance.
+you can use a :class:`~PySide6.QtCore.QAbstractTableModel` instance.
 
 .. note:: You could also use the default item model that comes with a
-   class:`~PySide6.QtWidgets.QTableWidget` instead. QTableWidget is a
+   :class:`~PySide6.QtWidgets.QTableWidget` instead. QTableWidget is a
    convenience class that reduces your codebase considerably as you don't need
    to implement a data model. However, it's less flexible than a QTableView,
    as QTableWidget cannot be used with just any data. For more insight about
index 4edde69c17c55b3afc1399af6ea3efa7c57d7e9f..c5c8cf5e4d72124c76e528e94d2c80bd21e616d7 100644 (file)
@@ -18,7 +18,7 @@ avoid faulty data or unexpected behavior.
 
 The Date column provides data in UTC format (for example,
 2018-12-11T21:14:44.682Z), so you could easily map it to a
-class:`~PySide6.QtCore.QDateTime` object
+:class:`~PySide6.QtCore.QDateTime` object
 defining the structure of the string. Additionally, you can adapt the time
 based on the timezone you are in, using QTimeZone.
 
index 22723af1fe99ce2e13ade53b8d0a3e417dc7b734..8edada0e36414bf2b3b8aba49673ffafd592945e 100644 (file)
@@ -76,7 +76,7 @@ Empty widget and data
 ---------------------
 
 The `QMainWindow` enables us to set a central widget that will be displayed when showing the window
-(`read more <https://doc.qt.io/qt-5/qmainwindow.html#details>`_).
+(`read more <https://doc.qt.io/qt-6/qmainwindow.html#details>`_).
 This central widget could be another class derived from `QWidget`.
 
 Additionally, you will define example data to visualize later.
index 1caf18d457086e01632310cc91cf2952f34631ed..f47e88002962d00a87b6b2a7b400daab81f148fc 100644 (file)
@@ -127,7 +127,7 @@ application and PySide6 integration:
       :emphasize-lines: 8
 
    You can read more about this configuration file
-   `here <https://doc.qt.io/qt-5/qtquickcontrols2-configuration.html>`_.
+   `here <https://doc.qt.io/qt-6/qtquickcontrols2-configuration.html>`_.
 
    The final look of your application will be:
 
index 85db25defff98689d50c83110152dba0f1fa4392..e025f718b8e73b15368d8d1a6dff0e6aa0615ca9 100644 (file)
@@ -126,18 +126,18 @@ so this results in each item consuming the entire width of the layout.
 On the other hand, the `Layout.fillHeight`_ statement in the `ListView`_ enables it to occupy the
 remaining space that is left after accommodating the Pane.
 
-.. _Item Positioners: https://doc.qt.io/qt-5/qtquick-positioning-layouts.html
-.. _Qt Quick Layouts: https://doc.qt.io/qt-5/qtquicklayouts-index.html
-.. _Row: https://doc.qt.io/qt-5/qml-qtquick-row.html
-.. _Column: https://doc.qt.io/qt-5/qml-qtquick-column.html
-.. _ColumnLayout: https://doc.qt.io/qt-5/qml-qtquick-layouts-columnlayout.html
-.. _ListView: https://doc.qt.io/qt-5/qml-qtquick-listview.html
-.. _Pane: https://doc.qt.io/qt-5/qml-qtquick-controls2-pane.html
-.. _Frame: https://doc.qt.io/qt-5/qml-qtquick-controls2-frame.html
-.. _attached properties: https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html
-.. _Layout.fillWidth: https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html#fillWidth-attached-prop
-.. _Layout.fillHeight: https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html#fillHeight-attached-prop
-.. _Qt Quick QML Types: https://doc.qt.io/qt-5/qtquick-qmlmodule.html
+.. _Item Positioners: https://doc.qt.io/qt-6/qtquick-positioning-layouts.html
+.. _Qt Quick Layouts: https://doc.qt.io/qt-6/qtquicklayouts-index.html
+.. _Row: https://doc.qt.io/qt-6/qml-qtquick-row.html
+.. _Column: https://doc.qt.io/qt-6/qml-qtquick-column.html
+.. _ColumnLayout: https://doc.qt.io/qt-6/qml-qtquick-layouts-columnlayout.html
+.. _ListView: https://doc.qt.io/qt-6/qml-qtquick-listview.html
+.. _Pane: https://doc.qt.io/qt-6/qml-qtquick-controls2-pane.html
+.. _Frame: https://doc.qt.io/qt-6/qml-qtquick-controls2-frame.html
+.. _attached properties: https://doc.qt.io/qt-6/qml-qtquick-layouts-layout.html
+.. _Layout.fillWidth: https://doc.qt.io/qt-6/qml-qtquick-layouts-layout.html#fillWidth-attached-prop
+.. _Layout.fillHeight: https://doc.qt.io/qt-6/qml-qtquick-layouts-layout.html#fillHeight-attached-prop
+.. _Qt Quick QML Types: https://doc.qt.io/qt-6/qtquick-qmlmodule.html
 
 Let's look at the ``Listview`` in detail:
 
@@ -183,9 +183,9 @@ Lastly, we have a button that allows us to call the ``send_message`` method we d
 ``sqlDialog.py``, since we're just having a mock up example here and there is only one possible
 recipient and one possible sender for this conversation we're just using strings here.
 
-.. _displayMarginBeginning: https://doc.qt.io/qt-5/qml-qtquick-listview.html#displayMarginBeginning-prop
-.. _displayMarginEnd: https://doc.qt.io/qt-5/qml-qtquick-listview.html#displayMarginEnd-prop
-.. _TextArea: https://doc.qt.io/qt-5/qml-qtquick-controls2-textarea.html
+.. _displayMarginBeginning: https://doc.qt.io/qt-6/qml-qtquick-listview.html#displayMarginBeginning-prop
+.. _displayMarginEnd: https://doc.qt.io/qt-6/qml-qtquick-listview.html#displayMarginEnd-prop
+.. _TextArea: https://doc.qt.io/qt-6/qml-qtquick-controls2-textarea.html
 
 ``Main.qml`` needs to be put into a directory named :code:`App` along
 with a file named ``qmldir`` to describe a basic QML module:
index beb8a670be2e11afd62c1318b96c94db4043eead..9698446da4ff3330322f7a2fa277da06603d7c40 100644 (file)
@@ -173,7 +173,7 @@ DataList *dataListFromCapsule(PyObject *capsule)
 
 static const char *Slot_SignatureStrings[] = {
     "PySide6.QtCore.Slot(self,*types:typing.Union[type,str],name:str=nullptr,result:typing.Union[type,str]=nullptr)",
-    "PySide6.QtCore.Slot.__call__(self,function:collections.abc.Callable[...,typing.Any])->typing.Any",
+    "PySide6.QtCore.Slot.__call__(self,function:_SlotFunc)->_SlotFunc",
     nullptr}; // Sentinel
 
 void init(PyObject *module)
index d8fd41188bac09eada339b76ca1f73a6363bec2b..07c1f1163f48478ae8344c54f3ad2c257a31335a 100644 (file)
@@ -116,7 +116,7 @@ PyObjectWrapper::PyObjectWrapper()
 {
     // PYSIDE-813: When PYSIDE-164 was solved by adding some thread allowance,
     // this code was no longer protected. It was hard to find this connection.
-    // See the website https://bugreports.qt.io/browse/PYSIDE-813 for details.
+    // See PYSIDE-813 for details.
     Shiboken::GilState gil;
     Py_XINCREF(m_me);
 }
index edcfe08eed4f46b076fb863fb89ecb6bdcae4bb0..be817869f214e4e43ecc5dd4aad810bf9b6e0cc4 100644 (file)
@@ -5,8 +5,7 @@ from __future__ import annotations
 '''
 Unit tests for attribute cache in Python 3
 
-This is the original code from the bug report
-https://bugreports.qt.io/browse/PYSIDE-60
+This is the original code from the bug report PYSIDE-60
 '''
 
 import os
index e8d4864ddd93a16556ee9c1631c5d947c2d503dd..94f7c4197bb16beaa0cc866bfdd0634fff62d819 100644 (file)
@@ -5,8 +5,7 @@ from __future__ import annotations
 """
 emoji-string-test.py
 
-This is the original code from the bug report
-https://bugreports.qt.io/browse/PYSIDE-336
+This is the original code from the bug report PYSIDE-336
 
 The only changes are the emoji constant creation which avoids unicode in the
 source itself, utf8 encoding in line 1 and a short plausibility test to make
index 5624ba76b53bc47d7e01944710b70869a0516e8e..53374588f061c4268210cabc866d82ccc15a0fef 100644 (file)
@@ -17,6 +17,8 @@ init_test_paths(False)
 from helper.usesqapplication import UsesQApplication
 
 from PySide6.QtCore import QTimer
+from PySide6.QtGui import QGuiApplication  # noqa: F401
+from PySide6.QtMultimedia import QMediaDevices
 
 try:
     from PySide6.QtTextToSpeech import QTextToSpeech
@@ -33,6 +35,8 @@ class QTextToSpeechTestCase(UsesQApplication):
             engines[0], engines[1] = engines[1], engines[0]
         if not engines:
             print('No QTextToSpeech engines available')
+        elif not QMediaDevices.audioOutputs():
+            print('No audio outputs available')
         else:
             speech = QTextToSpeech(engines[0])
             speech.stateChanged.connect(self._slotStateChanged)
index a6b2574d2933dec620c350f818ad062a086d007c..e02efd46d2dfa05b0ee241285ecc109427f9341a 100644 (file)
@@ -3,8 +3,7 @@
 from __future__ import annotations
 
 """
-This is the example from https://bugreports.qt.io/browse/PYSIDE-772
-with no interaction as a unittest.
+This is the example from PYSIDE-772 with no interaction as a unittest.
 """
 
 import os
index da20e3824989d9c37a7ef97c873c7d79fa345eaa..37fd4cad678632fec3c7a29e884240e73adb374c 100644 (file)
@@ -1,5 +1,5 @@
-To run these tests is necessary some manuall input (most of then not supported by QTest[1]),
+To run these tests, some manual input is necessary (most of them not supported by QTest[1]),
 because of that this is not part of automatic test context.
 
 
-[1]http://bugreports.qt.nokia.com/browse/QTBUG-13397
+[1]https://qt-project.atlassian.net/browse/QTBUG-13397
index 7e1ddbb17aa1ee7800779477118c6e6954ec2692..e4f0ee22dba6d9c2172990d2ddfd4a15aa856751 100644 (file)
@@ -3,8 +3,7 @@
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
 from __future__ import annotations
 
-''' PYSIDE-315: https://bugreports.qt.io/browse/PYSIDE-315
-    Test that all signals and slots of a class (including any mixin classes)
+''' PYSIDE-315: Test that all signals and slots of a class (including any mixin classes)
     are registered at type parsing time. Also test that the signal and slot
     indices do not change after signal connection or emission. '''
 
index f6504a78929dfc547e8eac152b93104aa909840f..c8435b9573c514a5d25e7549daf52f5134ee92c9 100644 (file)
@@ -3,8 +3,7 @@
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
 from __future__ import annotations
 
-""" PYSIDE-1755: https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1755
-    Tests that a unittest.mock.MagicMock() can be used as a slot for quick
+""" PYSIDE-1755: Tests that a unittest.mock.MagicMock() can be used as a slot for quick
     prototyping. """
 
 import os
index 0d1ae3754a3bd19664940e6503fbdca5d52e5be5..875bd64291d33cd1cd29cbb6107ca7d3d5357e52 100644 (file)
@@ -52,7 +52,7 @@ class qAppMacroTest(unittest.TestCase):
 
     def test_1093(self):
         # Test that without creating a QApplication staticMetaObject still exists.
-        # Please see https://bugreports.qt.io/browse/PYSIDE-1093 for explanation.
+        # Please see PYSIDE-1093 for explanation.
         # Note: This test must run first, otherwise we would be mislead!
         assert self._test_1093_is_first
         from PySide6 import QtCore
index e087333c4e83ad52205a8dac1158ad5cba8ab3a2..7711230ffd2874ce180c67f8b00f626ccbb789bd 100644 (file)
@@ -3,8 +3,7 @@
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
 from __future__ import annotations
 
-''' PYSIDE-315: https://bugreports.qt.io/browse/PYSIDE-315
-    Test that creating a signal in the wrong order triggers a warning. '''
+''' PYSIDE-315: Test that creating a signal in the wrong order triggers a warning. '''
 
 import os
 import sys
index e9be966c3b76eda0361f3c2dc02b924ccb285193..e45e32905a9f8c63c4361433d72e2a63183b54d6 100644 (file)
@@ -9,8 +9,7 @@ PYSIDE-68: Test that signals have a `__get__` function after all.
 We supply a `tp_descr_get` slot for the signal type.
 That creates the `__get__` method via `PyType_Ready`.
 
-The original test script was converted to a unittest.
-See https://bugreports.qt.io/browse/PYSIDE-68 .
+The original test script was converted to a unittest (see PYSIDE-68).
 
 Created:    16 May '12 21:25
 Updated:    17 Sep '20 17:02
index a4d41275e4db9b82b52aff56cf28441bdfd98132..9334e000f06e087c872945a16c609f6111a02f1a 100644 (file)
@@ -264,6 +264,7 @@ class TestPySide6DeployQml(DeployTestBase):
                             "xcbglintegrations"]
         # Plugins that needs to be passed to Nuitka
         plugins_nuitka = "networkinformation,platforminputcontexts,qml,qmltooling"
+        data_dir = str(self.temp_example_qml / "EditingModel")
         self.expected_run_cmd = (
             f"{sys.executable} -m nuitka {str(self.main_file)} --follow-imports"
             f" --enable-plugin=pyside6 --output-dir={str(self.deployment_files)} --quiet"
@@ -271,9 +272,7 @@ class TestPySide6DeployQml(DeployTestBase):
             f" {self.dlls_ignore_nuitka}"
             " --noinclude-dlls=*/qml/QtQuickEffectMaker/*"
             f" --include-qt-plugins={plugins_nuitka}"
-            f" --include-data-files={str(self.temp_example_qml / self.first_qml_file)}="
-            f"./main.qml --include-data-files="
-            f"{str(self.temp_example_qml / self.second_qml_file)}=./MovingRectangle.qml"
+            f" --include-data-dir={data_dir}=./EditingModel"
         )
 
         if sys.platform != "win32":
@@ -318,8 +317,8 @@ class TestPySide6DeployQml(DeployTestBase):
         self.assertEqual(config_obj.get_value("app", "exec_directory"), ".")
         self.assertEqual(config_obj.get_value("python", "packages"),
                          "Nuitka==2.7.11")
-        self.assertEqual(config_obj.get_value("qt", "qml_files"),
-                         "MovingRectangle.qml,main.qml")
+        expected_qml_files = f"EditingModel{os.sep}Main.qml,EditingModel{os.sep}MovingRectangle.qml"
+        self.assertEqual(config_obj.get_value("qt", "qml_files"), expected_qml_files)
         equ_base = "--quiet --noinclude-qt-translations"
         equ_value = equ_base + " --static-libpython=no" if is_pyenv_python() else equ_base
         self.assertEqual(config_obj.get_value("nuitka", "extra_args"), equ_value)
@@ -384,13 +383,13 @@ class TestPySide6DeployWebEngine(DeployTestBase):
         # Plugins that needs to be passed to Nuitka
         plugins_nuitka = "networkinformation,platforminputcontexts,qml,qmltooling"
         qml_files = [
-            "ApplicationRoot.qml",
-            "BrowserDialog.qml",
-            "BrowserWindow.qml",
-            "DownloadView.qml",
-            "FindBar.qml",
-            "FullScreenNotification.qml",
-            "WebAuthDialog.qml"
+            f"BrowserUtils{os.sep}ApplicationRoot.qml",
+            f"BrowserUtils{os.sep}BrowserDialog.qml",
+            f"BrowserUtils{os.sep}BrowserWindow.qml",
+            f"BrowserUtils{os.sep}DownloadView.qml",
+            f"BrowserUtils{os.sep}FindBar.qml",
+            f"BrowserUtils{os.sep}FullScreenNotification.qml",
+            f"BrowserUtils{os.sep}WebAuthDialog.qml"
         ]
         data_files_cmd = " ".join(
             [
@@ -500,7 +499,7 @@ class TestLongCommand(DeployTestBase):
         with open(self.temp_example_qml / "deploy_main.py", "r") as file:
             # check if 516 lines start with # nuitka-project:
             self.assertEqual(len([line for line in file.readlines()
-                                  if line.startswith("# nuitka-project:")]), 516)
+                                  if line.startswith("# nuitka-project:")]), 517)
 
 
 @unittest.skipIf(sys.platform == "darwin" and int(platform.mac_ver()[0].split('.')[0]) <= 11,
index 7bd76c7cdedd2a3955f02ec194f4cec08e512047..b2ed939ddd43179ac43ee54cf94cabaf8978280a 100644 (file)
@@ -1,5 +1,5 @@
 set(shiboken_MAJOR_VERSION "6")
 set(shiboken_MINOR_VERSION "10")
-set(shiboken_MICRO_VERSION "2")
+set(shiboken_MICRO_VERSION "3")
 set(shiboken_PRE_RELEASE_VERSION_TYPE "")
 set(shiboken_PRE_RELEASE_VERSION "")
index 4c63c3e88687e6fa483544a47c9f786c3e67a243..fe5af06f2c695f9f6993a258f2f1c5323597a45f 100644 (file)
@@ -249,11 +249,6 @@ static CXTranslationUnit createTranslationUnit(CXIndex index,
                                      // https://github.com/darlinghq/darling/issues/204
 #endif
         "-Wno-constant-logical-operand",
-#if CINDEX_VERSION_MAJOR > 0 || CINDEX_VERSION_MINOR >= 64 // Clang 21
-        // QTBUG-141204: Suppress "character-conversion" warnings in Qt: qchar.h:... warning: implicit
-        // conversion from 'const char16_t' to 'char32_t' may change the meaning of the represented code unit.
-        "-Wno-character-conversion",
-#endif
         "-x",
         "c++" // Treat .h as C++, not C
     };
index cad49086db327942840d356f97277a1ed782d3ba..552c39a68da899c59cf7a6568b4925dd4f7ec6db 100644 (file)
@@ -11,11 +11,11 @@ need to continue if you already have a built PySide.
 General Requirements
 ^^^^^^^^^^^^^^^^^^^^
 
-* **Python**: 3.7+
-* **Qt:** 6.0+
-* **libclang:** The libclang library, recommended: version 10 for 6.0+.
+* **Python**: 3.9+
+* **Qt:** 6.10+
+* **libclang:** The libclang library, recommended: version 18+ for Qt 6.10+.
   Prebuilt versions of it can be `downloaded here`_.
-* **CMake:** 3.1+ is needed.
+* **CMake:** 3.18+ is needed.
 
 .. _downloaded here: https://download.qt.io/development_releases/prebuilt/libclang/
 
index 3875c53fb864dfe56dd7853ddace95c88862e7aa..5ff16afe2ed08e309190f243a344d74a03861805 100644 (file)
@@ -909,6 +909,8 @@ QString QtDocGenerator::translateToPythonType(const AbstractMetaType &type,
         const auto &basicName = type.basicPrimitiveName();
         if (AbstractMetaType::cppSignedIntTypes().contains(basicName)
             || AbstractMetaType::cppUnsignedIntTypes().contains(basicName)) {
+            if (basicName == charT)
+                return u"single character string"_s;
             return intT;
         }
         if (AbstractMetaType::cppFloatTypes().contains(basicName))
index 36f34de5d787243726eac2db55f83b76cc03486c..f6f6954477bb7cd3860811ef81be90f15de6d1fa 100644 (file)
@@ -3799,9 +3799,9 @@ void CppGenerator::writeNamedArgumentResolution(TextStream &s,
         const auto &type = arg.modifiedType();
         const QString pyArgName = pythonArgsAt(pyArgIndex);
         s << "\n|| ";
-        s << '(' << pyArgName << " != nullptr && !";
+        s << '(' << pyArgName << " != nullptr && !(";
         writeTypeCheck(s, type, pyArgName, isNumber(type.typeEntry()), {});
-        s << ')';
+        s << "))";
     }
     s << outdent << ") {\n" << indent
         << "Py_XINCREF(errInfo.object());\n" // PYSIDE-3133, 0 if conversion fails
index cca7ae7424c059e8b45ce8bf04c864b5f144fa6e..4bedf3f668a10ad85b9e6e60c00a33ae0dcc6acc 100644 (file)
@@ -166,6 +166,8 @@ class Formatter(Writer):
         # PlaceholderType fix to avoid the '~' from TypeVar.__repr__
         if "~PlaceholderType" in source:
             source = source.replace("~PlaceholderType", "PlaceholderType")
+        if "~_SlotFunc" in source:
+            source = source.replace("~_SlotFunc", "_SlotFunc")
         # Replace all "NoneType" strings by "None" which is a typing convention.
         return source.replace("NoneType", "None")
 
@@ -176,6 +178,9 @@ class Formatter(Writer):
         # PlaceholderType fix to avoid the '~' from TypeVar.__repr__
         if "~PlaceholderType" in source:
             source = source.replace("~PlaceholderType", "PlaceholderType")
+        if "~_SlotFunc" in source:
+            source = source.replace("~_SlotFunc", "_SlotFunc")
+
 
         while match := cls.opt_uni_searcher.search(source):
             start = match.start()
@@ -415,6 +420,8 @@ def generate_pyi(import_name, outpath, options):
                 if import_name == "PySide6.QtCore":
                     wr.print("PlaceholderType = typing.TypeVar(\"PlaceholderType\", "
                              "bound=PySide6.QtCore.QObject)")
+                    wr.print("_SlotFunc = typing.TypeVar(\"_SlotFunc\", "
+                             "bound=collections.abc.Callable[..., object])")
                 wr.print()
             else:
                 wr.print(line)
index 9d1c3fd5f0cd3343c515c323656f3f4bdf3daa71..c0fdf1c326e379bf7910ae67443c3e0513ba0579 100644 (file)
@@ -55,6 +55,8 @@ NoneType = type(None)
 # Later it will be bound to a QObject, within the QtCore types extensions
 PlaceholderType = TypeVar("PlaceholderType")
 
+_SlotFunc = TypeVar("_SlotFunc")
+
 _S = TypeVar("_S")
 
 MultiMap = typing.DefaultDict[str, typing.List[str]]
index ad2f4dd5a33d8d8a564f2cb8fcd5d2d09fbe4eff..bf8fcc3f0bd14dc0e55e2feec9c9dc27d30e034b 100644 (file)
@@ -89,6 +89,11 @@ Event *returnNullValueTypePointer()
     return nullptr;
 }
 
+int asciiCode(char character)
+{
+    return character;
+}
+
 unsigned int doubleUnsignedInt(unsigned int value)
 {
     return value * 2;
index ef88e543e6c56ad22a0381f8f045ecc4b8ae2d54..446dfb64c95d5c2167f5329994d4fea1ff58efc2 100644 (file)
@@ -47,6 +47,7 @@ LIBSAMPLE_API Event *returnNullValueTypePointer();
 LIBSAMPLE_API GlobalOverloadFuncEnum overloadedFunc(int val);
 LIBSAMPLE_API GlobalOverloadFuncEnum overloadedFunc(double val);
 
+LIBSAMPLE_API int asciiCode(char character = 'a');
 LIBSAMPLE_API unsigned int doubleUnsignedInt(unsigned int value);
 LIBSAMPLE_API long long doubleLongLong(long long value);
 LIBSAMPLE_API unsigned long long doubleUnsignedLongLong(unsigned long long value);
index 6af597192829bb83b56f45f71a8fc3a56afe796f..838f1a3ee968e0b90efc7b18dd641497c5e264a6 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "polygon.h"
 
+#include <algorithm>
+
 Polygon::Polygon(double x, double y) : m_points({Point(x, y)})
 {
 }
@@ -37,3 +39,8 @@ void Polygon::stealOwnershipFromPython(Polygon *polygon)
 {
     delete polygon;
 }
+
+bool Polygon::contains(Point needle) const
+{
+    return std::find(m_points.cbegin(), m_points.cend(), needle) != m_points.cend();
+}
index 2424ddd519d0b8aa9f954e195a8def1bf242350e..ff07122ccf5d4b5a255046c064f9ed5992cfffe5 100644 (file)
@@ -32,6 +32,8 @@ public:
     // This method invalidates the argument to be used in a call to doublePolygonScale(Polygon).
     static void stealOwnershipFromPython(Polygon *polygon);
 
+    bool contains(Point needle) const;
+
 private:
     PointList m_points;
 };
index 9aeeeed20ecbd7cf18ded37f02c0dc5ec23cf6bf..9b3369925e11fd2a092a8831e6aa357941fbde90 100644 (file)
@@ -14,7 +14,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from shiboken_paths import init_paths
 init_paths()
 
-from sample import SampleNamespace, Point, ObjectType, ObjectModel
+from sample import asciiCode, SampleNamespace, Point, ObjectType, ObjectModel
 
 
 class DecisorTest(unittest.TestCase):
@@ -48,6 +48,13 @@ class DecisorTest(unittest.TestCase):
         self.assertNotEqual(ObjectModel.receivesObjectTypeFamily(objectmodel),
                             ObjectModel.MethodCalled.ObjectTypeCalled)
 
+    def testKeywordArguments(self):
+        '''PYSIDE-3281: Test the complex SbkChar type check expression in conjunction with
+           keyword arguments.'''
+        self.assertEqual(asciiCode("a"), ord('a'))  # Default parameter "a"
+        self.assertEqual(asciiCode("b"), ord('b'))  # Positional
+        self.assertEqual(asciiCode(character="b"), ord('b'))  # Keyword
+
 
 if __name__ == '__main__':
     unittest.main()
index 43b84d36ede6d4bdc9c100f3a0b98e3c9a7c920d..8b3559dacdd1c29c777e75f935853231ac4ef303 100644 (file)
@@ -86,6 +86,16 @@ class ModuleTest(unittest.TestCase):
         self.assertIsNone(sample.SampleNamespace.optionalMultiply(v1, None))
         self.assertIsNone(sample.SampleNamespace.optionalMultiply(None, v2))
 
+    def testSequenceProtocol(self):
+        """Test slot Py_sq_contains of Polygon."""
+        point1 = sample.Point(1, 2)
+        point2 = sample.Point(3, 4)
+        polygon = sample.Polygon()
+        polygon.addPoint(point1)
+        polygon.addPoint(point2)
+        self.assertTrue(point1 in polygon)
+        self.assertFalse(sample.Point(5, 6) in polygon)
+
 
 if __name__ == '__main__':
     unittest.main()
index a6cd6e055b5e0d49770b3a67ed397eb0cb237262..19d86e9fd7084d95d0572e01877d137672879a1f 100644 (file)
@@ -302,3 +302,8 @@ PyObject *type = PyObject_Type(%PYSELF);
 PyObject *args = Py_BuildValue("(dd)", %CPPSELF.x(), %CPPSELF.y());
 %PYARG_0 = Py_BuildValue("(OO)", type, args);
 // @snippet point-reduce
+
+// @snippet polygon-contains
+auto needle = %CONVERTTOCPP[Point](_value);
+return %CPPSELF.contains(needle) ? 1 : 0;
+// @snippet polygon-contains
index 73f2e14252253e94be06de89f55aa6dd2ba8c656..0655da80e9633b4edca8e8ded46068deecffd868 100644 (file)
     <function signature="transmuteComplexIntoPoint(const Complex&amp;)" />
     <function signature="transmutePointIntoComplex(const Point&amp;)" />
     <function signature="sumComplexPair(std::pair&lt;Complex, Complex>)" />
+    <function signature="asciiCode(char)"/>
     <function signature="doubleUnsignedInt(unsigned int)" />
     <function signature="doubleLongLong(long long)" />
     <function signature="doubleUnsignedLongLong(unsigned long long)" />
                 <define-ownership owner="c++"/>
             </modify-argument>
         </modify-function>
+        <add-function signature="__contains__(PyObject*@needle@)" return-type="int">
+          <inject-code class="target" position="beginning"
+                       file="samplesnippets.cpp" snippet="polygon-contains"/>
+        </add-function>
     </value-type>
 
     <value-type name="Time">
index b652f3e26e5476f40d86db8e5c7bf012a102c249..dd27eb754234f6bd13fdb3a9912b16424d9ccc84 100644 (file)
@@ -48,7 +48,7 @@ Homepage = "https://pyside.org"
 Documentation = "https://doc.qt.io/qtforpython"
 Repository = "https://code.qt.io/cgit/pyside/pyside-setup.git/"
 Changelog = "https://code.qt.io/cgit/pyside/pyside-setup.git/tree/doc/changelogs"
-Tracker = "https://bugreports.qt.io/projects/PYSIDE"
+Tracker = "https://qt-project.atlassian.net/browse/PYSIDE"
 
 # PROJECT_SCRIPTS