aboutsummaryrefslogtreecommitdiffstats
path: root/examples/quick/multieffect
diff options
context:
space:
mode:
Diffstat (limited to 'examples/quick/multieffect')
-rw-r--r--examples/quick/multieffect/CMakeLists.txt5
-rw-r--r--examples/quick/multieffect/itemswitcher/CMakeLists.txt70
-rw-r--r--examples/quick/multieffect/itemswitcher/doc/images/qml-multieffectitemswitcher-example.jpgbin0 -> 69659 bytes
-rw-r--r--examples/quick/multieffect/itemswitcher/doc/src/itemswitcher.qdoc16
-rw-r--r--examples/quick/multieffect/itemswitcher/itemswitcher.pro10
-rw-r--r--examples/quick/multieffect/itemswitcher/main.cpp4
-rw-r--r--examples/quick/multieffect/itemswitcher/qml.qrc27
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/ItemSwitcher.qml57
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/PagesItem.qml49
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/PagesView.qml30
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/SettingsComponentButton.qml35
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/SettingsComponentSlider.qml54
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/SettingsView.qml168
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/SwitchEffect3DFlip.qml93
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/SwitchEffectBlinds.qml49
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/SwitchEffectBlur.qml34
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/SwitchEffectHeart.qml48
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/SwitchEffectStars.qml50
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/SwitchEffectThunder.qml87
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/images/Built_with_Qt.pngbin0 -> 13891 bytes
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/images/Built_with_Qt_RGB_logo.pngbin0 -> 14224 bytes
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/images/arrow.pngbin0 -> 835 bytes
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/images/background.pngbin0 -> 22366 bytes
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/images/hblinds.pngbin0 -> 12054 bytes
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/images/heart.pngbin0 -> 3786 bytes
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/images/quit_coding.pngbin0 -> 23713 bytes
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/images/smoke.pngbin0 -> 45280 bytes
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/images/star.pngbin0 -> 2852 bytes
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/images/stripes.pngbin0 -> 41520 bytes
-rw-r--r--examples/quick/multieffect/itemswitcher/qml/main.qml219
-rw-r--r--examples/quick/multieffect/multieffect.pro3
-rw-r--r--examples/quick/multieffect/testbed/CMakeLists.txt74
-rw-r--r--examples/quick/multieffect/testbed/doc/images/qml-multieffecttestbed-example.jpgbin0 -> 59436 bytes
-rw-r--r--examples/quick/multieffect/testbed/doc/src/testbed.qdoc16
-rw-r--r--examples/quick/multieffect/testbed/main.cpp4
-rw-r--r--examples/quick/multieffect/testbed/qml.qrc31
-rw-r--r--examples/quick/multieffect/testbed/qml/CustomMultiEffect/BlurHelper.qml66
-rw-r--r--examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qep383
-rw-r--r--examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qml97
-rw-r--r--examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.frag.qsbbin0 -> 1563 bytes
-rw-r--r--examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.vert.qsbbin0 -> 1934 bytes
-rw-r--r--examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.frag.qsbbin0 -> 6495 bytes
-rw-r--r--examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.vert.qsbbin0 -> 5369 bytes
-rw-r--r--examples/quick/multieffect/testbed/qml/FpsItem.qml65
-rw-r--r--examples/quick/multieffect/testbed/qml/ResetSettingsOverlay.qml170
-rw-r--r--examples/quick/multieffect/testbed/qml/Settings.qml92
-rw-r--r--examples/quick/multieffect/testbed/qml/SettingsComponentCheckBox.qml35
-rw-r--r--examples/quick/multieffect/testbed/qml/SettingsComponentColorSelector.qml135
-rw-r--r--examples/quick/multieffect/testbed/qml/SettingsComponentSlider.qml54
-rw-r--r--examples/quick/multieffect/testbed/qml/SettingsComponentView.qml147
-rw-r--r--examples/quick/multieffect/testbed/qml/SettingsView.qml407
-rw-r--r--examples/quick/multieffect/testbed/qml/ShaderView.qml29
-rw-r--r--examples/quick/multieffect/testbed/qml/TestMaskItem.qml17
-rw-r--r--examples/quick/multieffect/testbed/qml/TestSourceItem.qml113
-rw-r--r--examples/quick/multieffect/testbed/qml/WarningsItem.qml66
-rw-r--r--examples/quick/multieffect/testbed/qml/WarningsView.qml38
-rw-r--r--examples/quick/multieffect/testbed/qml/images/Built_with_Qt.pngbin0 -> 13891 bytes
-rw-r--r--examples/quick/multieffect/testbed/qml/images/Built_with_Qt_RGB_logo.pngbin0 -> 14224 bytes
-rw-r--r--examples/quick/multieffect/testbed/qml/images/arrow.pngbin0 -> 835 bytes
-rw-r--r--examples/quick/multieffect/testbed/qml/images/pause.pngbin0 -> 245 bytes
-rw-r--r--examples/quick/multieffect/testbed/qml/images/play.pngbin0 -> 281 bytes
-rw-r--r--examples/quick/multieffect/testbed/qml/images/spinner.pngbin0 -> 1687 bytes
-rw-r--r--examples/quick/multieffect/testbed/qml/images/warning.pngbin0 -> 10285 bytes
-rw-r--r--examples/quick/multieffect/testbed/qml/main.qml186
-rw-r--r--examples/quick/multieffect/testbed/testbed.pro10
65 files changed, 3343 insertions, 0 deletions
diff --git a/examples/quick/multieffect/CMakeLists.txt b/examples/quick/multieffect/CMakeLists.txt
new file mode 100644
index 0000000000..802cb16eea
--- /dev/null
+++ b/examples/quick/multieffect/CMakeLists.txt
@@ -0,0 +1,5 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+qt_internal_add_example(itemswitcher)
+qt_internal_add_example(testbed)
diff --git a/examples/quick/multieffect/itemswitcher/CMakeLists.txt b/examples/quick/multieffect/itemswitcher/CMakeLists.txt
new file mode 100644
index 0000000000..cb2043d023
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/CMakeLists.txt
@@ -0,0 +1,70 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(itemswitcher LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick QuickControls2)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+add_subdirectory("../../shared" "shared")
+
+qt_add_executable(itemswitcherexample WIN32 MACOSX_BUNDLE
+ main.cpp
+)
+
+target_link_libraries(itemswitcherexample PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+ Qt6::QuickControls2
+)
+
+add_dependencies(itemswitcherexample itemswitcher_shared)
+
+# Resources:
+qt_add_qml_module(itemswitcherexample
+ URI itemswitcher
+ QML_FILES
+ "qml/main.qml"
+ "qml/PagesView.qml"
+ "qml/SwitchEffect3DFlip.qml"
+ "qml/SwitchEffectBlur.qml"
+ "qml/SwitchEffectStars.qml"
+ "qml/ItemSwitcher.qml"
+ "qml/PagesItem.qml"
+ "qml/SettingsView.qml"
+ "qml/SwitchEffectBlinds.qml"
+ "qml/SwitchEffectHeart.qml"
+ "qml/SwitchEffectThunder.qml"
+ "qml/SettingsComponentButton.qml"
+ "qml/SettingsComponentSlider.qml"
+ RESOURCES
+ "qml/images/background.png"
+ "qml/images/hblinds.png"
+ "qml/images/heart.png"
+ "qml/images/quit_coding.png"
+ "qml/images/smoke.png"
+ "qml/images/star.png"
+ "qml/images/stripes.png"
+ "qml/images/arrow.png"
+ "qml/images/Built_with_Qt.png"
+ "qml/images/Built_with_Qt_RGB_logo.png"
+)
+
+install(TARGETS itemswitcherexample
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET itemswitcherexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/multieffect/itemswitcher/doc/images/qml-multieffectitemswitcher-example.jpg b/examples/quick/multieffect/itemswitcher/doc/images/qml-multieffectitemswitcher-example.jpg
new file mode 100644
index 0000000000..d1d3393e3f
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/doc/images/qml-multieffectitemswitcher-example.jpg
Binary files differ
diff --git a/examples/quick/multieffect/itemswitcher/doc/src/itemswitcher.qdoc b/examples/quick/multieffect/itemswitcher/doc/src/itemswitcher.qdoc
new file mode 100644
index 0000000000..f5588cb71e
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/doc/src/itemswitcher.qdoc
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+ \title Qt Quick Examples - MultiEffect Item Switcher
+ \example multieffect/itemswitcher
+ \image qml-multieffectitemswitcher-example.jpg
+ \brief Demonstrates MultiEffect usage.
+ \ingroup qtquickexamples
+ \examplecategory {Graphics}
+
+ This example demonstrates advanced usage of the MultiEffect type, which
+ offers a simple, efficient solution for applying effects such as blur, drop
+ shadow, or colorize to an Item and its children.
+
+ \include examples-run.qdocinc
+*/
diff --git a/examples/quick/multieffect/itemswitcher/itemswitcher.pro b/examples/quick/multieffect/itemswitcher/itemswitcher.pro
new file mode 100644
index 0000000000..f1ab3e381b
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/itemswitcher.pro
@@ -0,0 +1,10 @@
+TEMPLATE = app
+
+QT += quick qml
+QT += quickcontrols2
+SOURCES += main.cpp
+RESOURCES += \
+ qml.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/multieffect/itemswitcher
+INSTALLS += target
diff --git a/examples/quick/multieffect/itemswitcher/main.cpp b/examples/quick/multieffect/itemswitcher/main.cpp
new file mode 100644
index 0000000000..a7d7046411
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/main.cpp
@@ -0,0 +1,4 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+#include "../../shared/shared.h"
+DECLARATIVE_EXAMPLE_MAIN(itemswitcher/qml/main)
diff --git a/examples/quick/multieffect/itemswitcher/qml.qrc b/examples/quick/multieffect/itemswitcher/qml.qrc
new file mode 100644
index 0000000000..3ccc23502a
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml.qrc
@@ -0,0 +1,27 @@
+<RCC>
+ <qresource prefix="/qt/qml/itemswitcher">
+ <file>qml/ItemSwitcher.qml</file>
+ <file>qml/main.qml</file>
+ <file>qml/SwitchEffectBlur.qml</file>
+ <file>qml/SwitchEffectBlinds.qml</file>
+ <file>qml/images/hblinds.png</file>
+ <file>qml/SwitchEffectHeart.qml</file>
+ <file>qml/images/heart.png</file>
+ <file>qml/SwitchEffectStars.qml</file>
+ <file>qml/images/star.png</file>
+ <file>qml/PagesView.qml</file>
+ <file>qml/PagesItem.qml</file>
+ <file>qml/SwitchEffectThunder.qml</file>
+ <file>qml/images/stripes.png</file>
+ <file>qml/images/background.png</file>
+ <file>qml/SettingsView.qml</file>
+ <file>qml/images/quit_coding.png</file>
+ <file>qml/images/smoke.png</file>
+ <file>qml/SwitchEffect3DFlip.qml</file>
+ <file>qml/SettingsComponentButton.qml</file>
+ <file>qml/SettingsComponentSlider.qml</file>
+ <file>qml/images/arrow.png</file>
+ <file>qml/images/Built_with_Qt.png</file>
+ <file>qml/images/Built_with_Qt_RGB_logo.png</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/multieffect/itemswitcher/qml/ItemSwitcher.qml b/examples/quick/multieffect/itemswitcher/qml/ItemSwitcher.qml
new file mode 100644
index 0000000000..eb405749be
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/ItemSwitcher.qml
@@ -0,0 +1,57 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ id: rootItem
+
+ property var sourceItems: []
+
+ property Item fromItem
+ property Item toItem
+
+ property var effect
+
+ property int currentIndex: 0
+ property int previousIndex: 0
+ property real inAnimation: 0
+ readonly property real outAnimation: 1.0 - inAnimation
+ // Duration of switch animation, in ms
+ property int duration: 1500
+
+ property bool _initialized: false
+
+ onCurrentIndexChanged: {
+ fromItem = sourceItems[previousIndex];
+ toItem = sourceItems[currentIndex];
+ if (_initialized)
+ switchAnimation.restart();
+ previousIndex = currentIndex;
+ }
+
+ // Initialize the items and currentIndex
+ Timer {
+ running: true
+ interval: 0
+ onTriggered: {
+ fromItem = sourceItems[previousIndex];
+ toItem = sourceItems[currentIndex];
+ previousIndex = currentIndex;
+ _initialized = true;
+ }
+ }
+
+ SequentialAnimation {
+ id: switchAnimation
+ alwaysRunToEnd: true
+ NumberAnimation {
+ target: rootItem
+ property: "inAnimation"
+ from: 0
+ to: 1
+ duration: rootItem.duration
+ easing.type: Easing.InOutQuad
+ }
+ }
+}
diff --git a/examples/quick/multieffect/itemswitcher/qml/PagesItem.qml b/examples/quick/multieffect/itemswitcher/qml/PagesItem.qml
new file mode 100644
index 0000000000..87b39d4947
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/PagesItem.qml
@@ -0,0 +1,49 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ id: rootItem
+
+ property Item source
+ property bool selected: false
+ property string text
+
+ signal clicked
+
+ Rectangle {
+ anchors.fill: parent
+ color: "#000000"
+ border.color: "#f0f0f0"
+ opacity: rootItem.selected ? 0.4 : 0
+ Behavior on opacity {
+ NumberAnimation {
+ duration: 400
+ easing.type: Easing.InOutQuad
+ }
+ }
+ }
+
+ ShaderEffectSource {
+ anchors.fill: parent
+ anchors.margins: 10
+ sourceItem: rootItem.source
+ smooth: true
+ mipmap: true
+ }
+ Text {
+ anchors.centerIn: parent
+ visible: rootItem.text != ""
+ text: rootItem.text
+ font.pixelSize: 14 * dp
+ color: "#ffffff"
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ rootItem.clicked();
+ }
+ }
+}
diff --git a/examples/quick/multieffect/itemswitcher/qml/PagesView.qml b/examples/quick/multieffect/itemswitcher/qml/PagesView.qml
new file mode 100644
index 0000000000..b6e5c45a94
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/PagesView.qml
@@ -0,0 +1,30 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ id: rootItem
+
+ property real itemSize: 120 * dp
+ property real margin: 10 * dp
+
+ default property alias contents: contentItem.children
+
+ width: contentItem.width + 2 * margin
+ height: itemSize + 2 * margin
+
+ Rectangle {
+ anchors.fill: parent
+ color: "#606060"
+ border.color: "#f0f0f0"
+ border.width: 1
+ opacity: 0.4
+ }
+ Row {
+ id: contentItem
+ x: margin
+ y: margin
+ spacing: margin
+ }
+}
diff --git a/examples/quick/multieffect/itemswitcher/qml/SettingsComponentButton.qml b/examples/quick/multieffect/itemswitcher/qml/SettingsComponentButton.qml
new file mode 100644
index 0000000000..26a0085d8a
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/SettingsComponentButton.qml
@@ -0,0 +1,35 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ id: rootItem
+
+ property alias text: textItem.text
+ property bool selected: false
+
+ signal clicked
+
+ width: parent.width
+ height: 40 * dp
+ Rectangle {
+ anchors.fill: parent
+ color: "#606060"
+ border.color: "#d0d0d0"
+ border.width: 1
+ opacity: selected ? 0.8 : 0.4
+ }
+ Text {
+ id: textItem
+ anchors.centerIn: parent
+ font.pixelSize: 16 * dp
+ color: "#f0f0f0"
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ rootItem.clicked();
+ }
+ }
+}
diff --git a/examples/quick/multieffect/itemswitcher/qml/SettingsComponentSlider.qml b/examples/quick/multieffect/itemswitcher/qml/SettingsComponentSlider.qml
new file mode 100644
index 0000000000..ed04584e44
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/SettingsComponentSlider.qml
@@ -0,0 +1,54 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+Column {
+ id: rootItem
+
+ property alias text: textItem.text
+ property alias value: slider.value
+ property alias from: slider.from
+ property alias to: slider.to
+ property alias checked: checkBox.checked
+ property alias stepSize: slider.stepSize
+ property bool showCheckbox: false
+
+ signal toggled
+ signal moved
+
+ Material.theme: Material.Dark
+ Material.accent: Material.LightGreen
+ spacing: -12
+
+ Text {
+ id: textItem
+ anchors.horizontalCenter: parent.horizontalCenter
+ color: "#f0f0f0"
+ font.pixelSize: 14 * dp
+ }
+
+ Row {
+ CheckBox {
+ id: checkBox
+ visible: rootItem.showCheckbox
+ checked: true
+ onToggled: {
+ rootItem.toggled();
+ }
+ }
+ Slider {
+ id: slider
+ property real sliderWidth: settings.settingsViewWidth - 50
+ width: rootItem.showCheckbox ? sliderWidth : sliderWidth + checkBox.width
+ value: 50
+ from: 0
+ to: 800
+ onMoved: {
+ rootItem.moved();
+ }
+ }
+ }
+}
diff --git a/examples/quick/multieffect/itemswitcher/qml/SettingsView.qml b/examples/quick/multieffect/itemswitcher/qml/SettingsView.qml
new file mode 100644
index 0000000000..cf1a753b4d
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/SettingsView.qml
@@ -0,0 +1,168 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls.Material
+
+Item {
+ id: rootItem
+
+ property bool show: true
+ property real showAnimation: show ? 1 : 0
+
+ Material.theme: Material.Dark
+ Material.accent: Material.LightGreen
+ width: settings.settingsViewWidth
+ x: -(width + 30) * (1 - showAnimation) + 20
+
+ Behavior on showAnimation {
+ NumberAnimation {
+ duration: 400
+ easing.type: Easing.InOutQuad
+ }
+ }
+
+ // Open/close button
+ Item {
+ width: 30 * dp
+ height: 30 * dp
+ anchors.left: parent.right
+ anchors.leftMargin: 20
+ anchors.top: parent.top
+ anchors.topMargin: -10
+ Rectangle {
+ anchors.fill: parent
+ color: "#404040"
+ opacity: 0.4
+ border.width: 1
+ border.color: "#808080"
+ }
+
+ Image {
+ anchors.centerIn: parent
+ source: "images/arrow.png"
+ rotation: rootItem.showAnimation * 180
+ }
+ MouseArea {
+ anchors.fill: parent
+ anchors.margins: -30 * dp
+ onClicked: {
+ rootItem.show = !rootItem.show;
+ }
+ }
+ }
+
+ // Background
+ Rectangle {
+ anchors.fill: scrollView
+ opacity: showAnimation ? 0.6 : 0
+ visible: opacity
+ anchors.margins: -10
+ color: "#202020"
+ border.color: "#808080"
+ border.width: 1
+ }
+
+ ScrollView {
+ id: scrollView
+ anchors.fill: parent
+ ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
+ ScrollBar.vertical.interactive: false
+ clip: true
+ Column {
+ id: settingsArea
+ width: rootItem.width
+ opacity: showAnimation
+ visible: opacity
+ spacing: 8 * dp
+
+ Item {
+ width: 1
+ height: 20 * dp
+ }
+ Image {
+ anchors.horizontalCenter: parent.horizontalCenter
+ fillMode: Image.PreserveAspectFit
+ width: parent.width * 0.8
+ height: width * 0.25
+ source: "images/Built_with_Qt_RGB_logo.png"
+ }
+ Item {
+ width: 1
+ height: 28 * dp
+ }
+ Text {
+ anchors.horizontalCenter: parent.horizontalCenter
+ text: qsTr("Switching Effects")
+ font.pixelSize: 20 * dp
+ font.bold: true
+ color: "#f0f0f0"
+ }
+ SettingsComponentButton {
+ text: "Blinds"
+ selected: settings.effectIndex === 0
+ onClicked: {
+ settings.effectIndex = 0;
+ }
+ }
+ SettingsComponentButton {
+ text: "Blurry"
+ selected: settings.effectIndex === 1
+ onClicked: {
+ settings.effectIndex = 1;
+ }
+ }
+ SettingsComponentButton {
+ text: "Heart"
+ selected: settings.effectIndex === 2
+ onClicked: {
+ settings.effectIndex = 2;
+ }
+ }
+ SettingsComponentButton {
+ text: "Stars"
+ selected: settings.effectIndex === 3
+ onClicked: {
+ settings.effectIndex = 3;
+ }
+ }
+ SettingsComponentButton {
+ text: "Thunder"
+ selected: settings.effectIndex === 4
+ onClicked: {
+ settings.effectIndex = 4;
+ }
+ }
+ SettingsComponentButton {
+ text: "3D Flip"
+ selected: settings.effectIndex === 5
+ onClicked: {
+ settings.effectIndex = 5;
+ }
+ }
+ Item {
+ width: 1
+ height: 10
+ }
+
+ SettingsComponentSlider {
+ text: qsTr("Animation Duration") + ": " + value.toFixed(0) + " ms"
+ value: settings.switchDuration
+ from: 500
+ to: 5000
+ onMoved: {
+ settings.switchDuration = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Animation Time") + ": " + (value * settings.switchDuration).toFixed(0) + " ms"
+ value: itemSwitcher.inAnimation
+ from: 0
+ to: 1
+ onMoved: {
+ itemSwitcher.inAnimation = value;
+ }
+ }
+ }
+ }
+}
diff --git a/examples/quick/multieffect/itemswitcher/qml/SwitchEffect3DFlip.qml b/examples/quick/multieffect/itemswitcher/qml/SwitchEffect3DFlip.qml
new file mode 100644
index 0000000000..bb33b8c6b6
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/SwitchEffect3DFlip.qml
@@ -0,0 +1,93 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Effects
+
+Item {
+ id: rootItem
+ // We expect all effects to be placed under ItemSwitcher
+ property Item switcher: rootItem.parent
+
+ anchors.fill: parent
+
+ MultiEffect {
+ source: switcher.fromItem
+ width: parent.width
+ height: parent.height
+ x: switcher.inAnimation * rootItem.width
+ blurEnabled: true
+ blur: switcher.inAnimation
+ blurMax: 32
+ blurMultiplier: 0.5
+ opacity: switcher.outAnimation
+
+ saturation: -switcher.inAnimation * 1.5
+
+ maskEnabled: true
+ maskSource: Image {
+ source: "images/smoke.png"
+ visible: false
+ }
+ maskThresholdMin: switcher.inAnimation * 0.6
+ maskSpreadAtMin: 0.1
+ maskThresholdMax: 1.0 - (switcher.inAnimation * 0.6)
+ maskSpreadAtMax: 0.1
+
+ shadowEnabled: true
+ shadowOpacity: 0.5
+ shadowBlur: 0.8
+ shadowVerticalOffset: 5
+ shadowHorizontalOffset: 10 + (x * 0.2)
+ shadowScale: 1.02
+
+ transform: Rotation {
+ origin.x: parent.width / 2
+ origin.y: parent.height / 2
+ axis { x: 0; y: 1; z: 0 }
+ angle: switcher.inAnimation * 60
+ }
+ rotation: -switcher.inAnimation * 20
+ scale: 1.0 + (switcher.inAnimation * 0.2)
+ }
+
+ MultiEffect {
+ source: switcher.toItem
+ width: parent.width
+ height: parent.height
+ x: -switcher.outAnimation * rootItem.width
+ blurEnabled: true
+ blur: switcher.outAnimation * 2
+ blurMax: 32
+ blurMultiplier: 0.5
+ opacity: switcher.inAnimation
+
+ saturation: -switcher.outAnimation * 1.5
+
+ maskEnabled: true
+ maskSource: Image {
+ source: "images/smoke.png"
+ visible: false
+ }
+ maskThresholdMin: switcher.outAnimation * 0.6
+ maskSpreadAtMin: 0.1
+ maskThresholdMax: 1.0 - (switcher.outAnimation * 0.6)
+ maskSpreadAtMax: 0.1
+
+ shadowEnabled: true
+ shadowOpacity: 0.5
+ shadowBlur: 0.8
+ shadowVerticalOffset: 5
+ shadowHorizontalOffset: 10 + (x * 0.2)
+ shadowScale: 1.02
+
+ transform: Rotation {
+ origin.x: parent.width / 2
+ origin.y: parent.height / 2
+ axis { x: 0; y: 1; z: 0 }
+ angle: -switcher.outAnimation * 60
+ }
+ rotation: switcher.outAnimation * 20
+ scale: 1.0 - (switcher.outAnimation * 0.4)
+ }
+}
diff --git a/examples/quick/multieffect/itemswitcher/qml/SwitchEffectBlinds.qml b/examples/quick/multieffect/itemswitcher/qml/SwitchEffectBlinds.qml
new file mode 100644
index 0000000000..8849631be9
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/SwitchEffectBlinds.qml
@@ -0,0 +1,49 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Effects
+
+Item {
+ id: rootItem
+ // We expect all effects to be placed under ItemSwitcher
+ property Item switcher: rootItem.parent
+ property real rotation: 0
+
+ anchors.fill: parent
+
+ Item {
+ id: mask
+ anchors.fill: parent
+ layer.enabled: true
+ visible: false
+ smooth: false
+ clip: true
+ Image {
+ anchors.fill: parent
+ anchors.margins: -parent.width * 0.25
+ source: "images/hblinds.png"
+ rotation: rootItem.rotation
+ smooth: false
+ }
+ }
+
+ // Item going out
+ MultiEffect {
+ source: switcher.fromItem
+ anchors.fill: parent
+ maskEnabled: true
+ maskSource: mask
+ maskThresholdMin: switcher.inAnimation
+ maskSpreadAtMin: 0.4
+ }
+ // Item coming in
+ MultiEffect {
+ source: switcher.toItem
+ anchors.fill: parent
+ maskEnabled: true
+ maskSource: mask
+ maskThresholdMax: switcher.inAnimation
+ maskSpreadAtMax: 0.4
+ }
+}
diff --git a/examples/quick/multieffect/itemswitcher/qml/SwitchEffectBlur.qml b/examples/quick/multieffect/itemswitcher/qml/SwitchEffectBlur.qml
new file mode 100644
index 0000000000..7df93a2f99
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/SwitchEffectBlur.qml
@@ -0,0 +1,34 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Effects
+
+Item {
+ id: rootItem
+ // We expect all effects to be placed under ItemSwitcher
+ property Item switcher: rootItem.parent
+
+ anchors.fill: parent
+
+ MultiEffect {
+ source: switcher.fromItem
+ anchors.fill: parent
+ blurEnabled: true
+ blur: switcher.inAnimation * 4
+ blurMax: 32
+ blurMultiplier: 0.5
+ opacity: switcher.outAnimation
+ saturation: -switcher.inAnimation * 2
+ }
+ MultiEffect {
+ source: switcher.toItem
+ anchors.fill: parent
+ blurEnabled: true
+ blur: switcher.outAnimation * 4
+ blurMax: 32
+ blurMultiplier: 0.5
+ opacity: switcher.inAnimation
+ saturation: -switcher.outAnimation * 2
+ }
+}
diff --git a/examples/quick/multieffect/itemswitcher/qml/SwitchEffectHeart.qml b/examples/quick/multieffect/itemswitcher/qml/SwitchEffectHeart.qml
new file mode 100644
index 0000000000..e80c54460c
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/SwitchEffectHeart.qml
@@ -0,0 +1,48 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Effects
+
+Item {
+ id: rootItem
+ // We expect all effects to be placed under ItemSwitcher
+ property Item switcher: rootItem.parent
+
+ anchors.fill: parent
+
+ Item {
+ id: mask
+ anchors.fill: parent
+ layer.enabled: true
+ visible: false
+ clip: true
+ Image {
+ anchors.fill: parent
+ source: "images/heart.png"
+ scale: switcher.inAnimation * 3
+ }
+ }
+
+ // Item going out
+ MultiEffect {
+ source: switcher.fromItem
+ anchors.fill: parent
+ maskEnabled: true
+ maskSource: mask
+ maskInverted: true
+ maskThresholdMin: 0.5
+ maskSpreadAtMin: 0.0
+ }
+ // Item coming in
+ MultiEffect {
+ source: switcher.toItem
+ anchors.fill: parent
+ maskEnabled: true
+ maskSource: mask
+ maskThresholdMin: 0.5
+ maskSpreadAtMin: 0.0
+ colorizationColor: "red"
+ colorization: Math.max(0, 1.0 - switcher.inAnimation * 2)
+ }
+}
diff --git a/examples/quick/multieffect/itemswitcher/qml/SwitchEffectStars.qml b/examples/quick/multieffect/itemswitcher/qml/SwitchEffectStars.qml
new file mode 100644
index 0000000000..c533572e66
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/SwitchEffectStars.qml
@@ -0,0 +1,50 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Effects
+
+Item {
+ id: rootItem
+ // We expect all effects to be placed under ItemSwitcher
+ property Item switcher: rootItem.parent
+
+ anchors.fill: parent
+
+ Item {
+ id: mask
+ anchors.fill: parent
+ layer.enabled: true
+ visible: false
+ clip: true
+ Image {
+ anchors.fill: parent
+ source: "images/star.png"
+ scale: switcher.inAnimation * 5
+ rotation: switcher.outAnimation * 360
+ }
+ }
+
+ // Item going out
+ MultiEffect {
+ source: switcher.fromItem
+ anchors.fill: parent
+ maskEnabled: true
+ maskSource: mask
+ maskInverted: true
+ maskThresholdMin: 0.5
+ maskSpreadAtMin: 0.5
+ }
+ // Item coming in
+ MultiEffect {
+ source: switcher.toItem
+ anchors.fill: parent
+ maskEnabled: true
+ maskSource: mask
+ maskThresholdMin: 0.5
+ maskSpreadAtMin: 0.5
+ colorizationColor: "#ffd020"
+ colorization: Math.max(0, 1.0 - switcher.inAnimation * 2)
+ brightness: Math.max(0.0, 0.8 - switcher.inAnimation * 2)
+ }
+}
diff --git a/examples/quick/multieffect/itemswitcher/qml/SwitchEffectThunder.qml b/examples/quick/multieffect/itemswitcher/qml/SwitchEffectThunder.qml
new file mode 100644
index 0000000000..80a6e9bf8f
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/SwitchEffectThunder.qml
@@ -0,0 +1,87 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Effects
+
+Item {
+ id: rootItem
+ // We expect all effects to be placed under ItemSwitcher
+ property Item switcher: rootItem.parent
+
+ property real _xPos: Math.sin(switcher.inAnimation * Math.PI * 50) * width * 0.03 * (0.5 - Math.abs(0.5 - switcher.inAnimation))
+ property real _yPos: Math.sin(switcher.inAnimation * Math.PI * 35) * width * 0.02 * (0.5 - Math.abs(0.5 - switcher.inAnimation))
+
+ anchors.fill: parent
+
+ Image {
+ id: maskImage
+ source: "images/stripes.png"
+ visible: false
+ }
+
+ MultiEffect {
+ source: switcher.fromItem
+ width: parent.width
+ height: parent.height
+ x: rootItem._xPos
+ y: rootItem._yPos
+ blurEnabled: true
+ blur: switcher.inAnimation
+ blurMax: 32
+ blurMultiplier: 0.5
+ opacity: switcher.outAnimation
+ colorizationColor: "#f0d060"
+ colorization: switcher.inAnimation
+
+ contrast: switcher.inAnimation
+ brightness: switcher.inAnimation
+
+ maskEnabled: true
+ maskSource: maskImage
+ maskThresholdMin: switcher.inAnimation * 0.9
+ maskSpreadAtMin: 0.2
+ maskThresholdMax: 1.0
+
+ shadowEnabled: true
+ shadowColor: "#f04000"
+ shadowBlur: 1.0
+ shadowOpacity: 5.0 - switcher.outAnimation * 5.0
+ shadowHorizontalOffset: 0
+ shadowVerticalOffset: 0
+ shadowScale: 1.04
+
+ }
+ MultiEffect {
+ source: switcher.toItem
+ width: parent.width
+ height: parent.height
+ x: -rootItem._xPos
+ y: -rootItem._yPos
+ blurEnabled: true
+ blur: switcher.outAnimation * 2
+ blurMax: 32
+ blurMultiplier: 0.5
+ opacity: switcher.inAnimation * 3.0 - 1.0
+
+ colorizationColor: "#f0d060"
+ colorization: switcher.outAnimation
+ contrast: switcher.outAnimation
+ brightness: switcher.outAnimation
+
+ maskEnabled: true
+ maskSource: maskImage
+ maskThresholdMin: switcher.outAnimation * 0.6
+ maskSpreadAtMin: 0.2
+ maskThresholdMax: 1.0
+
+ shadowEnabled: true
+ shadowColor: "#f04000"
+ shadowBlur: 1.0
+ shadowOpacity: 5.0 - switcher.inAnimation * 5.0
+ shadowHorizontalOffset: 0
+ shadowVerticalOffset: 0
+ shadowScale: 1.04
+ }
+
+}
diff --git a/examples/quick/multieffect/itemswitcher/qml/images/Built_with_Qt.png b/examples/quick/multieffect/itemswitcher/qml/images/Built_with_Qt.png
new file mode 100644
index 0000000000..e612481510
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/images/Built_with_Qt.png
Binary files differ
diff --git a/examples/quick/multieffect/itemswitcher/qml/images/Built_with_Qt_RGB_logo.png b/examples/quick/multieffect/itemswitcher/qml/images/Built_with_Qt_RGB_logo.png
new file mode 100644
index 0000000000..8d2dfc17ec
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/images/Built_with_Qt_RGB_logo.png
Binary files differ
diff --git a/examples/quick/multieffect/itemswitcher/qml/images/arrow.png b/examples/quick/multieffect/itemswitcher/qml/images/arrow.png
new file mode 100644
index 0000000000..067bec4509
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/images/arrow.png
Binary files differ
diff --git a/examples/quick/multieffect/itemswitcher/qml/images/background.png b/examples/quick/multieffect/itemswitcher/qml/images/background.png
new file mode 100644
index 0000000000..9b9e9e487d
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/images/background.png
Binary files differ
diff --git a/examples/quick/multieffect/itemswitcher/qml/images/hblinds.png b/examples/quick/multieffect/itemswitcher/qml/images/hblinds.png
new file mode 100644
index 0000000000..09ce73ee03
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/images/hblinds.png
Binary files differ
diff --git a/examples/quick/multieffect/itemswitcher/qml/images/heart.png b/examples/quick/multieffect/itemswitcher/qml/images/heart.png
new file mode 100644
index 0000000000..dda1f4bab1
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/images/heart.png
Binary files differ
diff --git a/examples/quick/multieffect/itemswitcher/qml/images/quit_coding.png b/examples/quick/multieffect/itemswitcher/qml/images/quit_coding.png
new file mode 100644
index 0000000000..0930013af4
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/images/quit_coding.png
Binary files differ
diff --git a/examples/quick/multieffect/itemswitcher/qml/images/smoke.png b/examples/quick/multieffect/itemswitcher/qml/images/smoke.png
new file mode 100644
index 0000000000..83dfcc453c
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/images/smoke.png
Binary files differ
diff --git a/examples/quick/multieffect/itemswitcher/qml/images/star.png b/examples/quick/multieffect/itemswitcher/qml/images/star.png
new file mode 100644
index 0000000000..f83bd87892
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/images/star.png
Binary files differ
diff --git a/examples/quick/multieffect/itemswitcher/qml/images/stripes.png b/examples/quick/multieffect/itemswitcher/qml/images/stripes.png
new file mode 100644
index 0000000000..7a79767e6e
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/images/stripes.png
Binary files differ
diff --git a/examples/quick/multieffect/itemswitcher/qml/main.qml b/examples/quick/multieffect/itemswitcher/qml/main.qml
new file mode 100644
index 0000000000..9685c71ccb
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/qml/main.qml
@@ -0,0 +1,219 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+Rectangle {
+ id: mainWindow
+
+ // Multiplier for resolution independency
+ readonly property real dp: 0.2 + Math.min(width, height) / 1200
+
+ width: 1280
+ height: 720
+ visible: true
+ color: "#404040"
+
+ QtObject {
+ id: settings
+ property real settingsViewWidth: 100 + 150 * dp
+ property int effectIndex: 0
+ property int switchDuration: 1500
+ property int itemSize: mainWindow.height * 0.6
+ }
+
+ Item {
+ id: testItem1
+ width: 1
+ height: 1
+ }
+
+ Rectangle {
+ id: testItem2
+ anchors.fill: itemSwitcher
+ color: "#d0d0d0"
+ visible: false
+ Image {
+ anchors.fill: parent
+ anchors.margins: 4
+ source: "images/background.png"
+ }
+ Text {
+ anchors.centerIn: parent
+ font.pixelSize: 40 * dp
+ horizontalAlignment: Text.AlignHCenter
+ text: "This is the\nfirst item"
+ color: "#ffffff"
+ style: Text.Outline
+ styleColor: "#202020"
+ }
+ }
+
+ Rectangle {
+ id: testItem3Content
+ anchors.fill: itemSwitcher
+ color: "white"
+ border.width: 5
+ visible: itemSwitcher.currentIndex === 2
+ Text {
+ anchors.centerIn: parent
+ font.pixelSize: 48 * dp
+ horizontalAlignment: Text.AlignHCenter
+ text: "This is a\nDIFFERENT\nsecond item"
+ rotation: slider.value * 360
+ }
+ Slider {
+ id: slider
+ anchors.top: parent.top
+ anchors.topMargin: 20 * dp
+ anchors.horizontalCenter: parent.horizontalCenter
+ from: 0
+ to: 1
+ width: parent.width * 0.8
+ }
+ Button {
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 20 * dp
+ anchors.horizontalCenter: parent.horizontalCenter
+ text: "Controls Button"
+ }
+ }
+ ShaderEffectSource {
+ // Wrap testItem3 into ShaderEffectSource so it doesn't need
+ // visible = false and can be interactive.
+ id: testItem3
+ anchors.fill: testItem3Content
+ sourceItem: testItem3Content
+ hideSource: true
+ visible: false
+ }
+
+ Image {
+ id: testItem4
+ source: "images/Built_with_Qt.png"
+ anchors.fill: itemSwitcher
+ visible: false
+ }
+
+ Image {
+ id: testItem5
+ source: "images/quit_coding.png"
+ anchors.fill: itemSwitcher
+ visible: false
+ }
+
+ Item {
+ id: mainArea
+ anchors.left: settingsView.right
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+
+ PagesView {
+ id: pagesView
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+ anchors.topMargin: 20 * dp
+ PagesItem {
+ width: pagesView.itemSize
+ height: pagesView.itemSize
+ source: testItem1
+ text: "(EMPTY)"
+ selected: itemSwitcher.currentIndex === 0
+ onClicked: {
+ itemSwitcher.currentIndex = 0;
+ }
+ }
+ PagesItem {
+ width: pagesView.itemSize
+ height: pagesView.itemSize
+ source: testItem2
+ selected: itemSwitcher.currentIndex === 1
+ onClicked: {
+ itemSwitcher.currentIndex = 1;
+ }
+ }
+ PagesItem {
+ width: pagesView.itemSize
+ height: pagesView.itemSize
+ source: testItem3
+ selected: itemSwitcher.currentIndex === 2
+ onClicked: {
+ itemSwitcher.currentIndex = 2;
+ }
+ }
+ PagesItem {
+ width: pagesView.itemSize
+ height: pagesView.itemSize
+ source: testItem4
+ selected: itemSwitcher.currentIndex === 3
+ onClicked: {
+ itemSwitcher.currentIndex = 3;
+ }
+ }
+ PagesItem {
+ width: pagesView.itemSize
+ height: pagesView.itemSize
+ source: testItem5
+ selected: itemSwitcher.currentIndex === 4
+ onClicked: {
+ itemSwitcher.currentIndex = 4;
+ }
+ }
+ }
+ }
+ ItemSwitcher {
+ id: itemSwitcher
+ anchors.centerIn: mainArea
+ anchors.verticalCenterOffset: pagesView.height / 2
+ width: settings.itemSize
+ height: settings.itemSize
+ duration: settings.switchDuration
+ Component.onCompleted: {
+ // Add all switchable items into switcher
+ sourceItems.push(testItem1);
+ sourceItems.push(testItem2);
+ sourceItems.push(testItem3);
+ sourceItems.push(testItem4);
+ sourceItems.push(testItem5);
+ // From item is the currently selected one
+ currentIndex = settings.effectIndex;
+ }
+
+ SwitchEffectBlinds {
+ id: blindsEffect
+ visible: settings.effectIndex == 0
+ rotation: 45
+ }
+ SwitchEffectBlur {
+ id: blurEffect
+ visible: settings.effectIndex == 1
+ }
+ SwitchEffectHeart {
+ id: heartEffect
+ visible: settings.effectIndex == 2
+ }
+ SwitchEffectStars {
+ id: starsEffect
+ visible: settings.effectIndex == 3
+ }
+ SwitchEffectThunder {
+ id: thunderEffect
+ visible: settings.effectIndex == 4
+ }
+ SwitchEffect3DFlip {
+ id: flipEffect
+ visible: settings.effectIndex == 5
+ }
+ }
+
+ SettingsView {
+ id: settingsView
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.margins: 20
+ }
+}
diff --git a/examples/quick/multieffect/multieffect.pro b/examples/quick/multieffect/multieffect.pro
new file mode 100644
index 0000000000..b06faae7df
--- /dev/null
+++ b/examples/quick/multieffect/multieffect.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+SUBDIRS += testbed \
+ itemswitcher
diff --git a/examples/quick/multieffect/testbed/CMakeLists.txt b/examples/quick/multieffect/testbed/CMakeLists.txt
new file mode 100644
index 0000000000..fb8c566378
--- /dev/null
+++ b/examples/quick/multieffect/testbed/CMakeLists.txt
@@ -0,0 +1,74 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(testbed LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick QuickControls2)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+add_subdirectory("../../shared" "shared")
+
+qt_add_executable(testbedexample WIN32 MACOSX_BUNDLE
+ main.cpp
+)
+
+target_link_libraries(testbedexample PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+ Qt6::QuickControls2
+)
+
+add_dependencies(testbedexample testbed_shared)
+
+# Resources:
+qt_add_qml_module(testbedexample
+ URI testbed
+ QML_FILES
+ "qml/FpsItem.qml"
+ "qml/main.qml"
+ "qml/ResetSettingsOverlay.qml"
+ "qml/Settings.qml"
+ "qml/SettingsView.qml"
+ "qml/ShaderView.qml"
+ "qml/TestMaskItem.qml"
+ "qml/TestSourceItem.qml"
+ "qml/WarningsItem.qml"
+ "qml/WarningsView.qml"
+ "qml/SettingsComponentView.qml"
+ "qml/SettingsComponentSlider.qml"
+ "qml/SettingsComponentCheckBox.qml"
+ "qml/SettingsComponentColorSelector.qml"
+ "qml/CustomMultiEffect/BlurHelper.qml"
+ "qml/CustomMultiEffect/CustomMultiEffect.qml"
+ RESOURCES
+ "qml/images/pause.png"
+ "qml/images/play.png"
+ "qml/images/spinner.png"
+ "qml/images/warning.png"
+ "qml/images/arrow.png"
+ "qml/images/Built_with_Qt.png"
+ "qml/images/Built_with_Qt_RGB_logo.png"
+ "qml/CustomMultiEffect/bluritems.frag.qsb"
+ "qml/CustomMultiEffect/bluritems.vert.qsb"
+ "qml/CustomMultiEffect/custommultieffect.frag.qsb"
+ "qml/CustomMultiEffect/custommultieffect.vert.qsb"
+)
+
+install(TARGETS testbedexample
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET testbedexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/multieffect/testbed/doc/images/qml-multieffecttestbed-example.jpg b/examples/quick/multieffect/testbed/doc/images/qml-multieffecttestbed-example.jpg
new file mode 100644
index 0000000000..de6b05da56
--- /dev/null
+++ b/examples/quick/multieffect/testbed/doc/images/qml-multieffecttestbed-example.jpg
Binary files differ
diff --git a/examples/quick/multieffect/testbed/doc/src/testbed.qdoc b/examples/quick/multieffect/testbed/doc/src/testbed.qdoc
new file mode 100644
index 0000000000..64a815b3a8
--- /dev/null
+++ b/examples/quick/multieffect/testbed/doc/src/testbed.qdoc
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+ \title Qt Quick Examples - MultiEffect Test Bed
+ \example multieffect/testbed
+ \image qml-multieffecttestbed-example.jpg
+ \brief Demonstrates MultiEffect usage.
+ \ingroup qtquickexamples
+ \examplecategory {Graphics}
+
+ This example demonstrates the MultiEffect type, which offers a simple,
+ efficient solution for applying effects such as blur, drop shadow, or
+ colorize to an Item and its children.
+
+ \include examples-run.qdocinc
+*/
diff --git a/examples/quick/multieffect/testbed/main.cpp b/examples/quick/multieffect/testbed/main.cpp
new file mode 100644
index 0000000000..130e3ab0b4
--- /dev/null
+++ b/examples/quick/multieffect/testbed/main.cpp
@@ -0,0 +1,4 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+#include "../../shared/shared.h"
+DECLARATIVE_EXAMPLE_MAIN(testbed/qml/main)
diff --git a/examples/quick/multieffect/testbed/qml.qrc b/examples/quick/multieffect/testbed/qml.qrc
new file mode 100644
index 0000000000..4c23cff873
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml.qrc
@@ -0,0 +1,31 @@
+<RCC>
+ <qresource prefix="/qt/qml/testbed">
+ <file>qml/main.qml</file>
+ <file>qml/Settings.qml</file>
+ <file>qml/ResetSettingsOverlay.qml</file>
+ <file>qml/FpsItem.qml</file>
+ <file>qml/images/spinner.png</file>
+ <file>qml/images/warning.png</file>
+ <file>qml/WarningsView.qml</file>
+ <file>qml/WarningsItem.qml</file>
+ <file>qml/ShaderView.qml</file>
+ <file>qml/TestSourceItem.qml</file>
+ <file>qml/TestMaskItem.qml</file>
+ <file>qml/SettingsView.qml</file>
+ <file>qml/images/pause.png</file>
+ <file>qml/images/play.png</file>
+ <file>qml/SettingsComponentCheckBox.qml</file>
+ <file>qml/SettingsComponentColorSelector.qml</file>
+ <file>qml/SettingsComponentSlider.qml</file>
+ <file>qml/SettingsComponentView.qml</file>
+ <file>qml/images/arrow.png</file>
+ <file>qml/images/Built_with_Qt.png</file>
+ <file>qml/images/Built_with_Qt_RGB_logo.png</file>
+ <file>qml/CustomMultiEffect/BlurHelper.qml</file>
+ <file>qml/CustomMultiEffect/bluritems.frag.qsb</file>
+ <file>qml/CustomMultiEffect/bluritems.vert.qsb</file>
+ <file>qml/CustomMultiEffect/custommultieffect.frag.qsb</file>
+ <file>qml/CustomMultiEffect/CustomMultiEffect.qml</file>
+ <file>qml/CustomMultiEffect/custommultieffect.vert.qsb</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/BlurHelper.qml b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/BlurHelper.qml
new file mode 100644
index 0000000000..7a9390791b
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/BlurHelper.qml
@@ -0,0 +1,66 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ id: rootItem
+ property alias blurSrc1: blurredItemSource1
+ property alias blurSrc2: blurredItemSource2
+ property alias blurSrc3: blurredItemSource3
+ property alias blurSrc4: blurredItemSource4
+ property alias blurSrc5: blurredItemSource5
+
+ component BlurItem : ShaderEffect {
+ property vector2d offset: Qt.vector2d((1.0 + rootItem.blurMultiplier) / width,
+ (1.0 + rootItem.blurMultiplier) / height)
+ visible: false
+ layer.enabled: true
+ layer.smooth: true
+ vertexShader: "bluritems.vert.qsb"
+ fragmentShader: "bluritems.frag.qsb"
+ }
+
+ QtObject {
+ id: priv
+ property bool useBlurItem1: true
+ property bool useBlurItem2: rootItem.blurMax > 2
+ property bool useBlurItem3: rootItem.blurMax > 8
+ property bool useBlurItem4: rootItem.blurMax > 16
+ property bool useBlurItem5: rootItem.blurMax > 32
+ }
+
+ BlurItem {
+ id: blurredItemSource1
+ property Item src: priv.useBlurItem1 ? source : null
+ // Size of the first blurred item is by default half of the source.
+ // Increase for quality and decrease for performance & more blur.
+ readonly property int blurItemSize: 8
+ width: src ? Math.ceil(src.width / 16) * blurItemSize : 0
+ height: src ? Math.ceil(src.height / 16) * blurItemSize : 0
+ }
+ BlurItem {
+ id: blurredItemSource2
+ property Item src: priv.useBlurItem2 ? blurredItemSource1 : null
+ width: blurredItemSource1.width * 0.5
+ height: blurredItemSource1.height * 0.5
+ }
+ BlurItem {
+ id: blurredItemSource3
+ property Item src: priv.useBlurItem3 ? blurredItemSource2 : null
+ width: blurredItemSource2.width * 0.5
+ height: blurredItemSource2.height * 0.5
+ }
+ BlurItem {
+ id: blurredItemSource4
+ property Item src: priv.useBlurItem4 ? blurredItemSource3 : null
+ width: blurredItemSource3.width * 0.5
+ height: blurredItemSource3.height * 0.5
+ }
+ BlurItem {
+ id: blurredItemSource5
+ property Item src: priv.useBlurItem5 ? blurredItemSource4 : null
+ width: blurredItemSource4.width * 0.5
+ height: blurredItemSource4.height * 0.5
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qep b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qep
new file mode 100644
index 0000000000..2600bc505f
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qep
@@ -0,0 +1,383 @@
+{
+ "QEP": {
+ "QQEM": "0.43",
+ "connections": [
+ {
+ "fromId": 2,
+ "toId": 1
+ },
+ {
+ "fromId": 0,
+ "toId": 3
+ },
+ {
+ "fromId": 3,
+ "toId": 2
+ }
+ ],
+ "exportDirectory": ".",
+ "exportFlags": 11,
+ "exportName": "CustomMultiEffect",
+ "nodes": [
+ {
+ "fragmentCode": [
+ "void main() {",
+ " fragColor = texture(iSource, texCoord);",
+ " @nodes",
+ " fragColor = fragColor * qt_Opacity;",
+ "}"
+ ],
+ "name": "Main",
+ "nodeId": 0,
+ "type": 0,
+ "vertexCode": [
+ "void main() {",
+ " texCoord = qt_MultiTexCoord0;",
+ " fragCoord = qt_Vertex.xy;",
+ " vec2 vertCoord = qt_Vertex.xy;",
+ " @nodes",
+ " gl_Position = qt_Matrix * vec4(vertCoord, 0.0, 1.0);",
+ "}"
+ ],
+ "x": 140,
+ "y": 35.25
+ },
+ {
+ "name": "Output",
+ "nodeId": 1,
+ "type": 1,
+ "x": 140,
+ "y": 589.75
+ },
+ {
+ "description": "This node matches to features and API of the Qt Quick MultiEffect element. This makes it easy to build customized MultiEffects.",
+ "fragmentCode": [
+ "{",
+ " vec4 color = fragColor;",
+ "#if (BLUR_ENABLED)",
+ " // blur",
+ " vec4 blurredColor = texture(iSource, texCoord) * blurWeight1[0];",
+ " blurredColor += texture(iSourceBlur1, texCoord) * blurWeight1[1];",
+ "#if (BLUR_HELPER_MAX_LEVEL > 2)",
+ " blurredColor += texture(iSourceBlur2, texCoord) * blurWeight1[2];",
+ "#endif",
+ "#if (BLUR_HELPER_MAX_LEVEL > 8)",
+ " blurredColor += texture(iSourceBlur3, texCoord) * blurWeight1[3];",
+ "#endif",
+ "#if (BLUR_HELPER_MAX_LEVEL > 16)",
+ " blurredColor += texture(iSourceBlur4, texCoord) * blurWeight2[0];",
+ "#endif",
+ "#if (BLUR_HELPER_MAX_LEVEL > 32)",
+ " blurredColor += texture(iSourceBlur5, texCoord) * blurWeight2[1];",
+ "#endif",
+ " color = blurredColor;",
+ "#endif",
+ " // contrast, brightness, saturation and colorization",
+ " color.rgb = (color.rgb - 0.5 * color.a) * (1.0 + contrast) + 0.5 * color.a;",
+ " color.rgb += brightness * color.a;",
+ " float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));",
+ " float colorizationAlpha = colorization * colorizationColor.a;",
+ " color.rgb = mix(color.rgb, gray * colorizationColor.rgb, colorizationAlpha);",
+ " color.rgb = mix(vec3(gray), color.rgb, 1.0 + saturation);",
+ "#if (SHADOW_ENABLED)",
+ " // shadow",
+ " float shadow = texture(iSource, shadowTexCoord).a * shadowBlurWeight1[0];",
+ " shadow += texture(iSourceBlur1, shadowTexCoord).a * shadowBlurWeight1[1];",
+ "#if (BLUR_HELPER_MAX_LEVEL > 2)",
+ " shadow += texture(iSourceBlur2, shadowTexCoord).a * shadowBlurWeight1[2];",
+ "#endif",
+ "#if (BLUR_HELPER_MAX_LEVEL > 8)",
+ " shadow += texture(iSourceBlur3, shadowTexCoord).a * shadowBlurWeight1[3];",
+ "#endif",
+ "#if (BLUR_HELPER_MAX_LEVEL > 16)",
+ " shadow += texture(iSourceBlur4, shadowTexCoord).a * shadowBlurWeight2[0];",
+ "#endif",
+ "#if (BLUR_HELPER_MAX_LEVEL > 32)",
+ " shadow += texture(iSourceBlur5, shadowTexCoord).a * shadowBlurWeight2[1];",
+ "#endif",
+ " shadow *= (shadowColor.a * shadowOpacity);",
+ " float saa = (1.0 - color.a) * (1.0 - shadow);",
+ " color.rgb = mix(shadowColor.rgb * shadow, color.rgb, color.a + saa);",
+ " color.a = 1.0 - saa;",
+ "#endif",
+ "#if (MASK_ENABLED)",
+ " // mask",
+ " vec4 maskTexture = texture(maskSource, texCoord);",
+ " float alphaMask = maskTexture.a;",
+ " const float mSLow = 1.0 + maskSpreadAtMin;",
+ " const float mSUp = 1.0 + maskSpreadAtMax;",
+ " float m1 = smoothstep(maskThresholdMin * mSLow - (mSLow - 0.999), maskThresholdMin * mSLow, alphaMask);",
+ " float m2 = smoothstep((1.0 - maskThresholdMax) * mSUp - (mSUp - 0.999), (1.0 - maskThresholdMax) * mSUp, (1.0 - alphaMask));",
+ " float mm = m1 * m2;",
+ " color *= (1.0 - float(maskInverted)) * mm + float(maskInverted) * (1.0 - mm);",
+ "#endif",
+ " fragColor = color;",
+ "}"
+ ],
+ "name": "MultiEffect",
+ "nodeId": 2,
+ "properties": [
+ {
+ "defaultValue": "0",
+ "maxValue": "1",
+ "minValue": "-1",
+ "name": "contrast",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "0",
+ "maxValue": "1",
+ "minValue": "-1",
+ "name": "brightness",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "0",
+ "maxValue": "1",
+ "minValue": "-1",
+ "name": "saturation",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "0",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "colorization",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "1, 0, 0, 1",
+ "name": "colorizationColor",
+ "type": "color",
+ "value": "1, 0, 0, 1"
+ },
+ {
+ "defaultValue": "1",
+ "description": "Enables the blur effect.",
+ "name": "BLUR_ENABLED",
+ "type": "define",
+ "value": "1"
+ },
+ {
+ "defaultValue": "32",
+ "maxValue": "64",
+ "minValue": "0",
+ "name": "blurMax",
+ "type": "int",
+ "value": "32"
+ },
+ {
+ "defaultValue": "0",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "blur",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "0",
+ "description": "Enables the shadow effect.",
+ "name": "SHADOW_ENABLED",
+ "type": "define",
+ "value": "1"
+ },
+ {
+ "defaultValue": "1",
+ "description": "This property defines how much blur (radius) is applied to the shadow.\n\nThe value ranges from 0.0 (no blur) to 1.0 (full blur). By default, the property is set to \\c 0.0 (no change). The amount of full blur is affected by blurHelperBlurMultiplier.",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "shadowBlur",
+ "type": "float",
+ "value": "1"
+ },
+ {
+ "defaultValue": "1",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "shadowOpacity",
+ "type": "float",
+ "value": "1"
+ },
+ {
+ "defaultValue": "0, 0, 0, 1",
+ "name": "shadowColor",
+ "type": "color",
+ "value": "0, 0, 0, 1"
+ },
+ {
+ "defaultValue": "1",
+ "maxValue": "1.2",
+ "minValue": "0.8",
+ "name": "shadowScale",
+ "type": "float",
+ "value": "1"
+ },
+ {
+ "defaultValue": "0",
+ "maxValue": "30",
+ "minValue": "-30",
+ "name": "shadowHorizontalOffset",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "0",
+ "maxValue": "30",
+ "minValue": "-30",
+ "name": "shadowVerticalOffset",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "1",
+ "description": "Enables the mask effect.",
+ "name": "MASK_ENABLED",
+ "type": "define",
+ "value": "1"
+ },
+ {
+ "defaultValue": "",
+ "description": "Source item for the mask effect. By default the alpha channel of the source item is used for masking but this can be easily adjusted in the shader.",
+ "name": "maskSource",
+ "type": "image",
+ "value": ""
+ },
+ {
+ "defaultValue": "0",
+ "description": "This property defines a lower threshold value for the mask pixels. The mask pixels that have an alpha value below this property are used to completely mask away the corresponding pixels from the source item. The mask pixels that have a higher alpha value are used to alphablend the source item to the display.\n\nThe value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By default, the property is set to 0.0.",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "maskThresholdMin",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "0",
+ "description": "This property defines the smoothness of the mask edges near the maskThresholdMin. Setting higher spread values softens the transition from the transparent mask pixels towards opaque mask pixels by adding interpolated values between them.\n\nThe value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge). By default, the property is set to 0.0.",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "maskSpreadAtMin",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "1",
+ "description": "This property defines an upper threshold value for the mask pixels. The mask pixels that have an alpha value below this property are used to completely mask away the corresponding pixels from the source item. The mask pixels that have a higher alpha value are used to alphablend the source item to the display.\n\nThe value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By default, the property is set to 1.0.",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "maskThresholdMax",
+ "type": "float",
+ "value": "1"
+ },
+ {
+ "defaultValue": "0",
+ "description": "This property defines the smoothness of the mask edges near the maskThresholdMax. Using higher spread values softens the transition from the transparent mask pixels towards opaque mask pixels by adding interpolated values between them.\n\nThe value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge). By default, the property is set to 0.0.",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "maskSpreadAtMax",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "false",
+ "description": "This property switches the mask to the opposite side; instead of masking away the content outside maskThresholdMin and maskThresholdMax, content between them will get masked away.\n\nBy default, the property is set to false.",
+ "name": "maskInverted",
+ "type": "bool",
+ "value": "false"
+ }
+ ],
+ "type": 2,
+ "vertexCode": [
+ "out vec4 blurWeight1;",
+ "out vec2 blurWeight2;",
+ "out vec2 shadowTexCoord;",
+ "out vec4 shadowBlurWeight1;",
+ "out vec2 shadowBlurWeight2;",
+ "",
+ "float blurWeight(float v) {",
+ " return max(0.0, min(1.0, 1.0 - v * 2.0));",
+ "}",
+ "",
+ "@main",
+ "{",
+ "#if (BLUR_ENABLED)",
+ " float blurLod = sqrt(blur * (blurMax / 64.0)) * 1.2 - 0.2;",
+ " float bw1 = blurWeight(abs(blurLod - 0.1));",
+ " float bw2 = blurWeight(abs(blurLod - 0.3));",
+ " float bw3 = blurWeight(abs(blurLod - 0.5));",
+ " float bw4 = blurWeight(abs(blurLod - 0.7));",
+ " float bw5 = blurWeight(abs(blurLod - 0.9));",
+ " float bw6 = blurWeight(abs(blurLod - 1.1));",
+ "",
+ " float bsum = bw1 + bw2 + bw3 + bw4 + bw5 + bw6;",
+ " blurWeight1 = vec4(bw1 / bsum, bw2 / bsum, bw3 / bsum, bw4 / bsum);",
+ " blurWeight2 = vec2(bw5 / bsum, bw6 / bsum);",
+ "#endif",
+ "#if (SHADOW_ENABLED)",
+ " float shadowBlurLod = sqrt(shadowBlur * (blurMax / 64.0)) * 1.2 - 0.2;",
+ " float sbw1 = blurWeight(abs(shadowBlurLod - 0.1));",
+ " float sbw2 = blurWeight(abs(shadowBlurLod - 0.3));",
+ " float sbw3 = blurWeight(abs(shadowBlurLod - 0.5));",
+ " float sbw4 = blurWeight(abs(shadowBlurLod - 0.7));",
+ " float sbw5 = blurWeight(abs(shadowBlurLod - 0.9));",
+ " float sbw6 = blurWeight(abs(shadowBlurLod - 1.1));",
+ "",
+ " float sbsum = sbw1 + sbw2 + sbw3 + sbw4 + sbw5 + sbw6;",
+ " shadowBlurWeight1 = vec4(sbw1 / sbsum, sbw2 / sbsum, sbw3 / sbsum, sbw4 / sbsum);",
+ " shadowBlurWeight2 = vec2(sbw5 / sbsum, sbw6 / sbsum);",
+ "",
+ " vec2 shadowOffset = vec2(shadowHorizontalOffset / iResolution.x, shadowVerticalOffset / iResolution.y);",
+ " float invertedScale = 1.0 / shadowScale;",
+ " float s = (1.0 - invertedScale) * 0.5;",
+ " vec2 shadowCenterOffset = vec2(s);",
+ " shadowTexCoord = qt_MultiTexCoord0 - shadowOffset;",
+ " shadowTexCoord = (shadowTexCoord * invertedScale) + shadowCenterOffset;",
+ "#endif",
+ "}"
+ ],
+ "x": 105,
+ "y": 327.5
+ },
+ {
+ "description": "This node is required e.g. for FastBlur and DropShadow. It generates blurred iSourceBlur[1..6] samplers to be available for shaders.",
+ "fragmentCode": [
+ "@blursources"
+ ],
+ "name": "BlurHelper",
+ "nodeId": 3,
+ "properties": [
+ {
+ "defaultValue": "64",
+ "description": "This property defines the maximum pixel radius that blur with value 1.0 will reach.\n\nMeaningful range of this value is from 2 (subtle blur) to 64 (high blur). By default, the property is set to 32. For the most optimal performance, select as small value as you need.\n\nNote: This affects to both blur and shadow effects.",
+ "name": "BLUR_HELPER_MAX_LEVEL",
+ "type": "define",
+ "value": "64"
+ },
+ {
+ "defaultValue": "0",
+ "description": "This property defines a multiplier for extending the blur radius.\n\nThe value ranges from 0.0 (not multiplied) to inf. By default, the property is set to 0.0. Incresing the multiplier extends the blur radius, but decreases the blur quality. This is more performant option for a bigger blur radius than BLUR_HELPER_MAX_LEVEL as it doesn't increase the amount of texture lookups.\n\nNote: This affects to both blur and shadow effects.",
+ "maxValue": "2",
+ "minValue": "0",
+ "name": "blurMultiplier",
+ "type": "float",
+ "value": "0"
+ }
+ ],
+ "type": 2,
+ "x": 105,
+ "y": 188.875
+ }
+ ],
+ "settings": {
+ "headings": [
+ "// Copyright (C) 2023 The Qt Company Ltd.",
+ "// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause"
+ ]
+ },
+ "version": 1
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qml b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qml
new file mode 100644
index 0000000000..d0fdf9c1b9
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qml
@@ -0,0 +1,97 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// Created with Qt Quick Effect Maker (version 0.43), Thu Feb 16 13:48:55 2023
+
+import QtQuick
+
+Item {
+ id: rootItem
+
+ // This is the main source for the effect
+ property Item source: null
+
+ property real contrast: 0
+ property real brightness: 0
+ property real saturation: 0
+ property real colorization: 0
+ property color colorizationColor: Qt.rgba(1, 0, 0, 1)
+ property int blurMax: 32
+ property real blur: 0
+ // This property defines how much blur (radius) is applied to the shadow.
+ //
+ // The value ranges from 0.0 (no blur) to 1.0 (full blur). By default, the property is set to \c 0.0 (no change). The amount of full blur is affected by blurHelperBlurMultiplier.
+ property real shadowBlur: 1
+ property real shadowOpacity: 1
+ property color shadowColor: Qt.rgba(0, 0, 0, 1)
+ property real shadowScale: 1
+ property real shadowHorizontalOffset: 0
+ property real shadowVerticalOffset: 0
+ // Source item for the mask effect. By default the alpha channel of the source item is used for masking but this can be easily adjusted in the shader.
+ property var maskSource: null
+ // This property defines a lower threshold value for the mask pixels. The mask pixels that have an alpha value below this property are used to completely mask away the corresponding pixels from the source item. The mask pixels that have a higher alpha value are used to alphablend the source item to the display.
+ //
+ // The value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By default, the property is set to 0.0.
+ property real maskThresholdMin: 0
+ // This property defines the smoothness of the mask edges near the maskThresholdMin. Setting higher spread values softens the transition from the transparent mask pixels towards opaque mask pixels by adding interpolated values between them.
+ //
+ // The value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge). By default, the property is set to 0.0.
+ property real maskSpreadAtMin: 0
+ // This property defines an upper threshold value for the mask pixels. The mask pixels that have an alpha value below this property are used to completely mask away the corresponding pixels from the source item. The mask pixels that have a higher alpha value are used to alphablend the source item to the display.
+ //
+ // The value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By default, the property is set to 1.0.
+ property real maskThresholdMax: 1
+ // This property defines the smoothness of the mask edges near the maskThresholdMax. Using higher spread values softens the transition from the transparent mask pixels towards opaque mask pixels by adding interpolated values between them.
+ //
+ // The value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge). By default, the property is set to 0.0.
+ property real maskSpreadAtMax: 0
+ // This property switches the mask to the opposite side; instead of masking away the content outside maskThresholdMin and maskThresholdMax, content between them will get masked away.
+ //
+ // By default, the property is set to false.
+ property bool maskInverted: false
+ // This property defines a multiplier for extending the blur radius.
+ //
+ // The value ranges from 0.0 (not multiplied) to inf. By default, the property is set to 0.0. Incresing the multiplier extends the blur radius, but decreases the blur quality. This is more performant option for a bigger blur radius than BLUR_HELPER_MAX_LEVEL as it doesn't increase the amount of texture lookups.
+ //
+ // Note: This affects to both blur and shadow effects.
+ property real blurMultiplier: 0
+
+ BlurHelper {
+ id: blurHelper
+ anchors.fill: parent
+ property int blurMax: 64
+ property real blurMultiplier: rootItem.blurMultiplier
+ }
+ ShaderEffect {
+ readonly property alias iSource: rootItem.source
+ readonly property vector3d iResolution: Qt.vector3d(width, height, 1.0)
+ readonly property alias iSourceBlur1: blurHelper.blurSrc1
+ readonly property alias iSourceBlur2: blurHelper.blurSrc2
+ readonly property alias iSourceBlur3: blurHelper.blurSrc3
+ readonly property alias iSourceBlur4: blurHelper.blurSrc4
+ readonly property alias iSourceBlur5: blurHelper.blurSrc5
+ readonly property alias contrast: rootItem.contrast
+ readonly property alias brightness: rootItem.brightness
+ readonly property alias saturation: rootItem.saturation
+ readonly property alias colorization: rootItem.colorization
+ readonly property alias colorizationColor: rootItem.colorizationColor
+ readonly property alias blurMax: rootItem.blurMax
+ readonly property alias blur: rootItem.blur
+ readonly property alias shadowBlur: rootItem.shadowBlur
+ readonly property alias shadowOpacity: rootItem.shadowOpacity
+ readonly property alias shadowColor: rootItem.shadowColor
+ readonly property alias shadowScale: rootItem.shadowScale
+ readonly property alias shadowHorizontalOffset: rootItem.shadowHorizontalOffset
+ readonly property alias shadowVerticalOffset: rootItem.shadowVerticalOffset
+ readonly property alias maskSource: rootItem.maskSource
+ readonly property alias maskThresholdMin: rootItem.maskThresholdMin
+ readonly property alias maskSpreadAtMin: rootItem.maskSpreadAtMin
+ readonly property alias maskThresholdMax: rootItem.maskThresholdMax
+ readonly property alias maskSpreadAtMax: rootItem.maskSpreadAtMax
+ readonly property alias maskInverted: rootItem.maskInverted
+ readonly property alias blurMultiplier: rootItem.blurMultiplier
+
+ vertexShader: 'custommultieffect.vert.qsb'
+ fragmentShader: 'custommultieffect.frag.qsb'
+ anchors.fill: parent
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.frag.qsb b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.frag.qsb
new file mode 100644
index 0000000000..054745d277
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.frag.qsb
Binary files differ
diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.vert.qsb b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.vert.qsb
new file mode 100644
index 0000000000..6aa0eb1d4e
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.vert.qsb
Binary files differ
diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.frag.qsb b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.frag.qsb
new file mode 100644
index 0000000000..370cf501dc
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.frag.qsb
Binary files differ
diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.vert.qsb b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.vert.qsb
new file mode 100644
index 0000000000..c522c25aee
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.vert.qsb
Binary files differ
diff --git a/examples/quick/multieffect/testbed/qml/FpsItem.qml b/examples/quick/multieffect/testbed/qml/FpsItem.qml
new file mode 100644
index 0000000000..53042655a3
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/FpsItem.qml
@@ -0,0 +1,65 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ id: root
+ property int frameCounter: 0
+ property int frameCounterAvg: 0
+ property int counter: 0
+ property int fps: 0
+ property int fpsAvg: 0
+
+ width: 200 * dp
+ height: Math.floor(42 * dp)
+
+ Image {
+ id: spinnerImage
+ anchors.verticalCenter: parent.verticalCenter
+ x: 4 * dp
+ width: 32 * dp
+ height: width
+ source: "images/spinner.png"
+ NumberAnimation on rotation {
+ from:0
+ to: 360
+ duration: 800
+ loops: Animation.Infinite
+ }
+ onRotationChanged: frameCounter++;
+ }
+ Image {
+ anchors.centerIn: spinnerImage
+ width: 18 * dp
+ height: width
+ source: settings.animateMovement ? "images/play.png" : "images/pause.png"
+ opacity: 0.5
+ }
+
+ Text {
+ anchors.left: spinnerImage.right
+ anchors.leftMargin: 8 * dp
+ anchors.verticalCenter: spinnerImage.verticalCenter
+ color: "#c0c0c0"
+ font.pixelSize: 22 * dp
+ text: "Ø " + root.fpsAvg + " | " + root.fps + " fps"
+ }
+
+ Timer {
+ interval: 2000
+ repeat: true
+ running: true
+ onTriggered: {
+ frameCounterAvg += frameCounter;
+ root.fps = Math.ceil(frameCounter / 2);
+ counter++;
+ frameCounter = 0;
+ if (counter >= 3) {
+ root.fpsAvg = Math.ceil(frameCounterAvg / (2 * counter));
+ frameCounterAvg = 0;
+ counter = 0;
+ }
+ }
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/ResetSettingsOverlay.qml b/examples/quick/multieffect/testbed/qml/ResetSettingsOverlay.qml
new file mode 100644
index 0000000000..3931548124
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/ResetSettingsOverlay.qml
@@ -0,0 +1,170 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ id: rootItem
+
+ property real showAnimationProgress: 0
+ property int itemWidth: 256 * dp
+ property int lineSpacing1: 0
+ property int lineWidth1: 20 * dp
+ property int lineSpacing2: 10 * dp
+ property int lineWidth2: 10 * dp
+
+ readonly property int cx: itemWidth / 2
+ readonly property int cy: itemWidth / 2
+
+ signal animationFinished
+
+ function startShow() {
+ hideAnimationItem.stop();
+ showAnimationItem.restart();
+ }
+ function stopShow() {
+ showAnimationItem.stop();
+ hideAnimationItem.restart();
+ }
+
+ function ringProgress() {
+ return showAnimationProgress * (2 * Math.PI) - (Math.PI / 2);
+ }
+
+ function useLargeArcFunc() {
+ return (ringProgress() > Math.PI / 2);
+ }
+
+ anchors.fill: parent
+ visible: opacity
+ opacity: 0
+
+ SequentialAnimation {
+ id: showAnimationItem
+ PauseAnimation {
+ duration: 400
+ }
+ ScriptAction {
+ script: {
+ rootItem.showAnimationProgress = 0;
+ }
+ }
+ NumberAnimation {
+ target: rootItem
+ property: "opacity"
+ to: 1
+ duration: 400
+ easing.type: Easing.InOutQuad
+ }
+ NumberAnimation {
+ target: rootItem
+ property: "showAnimationProgress"
+ to: 1
+ duration: 2000
+ easing.type: Easing.InOutQuad
+ }
+ ScriptAction {
+ script: rootItem.animationFinished();
+ }
+ }
+ SequentialAnimation {
+ id: hideAnimationItem
+ NumberAnimation {
+ target: rootItem
+ property: "opacity"
+ to: 0
+ duration: 400
+ easing.type: Easing.InOutQuad
+ }
+ ScriptAction {
+ script: {
+ rootItem.showAnimationProgress = 0;
+ }
+ }
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ color: "#000000"
+ opacity: 0.6
+ }
+
+ Shape {
+ id: shapeItem
+ x: (parent.width / 2) - (itemWidth / 2)
+ y: (parent.height / 2) - (itemWidth / 2)
+ opacity: rootItem.showAnimationProgress
+ width: itemWidth
+ height: itemWidth
+ rotation: rootItem.showAnimationProgress * 360
+ ShapePath {
+ strokeColor: "#606060"
+ fillColor: "transparent"
+ strokeWidth: lineWidth1
+ capStyle: ShapePath.RoundCap
+ PathMove {
+ x: itemWidth / 2
+ y: pathArc1.spacing
+ }
+ PathArc {
+ id: pathArc1
+ property real spacing: lineSpacing1 + lineWidth1
+ x: cx + ((itemWidth / 2 - spacing) * Math.cos(ringProgress()))
+ y: cy + ((itemWidth / 2 - spacing) * Math.sin(ringProgress()))
+ radiusX: itemWidth / 2 - spacing
+ radiusY: itemWidth / 2 - spacing
+ useLargeArc: useLargeArcFunc()
+ }
+ }
+
+ ShapePath {
+ strokeColor: "#808080"
+ fillColor: "transparent"
+ strokeWidth: lineWidth2
+ capStyle: ShapePath.RoundCap
+ PathMove {
+ x: itemWidth / 2
+ y: pathArc2.spacing
+ }
+ PathArc {
+ id: pathArc2
+ property real spacing: lineSpacing2 + lineWidth2
+ x: cx + ((itemWidth / 2 - spacing) * Math.cos(ringProgress()))
+ y: cy + ((itemWidth / 2 - spacing) * Math.sin(ringProgress()))
+ radiusX: itemWidth / 2 - spacing
+ radiusY: itemWidth / 2 - spacing
+ useLargeArc: useLargeArcFunc()
+ }
+ }
+ }
+ Text {
+ id: textItem
+ anchors.centerIn: shapeItem
+ font.pixelSize: 32 * dp
+ color: "#d0d0d0"
+ text: showAnimationProgress < 1 ? qsTr("Reset to default settings?") : qsTr("Reseted!")
+ Behavior on text {
+ SequentialAnimation {
+ NumberAnimation {
+ target: textItem
+ properties: "scale, opacity"
+ to: 0
+ duration: 200
+ easing.type: Easing.InOutQuad
+ }
+ PropertyAction {
+ target: textItem
+ property: "text"
+ }
+ NumberAnimation {
+ target: textItem
+ properties: "scale, opacity"
+ to: 1.0
+ duration: 200
+ easing.type: Easing.InOutQuad
+ }
+ }
+ }
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/Settings.qml b/examples/quick/multieffect/testbed/qml/Settings.qml
new file mode 100644
index 0000000000..05947c2ab4
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/Settings.qml
@@ -0,0 +1,92 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+QtObject {
+ id: rootItem
+
+ // Emitted when settings are reseted to default
+ signal reseted
+
+ // When adding settings here remember to add them also into reset()
+
+ // *** General settings - No UI for these ***
+ // Change to false to not show settings view at all
+ property bool showSettingsView: true
+ property real settingsViewWidth: 100 + 150 * dp
+ property bool animateMovement: true
+ property bool showShader: false
+ property bool showItemSize: false
+ property bool showCustomMultiEffect: false
+
+ property bool autoPaddingEnabled: true
+ property rect paddingRect: Qt.rect(0, 0, 0, 0)
+
+ property bool brightnessEnabled: true
+ property real brightness: 0.0
+ property bool contrastEnabled: true
+ property real contrast: 0.0
+ property bool saturationEnabled: true
+ property real saturation: 0.0
+ property bool colorizationEnabled: true
+ property color colorizationColor: Qt.rgba(1.0, 0.0, 0.0, 1.0)
+ property real colorization: 0.0
+
+ property bool blurEnabled: true
+ property real blur: 0.0
+ property int blurMax: 32
+ property real blurMultiplier: 0.0
+
+ property bool shadowEnabled: true
+ property real shadowOpacity: 1.0
+ property real shadowBlur: 1.0
+ property real shadowHorizontalOffset: 10
+ property real shadowVerticalOffset: 5
+ property color shadowColor: Qt.rgba(0.0, 0.0, 0.0, 1.0)
+ property real shadowScale: 1.0
+
+ property bool maskEnabled: true
+ property bool maskInverted: false
+ property real maskThresholdMin: 0.0
+ property real maskSpreadAtMin: 0.0
+ property real maskThresholdMax: 1.0
+ property real maskSpreadAtMax: 0.0
+
+ function reset() {
+ autoPaddingEnabled = defaultSettings.autoPaddingEnabled;
+ paddingRect = defaultSettings.paddingRect;
+
+ brightnessEnabled = defaultSettings.brightnessEnabled;
+ brightness = defaultSettings.brightness;
+ contrastEnabled = defaultSettings.contrastEnabled;
+ contrast = defaultSettings.contrast;
+ saturationEnabled = defaultSettings.saturationEnabled;
+ saturation = defaultSettings.saturation;
+ colorizationEnabled = defaultSettings.colorizationEnabled;
+ colorizationColor = defaultSettings.colorizationColor;
+ colorization = defaultSettings.colorization;
+
+ blurEnabled = defaultSettings.blurEnabled;
+ blur = defaultSettings.blur;
+ blurMax = defaultSettings.blurMax;
+ blurMultiplier = defaultSettings.blurMultiplier;
+
+ shadowEnabled = defaultSettings.shadowEnabled;
+ shadowOpacity = defaultSettings.shadowOpacity;
+ shadowBlur = defaultSettings.shadowBlur;
+ shadowHorizontalOffset = defaultSettings.shadowHorizontalOffset;
+ shadowVerticalOffset = defaultSettings.shadowVerticalOffset;
+ shadowColor = defaultSettings.shadowColor;
+ shadowScale = defaultSettings.shadowScale;
+
+ maskEnabled = defaultSettings.maskEnabled;
+ maskInverted = defaultSettings.maskInverted;
+ maskThresholdMin = defaultSettings.maskThresholdMin;
+ maskSpreadAtMin = defaultSettings.maskSpreadAtMin;
+ maskThresholdMax = defaultSettings.maskThresholdMax;
+ maskSpreadAtMax = defaultSettings.maskSpreadAtMax;
+
+ rootItem.reseted();
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/SettingsComponentCheckBox.qml b/examples/quick/multieffect/testbed/qml/SettingsComponentCheckBox.qml
new file mode 100644
index 0000000000..ef1cb27d89
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/SettingsComponentCheckBox.qml
@@ -0,0 +1,35 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+Column {
+ id: rootItem
+
+ property alias text: textItem.text
+ property alias checked: checkBox.checked
+
+ signal toggled
+
+ Material.theme: Material.Dark
+ Material.accent: Material.LightGreen
+ spacing: -12
+
+ Row {
+ CheckBox {
+ id: checkBox
+ checked: true
+ onToggled: {
+ rootItem.toggled();
+ }
+ }
+ Text {
+ id: textItem
+ anchors.verticalCenter: parent.verticalCenter
+ color: "#f0f0f0"
+ font.pixelSize: 14 * dp
+ }
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/SettingsComponentColorSelector.qml b/examples/quick/multieffect/testbed/qml/SettingsComponentColorSelector.qml
new file mode 100644
index 0000000000..c158f84a7d
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/SettingsComponentColorSelector.qml
@@ -0,0 +1,135 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+Column {
+ id: rootItem
+
+ property alias text: textItem.text
+ readonly property color value: Qt.rgba(colorRed, colorGreen, colorBlue, 1.0)
+ readonly property real colorRed: sliderRed.value
+ readonly property real colorGreen: sliderGreen.value
+ readonly property real colorBlue: sliderBlue.value
+ readonly property real itemWidth: (rootItem.width / 3) - itemMargin
+ readonly property real itemMargin: 4
+ readonly property real colorBarHeight: 4
+
+ // Use this to set the initial values
+ function setValues(r, g, b) {
+ sliderRed.value = r;
+ sliderGreen.value = g;
+ sliderBlue.value = b;
+ }
+
+ Material.theme: Material.Dark
+ Material.accent: Material.LightGreen
+ spacing: -12
+ width: 200
+
+ Text {
+ id: textItem
+ anchors.horizontalCenter: parent.horizontalCenter
+ color: "#f0f0f0"
+ font.pixelSize: 14 * dp
+ }
+ Item {
+ width: 1
+ height: 36
+ }
+
+ Row {
+ x: itemMargin / 2
+ spacing: itemMargin
+ opacity: rootItem.enabled ? 1.0 : 0.2
+ Column {
+ Rectangle {
+ width: rootItem.itemWidth
+ height: colorBarHeight
+ border.width: 1
+ border.color: "#202020"
+ gradient: Gradient {
+ orientation: Gradient.Horizontal
+ GradientStop { position: 0.0; color: Qt.rgba(0.0, colorGreen, colorBlue, 1.0) }
+ GradientStop { position: 1.0; color: Qt.rgba(1.0, colorGreen, colorBlue, 1.0) }
+ }
+ Text {
+ id: textItemRed
+ anchors.centerIn: parent
+ color: "#f0f0f0"
+ style: Text.Outline
+ styleColor: "#000000"
+ text: "R: " + Math.ceil(sliderRed.value * 255)
+ font.pixelSize: 14 * dp
+ }
+ }
+ Slider {
+ id: sliderRed
+ width: rootItem.itemWidth
+ value: 0
+ from: 0
+ to: 1
+ }
+ }
+ Column {
+ Rectangle {
+ width: rootItem.itemWidth
+ height: colorBarHeight
+ border.width: 1
+ border.color: "#202020"
+ gradient: Gradient {
+ orientation: Gradient.Horizontal
+ GradientStop { position: 0.0; color: Qt.rgba(colorRed, 0.0, colorBlue, 1.0) }
+ GradientStop { position: 1.0; color: Qt.rgba(colorRed, 1.0, colorBlue, 1.0) }
+ }
+ Text {
+ id: textItemGreen
+ anchors.centerIn: parent
+ color: "#f0f0f0"
+ style: Text.Outline
+ styleColor: "#000000"
+ text: "G: " + Math.ceil(sliderGreen.value * 255)
+ font.pixelSize: 14 * dp
+ }
+ }
+ Slider {
+ id: sliderGreen
+ width: rootItem.itemWidth
+ value: 0
+ from: 0
+ to: 1
+ }
+ }
+ Column {
+ Rectangle {
+ width: rootItem.itemWidth
+ height: colorBarHeight
+ border.width: 1
+ border.color: "#202020"
+ gradient: Gradient {
+ orientation: Gradient.Horizontal
+ GradientStop { position: 0.0; color: Qt.rgba(colorRed, colorGreen, 0.0, 1.0) }
+ GradientStop { position: 1.0; color: Qt.rgba(colorRed, colorGreen, 1.0, 1.0) }
+ }
+ Text {
+ id: textItemBlue
+ anchors.centerIn: parent
+ color: "#f0f0f0"
+ style: Text.Outline
+ styleColor: "#000000"
+ text: "B: " + Math.ceil(sliderBlue.value * 255)
+ font.pixelSize: 14 * dp
+ }
+ }
+ Slider {
+ id: sliderBlue
+ width: rootItem.itemWidth
+ value: 0
+ from: 0
+ to: 1
+ }
+ }
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/SettingsComponentSlider.qml b/examples/quick/multieffect/testbed/qml/SettingsComponentSlider.qml
new file mode 100644
index 0000000000..ed04584e44
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/SettingsComponentSlider.qml
@@ -0,0 +1,54 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+Column {
+ id: rootItem
+
+ property alias text: textItem.text
+ property alias value: slider.value
+ property alias from: slider.from
+ property alias to: slider.to
+ property alias checked: checkBox.checked
+ property alias stepSize: slider.stepSize
+ property bool showCheckbox: false
+
+ signal toggled
+ signal moved
+
+ Material.theme: Material.Dark
+ Material.accent: Material.LightGreen
+ spacing: -12
+
+ Text {
+ id: textItem
+ anchors.horizontalCenter: parent.horizontalCenter
+ color: "#f0f0f0"
+ font.pixelSize: 14 * dp
+ }
+
+ Row {
+ CheckBox {
+ id: checkBox
+ visible: rootItem.showCheckbox
+ checked: true
+ onToggled: {
+ rootItem.toggled();
+ }
+ }
+ Slider {
+ id: slider
+ property real sliderWidth: settings.settingsViewWidth - 50
+ width: rootItem.showCheckbox ? sliderWidth : sliderWidth + checkBox.width
+ value: 50
+ from: 0
+ to: 800
+ onMoved: {
+ rootItem.moved();
+ }
+ }
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/SettingsComponentView.qml b/examples/quick/multieffect/testbed/qml/SettingsComponentView.qml
new file mode 100644
index 0000000000..08eb0ad300
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/SettingsComponentView.qml
@@ -0,0 +1,147 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Column {
+ id: rootItem
+
+ property alias text: textItem.text
+ property bool show: true
+
+ default property alias contents: contentItem.children
+ property real showHideAnimationSpeed: 400
+
+ width: settings.settingsViewWidth
+
+ Component.onCompleted: {
+ // Set initial open state
+ contentItem.visible = rootItem.show;
+ contentItem.opacity = rootItem.show;
+ contentItemArea.height = rootItem.show ? contentItem.height : 0;
+ }
+
+ Item {
+ id: lightsSettings
+ width: parent.width
+ height: 30
+ Rectangle {
+ anchors.fill: parent
+ color: "#404040"
+ border.width: 1
+ border.color: "#808080"
+ opacity: 0.4
+ }
+ Image {
+ x: 8
+ source: "images/arrow.png"
+ anchors.verticalCenter: parent.verticalCenter
+ rotation: rootItem.show ? 90 : 0
+ Behavior on rotation {
+ NumberAnimation {
+ duration: showHideAnimationSpeed
+ easing.type: Easing.InOutQuad
+ }
+ }
+ }
+
+ Text {
+ id: textItem
+ x: 30
+ anchors.verticalCenter: parent.verticalCenter
+ color: "#f0f0f0"
+ font.bold: true
+ font.pixelSize: 16 * dp
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ rootItem.show = !rootItem.show;
+ if (rootItem.show) {
+ hideAnimation.stop();
+ showAnimation.start();
+ } else {
+ showAnimation.stop();
+ hideAnimation.start();
+ }
+
+ }
+ }
+ }
+
+ Item {
+ width: 1
+ height: 5
+ }
+
+ SequentialAnimation {
+ id: showAnimation
+ ScriptAction {
+ script: contentItem.visible = true;
+ }
+ ParallelAnimation {
+ NumberAnimation {
+ target: contentItemArea
+ property: "height"
+ to: contentItem.height
+ duration: showHideAnimationSpeed
+ easing.type: Easing.InOutQuad
+ }
+ SequentialAnimation {
+ PauseAnimation {
+ duration: showHideAnimationSpeed / 2
+ }
+ NumberAnimation {
+ target: contentItem
+ property: "opacity"
+ to: 1.0
+ duration: showHideAnimationSpeed / 2
+ easing.type: Easing.InOutQuad
+ }
+ }
+ }
+ }
+
+ SequentialAnimation {
+ id: hideAnimation
+ ParallelAnimation {
+ NumberAnimation {
+ target: contentItemArea
+ property: "height"
+ to: 0
+ duration: showHideAnimationSpeed
+ easing.type: Easing.InOutQuad
+ }
+ SequentialAnimation {
+ NumberAnimation {
+ target: contentItem
+ property: "opacity"
+ to: 0
+ duration: showHideAnimationSpeed / 2
+ easing.type: Easing.InOutQuad
+ }
+ PauseAnimation {
+ duration: showHideAnimationSpeed / 2
+ }
+ }
+ }
+ ScriptAction {
+ script: contentItem.visible = false;
+ }
+ }
+
+ Item {
+ id: contentItemArea
+ width: parent.width - 10
+ x: 5
+ Column {
+ id: contentItem
+ spacing: -10
+ }
+ }
+
+ Item {
+ width: 1
+ height: 5
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/SettingsView.qml b/examples/quick/multieffect/testbed/qml/SettingsView.qml
new file mode 100644
index 0000000000..789f7685c2
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/SettingsView.qml
@@ -0,0 +1,407 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls.Material
+
+Item {
+ id: rootItem
+
+ property bool show: true
+ property real showAnimation: show ? 1 : 0
+
+ function resetSettings() {
+ colorizationColorSelector.setValues(defaultSettings.colorizationColor.r,
+ defaultSettings.colorizationColor.g,
+ defaultSettings.colorizationColor.b);
+ shadowColorSelector.setValues(defaultSettings.shadowColor.r,
+ defaultSettings.shadowColor.g,
+ defaultSettings.shadowColor.b);
+ }
+
+ Material.theme: Material.Dark
+ Material.accent: Material.LightGreen
+ width: settings.settingsViewWidth
+ x: -(width + 30) * (1 - showAnimation) + 20
+
+ Behavior on showAnimation {
+ NumberAnimation {
+ duration: 400
+ easing.type: Easing.InOutQuad
+ }
+ }
+
+ // Open/close button
+ Item {
+ width: 30 * dp
+ height: 30 * dp
+ anchors.left: parent.right
+ anchors.leftMargin: 20
+ anchors.top: parent.top
+ anchors.topMargin: -10
+ Rectangle {
+ anchors.fill: parent
+ color: "#404040"
+ opacity: 0.4
+ border.width: 1
+ border.color: "#808080"
+ }
+
+ Image {
+ anchors.centerIn: parent
+ source: "images/arrow.png"
+ rotation: rootItem.showAnimation * 180
+ }
+ MouseArea {
+ anchors.fill: parent
+ anchors.margins: -30 * dp
+ onClicked: {
+ rootItem.show = !rootItem.show;
+ }
+ }
+ }
+
+ // Background
+ Rectangle {
+ anchors.fill: scrollView
+ opacity: showAnimation ? 0.6 : 0
+ visible: opacity
+ anchors.margins: -10
+ color: "#202020"
+ border.color: "#808080"
+ border.width: 1
+ }
+
+ ScrollView {
+ id: scrollView
+ anchors.fill: parent
+ ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
+ ScrollBar.vertical.interactive: false
+ clip: true
+ Column {
+ id: settingsArea
+ anchors.fill: parent
+ opacity: showAnimation
+ visible: opacity
+
+ Item {
+ width: 1
+ height: 20 * dp
+ }
+ Image {
+ anchors.horizontalCenter: parent.horizontalCenter
+ fillMode: Image.PreserveAspectFit
+ width: parent.width * 0.8
+ height: width * 0.25
+ source: "images/Built_with_Qt_RGB_logo.png"
+ }
+ Item {
+ width: 1
+ height: 28 * dp
+ }
+
+ SettingsComponentView {
+ id: settingsViewGeneral
+ text: qsTr("General")
+ show: true
+ SettingsComponentCheckBox {
+ text: "Show Shaders"
+ checked: settings.showShader
+ onToggled: {
+ settings.showShader = checked;
+ }
+ }
+ SettingsComponentCheckBox {
+ text: "Show Item Size"
+ checked: settings.showItemSize
+ onToggled: {
+ settings.showItemSize = checked;
+ }
+ }
+ SettingsComponentCheckBox {
+ text: "AutoPadding Enabled"
+ checked: settings.autoPaddingEnabled
+ onToggled: {
+ settings.autoPaddingEnabled = checked;
+ }
+ }
+ SettingsComponentCheckBox {
+ text: "Show Custom MultiEffect"
+ checked: settings.showCustomMultiEffect
+ onToggled: {
+ settings.showCustomMultiEffect = checked;
+ }
+ }
+ Item {
+ width: 1
+ height: 20 * dp
+ }
+ SettingsComponentSlider {
+ text: qsTr("Padding Left") + ": " + value.toFixed(0)
+ value: settings.paddingRect.x
+ from: 0.0
+ to: 300
+ onMoved: {
+ settings.paddingRect.x = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Padding Right") + ": " + value.toFixed(0)
+ value: settings.paddingRect.width
+ from: 0.0
+ to: 300
+ onMoved: {
+ settings.paddingRect.width = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Padding Top") + ": " + value.toFixed(0)
+ value: settings.paddingRect.y
+ from: 0.0
+ to: 300
+ onMoved: {
+ settings.paddingRect.y = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Padding Bottom") + ": " + value.toFixed(0)
+ value: settings.paddingRect.height
+ from: 0.0
+ to: 300
+ onMoved: {
+ settings.paddingRect.height = value;
+ }
+ }
+ }
+
+ SettingsComponentView {
+ text: qsTr("Color")
+ show: false
+ SettingsComponentSlider {
+ text: qsTr("Brightness") + ": " + value.toFixed(3)
+ showCheckbox: true
+ checked: settings.brightnessEnabled
+ onToggled: {
+ settings.brightnessEnabled = checked;
+ }
+ value: settings.brightness
+ from: -1
+ to: 1
+ onMoved: {
+ settings.brightness = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Contrast") + ": " + value.toFixed(3)
+ showCheckbox: true
+ checked: settings.contrastEnabled
+ onToggled: {
+ settings.contrastEnabled = checked;
+ }
+ value: settings.contrast
+ from: -1
+ to: 1
+ onMoved: {
+ settings.contrast = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Saturation") + ": " + value.toFixed(3)
+ showCheckbox: true
+ checked: settings.saturationEnabled
+ onToggled: {
+ settings.saturationEnabled = checked;
+ }
+ value: settings.saturation
+ from: -1
+ to: 1
+ onMoved: {
+ settings.saturation = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Colorization") + ": " + value.toFixed(3)
+ showCheckbox: true
+ checked: settings.colorizationEnabled
+ onToggled: {
+ settings.colorizationEnabled = checked;
+ }
+ value: settings.colorization
+ from: 0
+ to: 1
+ onMoved: {
+ settings.colorization = value;
+ }
+ }
+ SettingsComponentColorSelector {
+ id: colorizationColorSelector
+ text: qsTr("Colorization Color")
+ width: settings.settingsViewWidth - 10
+ onValueChanged: {
+ settings.colorizationColor = value;
+ }
+ }
+ }
+ SettingsComponentView {
+ text: qsTr("Blur")
+ show: false
+
+ SettingsComponentSlider {
+ text: qsTr("Blur") + ": " + value.toFixed(3)
+ showCheckbox: true
+ checked: settings.blurEnabled
+ onToggled: {
+ settings.blurEnabled = checked;
+ }
+ value: settings.blur
+ from: 0
+ to: 1.0
+ onMoved: {
+ settings.blur = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Blur Multiplier") + ": " + value.toFixed(3)
+ value: settings.blurMultiplier
+ from: 0.0
+ to: 2.0
+ onMoved: {
+ settings.blurMultiplier = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Blur Max") + ": " + value.toFixed(0)
+ value: settings.blurMax
+ from: 0
+ to: 64
+ stepSize: 2
+ onMoved: {
+ settings.blurMax = value;
+ }
+ }
+
+ }
+ SettingsComponentView {
+ text: qsTr("Shadow")
+ show: false
+
+ SettingsComponentSlider {
+ text: qsTr("Shadow") + ": " + value.toFixed(3)
+ showCheckbox: true
+ checked: settings.shadowEnabled
+ onToggled: {
+ settings.shadowEnabled = checked;
+ }
+ value: settings.shadowOpacity
+ from: 0
+ to: 1.0
+ onMoved: {
+ settings.shadowOpacity = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Shadow Blur") + ": " + value.toFixed(3)
+ value: settings.shadowBlur
+ from: 0.0
+ to: 1.0
+ onMoved: {
+ settings.shadowBlur = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Shadow HorizontalOffset") + ": " + value.toFixed(1)
+ value: settings.shadowHorizontalOffset
+ from: -20.0
+ to: 20.0
+ onMoved: {
+ settings.shadowHorizontalOffset = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Shadow VerticalOffset") + ": " + value.toFixed(1)
+ value: settings.shadowVerticalOffset
+ from: -20.0
+ to: 20.0
+ onMoved: {
+ settings.shadowVerticalOffset = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Shadow Scale") + ": " + value.toFixed(3)
+ value: settings.shadowScale
+ from: 0.8
+ to: 1.2
+ onMoved: {
+ settings.shadowScale = value;
+ }
+ }
+ SettingsComponentColorSelector {
+ id: shadowColorSelector
+ text: qsTr("Shadow Color")
+ width: settings.settingsViewWidth - 10
+ onValueChanged: {
+ settings.shadowColor = value;
+ }
+ }
+ }
+ SettingsComponentView {
+ text: qsTr("Mask")
+ show: false
+ SettingsComponentCheckBox {
+ text: "Mask Enabled"
+ checked: settings.maskEnabled
+ onToggled: {
+ settings.maskEnabled = checked;
+ }
+ }
+ SettingsComponentCheckBox {
+ text: "Mask Inverted"
+ checked: settings.maskInverted
+ onToggled: {
+ settings.maskInverted = checked;
+ }
+ }
+ Item {
+ width: 1
+ height: 20 * dp
+ }
+ SettingsComponentSlider {
+ text: qsTr("Mask Threshold Min") + ": " + value.toFixed(3)
+ value: settings.maskThresholdMin
+ from: 0.0
+ to: 1.0
+ onMoved: {
+ settings.maskThresholdMin = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Mask Spread At Min") + ": " + value.toFixed(3)
+ value: settings.maskSpreadAtMin
+ from: 0.0
+ to: 1.0
+ onMoved: {
+ settings.maskSpreadAtMin = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Mask Threshold Max") + ": " + value.toFixed(3)
+ value: settings.maskThresholdMax
+ from: 0.0
+ to: 1.0
+ onMoved: {
+ settings.maskThresholdMax = value;
+ }
+ }
+ SettingsComponentSlider {
+ text: qsTr("Mask Spread At Max") + ": " + value.toFixed(3)
+ value: settings.maskSpreadAtMax
+ from: 0.0
+ to: 1.0
+ onMoved: {
+ settings.maskSpreadAtMax = value;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/ShaderView.qml b/examples/quick/multieffect/testbed/qml/ShaderView.qml
new file mode 100644
index 0000000000..fc89f5aec5
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/ShaderView.qml
@@ -0,0 +1,29 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ id: rootItem
+
+ property string text
+
+ width: textItem.width
+ height: textItem.height
+
+ Rectangle {
+ anchors.fill: textItem
+ anchors.margins: -10
+ z: -1
+ color: "#000000"
+ opacity: 0.6
+ border.color: "#ffffff"
+ border.width: 2
+ }
+ Text {
+ id: textItem
+ text: rootItem.text
+ font.pixelSize: 16
+ color: "#ffffff"
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/TestMaskItem.qml b/examples/quick/multieffect/testbed/qml/TestMaskItem.qml
new file mode 100644
index 0000000000..4ba5950b96
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/TestMaskItem.qml
@@ -0,0 +1,17 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ id: rootItem
+ layer.enabled: true
+ visible: false
+ Rectangle {
+ anchors.fill: parent
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "#00000000" }
+ GradientStop { position: 1.0; color: "#ffffffff" }
+ }
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/TestSourceItem.qml b/examples/quick/multieffect/testbed/qml/TestSourceItem.qml
new file mode 100644
index 0000000000..ca5ca387ec
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/TestSourceItem.qml
@@ -0,0 +1,113 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+Item {
+ id: rootItem
+
+ Material.theme: Material.Dark
+ Material.accent: Material.LightGreen
+
+ Image {
+ anchors.centerIn: parent
+ width: parent.width / 2
+ height: parent.height / 2
+ sourceSize.width: width
+ sourceSize.height: height
+ source: "images/Built_with_Qt.png"
+ fillMode: Image.PreserveAspectFit
+ SequentialAnimation on anchors.verticalCenterOffset {
+ loops: Animation.Infinite
+ paused: !settings.animateMovement
+ NumberAnimation {
+ to: 50
+ duration: 2000
+ easing.type: Easing.InOutQuad
+ }
+ NumberAnimation {
+ to: -50
+ duration: 2000
+ easing.type: Easing.InOutQuad
+ }
+ }
+ }
+ Text {
+ font.pixelSize: 50
+ font.bold: true
+ text: "TESTING"
+ color: "white"
+ }
+ Image {
+ source: "images/warning.png"
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ mipmap: true
+ SequentialAnimation on scale {
+ loops: Animation.Infinite
+ paused: !settings.animateMovement
+ NumberAnimation {
+ to: 0.4
+ duration: 3000
+ easing.type: Easing.InOutQuad
+ }
+ NumberAnimation {
+ to: 1.0
+ duration: 1000
+ easing.type: Easing.OutBack
+ }
+ }
+
+ }
+ Rectangle {
+ width: parent.width * 0.2
+ height: width
+ anchors.top: parent.top
+ anchors.topMargin: width * 0.2
+ anchors.right: parent.right
+ anchors.rightMargin: width * 0.2
+ color: "#808080"
+ border.color: "#f0f0f0"
+ border.width: 2
+ radius: width * 0.1
+ SequentialAnimation on opacity {
+ paused: !settings.animateMovement
+ loops: Animation.Infinite
+ NumberAnimation {
+ to: 0.0
+ duration: 2000
+ easing.type: Easing.InOutQuad
+ }
+ NumberAnimation {
+ to: 1.0
+ duration: 2000
+ easing.type: Easing.InOutQuad
+ }
+ }
+
+ NumberAnimation on rotation {
+ paused: !settings.animateMovement
+ loops: Animation.Infinite
+ from: 0
+ to: 360
+ duration: 10000
+ }
+ }
+
+ Column {
+ anchors.right: parent.right
+ anchors.rightMargin: width * 0.2
+ anchors.bottom: parent.bottom
+ Button {
+ id: button
+ text: "Controls Button"
+ Material.theme: Material.Light
+ }
+ Slider {
+ width: button.width
+ }
+ }
+}
+
diff --git a/examples/quick/multieffect/testbed/qml/WarningsItem.qml b/examples/quick/multieffect/testbed/qml/WarningsItem.qml
new file mode 100644
index 0000000000..201332c87b
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/WarningsItem.qml
@@ -0,0 +1,66 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ id: rootItem
+
+ property string text
+
+ function show() {
+ showAnimation.restart();
+ }
+
+ height: 42
+ width: warningIcon.width + textItem.width + 40
+ opacity: 0
+
+ Rectangle {
+ anchors.fill: parent
+ color: "#000000"
+ opacity: 0.6
+ }
+
+ SequentialAnimation {
+ id: showAnimation
+ NumberAnimation {
+ target: rootItem
+ property: "opacity"
+ to: 1
+ duration: 400
+ easing.type: Easing.InOutQuad
+ }
+ PauseAnimation {
+ duration: 2000
+ }
+ NumberAnimation {
+ target: rootItem
+ property: "opacity"
+ to: 0
+ duration: 400
+ easing.type: Easing.InOutQuad
+ }
+ }
+
+ Image {
+ id: warningIcon
+ anchors.verticalCenter: parent.verticalCenter
+ x: 8
+ source: "images/warning.png"
+ mipmap: true
+ width: 24
+ height: width
+ }
+
+ Text {
+ id: textItem
+ anchors.left: warningIcon.right
+ anchors.leftMargin: 8
+ anchors.verticalCenter: parent.verticalCenter
+ color: "#ffffff"
+ font.pixelSize: 16
+ text: rootItem.text
+ }
+
+}
diff --git a/examples/quick/multieffect/testbed/qml/WarningsView.qml b/examples/quick/multieffect/testbed/qml/WarningsView.qml
new file mode 100644
index 0000000000..c25ad5fccd
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/WarningsView.qml
@@ -0,0 +1,38 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ id: rootItem
+
+ function showShaderWarning() {
+ shaderWarning.show();
+ }
+ function showSizeWarning() {
+ sizeWarning.show();
+ }
+
+ height: 60
+
+ WarningsItem {
+ id: shaderWarning
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ text: qsTr("Shader changed!")
+ }
+ WarningsItem {
+ id: sizeWarning
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: shaderWarning.right
+ anchors.leftMargin: 16
+ text: qsTr("Item resized!")
+ }
+ WarningsItem {
+ id: customEffectWarning
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ text: qsTr("<b>Note:</b> Custom MultiEffect doesn't support all the MultiEffect features, like paddings and disabling effects.")
+ opacity: settings.showCustomMultiEffect
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/images/Built_with_Qt.png b/examples/quick/multieffect/testbed/qml/images/Built_with_Qt.png
new file mode 100644
index 0000000000..e612481510
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/images/Built_with_Qt.png
Binary files differ
diff --git a/examples/quick/multieffect/testbed/qml/images/Built_with_Qt_RGB_logo.png b/examples/quick/multieffect/testbed/qml/images/Built_with_Qt_RGB_logo.png
new file mode 100644
index 0000000000..8d2dfc17ec
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/images/Built_with_Qt_RGB_logo.png
Binary files differ
diff --git a/examples/quick/multieffect/testbed/qml/images/arrow.png b/examples/quick/multieffect/testbed/qml/images/arrow.png
new file mode 100644
index 0000000000..067bec4509
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/images/arrow.png
Binary files differ
diff --git a/examples/quick/multieffect/testbed/qml/images/pause.png b/examples/quick/multieffect/testbed/qml/images/pause.png
new file mode 100644
index 0000000000..ef6e0ac26a
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/images/pause.png
Binary files differ
diff --git a/examples/quick/multieffect/testbed/qml/images/play.png b/examples/quick/multieffect/testbed/qml/images/play.png
new file mode 100644
index 0000000000..4cd0f78184
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/images/play.png
Binary files differ
diff --git a/examples/quick/multieffect/testbed/qml/images/spinner.png b/examples/quick/multieffect/testbed/qml/images/spinner.png
new file mode 100644
index 0000000000..3ff86d53ac
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/images/spinner.png
Binary files differ
diff --git a/examples/quick/multieffect/testbed/qml/images/warning.png b/examples/quick/multieffect/testbed/qml/images/warning.png
new file mode 100644
index 0000000000..08d74b8b83
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/images/warning.png
Binary files differ
diff --git a/examples/quick/multieffect/testbed/qml/main.qml b/examples/quick/multieffect/testbed/qml/main.qml
new file mode 100644
index 0000000000..8fc761169e
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/main.qml
@@ -0,0 +1,186 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Effects
+import "CustomMultiEffect"
+
+Rectangle {
+ id: mainWindow
+
+ // Multiplier for resolution independency
+ readonly property real dp: 0.2 + Math.min(width, height) / 1200
+
+ width: 1280
+ height: 720
+ color: "#404040"
+
+ Settings {
+ id: settings
+ onReseted: {
+ settingsView.resetSettings();
+ }
+ }
+
+ Settings {
+ id: defaultSettings
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ if (resetSettingsOverlay.showAnimationProgress == 0)
+ settings.animateMovement = !settings.animateMovement;
+ }
+ onPressed: {
+ resetSettingsOverlay.startShow();
+ }
+ onReleased: {
+ resetSettingsOverlay.stopShow();
+ }
+ }
+
+
+ Item {
+ id: mainArea
+ anchors.left: settingsView.right
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+
+ TestSourceItem {
+ id: testSourceItem
+ anchors.centerIn: parent
+ width: parent.width / 2
+ height: parent.height / 2
+
+ layer.enabled: true
+ visible: false
+ }
+ TestMaskItem {
+ id: testMaskItem
+ anchors.fill: testSourceItem
+ }
+
+ Rectangle {
+ readonly property int margin: 2
+ x: quickMultiEffect.x + quickMultiEffect.itemRect.x - margin
+ y: quickMultiEffect.y + quickMultiEffect.itemRect.y - margin
+ width: quickMultiEffect.itemRect.width + margin * 2
+ height: quickMultiEffect.itemRect.height + margin * 2
+ visible: settings.showItemSize
+ color: "transparent"
+ border.color: "#ffffff"
+ border.width: 2
+ }
+
+ MultiEffect {
+ id: quickMultiEffect
+ anchors.fill: testSourceItem
+ visible: !settings.showCustomMultiEffect
+ source: testSourceItem
+ maskSource: testMaskItem
+ autoPaddingEnabled: settings.autoPaddingEnabled
+ paddingRect: settings.paddingRect
+ brightness: settings.brightnessEnabled ? settings.brightness : 0
+ contrast: settings.contrastEnabled ? settings.contrast : 0
+ saturation: settings.saturationEnabled ? settings.saturation : 0
+ colorizationColor: settings.colorizationColor
+ colorization: settings.colorizationEnabled ? settings.colorization : 0
+ blurEnabled: settings.blurEnabled
+ blur: settings.blur
+ blurMax: settings.blurMax
+ blurMultiplier: settings.blurMultiplier
+ shadowEnabled: settings.shadowEnabled
+ shadowOpacity: settings.shadowOpacity
+ shadowBlur: settings.shadowBlur
+ shadowHorizontalOffset: settings.shadowHorizontalOffset
+ shadowVerticalOffset: settings.shadowVerticalOffset
+ shadowColor: settings.shadowColor
+ shadowScale: settings.shadowScale
+ maskEnabled: settings.maskEnabled
+ maskInverted: settings.maskInverted
+ maskThresholdMin: settings.maskThresholdMin
+ maskSpreadAtMin: settings.maskSpreadAtMin
+ maskThresholdMax: settings.maskThresholdMax
+ maskSpreadAtMax: settings.maskSpreadAtMax
+
+ onItemSizeChanged: {
+ if (visible)
+ warningsView.showSizeWarning();
+ }
+ onShaderChanged: {
+ if (visible)
+ warningsView.showShaderWarning();
+ }
+ }
+
+ // For comparison, custom effect created with QQEM and the MultiEffect node.
+ CustomMultiEffect {
+ id: customMultiEffect
+ anchors.fill: testSourceItem
+ visible: settings.showCustomMultiEffect
+ source: testSourceItem
+ maskSource: testMaskItem
+ brightness: settings.brightnessEnabled ? settings.brightness : 0
+ contrast: settings.contrastEnabled ? settings.contrast : 0
+ saturation: settings.saturationEnabled ? settings.saturation : 0
+ colorizationColor: settings.colorizationColor
+ colorization: settings.colorizationEnabled ? settings.colorization : 0
+ blur: settings.blur
+ blurMax: settings.blurMax
+ blurMultiplier: settings.blurMultiplier
+ shadowOpacity: settings.shadowOpacity
+ shadowBlur: settings.shadowBlur
+ shadowHorizontalOffset: settings.shadowHorizontalOffset
+ shadowVerticalOffset: settings.shadowVerticalOffset
+ shadowColor: settings.shadowColor
+ shadowScale: settings.shadowScale
+ maskInverted: settings.maskInverted
+ maskThresholdMin: settings.maskThresholdMin
+ maskSpreadAtMin: settings.maskSpreadAtMin
+ maskThresholdMax: settings.maskThresholdMax
+ maskSpreadAtMax: settings.maskSpreadAtMax
+ }
+ }
+
+ SettingsView {
+ id: settingsView
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.margins: 20
+ visible: settings.showSettingsView
+ Component.onCompleted: {
+ settings.reset();
+ }
+ }
+
+ FpsItem {
+ anchors.right: parent.right
+ }
+
+ ShaderView {
+ id: shaderView
+ visible: settings.showShader
+ anchors.horizontalCenter: mainArea.horizontalCenter
+ anchors.top: mainArea.top
+ anchors.topMargin: 20
+ text: "Fragment shader: " + quickMultiEffect.fragmentShader + "\nVertex shader: " + quickMultiEffect.vertexShader
+ }
+
+ WarningsView {
+ id: warningsView
+ anchors.bottom: parent.bottom
+ anchors.left: settingsView.right
+ anchors.leftMargin: 40
+ anchors.right: parent.right
+ }
+
+ ResetSettingsOverlay {
+ id: resetSettingsOverlay
+ onAnimationFinished: {
+ settings.reset();
+ }
+ }
+}
diff --git a/examples/quick/multieffect/testbed/testbed.pro b/examples/quick/multieffect/testbed/testbed.pro
new file mode 100644
index 0000000000..e1ae55b118
--- /dev/null
+++ b/examples/quick/multieffect/testbed/testbed.pro
@@ -0,0 +1,10 @@
+TEMPLATE = app
+
+QT += quick qml
+QT += quickcontrols2
+SOURCES += main.cpp
+RESOURCES += \
+ qml.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/multieffect/testbed
+INSTALLS += target