aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--coin/instructions/common_environment.yaml104
-rw-r--r--coin/instructions_utils.py6
-rw-r--r--doc/changelogs/changes-6.9.340
-rw-r--r--examples/multimedia/audiooutput/audiooutput.py23
-rw-r--r--examples/multimedia/audiosource/audiosource.py18
-rw-r--r--examples/multimedia/player/player.py8
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/Main.qml3
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/About.qml2
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Editor.qml1
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/ResizeButton.qml1
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Sidebar.qml2
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/qmldir3
-rw-r--r--examples/webenginequick/nanobrowser/ApplicationRoot.qml16
-rw-r--r--examples/webenginequick/nanobrowser/BrowserWindow.qml225
-rw-r--r--examples/webenginequick/nanobrowser/DownloadView.qml27
-rw-r--r--examples/webenginequick/nanobrowser/FindBar.qml25
-rw-r--r--examples/webenginequick/nanobrowser/FullScreenNotification.qml6
-rw-r--r--examples/webenginequick/nanobrowser/rc_resources.py10
-rw-r--r--requirements-doc.txt1
-rw-r--r--sources/pyside6/PySide6/QtMultimedia/typesystem_multimedia.xml5
-rw-r--r--sources/pyside6/PySide6/glue/qtmultimedia.cpp12
-rw-r--r--sources/pyside6/doc/conf.py.in2
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp113
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetabuilder_p.h6
-rw-r--r--sources/shiboken6/ApiExtractor/addedfunction.cpp6
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp24
-rw-r--r--sources/shiboken6/ApiExtractor/documentation.h1
-rw-r--r--sources/shiboken6/ApiExtractor/enumtypeentry.h12
-rw-r--r--sources/shiboken6/ApiExtractor/parser/codemodel.cpp17
-rw-r--r--sources/shiboken6/ApiExtractor/parser/codemodel.h6
-rw-r--r--sources/shiboken6/ApiExtractor/parser/codemodel_enums.h10
-rw-r--r--sources/shiboken6/ApiExtractor/parser/typeinfo.cpp39
-rw-r--r--sources/shiboken6/ApiExtractor/parser/typeinfo.h3
-rw-r--r--sources/shiboken6/ApiExtractor/qtdocparser.cpp52
-rw-r--r--sources/shiboken6/ApiExtractor/tests/a.xml2
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.cpp5
-rw-r--r--sources/shiboken6/ApiExtractor/typesystem.cpp26
-rw-r--r--sources/shiboken6/doc/typesystem_specifying_types.rst17
-rw-r--r--sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp60
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.cpp24
-rw-r--r--sources/shiboken6/generator/shiboken/headergenerator.cpp6
-rw-r--r--sources/shiboken6/tests/libsample/functions.h2
-rw-r--r--sources/shiboken6/tests/libsample/samplenamespace.cpp10
-rw-r--r--sources/shiboken6/tests/libsample/samplenamespace.h16
-rw-r--r--sources/shiboken6/tests/samplebinding/namespace_test.py14
-rw-r--r--sources/shiboken6/tests/samplebinding/typesystem_sample.xml5
46 files changed, 776 insertions, 240 deletions
diff --git a/coin/instructions/common_environment.yaml b/coin/instructions/common_environment.yaml
index 949eecad3..38bab3e31 100644
--- a/coin/instructions/common_environment.yaml
+++ b/coin/instructions/common_environment.yaml
@@ -328,6 +328,110 @@ instructions:
condition: property
property: host.osVersion
not_in_values: [RHEL_8_6, RHEL_8_8, RHEL_8_10]
+
+
+ - type: ExecuteCommand
+ command: "pyenv install 3.11.9"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 1200
+ ignoreExitCode: true
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.osVersion
+ in_values: [MacOS_13, MacOS_14]
+ - condition: property
+ property: host.arch
+ equals_value: ARM64
+ userMessageOnFailure: >
+ Failed to install python 3.11
+ - type: PrependToEnvironmentVariable
+ variableName: PATH
+ variableValue: "/Users/qt/.pyenv/versions/3.11.9/bin:"
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.osVersion
+ in_values: [MacOS_13, MacOS_14]
+ - condition: property
+ property: host.arch
+ equals_value: ARM64
+ - type: EnvironmentVariable
+ variableName: interpreter
+ variableValue: "python3.11"
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.osVersion
+ in_values: [MacOS_13, MacOS_14]
+ - condition: property
+ property: host.arch
+ equals_value: ARM64
+
+ - type: ExecuteCommand
+ command: "pyenv install 3.13.7"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 1200
+ ignoreExitCode: true
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.osVersion
+ equals_value: MacOS_15
+ - condition: property
+ property: host.arch
+ equals_value: ARM64
+ userMessageOnFailure: >
+ Failed to install python 3.13
+ - type: PrependToEnvironmentVariable
+ variableName: PATH
+ variableValue: "/Users/qt/.pyenv/versions/3.13.7/bin:"
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.osVersion
+ equals_value: MacOS_15
+ - condition: property
+ property: host.arch
+ equals_value: ARM64
+ - type: EnvironmentVariable
+ variableName: interpreter
+ variableValue: "python3.13"
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.osVersion
+ equals_value: MacOS_15
+ - condition: property
+ property: host.arch
+ equals_value: ARM64
+
+
+ - type: ExecuteCommand
+ command: "sudo yum -y install python3.11-devel python3.11-pip"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 1200
+ enable_if:
+ condition: property
+ property: host.osVersion
+ equals_value: RHEL_9_4
+ userMessageOnFailure: >
+ Failed to install python 3.11
+
+ - type: EnvironmentVariable
+ variableName: interpreter
+ variableValue: "python3.11"
+ enable_if:
+ condition: property
+ property: host.osVersion
+ equals_value: RHEL_9_4
+
# ToDo: can be removed after 3.11 is available on qt5#3.8 on macOS
# start of ToDo
- type: ExecuteCommand
diff --git a/coin/instructions_utils.py b/coin/instructions_utils.py
index 52c8211e0..0337a0a4c 100644
--- a/coin/instructions_utils.py
+++ b/coin/instructions_utils.py
@@ -148,8 +148,12 @@ def setup_virtualenv(python, exe, env, pip, log, ci):
# Within Ubuntu 24.04 one can't install anything with pip to outside of
# virtual env. Trust that we already have proper virtualenv installed.
if os.environ.get("HOST_OSVERSION_COIN") != "ubuntu_24_04":
+ virtualenv_version = "20.7.2"
+ # 20.7.2 is too old for 3.13
+ if sys.version_info[1] > 12:
+ virtualenv_version = "20.32.0"
run_instruction(
- [str(python), "-m", "pip", "install", "--user", "virtualenv==20.7.2"],
+ [str(python), "-m", "pip", "install", "--user", "virtualenv==" + virtualenv_version],
"Failed to pin virtualenv",
)
# installing to user base might not be in PATH by default.
diff --git a/doc/changelogs/changes-6.9.3 b/doc/changelogs/changes-6.9.3
new file mode 100644
index 000000000..b0f55fcfc
--- /dev/null
+++ b/doc/changelogs/changes-6.9.3
@@ -0,0 +1,40 @@
+Qt for Python 6.9.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-2308] type hints: The type annotation of the notify parameter of
+ QtCore.Property has been corrected.
+ - [PYSIDE-3047] type hints: The type annotations of QPolygon(F)'s
+ operator<< have been corrected.
+ - [PYSIDE-3048] type hints: The type annotations now contain enum values.
+ - [PYSIDE-3162] type hints: The 'from __future__ import annotations' have
+ been removed from the stub files.
+ - [PYSIDE-3163] type hints: The mypy comment has been removed from the
+ docstring.
+ - [PYSIDE-2206] Tye QtBluetooth/heartrate_game example has been updated.
+
+****************************************************************************
+* Shiboken6 *
+****************************************************************************
+
+ - [PYSIDE-1106] DocGenerator: Extracting documentation from C++ structs
+ has been fixed.
+ - [PYSIDE-3173] A crash when encountering UTF-8 encoding errors has been
+ changed to a fatal error.
+ - [PYSIDE-3175] A crash occurring when the typesystem's package attribute
+ is missing has been fixed.
diff --git a/examples/multimedia/audiooutput/audiooutput.py b/examples/multimedia/audiooutput/audiooutput.py
index b0ab567d8..cadfe2d09 100644
--- a/examples/multimedia/audiooutput/audiooutput.py
+++ b/examples/multimedia/audiooutput/audiooutput.py
@@ -11,8 +11,7 @@ from struct import pack
from PySide6.QtCore import (QByteArray, QIODevice, Qt, QSysInfo, QTimer,
qWarning, Slot)
-from PySide6.QtMultimedia import (QAudio, QAudioFormat,
- QAudioSink, QMediaDevices)
+from PySide6.QtMultimedia import (QAudioFormat, QAudioSink, QMediaDevices, QtAudio)
from PySide6.QtWidgets import (QApplication, QComboBox, QHBoxLayout, QLabel,
QMainWindow, QPushButton, QSlider,
QVBoxLayout, QWidget)
@@ -211,7 +210,7 @@ class AudioTest(QMainWindow):
@Slot()
def pull_timer_expired(self):
- if self.m_audioSink is not None and self.m_audioSink.state() != QAudio.State.StoppedState:
+ 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:
@@ -236,28 +235,28 @@ class AudioTest(QMainWindow):
@Slot()
def toggle_suspend_resume(self):
- if self.m_audioSink.state() == QAudio.State.SuspendedState:
+ if self.m_audioSink.state() == QtAudio.State.SuspendedState:
qWarning("status: Suspended, resume()")
self.m_audioSink.resume()
self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
- elif self.m_audioSink.state() == QAudio.State.ActiveState:
+ elif self.m_audioSink.state() == QtAudio.State.ActiveState:
qWarning("status: Active, suspend()")
self.m_audioSink.suspend()
self.m_suspendResumeButton.setText(self.RESUME_LABEL)
- elif self.m_audioSink.state() == QAudio.State.StoppedState:
+ elif self.m_audioSink.state() == QtAudio.State.StoppedState:
qWarning("status: Stopped, resume()")
self.m_audioSink.resume()
self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
- elif self.m_audioSink.state() == QAudio.State.IdleState:
+ elif self.m_audioSink.state() == QtAudio.State.IdleState:
qWarning("status: IdleState")
state_map = {
- QAudio.State.ActiveState: "ActiveState",
- QAudio.State.SuspendedState: "SuspendedState",
- QAudio.State.StoppedState: "StoppedState",
- QAudio.State.IdleState: "IdleState"}
+ QtAudio.State.ActiveState: "ActiveState",
+ QtAudio.State.SuspendedState: "SuspendedState",
+ QtAudio.State.StoppedState: "StoppedState",
+ QtAudio.State.IdleState: "IdleState"}
- @Slot("QAudio::State")
+ @Slot("QtAudio::State")
def handle_state_changed(self, state):
state = self.state_map.get(state, 'Unknown')
qWarning(f"state = {state}")
diff --git a/examples/multimedia/audiosource/audiosource.py b/examples/multimedia/audiosource/audiosource.py
index 1c0d98412..807a70526 100644
--- a/examples/multimedia/audiosource/audiosource.py
+++ b/examples/multimedia/audiosource/audiosource.py
@@ -19,7 +19,7 @@ import sys
import PySide6
from PySide6.QtCore import QByteArray, QMargins, Qt, Slot, qWarning
from PySide6.QtGui import QPainter, QPalette
-from PySide6.QtMultimedia import QAudio, QAudioDevice, QAudioFormat, QAudioSource, QMediaDevices
+from PySide6.QtMultimedia import QAudioDevice, QAudioFormat, QAudioSource, QMediaDevices, QtAudio
from PySide6.QtWidgets import (QApplication, QComboBox, QPushButton, QSlider, QVBoxLayout,
QWidget, QLabel)
@@ -163,10 +163,10 @@ class InputTest(QWidget):
self.m_audio_info = AudioInfo(format)
self.m_audio_input = QAudioSource(device_info, format)
- initial_volume = QAudio.convertVolume(
+ initial_volume = QtAudio.convertVolume(
self.m_audio_input.volume(),
- QAudio.VolumeScale.LinearVolumeScale,
- QAudio.VolumeScale.LogarithmicVolumeScale,
+ QtAudio.VolumeScale.LinearVolumeScale,
+ QtAudio.VolumeScale.LogarithmicVolumeScale,
)
self.m_volume_slider.setValue(int(round(initial_volume * 100)))
self.toggle_mode()
@@ -195,10 +195,10 @@ class InputTest(QWidget):
def toggle_suspend(self):
# toggle suspend/resume
state = self.m_audio_input.state()
- if (state == QAudio.State.SuspendedState) or (state == QAudio.State.StoppedState):
+ if (state == QtAudio.State.SuspendedState) or (state == QtAudio.State.StoppedState):
self.m_audio_input.resume()
self.m_suspend_resume_button.setText("Suspend recording")
- elif state == QAudio.State.ActiveState:
+ elif state == QtAudio.State.ActiveState:
self.m_audio_input.suspend()
self.m_suspend_resume_button.setText("Resume recording")
# else no-op
@@ -211,9 +211,9 @@ class InputTest(QWidget):
@Slot(int)
def slider_changed(self, value):
- linearVolume = QAudio.convertVolume(value / float(100),
- QAudio.VolumeScale.LogarithmicVolumeScale,
- QAudio.VolumeScale.LinearVolumeScale)
+ linearVolume = QtAudio.convertVolume(value / float(100),
+ QtAudio.VolumeScale.LogarithmicVolumeScale,
+ QtAudio.VolumeScale.LinearVolumeScale)
self.m_audio_input.setVolume(linearVolume)
diff --git a/examples/multimedia/player/player.py b/examples/multimedia/player/player.py
index 23fdbb4cc..985996115 100644
--- a/examples/multimedia/player/player.py
+++ b/examples/multimedia/player/player.py
@@ -10,7 +10,7 @@ from PySide6.QtGui import QAction, QIcon, QKeySequence
from PySide6.QtWidgets import (QApplication, QDialog, QFileDialog,
QMainWindow, QSlider, QStyle, QToolBar)
from PySide6.QtMultimedia import (QAudioOutput, QMediaFormat,
- QMediaPlayer, QAudio)
+ QMediaPlayer, QtAudio)
from PySide6.QtMultimediaWidgets import QVideoWidget
@@ -186,9 +186,9 @@ class MainWindow(QMainWindow):
@Slot()
def setVolume(self):
- self.volumeValue = QAudio.convertVolume(self._volume_slider.value() / 100.0,
- QAudio.VolumeScale.LogarithmicVolumeScale,
- QAudio.VolumeScale.LinearVolumeScale)
+ self.volumeValue = QtAudio.convertVolume(self._volume_slider.value() / 100.0,
+ QtAudio.VolumeScale.LogarithmicVolumeScale,
+ QtAudio.VolumeScale.LinearVolumeScale)
self._audio_output.setVolume(self.volumeValue)
diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/Main.qml b/examples/quickcontrols/filesystemexplorer/FileSystemModule/Main.qml
index 7f7798ed8..36f2ac3b2 100644
--- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/Main.qml
+++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/Main.qml
@@ -1,6 +1,5 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
import QtQuick
import QtQuick.Controls.Basic
import QtQuick.Layouts
@@ -22,7 +21,7 @@ ApplicationWindow {
visible: true
color: Colors.background
flags: Qt.Window | Qt.FramelessWindowHint
- title: qsTr("File System Explorer Example")
+ title: qsTr("File System Explorer")
function getInfoText() : string {
let out = root.currentFilePath
diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/About.qml b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/About.qml
index 178bf03e4..0d308a2a1 100644
--- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/About.qml
+++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/About.qml
@@ -16,7 +16,7 @@ ApplicationWindow {
id: menuBar
dragWindow: root
- implicitHeight: 27
+ implicitHeight: 30
infoText: "About Qt"
}
diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Editor.qml b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Editor.qml
index 80f7c04c5..2f995c88c 100644
--- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Editor.qml
+++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Editor.qml
@@ -36,6 +36,7 @@ Rectangle {
Layout.preferredWidth: fontMetrics.averageCharacterWidth
* (Math.floor(Math.log10(textArea.lineCount)) + 1) + 10
Layout.fillHeight: true
+ Layout.fillWidth: false
interactive: false
contentY: editorFlickable.contentY
diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/ResizeButton.qml b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/ResizeButton.qml
index 0df65bf82..5d3b68b35 100644
--- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/ResizeButton.qml
+++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/ResizeButton.qml
@@ -1,6 +1,7 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import QtQml
import QtQuick.Controls
import FileSystemModule
diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Sidebar.qml b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Sidebar.qml
index aac530394..f739e0f93 100644
--- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Sidebar.qml
+++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Sidebar.qml
@@ -50,6 +50,8 @@ Rectangle {
id: tabBarComponent
Layout.fillWidth: true
+ Layout.fillHeight: false
+
// ButtonGroup ensures that only one button can be checked at a time.
ButtonGroup {
buttons: tabBarComponent.contentChildren
diff --git a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qmldir b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qmldir
index b1f684600..f94e68a8a 100644
--- a/examples/quickcontrols/filesystemexplorer/FileSystemModule/qmldir
+++ b/examples/quickcontrols/filesystemexplorer/FileSystemModule/qmldir
@@ -1,8 +1,9 @@
module FileSystemModule
+
Main 1.0 Main.qml
About 1.0 qml/About.qml
-Editor 1.0 qml/Editor.qml
MyMenu 1.0 qml/MyMenu.qml
+Editor 1.0 qml/Editor.qml
Sidebar 1.0 qml/Sidebar.qml
MyMenuBar 1.0 qml/MyMenuBar.qml
singleton Colors 1.0 qml/Colors.qml
diff --git a/examples/webenginequick/nanobrowser/ApplicationRoot.qml b/examples/webenginequick/nanobrowser/ApplicationRoot.qml
index f36249802..ec5716207 100644
--- a/examples/webenginequick/nanobrowser/ApplicationRoot.qml
+++ b/examples/webenginequick/nanobrowser/ApplicationRoot.qml
@@ -1,22 +1,24 @@
// 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 QtObject defaultProfilePrototype : WebEngineProfilePrototype {
+ property WebEngineProfilePrototype defaultProfilePrototype : WebEngineProfilePrototype {
storageName: "Profile"
Component.onCompleted: {
- let fullVersionList = defaultProfilePrototype.instance().clientHints.fullVersionList;
+ let fullVersionList = root.defaultProfilePrototype.instance().clientHints.fullVersionList;
fullVersionList["QuickNanoBrowser"] = "1.0";
- defaultProfilePrototype.instance().clientHints.fullVersionList = fullVersionList;
+ root.defaultProfilePrototype.instance().clientHints.fullVersionList = fullVersionList;
}
}
- property QtObject otrPrototype : WebEngineProfilePrototype {
+ property WebEngineProfilePrototype otrPrototype : WebEngineProfilePrototype {
}
property Component browserWindowComponent: BrowserWindow {
@@ -26,18 +28,18 @@ QtObject {
onClosing: destroy()
}
function createWindow(profile) {
- var newWindow = browserWindowComponent.createObject(root);
+ 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);
+ var newDialog = browserDialogComponent.createObject(root) as BrowserDialog;
newDialog.currentWebView.profile = profile;
return newDialog;
}
function load(url) {
- var browserWindow = createWindow(defaultProfilePrototype.instance());
+ var browserWindow = createWindow(root.defaultProfilePrototype.instance());
browserWindow.currentWebView.url = url;
}
}
diff --git a/examples/webenginequick/nanobrowser/BrowserWindow.qml b/examples/webenginequick/nanobrowser/BrowserWindow.qml
index 365d77d21..474968b87 100644
--- a/examples/webenginequick/nanobrowser/BrowserWindow.qml
+++ b/examples/webenginequick/nanobrowser/BrowserWindow.qml
@@ -1,6 +1,8 @@
// 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
@@ -12,16 +14,17 @@ import QtWebEngine
import BrowserUtils
ApplicationWindow {
- id: browserWindow
- property QtObject applicationRoot
- property Item currentWebView: tabBar.currentIndex < tabBar.count ? tabLayout.children[tabBar.currentIndex] : null
+ 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: currentWebView && currentWebView.title
+ title: win.currentWebView?.title ?? ""
// Make sure the Qt.WindowFullscreenButtonHint is set on OS X.
Component.onCompleted: flags = flags | Qt.WindowFullscreenButtonHint
@@ -31,7 +34,7 @@ ApplicationWindow {
}
// When using style "mac", ToolButtons are not supposed to accept focus.
- property bool platformIsMac: Qt.platform.os == "osx"
+ property bool platformIsMac: Qt.platform.os === "osx"
Settings {
id : appSettings
@@ -46,6 +49,8 @@ ApplicationWindow {
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 {
@@ -65,14 +70,16 @@ ApplicationWindow {
Action {
shortcut: StandardKey.Refresh
onTriggered: {
- if (currentWebView)
- currentWebView.reload();
+ if (win.currentWebView)
+ win.currentWebView.reload();
}
}
Action {
shortcut: StandardKey.AddTab
onTriggered: {
- tabBar.createTab(tabBar.count != 0 ? currentWebView.profile : defaultProfilePrototype.instance());
+ tabBar.createTab(tabBar.count !== 0
+ ? win.currentWebView.profile
+ : (win.applicationRoot as ApplicationRoot).defaultProfilePrototype.instance());
addressBar.forceActiveFocus();
addressBar.selectAll();
}
@@ -80,20 +87,20 @@ ApplicationWindow {
Action {
shortcut: StandardKey.Close
onTriggered: {
- currentWebView.triggerWebAction(WebEngineView.RequestClose);
+ win.currentWebView.triggerWebAction(WebEngineView.RequestClose);
}
}
Action {
shortcut: StandardKey.Quit
- onTriggered: browserWindow.close()
+ onTriggered: win.close()
}
Action {
shortcut: "Escape"
onTriggered: {
- if (currentWebView.state == "FullScreen") {
- browserWindow.visibility = browserWindow.previousVisibility;
+ if (win.currentWebView.state === "FullScreen") {
+ win.visibility = win.previousVisibility;
fullScreenNotification.hide();
- currentWebView.triggerWebAction(WebEngineView.ExitFullScreen);
+ win.currentWebView.triggerWebAction(WebEngineView.ExitFullScreen);
}
if (findBar.visible)
@@ -102,52 +109,52 @@ ApplicationWindow {
}
Action {
shortcut: "Ctrl+0"
- onTriggered: currentWebView.zoomFactor = 1.0
+ onTriggered: win.currentWebView.zoomFactor = 1.0
}
Action {
shortcut: StandardKey.ZoomOut
- onTriggered: currentWebView.zoomFactor -= 0.1
+ onTriggered: win.currentWebView.zoomFactor -= 0.1
}
Action {
shortcut: StandardKey.ZoomIn
- onTriggered: currentWebView.zoomFactor += 0.1
+ onTriggered: win.currentWebView.zoomFactor += 0.1
}
Action {
shortcut: StandardKey.Copy
- onTriggered: currentWebView.triggerWebAction(WebEngineView.Copy)
+ onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Copy)
}
Action {
shortcut: StandardKey.Cut
- onTriggered: currentWebView.triggerWebAction(WebEngineView.Cut)
+ onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Cut)
}
Action {
shortcut: StandardKey.Paste
- onTriggered: currentWebView.triggerWebAction(WebEngineView.Paste)
+ onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Paste)
}
Action {
shortcut: "Shift+"+StandardKey.Paste
- onTriggered: currentWebView.triggerWebAction(WebEngineView.PasteAndMatchStyle)
+ onTriggered: win.currentWebView.triggerWebAction(WebEngineView.PasteAndMatchStyle)
}
Action {
shortcut: StandardKey.SelectAll
- onTriggered: currentWebView.triggerWebAction(WebEngineView.SelectAll)
+ onTriggered: win.currentWebView.triggerWebAction(WebEngineView.SelectAll)
}
Action {
shortcut: StandardKey.Undo
- onTriggered: currentWebView.triggerWebAction(WebEngineView.Undo)
+ onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Undo)
}
Action {
shortcut: StandardKey.Redo
- onTriggered: currentWebView.triggerWebAction(WebEngineView.Redo)
+ onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Redo)
}
Action {
shortcut: StandardKey.Back
- onTriggered: currentWebView.triggerWebAction(WebEngineView.Back)
+ onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Back)
}
Action {
shortcut: StandardKey.Forward
- onTriggered: currentWebView.triggerWebAction(WebEngineView.Forward)
+ onTriggered: win.currentWebView.triggerWebAction(WebEngineView.Forward)
}
Action {
shortcut: StandardKey.Find
@@ -170,16 +177,17 @@ ApplicationWindow {
RowLayout {
anchors.fill: parent
ToolButton {
- enabled: currentWebView && (currentWebView.canGoBack || currentWebView.canGoForward)
+ enabled: win.currentWebView?.canGoBack || win.currentWebView?.canGoForward
onClicked: historyMenu.open()
text: qsTr("â–ŧ")
Menu {
id: historyMenu
Instantiator {
- model: currentWebView && currentWebView.history.items
+ model: win.currentWebView?.history?.items
MenuItem {
+ required property var model
text: model.title
- onTriggered: currentWebView.goBackOrForward(model.offset)
+ onTriggered: win.currentWebView.goBackOrForward(model.offset)
checkable: !enabled
checked: !enabled
enabled: model.offset
@@ -197,23 +205,25 @@ ApplicationWindow {
ToolButton {
id: backButton
- icon.source: "qrc:/icons/go-previous.png"
- onClicked: currentWebView.goBack()
- enabled: currentWebView && currentWebView.canGoBack
- activeFocusOnTab: !browserWindow.platformIsMac
+ icon.source: "icons/3rdparty/go-previous.png"
+ onClicked: win.currentWebView.goBack()
+ enabled: win.currentWebView?.canGoBack ?? false
+ activeFocusOnTab: !win.platformIsMac
}
ToolButton {
id: forwardButton
- icon.source: "qrc:/icons/go-next.png"
- onClicked: currentWebView.goForward()
- enabled: currentWebView && currentWebView.canGoForward
- activeFocusOnTab: !browserWindow.platformIsMac
+ icon.source: "icons/3rdparty/go-next.png"
+ onClicked: win.currentWebView.goForward()
+ enabled: win.currentWebView?.canGoForward ?? false
+ activeFocusOnTab: !win.platformIsMac
}
ToolButton {
id: reloadButton
- icon.source: currentWebView && currentWebView.loading ? "qrc:/icons/process-stop.png" : "qrc:/icons/view-refresh.png"
- onClicked: currentWebView && currentWebView.loading ? currentWebView.stop() : currentWebView.reload()
- activeFocusOnTab: !browserWindow.platformIsMac
+ 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
@@ -224,7 +234,7 @@ ApplicationWindow {
id: faviconImage
width: 16; height: 16
sourceSize: Qt.size(width, height)
- source: currentWebView && currentWebView.icon ? currentWebView.icon : ''
+ source: win.currentWebView?.icon ? win.currentWebView.icon : ''
}
MouseArea {
id: textFieldMouseArea
@@ -272,10 +282,10 @@ ApplicationWindow {
focus: true
Layout.fillWidth: true
Binding on text {
- when: currentWebView
- value: currentWebView.url
+ when: win.currentWebView
+ value: win.currentWebView.url
}
- onAccepted: currentWebView.url = Utils.fromUserInput(text)
+ onAccepted: win.currentWebView.url = Utils.fromUserInput(text)
selectByMouse: true
}
ToolButton {
@@ -319,21 +329,25 @@ ApplicationWindow {
id: offTheRecordEnabled
text: "Off The Record"
checkable: true
- checked: currentWebView && currentWebView.profile === otrPrototype.instance()
- onToggled: function(checked) {
- if (currentWebView) {
- currentWebView.profile = checked ? otrPrototype.instance() : defaultProfilePrototype.instance();
+ 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: currentWebView && !currentWebView.profile.offTheRecord
- checked: currentWebView && (currentWebView.profile.httpCacheType === WebEngineProfile.DiskHttpCache)
- onToggled: function(checked) {
- if (currentWebView) {
- currentWebView.profile.httpCacheType = checked ? WebEngineProfile.DiskHttpCache : WebEngineProfile.MemoryHttpCache;
+ 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;
}
}
}
@@ -368,7 +382,6 @@ ApplicationWindow {
checkable: true
checked: WebEngine.settings.pdfViewerEnabled
}
-
Menu {
id: imageAnimationPolicy
title: "Image Animation Policy"
@@ -407,6 +420,18 @@ ApplicationWindow {
}
}
+ 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
+ }
}
}
}
@@ -417,14 +442,14 @@ ApplicationWindow {
left: parent.left
top: parent.bottom
right: parent.right
- leftMargin: parent.leftMargin
- rightMargin: parent.rightMargin
+ leftMargin: parent.anchors.leftMargin
+ rightMargin: parent.anchors.rightMargin
}
background: Item {}
z: -2
from: 0
to: 100
- value: (currentWebView && currentWebView.loadProgress < 100) ? currentWebView.loadProgress : 0
+ value: (win.currentWebView?.loadProgress < 100) ? win.currentWebView.loadProgress : 0
}
}
@@ -442,22 +467,22 @@ ApplicationWindow {
id: tabButtonComponent
TabButton {
- property color frameColor: "#999"
- property color fillColor: "#eee"
- property color nonSelectedColor: "#ddd"
+ id: tabButton
+ property color frameColor: "#999999"
+ property color fillColor: "#eeeeee"
+ property color nonSelectedColor: "#dddddd"
property string tabTitle: "New Tab"
- id: tabButton
contentItem: Rectangle {
id: tabRectangle
- color: tabButton.down ? fillColor : nonSelectedColor
+ color: tabButton.down ? tabButton.fillColor : tabButton.nonSelectedColor
border.width: 1
- border.color: frameColor
+ 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: frameColor}
- Rectangle { height: parent.height ; width: 1; color: frameColor}
- Rectangle { x: parent.width - 2; height: parent.height ; width: 1; color: frameColor}
+ 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
@@ -465,7 +490,7 @@ ApplicationWindow {
anchors.leftMargin: 6
text: tabButton.tabTitle
elide: Text.ElideRight
- color: tabButton.down ? "black" : frameColor
+ color: tabButton.down ? "black" : tabButton.frameColor
width: parent.width - button.background.width
}
Button {
@@ -477,16 +502,16 @@ ApplicationWindow {
background: Rectangle {
implicitWidth: 12
implicitHeight: 12
- color: button.hovered ? "#ccc" : tabRectangle.color
+ color: button.hovered ? "#cccccc" : tabRectangle.color
Text {text: "x"; anchors.centerIn: parent; color: "gray"}
}
onClicked: tabButton.closeTab()
}
}
- onClicked: addressBar.text = tabLayout.itemAt(TabBar.index).url;
+ onClicked: addressBar.text = (tabLayout.itemAt(TabBar.index) as WebEngineView).url;
function closeTab() {
- tabBar.removeView(TabBar.index);
+ tabBar.tryCloseView(TabBar.index);
}
}
}
@@ -496,10 +521,10 @@ ApplicationWindow {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
- Component.onCompleted: createTab(defaultProfilePrototype.instance())
+ Component.onCompleted: createTab((win.applicationRoot as ApplicationRoot).defaultProfilePrototype.instance())
function createTab(profile, focusOnNewTab = true, url = undefined) {
- var webview = tabComponent.createObject(tabLayout, {profile: profile});
+ 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) {
@@ -511,12 +536,17 @@ ApplicationWindow {
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 {
- browserWindow.close();
+ win.lastTabClosing = true;
+ win.close();
}
}
@@ -524,10 +554,11 @@ ApplicationWindow {
id: tabComponent
WebEngineView {
id: webEngineView
+ property int index;
focus: true
onLinkHovered: function(hoveredUrl) {
- if (hoveredUrl == "")
+ if (hoveredUrl === "")
hideStatusText.start();
else {
statusText.text = hoveredUrl;
@@ -563,6 +594,12 @@ ApplicationWindow {
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) {
@@ -578,29 +615,29 @@ ApplicationWindow {
if (!request.userInitiated)
console.warn("Blocked a popup window.");
else if (request.destination === WebEngineNewWindowRequest.InNewTab) {
- var tab = tabBar.createTab(currentWebView.profile, true, request.requestedUrl);
+ var tab = tabBar.createTab(win.currentWebView.profile, true, request.requestedUrl);
tab.acceptAsNewWindow(request);
} else if (request.destination === WebEngineNewWindowRequest.InNewBackgroundTab) {
- var backgroundTab = tabBar.createTab(currentWebView.profile, false);
+ var backgroundTab = tabBar.createTab(win.currentWebView.profile, false);
backgroundTab.acceptAsNewWindow(request);
} else if (request.destination === WebEngineNewWindowRequest.InNewDialog) {
- var dialog = applicationRoot.createDialog(currentWebView.profile);
- dialog.currentWebView.acceptAsNewWindow(request);
+ var dialog = (win.applicationRoot as ApplicationRoot).createDialog(win.currentWebView.profile);
+ dialog.win.currentWebView.acceptAsNewWindow(request);
} else {
- var window = applicationRoot.createWindow(currentWebView.profile);
- window.currentWebView.acceptAsNewWindow(request);
+ var window = (win.applicationRoot as ApplicationRoot).createWindow(win.currentWebView.profile);
+ window.win.currentWebView.acceptAsNewWindow(request);
}
}
onFullScreenRequested: function(request) {
if (request.toggleOn) {
webEngineView.state = "FullScreen";
- browserWindow.previousVisibility = browserWindow.visibility;
- browserWindow.showFullScreen();
+ win.previousVisibility = win.visibility;
+ win.showFullScreen();
fullScreenNotification.show();
} else {
webEngineView.state = "";
- browserWindow.visibility = browserWindow.previousVisibility;
+ win.visibility = win.previousVisibility;
fullScreenNotification.hide();
}
request.accept();
@@ -651,7 +688,7 @@ ApplicationWindow {
}
onLoadingChanged: function(loadRequest) {
- if (loadRequest.status == WebEngineView.LoadStartedStatus)
+ if (loadRequest.status === WebEngineView.LoadStartedStatus)
findBar.reset();
}
@@ -668,7 +705,7 @@ ApplicationWindow {
interval: 0
running: false
repeat: false
- onTriggered: currentWebView.reload()
+ onTriggered: win.currentWebView.reload()
}
}
}
@@ -682,7 +719,7 @@ ApplicationWindow {
anchors.right: parent.right
anchors.bottom: parent.bottom
onNewWindowRequested: function(request) {
- var tab = tabBar.createTab(currentWebView.profile);
+ var tab = tabBar.createTab(win.currentWebView.profile);
request.openIn(tab);
}
@@ -693,7 +730,7 @@ ApplicationWindow {
repeat: false
onTriggered: devToolsEnabled.checked = false
}
- onWindowCloseRequested: function(request) {
+ onWindowCloseRequested: function() {
// Delay hiding for keep the inspectedView set to receive the ACK message of close.
hideTimer.running = true;
}
@@ -744,7 +781,7 @@ ApplicationWindow {
Dialog {
id: permissionDialog
anchors.centerIn: parent
- width: Math.min(browserWindow.width, browserWindow.height) / 3 * 2
+ width: Math.min(win.width, win.height) / 3 * 2
contentWidth: mainTextForPermissionDialog.width
contentHeight: mainTextForPermissionDialog.height
standardButtons: Dialog.No | Dialog.Yes
@@ -859,13 +896,13 @@ ApplicationWindow {
onFindNext: {
if (text)
- currentWebView && currentWebView.findText(text);
+ win.currentWebView?.findText(text);
else if (!visible)
visible = true;
}
onFindPrevious: {
if (text)
- currentWebView && currentWebView.findText(text, WebEngineView.FindBackward);
+ win.currentWebView?.findText(text, WebEngineView.FindBackward);
else if (!visible)
visible = true;
}
@@ -898,4 +935,14 @@ ApplicationWindow {
}
}
}
+
+ 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
index b116ab867..ef0c7f5a7 100644
--- a/examples/webenginequick/nanobrowser/DownloadView.qml
+++ b/examples/webenginequick/nanobrowser/DownloadView.qml
@@ -1,10 +1,10 @@
// 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
-import QtWebEngine
-import QtQuick.Layouts
Rectangle {
id: downloadView
@@ -25,20 +25,26 @@ Rectangle {
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: downloadModel.downloads[index]
- ? downloadModel.downloads[index].receivedBytes / downloadModel.downloads[index].totalBytes : 0
+ property real progress: {
+ let d = downloadModel.downloads[downloadItem.index]
+ return d ? d.receivedBytes / d.totalBytes : 0
+ }
radius: 3
- color: width == listView.width ? "green" : "#2b74c7"
+ color: width === listView.width ? "green" : "#2b74c7"
width: listView.width * progress
height: cancelButton.height
@@ -54,7 +60,10 @@ Rectangle {
}
Label {
id: label
- text: downloadModel.downloads[index] ? downloadModel.downloads[index].downloadDirectory + "/" + downloadModel.downloads[index].downloadFileName : qsTr("")
+ text: {
+ let d = downloadModel.downloads[downloadItem.index]
+ return d ? d.downloadDirectory + "/" + d.downloadFileName : qsTr("")
+ }
anchors {
verticalCenter: cancelButton.verticalCenter
left: parent.left
@@ -64,16 +73,16 @@ Rectangle {
Button {
id: cancelButton
anchors.right: parent.right
- icon.source: "qrc:/icons/process-stop.png"
+ icon.source: "icons/3rdparty/process-stop.png"
onClicked: {
- var download = downloadModel.downloads[index];
+ var download = downloadModel.downloads[downloadItem.index];
download.cancel();
downloadModel.downloads = downloadModel.downloads.filter(function (el) {
return el.id !== download.id;
});
- downloadModel.remove(index);
+ downloadModel.remove(downloadItem.index);
}
}
}
diff --git a/examples/webenginequick/nanobrowser/FindBar.qml b/examples/webenginequick/nanobrowser/FindBar.qml
index 409d8dcff..013f28e88 100644
--- a/examples/webenginequick/nanobrowser/FindBar.qml
+++ b/examples/webenginequick/nanobrowser/FindBar.qml
@@ -63,46 +63,47 @@ Rectangle {
}
Label {
- text: activeMatch + "/" + numberOfMatches
- visible: findTextField.text != ""
+ text: root.activeMatch + "/" + root.numberOfMatches
+ visible: findTextField.text !== ""
color: "black"
}
Rectangle {
border.width: 1
- border.color: "#ddd"
- width: 2
- height: parent.height
- anchors.topMargin: 5
- anchors.bottomMargin: 5
+ border.color: "#dddddd"
+ Layout.preferredWidth: 2
+ Layout.preferredHeight: parent.height
}
ToolButton {
+ id: findBtnLeft
text: "<"
- enabled: numberOfMatches > 0
+ enabled: root.numberOfMatches > 0
onClicked: root.findPrevious()
contentItem: Text {
color: "black"
- text: parent.text
+ text: findBtnLeft.text
}
}
ToolButton {
+ id: findBtnRight
text: ">"
- enabled: numberOfMatches > 0
+ enabled: root.numberOfMatches > 0
onClicked: root.findNext()
contentItem: Text {
color: "black"
- text: parent.text
+ text: findBtnRight.text
}
}
ToolButton {
+ id: findBtnClose
text: "x"
onClicked: root.visible = false
contentItem: Text {
color: "black"
- text: parent.text
+ text: findBtnClose.text
}
}
}
diff --git a/examples/webenginequick/nanobrowser/FullScreenNotification.qml b/examples/webenginequick/nanobrowser/FullScreenNotification.qml
index 779406432..cdf154c78 100644
--- a/examples/webenginequick/nanobrowser/FullScreenNotification.qml
+++ b/examples/webenginequick/nanobrowser/FullScreenNotification.qml
@@ -28,8 +28,8 @@ Rectangle {
NumberAnimation {
duration: 750
onStopped: {
- if (opacity == 0)
- visible = false;
+ if (fullScreenNotification.opacity === 0)
+ fullScreenNotification.visible = false;
}
}
}
@@ -37,7 +37,7 @@ Rectangle {
Timer {
id: reset
interval: 5000
- onTriggered: hide()
+ onTriggered: fullScreenNotification.hide()
}
anchors.horizontalCenter: parent.horizontalCenter
diff --git a/examples/webenginequick/nanobrowser/rc_resources.py b/examples/webenginequick/nanobrowser/rc_resources.py
index 990f10274..b517c39a0 100644
--- a/examples/webenginequick/nanobrowser/rc_resources.py
+++ b/examples/webenginequick/nanobrowser/rc_resources.py
@@ -1,6 +1,6 @@
# Resource object code (Python 3)
# Created by: object code
-# Created by: The Resource Compiler for Qt version 6.4.0
+# Created by: The Resource Compiler for Qt version 6.10.0
# WARNING! All changes made in this file will be lost!
from PySide6 import QtCore
@@ -330,13 +330,13 @@ qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\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\x01{\xe0\xa8\xe4\xe2\
+\x00\x00\x01\x975l\xc7\xfb\
\x00\x00\x00R\x00\x00\x00\x00\x00\x01\x00\x00\x08\xfe\
-\x00\x00\x01{\xe0\xa8\xe4\xe2\
+\x00\x00\x01\x975l\xc7\xfb\
\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
-\x00\x00\x01{\xe0\xa8\xe4\xe2\
+\x00\x00\x01\x975l\xc7\xfb\
\x00\x00\x00x\x00\x00\x00\x00\x00\x01\x00\x00\x0d\xfa\
-\x00\x00\x01{\xe0\xa8\xe4\xe2\
+\x00\x00\x01\x975l\xc7\xfb\
"
def qInitResources():
diff --git a/requirements-doc.txt b/requirements-doc.txt
index 7e795c4f5..a7d4d5443 100644
--- a/requirements-doc.txt
+++ b/requirements-doc.txt
@@ -1,5 +1,6 @@
sphinx==7.4.7
sphinx-design==0.6.0
+sphinx-collapse
sphinx-copybutton==0.5.2
sphinx-tags==0.4
sphinx-toolbox==3.7.0
diff --git a/sources/pyside6/PySide6/QtMultimedia/typesystem_multimedia.xml b/sources/pyside6/PySide6/QtMultimedia/typesystem_multimedia.xml
index f62b1ec7d..cbb392761 100644
--- a/sources/pyside6/PySide6/QtMultimedia/typesystem_multimedia.xml
+++ b/sources/pyside6/PySide6/QtMultimedia/typesystem_multimedia.xml
@@ -21,14 +21,9 @@
</namespace-type>
<namespace-type name="QtAudio">
-<!-- FIXME PYSIDE7: Remove namespace "QAudio" and enable the enums.
<enum-type name="Error"/>
<enum-type name="State"/>
<enum-type name="VolumeScale"/>
--->
- <inject-code class="target" position="end"
- file="../glue/qtmultimedia.cpp"
- snippet="qtaudio-namespace-import-enums"/>
<modify-field name="NoError" remove="true"/>
<modify-field name="OpenError" remove="true"/>
<modify-field name="IOError" remove="true"/>
diff --git a/sources/pyside6/PySide6/glue/qtmultimedia.cpp b/sources/pyside6/PySide6/glue/qtmultimedia.cpp
index 65cefa5fc..457621638 100644
--- a/sources/pyside6/PySide6/glue/qtmultimedia.cpp
+++ b/sources/pyside6/PySide6/glue/qtmultimedia.cpp
@@ -26,15 +26,3 @@ const auto size = %CPPSELF.byteCount();
const float result = QtAudio::convertVolume(%1, %2, %3);
%PYARG_0 = %CONVERTTOPYTHON[float](result);
// @snippet qaudio-convertvolume
-
-// @snippet qtaudio-namespace-import-enums
-Shiboken::AutoDecRef qAudio(PyObject_GetAttrString(module, "QAudio"));
-if (!qAudio.isNull()) {
- Shiboken::AutoDecRef qAudioTypeDict(PepType_GetDict(reinterpret_cast<PyTypeObject *>(qAudio.object())));
- Shiboken::AutoDecRef qtAudioTypeDict(PepType_GetDict(pyType));
- for (const auto *oldEnum : {"Error", "State", "VolumeScale"}) {
- if (auto *enumType = PyDict_GetItemString(qAudioTypeDict, oldEnum))
- PyDict_SetItemString(qtAudioTypeDict, oldEnum, enumType);
- }
-}
-// @snippet qtaudio-namespace-import-enums
diff --git a/sources/pyside6/doc/conf.py.in b/sources/pyside6/doc/conf.py.in
index 5af91019b..38c2c606c 100644
--- a/sources/pyside6/doc/conf.py.in
+++ b/sources/pyside6/doc/conf.py.in
@@ -33,7 +33,7 @@ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.ifconfig',
'sphinx.ext.graphviz', 'inheritance_diagram', 'pysideinclude',
'sphinx.ext.viewcode',
'sphinx_design', 'sphinx_copybutton', 'myst_parser', 'sphinx_tags',
- 'sphinx_toolbox.decorators', 'sphinx_reredirects']
+ 'sphinx_toolbox.collapse', 'sphinx_toolbox.decorators', 'sphinx_reredirects']
myst_enable_extensions = [
"amsmath",
diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
index 9c65eeb5e..9807e1ea6 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
+++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
@@ -538,6 +538,15 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom,
}
}
+ const auto &globalTypeDefs = dom->typeDefs();
+ for (const auto &typeDef : globalTypeDefs) {
+ if (typeDef->underlyingTypeCategory() == TypeCategory::Enum) {
+ const auto metaEnum = traverseTypedefedEnum(dom, typeDef, {});
+ if (metaEnum.has_value())
+ m_globalEnums.append(metaEnum.value());
+ }
+ }
+
const auto &namespaceTypeValues = dom->namespaces();
ReportHandler::startProgress("Generated namespace model ("
+ QByteArray::number(namespaceTypeValues.size()) + ").");
@@ -827,11 +836,20 @@ AbstractMetaClassPtr
// specific typedefs to be used as classes.
const TypeDefList typeDefs = namespaceItem->typeDefs();
for (const TypeDefModelItem &typeDef : typeDefs) {
- const auto cls = traverseTypeDef(dom, typeDef, metaClass);
- if (cls) {
- metaClass->addInnerClass(cls);
- cls->setEnclosingClass(metaClass);
- addAbstractMetaClass(cls, typeDef.get());
+ switch (typeDef->underlyingTypeCategory()) {
+ case TypeCategory::Enum: {
+ const auto metaEnum = traverseTypedefedEnum(dom, typeDef, metaClass);
+ if (metaEnum.has_value())
+ metaClass->addEnum(metaEnum.value());
+ }
+ break;
+ default:
+ if (const auto cls = traverseTypeDef(dom, typeDef, metaClass)) {
+ metaClass->addInnerClass(cls);
+ cls->setEnclosingClass(metaClass);
+ addAbstractMetaClass(cls, typeDef.get());
+ }
+ break;
}
}
@@ -861,10 +879,10 @@ std::optional<AbstractMetaEnum>
QString qualifiedName = enumItem->qualifiedNameString();
TypeEntryPtr typeEntry;
- const auto enclosingTypeEntry = enclosing ? enclosing->typeEntry() : TypeEntryCPtr{};
if (enumItem->accessPolicy() == Access::Private) {
+ Q_ASSERT(enclosing);
typeEntry = std::make_shared<EnumTypeEntry>(enumItem->qualifiedName().constLast(),
- QVersionNumber(0, 0), enclosingTypeEntry);
+ QVersionNumber(0, 0), enclosing->typeEntry());
TypeDatabase::instance()->addType(typeEntry);
} else if (enumItem->enumKind() != AnonymousEnum) {
typeEntry = TypeDatabase::instance()->findType(qualifiedName);
@@ -880,12 +898,17 @@ std::optional<AbstractMetaEnum>
break;
}
}
+ return createMetaEnum(enumItem, qualifiedName, typeEntry, enclosing);
+}
- QString enumName = enumItem->name();
-
- QString className;
- if (enclosingTypeEntry)
- className = enclosingTypeEntry->qualifiedCppName();
+std::optional<AbstractMetaEnum>
+ AbstractMetaBuilderPrivate::createMetaEnum(const EnumModelItem &enumItem,
+ const QString &qualifiedName,
+ const TypeEntryPtr &typeEntry,
+ const AbstractMetaClassPtr &enclosing)
+{
+ const QString enumName = enumItem->name();
+ const QString className = enclosing ? enclosing->typeEntry()->qualifiedCppName() : QString{};
QString rejectReason;
if (TypeDatabase::instance()->isEnumRejected(className, enumName, &rejectReason)) {
@@ -967,6 +990,49 @@ std::optional<AbstractMetaEnum>
return metaEnum;
}
+// Add typedef'ed enumerations ("Using MyEnum=SomeNamespace::MyEnum") for which
+// a type entry exists.
+std::optional<AbstractMetaEnum>
+ AbstractMetaBuilderPrivate::traverseTypedefedEnum(const FileModelItem &dom,
+ const TypeDefModelItem &typeDefItem,
+ const AbstractMetaClassPtr &enclosing)
+{
+ if (enclosing && typeDefItem->accessPolicy() != Access::Public)
+ return std::nullopt; // Only for global/public enums typedef'ed into classes/namespaces
+ auto modelItem = CodeModel::findItem(typeDefItem->type().qualifiedName(), dom);
+ if (!modelItem || modelItem->kind() != _CodeModelItem::Kind_Enum)
+ return std::nullopt;
+ auto enumItem = std::static_pointer_cast<_EnumModelItem>(modelItem);
+ if (enumItem->accessPolicy() != Access::Public)
+ return std::nullopt;
+ // Name in class
+ QString qualifiedName = enclosing
+ ? enclosing->qualifiedCppName() + "::"_L1 + typeDefItem->name() : typeDefItem->name();
+ auto targetTypeEntry = TypeDatabase::instance()->findType(qualifiedName);
+ if (!targetTypeEntry || !targetTypeEntry->isEnum() || !targetTypeEntry->generateCode())
+ return std::nullopt;
+ auto targetEnumTypeEntry = std::static_pointer_cast<EnumTypeEntry>(targetTypeEntry);
+ auto sourceTypeEntry = TypeDatabase::instance()->findType(enumItem->qualifiedNameString());
+ if (!sourceTypeEntry || !sourceTypeEntry->isEnum())
+ return std::nullopt;
+
+ auto sourceEnumTypeEntry = std::static_pointer_cast<EnumTypeEntry>(sourceTypeEntry);
+ if (sourceEnumTypeEntry == targetEnumTypeEntry) // Reject "typedef Enum1 { V1 } Enum1;"
+ return std::nullopt;
+
+ const QString message = "Enum \""_L1 + qualifiedName + "\" is an alias to \""_L1
+ + enumItem->qualifiedNameString() + "\"."_L1;
+ ReportHandler::addGeneralMessage(message);
+ auto result = createMetaEnum(enumItem, qualifiedName, targetTypeEntry, enclosing);
+ if (result.has_value()) {
+ targetEnumTypeEntry->setAliasMode(EnumTypeEntry::AliasTarget);
+ targetEnumTypeEntry->setAliasTypeEntry(sourceEnumTypeEntry);
+ sourceEnumTypeEntry->setAliasMode(EnumTypeEntry::AliasSource);
+ sourceEnumTypeEntry->setAliasTypeEntry(targetEnumTypeEntry);
+ }
+ return result;
+}
+
AbstractMetaClassPtr
AbstractMetaBuilderPrivate::traverseTypeDef(const FileModelItem &dom,
const TypeDefModelItem &typeDef,
@@ -1192,10 +1258,20 @@ AbstractMetaClassPtr AbstractMetaBuilderPrivate::traverseClass(const FileModelIt
// specific typedefs to be used as classes.
const TypeDefList typeDefs = classItem->typeDefs();
for (const TypeDefModelItem &typeDef : typeDefs) {
- const auto cls = traverseTypeDef(dom, typeDef, metaClass);
- if (cls) {
- cls->setEnclosingClass(metaClass);
- addAbstractMetaClass(cls, typeDef.get());
+ if (typeDef->accessPolicy() != Access::Private) {
+ switch (typeDef->underlyingTypeCategory()) {
+ case TypeCategory::Enum: {
+ const auto metaEnum = traverseTypedefedEnum(dom, typeDef, metaClass);
+ if (metaEnum.has_value())
+ metaClass->addEnum(metaEnum.value());
+ }
+ break;
+ default:
+ if (const auto cls = traverseTypeDef(dom, typeDef, metaClass)) {
+ cls->setEnclosingClass(metaClass);
+ addAbstractMetaClass(cls, typeDef.get());
+ }
+ }
}
}
@@ -1706,10 +1782,7 @@ AbstractMetaFunctionPtr
const auto &args = addedFunc->arguments();
- qsizetype argCount = args.size();
- // Check "foo(void)"
- if (argCount == 1 && args.constFirst().typeInfo.isVoid())
- argCount = 0;
+ const qsizetype argCount = args.size();
for (qsizetype i = 0; i < argCount; ++i) {
const AddedFunction::Argument &arg = args.at(i);
auto type = translateType(arg.typeInfo, metaClass, {}, errorMessage);
diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder_p.h b/sources/shiboken6/ApiExtractor/abstractmetabuilder_p.h
index 28b23c35b..0a09d578b 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetabuilder_p.h
+++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder_p.h
@@ -96,6 +96,12 @@ public:
std::optional<AbstractMetaEnum> traverseEnum(const EnumModelItem &item,
const AbstractMetaClassPtr &enclosing);
void traverseEnums(const ScopeModelItem &item, const AbstractMetaClassPtr &parent);
+ std::optional<AbstractMetaEnum>
+ createMetaEnum(const EnumModelItem &enumItem, const QString &qualifiedName,
+ const TypeEntryPtr &typeEntry, const AbstractMetaClassPtr &enclosing);
+ std::optional<AbstractMetaEnum>
+ traverseTypedefedEnum(const FileModelItem &dom, const TypeDefModelItem &typeDefItem,
+ const AbstractMetaClassPtr &enclosing);
AbstractMetaFunctionList classFunctionList(const ScopeModelItem &scopeItem,
AbstractMetaClass::Attributes *constructorAttributes,
const AbstractMetaClassPtr &currentClass);
diff --git a/sources/shiboken6/ApiExtractor/addedfunction.cpp b/sources/shiboken6/ApiExtractor/addedfunction.cpp
index ee8009cfe..649296066 100644
--- a/sources/shiboken6/ApiExtractor/addedfunction.cpp
+++ b/sources/shiboken6/ApiExtractor/addedfunction.cpp
@@ -165,10 +165,12 @@ AddedFunction::AddedFunctionPtr
}
const auto paramString = signature.mid(openParenPos + 1, closingParenPos - openParenPos - 1);
- const auto params = AddedFunctionParser::splitParameters(paramString, errorMessage);
+ auto params = AddedFunctionParser::splitParameters(paramString, errorMessage);
if (params.isEmpty() && !errorMessage->isEmpty())
return {};
- for (const auto &p : params) {
+ if (params.size() == 1 && params.constFirst().type == "void"_L1)
+ params.clear(); // "void foo(void)" -> ""void foo()"
+ for (const auto &p : std::as_const(params)) {
TypeInfo type = p.type == u"..."
? TypeInfo::varArgsType() : TypeParser::parse(p.type, errorMessage);
if (!errorMessage->isEmpty()) {
diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp b/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp
index 33f3f1ae6..cc2924287 100644
--- a/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp
+++ b/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp
@@ -524,6 +524,27 @@ void BuilderPrivate::addTemplateInstantiations(const CXType &type,
typeName->remove(pos.first, pos.second - pos.first);
}
+static TypeCategory typeCategoryFromClang(CXTypeKind k)
+{
+ switch (k) {
+ case CXType_Void:
+ return TypeCategory::Void;
+ case CXType_Enum:
+ return TypeCategory::Enum;
+ case CXType_Pointer:
+ case CXType_BlockPointer:
+ return TypeCategory::Pointer;
+ case CXType_FunctionNoProto:
+ case CXType_FunctionProto:
+ return TypeCategory::Function;
+ default:
+ break;
+ }
+ if (k >= CXType_FirstBuiltin && k <= CXType_LastBuiltin)
+ return TypeCategory::Builtin;
+ return TypeCategory::Other;
+}
+
TypeInfo BuilderPrivate::createTypeInfoUncached(const CXType &type,
bool *cacheable) const
{
@@ -533,6 +554,7 @@ TypeInfo BuilderPrivate::createTypeInfoUncached(const CXType &type,
if (argCount >= 0) {
TypeInfo result = createTypeInfoUncached(clang_getResultType(pointeeType),
cacheable);
+ result.setTypeCategory(TypeCategory::Pointer);
result.setFunctionPointer(true);
for (int a = 0; a < argCount; ++a)
result.addArgument(createTypeInfoUncached(clang_getArgType(pointeeType, unsigned(a)),
@@ -542,6 +564,7 @@ TypeInfo BuilderPrivate::createTypeInfoUncached(const CXType &type,
}
TypeInfo typeInfo;
+ typeInfo.setTypeCategory(typeCategoryFromClang(clang_getCanonicalType(type).kind));
CXType nestedType = type;
for (; isArrayType(nestedType.kind); nestedType = clang_getArrayElementType(nestedType)) {
@@ -623,6 +646,7 @@ void BuilderPrivate::addTypeDef(const CXCursor &cursor, const CXType &cxType)
setFileName(cursor, item.get());
item->setType(createTypeInfo(cxType));
item->setScope(m_scope);
+ item->setAccessPolicy(accessPolicy(clang_getCXXAccessSpecifier(cursor)));
m_scopeStack.back()->addTypeDef(item);
}
diff --git a/sources/shiboken6/ApiExtractor/documentation.h b/sources/shiboken6/ApiExtractor/documentation.h
index 580d8f969..a623529ca 100644
--- a/sources/shiboken6/ApiExtractor/documentation.h
+++ b/sources/shiboken6/ApiExtractor/documentation.h
@@ -29,6 +29,7 @@ public:
bool equals(const Documentation &rhs) const;
+ bool hasDetailed() const { return !m_detailed.isEmpty(); }
const QString &detailed() const { return m_detailed; }
void setDetailed(const QString &detailed);
diff --git a/sources/shiboken6/ApiExtractor/enumtypeentry.h b/sources/shiboken6/ApiExtractor/enumtypeentry.h
index 3360d7db5..633ab3183 100644
--- a/sources/shiboken6/ApiExtractor/enumtypeentry.h
+++ b/sources/shiboken6/ApiExtractor/enumtypeentry.h
@@ -13,6 +13,12 @@ class EnumTypeEntryPrivate;
class EnumTypeEntry : public ConfigurableTypeEntry
{
public:
+ enum AliasMode : unsigned char {
+ NoAlias,
+ AliasSource, // Source of a C++ alias "using ThatEnum = ThisEnum";
+ AliasTarget // Target of a C++ alias "using ThisEnum = ThatEnum";
+ };
+
explicit EnumTypeEntry(const QString &entryName,
const QVersionNumber &vr,
const TypeEntryCPtr &parent);
@@ -40,6 +46,12 @@ public:
QString docFile() const;
void setDocFile(const QString &df);
+ AliasMode aliasMode() const;
+ void setAliasMode(AliasMode am);
+
+ EnumTypeEntryCPtr aliasTypeEntry() const;
+ void setAliasTypeEntry(const EnumTypeEntryCPtr &entry);
+
TypeEntry *clone() const override;
#ifndef QT_NO_DEBUG_STREAM
void formatDebug(QDebug &d) const override;
diff --git a/sources/shiboken6/ApiExtractor/parser/codemodel.cpp b/sources/shiboken6/ApiExtractor/parser/codemodel.cpp
index d7f431547..8a56c9eee 100644
--- a/sources/shiboken6/ApiExtractor/parser/codemodel.cpp
+++ b/sources/shiboken6/ApiExtractor/parser/codemodel.cpp
@@ -1206,11 +1206,26 @@ void _TypeDefModelItem::setType(const TypeInfo &type)
m_type = type;
}
+TypeCategory _TypeDefModelItem::underlyingTypeCategory() const
+{
+ return m_type.typeCategory();
+}
+
+Access _TypeDefModelItem::accessPolicy() const
+{
+ return m_accessPolicy;
+}
+
+void _TypeDefModelItem::setAccessPolicy(Access accessPolicy)
+{
+ m_accessPolicy = accessPolicy;
+}
+
#ifndef QT_NO_DEBUG_STREAM
void _TypeDefModelItem::formatDebug(QDebug &d) const
{
_CodeModelItem::formatDebug(d);
- d << ", type=" << m_type;
+ d << ", " << m_accessPolicy << ", type=" << m_type;
}
#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/parser/codemodel.h b/sources/shiboken6/ApiExtractor/parser/codemodel.h
index 122bbba15..77082efa5 100644
--- a/sources/shiboken6/ApiExtractor/parser/codemodel.h
+++ b/sources/shiboken6/ApiExtractor/parser/codemodel.h
@@ -559,11 +559,17 @@ public:
TypeInfo type() const;
void setType(const TypeInfo &type);
+ TypeCategory underlyingTypeCategory() const;
+
+ Access accessPolicy() const;
+ void setAccessPolicy(Access accessPolicy);
+
#ifndef QT_NO_DEBUG_STREAM
void formatDebug(QDebug &d) const override;
#endif
private:
+ Access m_accessPolicy = Access::Public;
TypeInfo m_type;
};
diff --git a/sources/shiboken6/ApiExtractor/parser/codemodel_enums.h b/sources/shiboken6/ApiExtractor/parser/codemodel_enums.h
index e5c429bd0..c3bb10e5f 100644
--- a/sources/shiboken6/ApiExtractor/parser/codemodel_enums.h
+++ b/sources/shiboken6/ApiExtractor/parser/codemodel_enums.h
@@ -58,4 +58,14 @@ enum class FunctionAttribute {
Q_DECLARE_FLAGS(FunctionAttributes, FunctionAttribute)
Q_DECLARE_OPERATORS_FOR_FLAGS(FunctionAttributes)
+// C++ type category for TypeInfo, reflecting clang's CXTypeKind
+enum class TypeCategory : unsigned char {
+ Other,
+ Builtin,
+ Enum,
+ Pointer,
+ Function,
+ Void
+};
+
#endif // CODEMODEL_ENUMS_H
diff --git a/sources/shiboken6/ApiExtractor/parser/typeinfo.cpp b/sources/shiboken6/ApiExtractor/parser/typeinfo.cpp
index 93627e6d5..c530cafea 100644
--- a/sources/shiboken6/ApiExtractor/parser/typeinfo.cpp
+++ b/sources/shiboken6/ApiExtractor/parser/typeinfo.cpp
@@ -25,7 +25,6 @@ class TypeInfoData : public QSharedData
public:
TypeInfoData();
- bool isVoid() const;
bool equals(const TypeInfoData &other) const;
bool isStdType() const;
void simplifyStdType();
@@ -48,6 +47,7 @@ public:
};
ReferenceType m_referenceType = NoReference;
+ TypeCategory m_category = TypeCategory::Other;
};
TypeInfoData::TypeInfoData() : flags(0)
@@ -143,18 +143,21 @@ void TypeInfo::addName(const QString &n)
d->m_qualifiedName.append(n);
}
-bool TypeInfoData::isVoid() const
+bool TypeInfo::isVoid() const
{
- return m_indirections.isEmpty() && m_referenceType == NoReference
- && m_arguments.isEmpty() && m_arrayElements.isEmpty()
- && m_instantiations.isEmpty()
- && m_qualifiedName.size() == 1
- && m_qualifiedName.constFirst() == u"void";
+ return d->m_category == TypeCategory::Void;
}
-bool TypeInfo::isVoid() const
+TypeCategory TypeInfo::typeCategory() const
+{
+ return d->m_category;
+
+}
+
+void TypeInfo::setTypeCategory(TypeCategory c)
{
- return d->isVoid();
+ if (d->m_category != c)
+ d->m_category = c;
}
bool TypeInfo::isConstant() const
@@ -457,6 +460,7 @@ bool TypeInfoData::equals(const TypeInfoData &other) const
return flags == other.flags
&& m_qualifiedName == other.m_qualifiedName
+ && m_category == other.m_category
&& (!m_functionPointer || m_arguments == other.m_arguments)
&& m_instantiations == other.m_instantiations;
}
@@ -584,6 +588,23 @@ void TypeInfo::formatDebug(QDebug &debug) const
debug << ", [const]";
if (d->m_volatile)
debug << ", [volatile]";
+ switch (d->m_category) {
+ case TypeCategory::Other:
+ case TypeCategory::Void:
+ break;
+ case TypeCategory::Builtin:
+ debug << ", [builtin]";
+ break;
+ case TypeCategory::Enum:
+ debug << ", [enum]";
+ break;
+ case TypeCategory::Pointer:
+ debug << ", [pointer]";
+ break;
+ case TypeCategory::Function:
+ debug << ", [function";
+ break;
+ }
if (!d->m_indirections.isEmpty()) {
debug << ", indirections=";
for (auto i : d->m_indirections)
diff --git a/sources/shiboken6/ApiExtractor/parser/typeinfo.h b/sources/shiboken6/ApiExtractor/parser/typeinfo.h
index 6f75b5737..092fbb724 100644
--- a/sources/shiboken6/ApiExtractor/parser/typeinfo.h
+++ b/sources/shiboken6/ApiExtractor/parser/typeinfo.h
@@ -48,6 +48,9 @@ public:
bool isVoid() const;
+ TypeCategory typeCategory() const;
+ void setTypeCategory(TypeCategory c);
+
bool isConstant() const;
void setConstant(bool is);
diff --git a/sources/shiboken6/ApiExtractor/qtdocparser.cpp b/sources/shiboken6/ApiExtractor/qtdocparser.cpp
index c7361b87a..a9b76d32c 100644
--- a/sources/shiboken6/ApiExtractor/qtdocparser.cpp
+++ b/sources/shiboken6/ApiExtractor/qtdocparser.cpp
@@ -30,6 +30,7 @@
#include <algorithm>
#include <iterator>
+#include <utility>
using namespace Qt::StringLiterals;
@@ -245,23 +246,35 @@ QtDocParser::FunctionDocumentationOpt
return std::nullopt;
}
-// Extract the <brief> section from a WebXML (class) documentation and remove it
-// from the source.
-static QString extractBrief(QString *value)
+// Extract the <brief>/detailed sections from a WebXML (class) documentation (from <description>)
+static std::pair<QString, QString> extractBrief(QString value)
{
- const auto briefStart = value->indexOf(briefStartElement);
- if (briefStart < 0)
- return {};
- const auto briefEnd = value->indexOf(briefEndElement,
- briefStart + briefStartElement.size());
- if (briefEnd < briefStart)
- return {};
- const auto briefLength = briefEnd + briefEndElement.size() - briefStart;
- QString briefValue = value->mid(briefStart, briefLength);
- briefValue.insert(briefValue.size() - briefEndElement.size(),
- u"<rst> More_...</rst>"_s);
- value->remove(briefStart, briefLength);
- return briefValue;
+ std::pair<QString, QString> result;
+ const auto briefStart = value.indexOf(briefStartElement);
+ if (briefStart > 0) {
+ const auto briefEnd = value.indexOf(briefEndElement,
+ briefStart + briefStartElement.size());
+ if (briefEnd > briefStart) {
+ const auto briefLength = briefEnd + briefEndElement.size() - briefStart;
+ if (briefLength > briefStartElement.size() + briefEndElement.size())
+ result.first = value.sliced(briefStart, briefLength);
+ value.remove(briefStart, briefLength);
+ // Remove any space/newlines between the <brief/> element and its
+ // surrounding XML elements.
+ auto lastElement = value.lastIndexOf(u'>', briefStart);
+ if (lastElement != -1) {
+ ++lastElement;
+ const auto nextElement = value.indexOf(u'<', briefStart);
+ if (nextElement > lastElement)
+ value.remove(lastElement, nextElement - lastElement);
+ }
+ }
+ }
+
+ if (value != "<description></description>"_L1)
+ result.second = value;
+
+ return result;
}
// Apply the documentation parsed from WebXML to a AbstractMetaFunction and complete argument
@@ -408,13 +421,12 @@ QString QtDocParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
qCWarning(lcShibokenDoc, "%s",
qPrintable(msgCannotFindDocumentation(sourceFileName, "class", className, {})));
}
- const QString brief = extractBrief(&docString);
+ const auto descriptionPair = extractBrief(docString);
Documentation doc;
doc.setSourceFile(sourceFileName);
- if (!brief.isEmpty())
- doc.setValue(brief, DocumentationType::Brief);
- doc.setValue(docString);
+ doc.setValue(descriptionPair.first, DocumentationType::Brief);
+ doc.setValue(descriptionPair.second, DocumentationType::Detailed);
metaClass->setDocumentation(doc);
//Functions Documentation
diff --git a/sources/shiboken6/ApiExtractor/tests/a.xml b/sources/shiboken6/ApiExtractor/tests/a.xml
index 3c09d3800..bb771ed1f 100644
--- a/sources/shiboken6/ApiExtractor/tests/a.xml
+++ b/sources/shiboken6/ApiExtractor/tests/a.xml
@@ -3,7 +3,7 @@
<WebXML>
<document>
<class name="A">
- <description>oi
+ <description><para>before brief</para>
<brief>Brief description</brief>
<para>Paragraph number 1</para>
<para>Paragraph number 2</para>
diff --git a/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.cpp b/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.cpp
index c2fc3b212..9e59ebde6 100644
--- a/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.cpp
+++ b/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.cpp
@@ -28,7 +28,7 @@ R"(<typesystem package="Foo">
</value-type>
<value-type name='A'>
<modify-documentation xpath='description/brief'>&lt;brief>Modified Brief&lt;/brief></modify-documentation>
- <modify-documentation xpath='description/para[3]'>&lt;para>Some changed contents here&lt;/para></modify-documentation>
+ <modify-documentation xpath='description/para[4]'>&lt;para>Some changed contents here&lt;/para></modify-documentation>
</value-type>
</typesystem>
)";
@@ -66,8 +66,7 @@ R"(<typesystem package="Foo">
const char expectedDoc[] =
R"(<?xml version="1.0"?>
-<description>oi
-<para>Paragraph number 1</para>
+<description><para>before brief</para><para>Paragraph number 1</para>
<para>Paragraph number 2</para>
<para>Some changed contents here</para>
</description>
diff --git a/sources/shiboken6/ApiExtractor/typesystem.cpp b/sources/shiboken6/ApiExtractor/typesystem.cpp
index 46e88291d..c02ec0c93 100644
--- a/sources/shiboken6/ApiExtractor/typesystem.cpp
+++ b/sources/shiboken6/ApiExtractor/typesystem.cpp
@@ -1225,7 +1225,9 @@ public:
FlagsTypeEntryPtr m_flags;
QString m_cppType;
QString m_docFile;
+ std::weak_ptr<const EnumTypeEntry> m_aliasTypeEntry;
TypeSystem::PythonEnumType m_pythonEnumType = TypeSystem::PythonEnumType::Unspecified;
+ EnumTypeEntry::AliasMode m_aliasMode = EnumTypeEntry::AliasMode::NoAlias;
};
EnumTypeEntry::EnumTypeEntry(const QString &entryName,
@@ -1330,6 +1332,30 @@ void EnumTypeEntry::setDocFile(const QString &df)
d->m_docFile = df;
}
+EnumTypeEntry::AliasMode EnumTypeEntry::aliasMode() const
+{
+ S_D(const EnumTypeEntry);
+ return d->m_aliasMode;
+}
+
+void EnumTypeEntry::setAliasMode(AliasMode am)
+{
+ S_D(EnumTypeEntry);
+ d->m_aliasMode = am;
+}
+
+EnumTypeEntryCPtr EnumTypeEntry::aliasTypeEntry() const
+{
+ S_D(const EnumTypeEntry);
+ return d->m_aliasTypeEntry.lock();
+}
+
+void EnumTypeEntry::setAliasTypeEntry(const EnumTypeEntryCPtr &entry)
+{
+ S_D(EnumTypeEntry);
+ d->m_aliasTypeEntry = entry;
+}
+
TypeEntry *EnumTypeEntry::clone() const
{
S_D(const EnumTypeEntry);
diff --git a/sources/shiboken6/doc/typesystem_specifying_types.rst b/sources/shiboken6/doc/typesystem_specifying_types.rst
index 798ef5719..7fe4df392 100644
--- a/sources/shiboken6/doc/typesystem_specifying_types.rst
+++ b/sources/shiboken6/doc/typesystem_specifying_types.rst
@@ -347,6 +347,23 @@ production of ABI compatible bindings.
The **flags-revision** attribute has the same purposes of **revision** attribute but
is used for the QFlag related to this enum.
+An enum can also be a C++ type alias:
+
+.. code-block:: c++
+
+ enum Option { Value1 = 0; }
+
+ class SomeClass {
+ public:
+ using OptionAlias = Option;
+ };
+
+In this case, when specifying `<enum-type name="OptionAlias"...>` in
+`SomeClass`, an enumeration `OptionAlias` will be generated into the class. The
+values of `OptionAlias` and `Option` can be used interchangeably. This feature
+is specifically intended for renaming enumerations by deprecating; it works for
+at most one alias.
+
.. _reject-enum-value:
reject-enum-value
diff --git a/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp b/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp
index c99acb3d2..87e01f35b 100644
--- a/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp
+++ b/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp
@@ -507,6 +507,25 @@ void QtDocGenerator::writeDetailedDescription(TextStream &s,
parsedImages);
}
+enum ClassDescriptionMode
+{
+ NoDescription,
+ BriefOnly,
+ DetailedOnly,
+ BriefAndDetailed,
+ BriefAndDetailedSections,
+};
+
+static ClassDescriptionMode classDescriptionMode(const Documentation &doc)
+{
+ if (!doc.hasDetailed())
+ return doc.hasBrief() ? BriefOnly : NoDescription;
+ if (!doc.hasBrief())
+ return DetailedOnly;
+ return doc.detailed().contains("<section"_L1)
+ ? BriefAndDetailedSections : BriefAndDetailed;
+}
+
void QtDocGenerator::doGenerateClass(TextStream &s, const QString &targetDir,
const AbstractMetaClassCPtr &metaClass)
{
@@ -521,8 +540,33 @@ void QtDocGenerator::doGenerateClass(TextStream &s, const QString &targetDir,
QtXmlToSphinxImages parsedImages;
auto documentation = metaClass->documentation();
const QString scope = classScope(metaClass);
- if (documentation.hasBrief())
+
+ const auto descriptionMode = classDescriptionMode(documentation);
+ switch (descriptionMode) {
+ case NoDescription:
+ case DetailedOnly:
+ break;
+ case BriefOnly:
writeFormattedBriefText(s, documentation, scope, &parsedImages);
+ break;
+ case BriefAndDetailed: {
+ // A "collapse" sphinx directive can be used for brief/expanding to details
+ // for descriptions consisting of a paragraph sequence.
+ writeFormattedBriefText(s, documentation, scope, &parsedImages);
+ s << "\n\n.. collapse:: Details\n\n";
+ Indentation detailIndent(s);
+ writeDetailedDescription(s, metaClass, scope, &parsedImages);
+ }
+ break;
+ case BriefAndDetailedSections: {
+ // If the the description has nested <section>'s (which break collapse::), we
+ // use a 'more' label for the detailed text to be written further down.
+ QString brief = documentation.brief();
+ brief.insert(brief.lastIndexOf(u'<'), "<rst> More_...</rst>"_L1);
+ writeFormattedText(s, brief, documentation.format(), scope, &parsedImages);
+ }
+ break;
+ }
if (!metaClass->baseClasses().isEmpty()) {
if (m_options.inheritanceDiagram) {
@@ -560,9 +604,17 @@ void QtDocGenerator::doGenerateClass(TextStream &s, const QString &targetDir,
" translation, you can also let us know by creating a ticket on\n"
" https:/bugreports.qt.io/projects/PYSIDE\n\n";
- s << '\n' << headline("Detailed Description") << ".. _More:\n";
-
- writeDetailedDescription(s, metaClass, scope, &parsedImages);
+ switch (descriptionMode) {
+ case DetailedOnly:
+ case BriefAndDetailedSections:
+ s << '\n' << headline("Detailed Description");
+ if (descriptionMode == BriefAndDetailedSections)
+ s << ".. _More:\n";
+ writeDetailedDescription(s, metaClass, scope, &parsedImages);
+ break;
+ default:
+ break;
+ }
writeEnums(s, metaClass->enums(), scope, &parsedImages);
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
index 8bc5e4dca..c90a0557c 100644
--- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
@@ -1737,6 +1737,17 @@ void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMeta
writePythonToCppFunction(s, c.toString(), enumConverterPythonType, typeName);
QString pyTypeCheck = u"PyObject_TypeCheck(pyIn, "_s + enumPythonType + u')';
+ switch (metaEnum.typeEntry()->aliasMode()) {
+ case EnumTypeEntry::NoAlias:
+ break;
+ case EnumTypeEntry::AliasSource:
+ case EnumTypeEntry::AliasTarget: {
+ const QString &aliasSourceType = cpythonTypeNameExt(metaEnum.typeEntry()->aliasTypeEntry());
+ pyTypeCheck += "\n || PyObject_TypeCheck(pyIn, "_L1 + aliasSourceType + u')';
+ }
+ break;
+ }
+
writeIsPythonConvertibleToCppFunction(s, enumConverterPythonType, typeName, pyTypeCheck);
c.clear();
@@ -3538,9 +3549,16 @@ void CppGenerator::writeIsPythonConvertibleToCppFunction(TextStream &s,
if (!condition.contains(u"pyIn"))
s << sbkUnusedVariableCast("pyIn");
}
- s << "if (" << condition << ")\n" << indent
- << "return " << pythonToCppFuncName << ";\n" << outdent
- << "return {};\n" << outdent << "}\n";
+
+ const bool useBrace = condition.contains(u'\n');
+ s << "if (" << condition << ')';
+ if (useBrace)
+ s<< " {";
+ s << '\n' << indent
+ << "return " << pythonToCppFuncName << ";\n" << outdent;
+ if (useBrace)
+ s<< "}\n";
+ s << "return {};\n" << outdent << "}\n";
}
void CppGenerator::writePythonToCppConversionFunctions(TextStream &s,
diff --git a/sources/shiboken6/generator/shiboken/headergenerator.cpp b/sources/shiboken6/generator/shiboken/headergenerator.cpp
index 181f04a9d..6ffeef9a3 100644
--- a/sources/shiboken6/generator/shiboken/headergenerator.cpp
+++ b/sources/shiboken6/generator/shiboken/headergenerator.cpp
@@ -814,7 +814,7 @@ bool HeaderGenerator::finishGeneration()
TextStream privateTypeFunctions(&privateParameters.typeFunctions, TextStream::Language::Cpp);
for (const AbstractMetaEnum &cppEnum : api().globalEnums()) {
- if (!cppEnum.isAnonymous()) {
+ if (!cppEnum.isAnonymous() && cppEnum.typeEntry()->aliasMode() != EnumTypeEntry::AliasSource) {
const auto te = cppEnum.typeEntry();
if (te->hasConfigCondition())
parameters.conditionalIncludes[te->configCondition()].append(te->include());
@@ -846,8 +846,10 @@ bool HeaderGenerator::finishGeneration()
ConfigurableScope configScope(typeFunctionsStr, classType);
for (const AbstractMetaEnum &cppEnum : metaClass->enums()) {
- if (cppEnum.isAnonymous() || cppEnum.isPrivate())
+ if (cppEnum.isAnonymous() || cppEnum.isPrivate()
+ || cppEnum.typeEntry()->aliasMode() == EnumTypeEntry::AliasSource) {
continue;
+ }
if (const auto inc = cppEnum.typeEntry()->include(); inc != classInclude)
par.includes.insert(inc);
writeProtectedEnumSurrogate(protEnumsSurrogates, cppEnum);
diff --git a/sources/shiboken6/tests/libsample/functions.h b/sources/shiboken6/tests/libsample/functions.h
index b745aed6b..ef88e543e 100644
--- a/sources/shiboken6/tests/libsample/functions.h
+++ b/sources/shiboken6/tests/libsample/functions.h
@@ -21,6 +21,8 @@ enum GlobalEnum {
ThirdThing
};
+using GlobalEnumAlias = GlobalEnum;
+
enum GlobalOverloadFuncEnum {
GlobalOverloadFunc_i,
GlobalOverloadFunc_d
diff --git a/sources/shiboken6/tests/libsample/samplenamespace.cpp b/sources/shiboken6/tests/libsample/samplenamespace.cpp
index 3836f43aa..5e7b67a62 100644
--- a/sources/shiboken6/tests/libsample/samplenamespace.cpp
+++ b/sources/shiboken6/tests/libsample/samplenamespace.cpp
@@ -11,6 +11,16 @@
namespace SampleNamespace
{
+SomeClass::OptionAlias SomeClass::passThroughOptionAlias(OptionAlias ov)
+{
+ return ov;
+}
+
+Option SomeClass::passThroughOption(Option ov)
+{
+ return ov;
+}
+
// PYSIDE-817, scoped enums must not be converted to int in the wrappers generated
// for the protected hacks
SomeClass::PublicScopedEnum SomeClass::protectedMethodReturningPublicScopedEnum() const
diff --git a/sources/shiboken6/tests/libsample/samplenamespace.h b/sources/shiboken6/tests/libsample/samplenamespace.h
index 63dc2f316..1613ce630 100644
--- a/sources/shiboken6/tests/libsample/samplenamespace.h
+++ b/sources/shiboken6/tests/libsample/samplenamespace.h
@@ -5,6 +5,7 @@
#define SAMPLENAMESPACE_H
#include "libsamplemacros.h"
+#include "samplenamespace.h"
#include "str.h"
#include "point.h"
#include "objecttype.h"
@@ -86,6 +87,12 @@ class LIBSAMPLE_API SomeClass
public:
enum class PublicScopedEnum { v1, v2 };
+ // Alias an enumeration
+ using OptionAlias = Option;
+ inline static constexpr auto None_ = Option::None_;
+ inline static constexpr auto RandomNumber = Option::RandomNumber;
+ inline static constexpr auto UnixTime = Option::UnixTime;
+
class SomeInnerClass
{
public:
@@ -107,16 +114,21 @@ public:
inline int someMethod(SomeInnerClass *) { return 0; }
virtual OkThisIsRecursiveEnough *someVirtualMethod(OkThisIsRecursiveEnough *arg)
{ return arg; }
- };
+ }; // OkThisIsRecursiveEnough
protected:
enum ProtectedEnum {
ProtectedItem0,
ProtectedItem1
};
- };
+ }; // SomeInnerClass
+
struct SomeOtherInnerClass {
std::list<SomeInnerClass> someInnerClasses;
};
+
+ static OptionAlias passThroughOptionAlias(OptionAlias ov);
+ static Option passThroughOption(Option ov);
+
protected:
enum ProtectedEnum {
ProtectedItem0,
diff --git a/sources/shiboken6/tests/samplebinding/namespace_test.py b/sources/shiboken6/tests/samplebinding/namespace_test.py
index 0d67c7497..0e92d6e27 100644
--- a/sources/shiboken6/tests/samplebinding/namespace_test.py
+++ b/sources/shiboken6/tests/samplebinding/namespace_test.py
@@ -64,6 +64,20 @@ class TestClassesUnderNamespace(unittest.TestCase):
cls.setValue(SampleNamespace.EnumWithinInlineNamespace.EWIN_Value1)
self.assertEqual(cls.value(), SampleNamespace.EnumWithinInlineNamespace.EWIN_Value1)
+ def testEnumAlias(self):
+ """Test whether an enumeration can be aliased to another one and values
+ can be used interchangeably."""
+ expected = SampleNamespace.SomeClass.OptionAlias.None_
+ actual = SampleNamespace.SomeClass.passThroughOptionAlias(expected)
+ self.assertEqual(expected, actual)
+ actual = SampleNamespace.SomeClass.passThroughOption(expected)
+ self.assertEqual(expected, actual)
+ # The alias source values should also work
+ actual = SampleNamespace.SomeClass.passThroughOptionAlias(SampleNamespace.Option.None_)
+ self.assertEqual(expected, actual)
+ actual = SampleNamespace.SomeClass.passThroughOption(SampleNamespace.Option.None_)
+ self.assertEqual(expected, actual)
+
if __name__ == '__main__':
unittest.main()
diff --git a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml
index 5503d9bb7..711db9c4a 100644
--- a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml
+++ b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml
@@ -420,6 +420,7 @@
-->
<enum-type name="GlobalEnum"/>
<enum-type name="GlobalOverloadFuncEnum"/>
+ <enum-type name="GlobalEnumAlias"/>
<enum-type identified-by-value="AnonymousGlobalEnum_Value0"/>
@@ -437,6 +438,10 @@
<enum-type name="SampleNamespace"/>
</object-type>
<value-type name="SomeClass">
+ <enum-type name="OptionAlias"/>
+ <modify-field name="None_" remove="true"/>
+ <modify-field name="RandomNumber" remove="true"/>
+ <modify-field name="RandomNumber" remove="true"/>
<enum-type name="PublicScopedEnum"/>
<value-type name="SomeInnerClass">
<object-type name="OkThisIsRecursiveEnough">