summaryrefslogtreecommitdiffstats
path: root/tests/manual
diff options
context:
space:
mode:
Diffstat (limited to 'tests/manual')
-rw-r--r--tests/manual/CMakeLists.txt15
-rw-r--r--tests/manual/android_content_uri/CMakeLists.txt8
-rw-r--r--tests/manual/android_content_uri/tst_content_uris.cpp2
-rw-r--r--tests/manual/cmake/test_copy_file_if_different_command/main.cpp2
-rw-r--r--tests/manual/cocoa/appicon/main.cpp2
-rw-r--r--tests/manual/cocoa/menurama/CMakeLists.txt2
-rw-r--r--tests/manual/cocoa/menurama/main.cpp2
-rw-r--r--tests/manual/cocoa/menurama/mainwindow.cpp2
-rw-r--r--tests/manual/cocoa/menurama/mainwindow.h2
-rw-r--r--tests/manual/cocoa/menurama/menuramaapplication.cpp4
-rw-r--r--tests/manual/cocoa/menurama/menuramaapplication.h2
-rw-r--r--tests/manual/cocoa/menus/main.cpp2
-rw-r--r--tests/manual/cocoa/nativewidgets/main.cpp2
-rw-r--r--tests/manual/cocoa/popups/main.cpp2
-rw-r--r--tests/manual/cocoa/qsystemtrayicon/main.cpp2
-rw-r--r--tests/manual/cocoa/qt_on_cocoa/main.mm2
-rw-r--r--tests/manual/cocoa/qt_on_cocoa/rasterwindow.cpp2
-rw-r--r--tests/manual/cocoa/qt_on_cocoa/rasterwindow.h2
-rw-r--r--tests/manual/cocoa/wheelevent/main.cpp2
-rw-r--r--tests/manual/cocoa/wheelevent/window.cpp2
-rw-r--r--tests/manual/cocoa/wheelevent/window.h2
-rw-r--r--tests/manual/corelib/CMakeLists.txt6
-rw-r--r--tests/manual/corelib/qdatastream/CMakeLists.txt15
-rw-r--r--tests/manual/corelib/qdatastream/qdatastream.pro6
-rw-r--r--tests/manual/corelib/qdatastream/tst_manualqdatastream.cpp234
-rw-r--r--tests/manual/corelib/time/CMakeLists.txt4
-rwxr-xr-xtests/manual/corelib/time/foreachzone30
-rw-r--r--tests/manual/corelib/time/zonechange/CMakeLists.txt9
-rw-r--r--tests/manual/corelib/time/zonechange/tst_zonechange.cpp60
-rw-r--r--tests/manual/corelib/tools/CMakeLists.txt12
-rw-r--r--tests/manual/corelib/tools/customtype/CMakeLists.txt15
-rw-r--r--tests/manual/corelib/tools/customtype/customtype.pro7
-rw-r--r--tests/manual/corelib/tools/customtype/main.cpp37
-rw-r--r--tests/manual/corelib/tools/customtype/message.cpp38
-rw-r--r--tests/manual/corelib/tools/customtype/message.h38
-rw-r--r--tests/manual/corelib/tools/customtypesending/CMakeLists.txt16
-rw-r--r--tests/manual/corelib/tools/customtypesending/customtypesending.pro9
-rw-r--r--tests/manual/corelib/tools/customtypesending/main.cpp31
-rw-r--r--tests/manual/corelib/tools/customtypesending/message.cpp19
-rw-r--r--tests/manual/corelib/tools/customtypesending/message.h34
-rw-r--r--tests/manual/corelib/tools/customtypesending/window.cpp43
-rw-r--r--tests/manual/corelib/tools/customtypesending/window.h35
-rw-r--r--tests/manual/corelib/tools/qhash/main.cpp2
-rw-r--r--tests/manual/corelib/tools/qlist/main.cpp2
-rw-r--r--tests/manual/corelib/tools/qmap/main.cpp2
-rw-r--r--tests/manual/corelib/tools/qset/main.cpp2
-rw-r--r--tests/manual/corelib/tools/qvarlengtharray/main.cpp2
-rw-r--r--tests/manual/corelib/tools/qvector/main.cpp2
-rw-r--r--tests/manual/diaglib/debugproxystyle.cpp2
-rw-r--r--tests/manual/diaglib/debugproxystyle.h2
-rw-r--r--tests/manual/diaglib/eventfilter.cpp2
-rw-r--r--tests/manual/diaglib/eventfilter.h2
-rw-r--r--tests/manual/diaglib/glinfo.cpp2
-rw-r--r--tests/manual/diaglib/glinfo.h2
-rw-r--r--tests/manual/diaglib/logwidget.cpp2
-rw-r--r--tests/manual/diaglib/logwidget.h2
-rw-r--r--tests/manual/diaglib/nativewindowdump.cpp2
-rw-r--r--tests/manual/diaglib/nativewindowdump.h2
-rw-r--r--tests/manual/diaglib/nativewindowdump_win.cpp2
-rw-r--r--tests/manual/diaglib/qwidgetdump.cpp2
-rw-r--r--tests/manual/diaglib/qwidgetdump.h2
-rw-r--r--tests/manual/diaglib/qwindowdump.cpp2
-rw-r--r--tests/manual/diaglib/qwindowdump.h2
-rw-r--r--tests/manual/diaglib/textdump.cpp2
-rw-r--r--tests/manual/diaglib/textdump.h2
-rw-r--r--tests/manual/dialogs/CMakeLists.txt5
-rw-r--r--tests/manual/dialogs/colordialogpanel.cpp4
-rw-r--r--tests/manual/dialogs/colordialogpanel.h2
-rw-r--r--tests/manual/dialogs/filedialogpanel.cpp15
-rw-r--r--tests/manual/dialogs/filedialogpanel.h9
-rw-r--r--tests/manual/dialogs/fontdialogpanel.cpp2
-rw-r--r--tests/manual/dialogs/fontdialogpanel.h2
-rw-r--r--tests/manual/dialogs/main.cpp2
-rw-r--r--tests/manual/dialogs/messageboxpanel.cpp2
-rw-r--r--tests/manual/dialogs/messageboxpanel.h2
-rw-r--r--tests/manual/dialogs/printdialogpanel.cpp4
-rw-r--r--tests/manual/dialogs/printdialogpanel.h2
-rw-r--r--tests/manual/dialogs/utils.cpp4
-rw-r--r--tests/manual/dialogs/utils.h2
-rw-r--r--tests/manual/dialogs/wizardpanel.cpp6
-rw-r--r--tests/manual/dialogs/wizardpanel.h2
-rw-r--r--tests/manual/embeddedintoforeignwindow/CMakeLists.txt2
-rw-r--r--tests/manual/embeddedintoforeignwindow/itemwindow.cpp4
-rw-r--r--tests/manual/embeddedintoforeignwindow/itemwindow.h5
-rw-r--r--tests/manual/embeddedintoforeignwindow/main.cpp2
-rw-r--r--tests/manual/embeddedwindows/CMakeLists.txt19
-rw-r--r--tests/manual/embeddedwindows/main.cpp110
-rw-r--r--tests/manual/examples/blurpicker/CMakeLists.txt58
-rw-r--r--tests/manual/examples/blurpicker/blureffect.cpp31
-rw-r--r--tests/manual/examples/blurpicker/blureffect.h29
-rw-r--r--tests/manual/examples/blurpicker/blurpicker.cpp122
-rw-r--r--tests/manual/examples/blurpicker/blurpicker.h38
-rw-r--r--tests/manual/examples/blurpicker/blurpicker.pro9
-rw-r--r--tests/manual/examples/blurpicker/blurpicker.qrc14
-rw-r--r--tests/manual/examples/blurpicker/images/README.txt5
-rw-r--r--tests/manual/examples/blurpicker/images/accessories-calculator.pngbin0 -> 3760 bytes
-rw-r--r--tests/manual/examples/blurpicker/images/accessories-text-editor.pngbin0 -> 4746 bytes
-rw-r--r--tests/manual/examples/blurpicker/images/background.jpgbin0 -> 16259 bytes
-rw-r--r--tests/manual/examples/blurpicker/images/help-browser.pngbin0 -> 5392 bytes
-rw-r--r--tests/manual/examples/blurpicker/images/internet-group-chat.pngbin0 -> 2809 bytes
-rw-r--r--tests/manual/examples/blurpicker/images/internet-mail.pngbin0 -> 3899 bytes
-rw-r--r--tests/manual/examples/blurpicker/images/internet-web-browser.pngbin0 -> 6376 bytes
-rw-r--r--tests/manual/examples/blurpicker/images/office-calendar.pngbin0 -> 4010 bytes
-rw-r--r--tests/manual/examples/blurpicker/images/system-users.pngbin0 -> 5353 bytes
-rw-r--r--tests/manual/examples/blurpicker/main.cpp18
-rw-r--r--tests/manual/examples/corelib/permissions/CMakeLists.txt47
-rw-r--r--tests/manual/examples/corelib/permissions/Info.plist59
-rw-r--r--tests/manual/examples/corelib/permissions/android/AndroidManifest.xml51
-rw-r--r--tests/manual/examples/corelib/permissions/main.cpp87
-rw-r--r--tests/manual/examples/opengl/computegles31/CMakeLists.txt2
-rw-r--r--tests/manual/examples/opengl/contextinfo/CMakeLists.txt39
-rw-r--r--tests/manual/examples/opengl/contextinfo/contextinfo.pro14
-rw-r--r--tests/manual/examples/opengl/contextinfo/main.cpp25
-rw-r--r--tests/manual/examples/opengl/contextinfo/renderwindow.cpp191
-rw-r--r--tests/manual/examples/opengl/contextinfo/renderwindow.h48
-rw-r--r--tests/manual/examples/opengl/contextinfo/widget.cpp360
-rw-r--r--tests/manual/examples/opengl/contextinfo/widget.h46
-rw-r--r--tests/manual/examples/opengl/hellowindow/CMakeLists.txt2
-rw-r--r--tests/manual/examples/opengl/paintedwindow/CMakeLists.txt2
-rw-r--r--tests/manual/examples/opengl/qopenglwidget/CMakeLists.txt53
-rw-r--r--tests/manual/examples/opengl/qopenglwidget/bubble.cpp99
-rw-r--r--tests/manual/examples/opengl/qopenglwidget/bubble.h39
-rw-r--r--tests/manual/examples/opengl/qopenglwidget/glwidget.cpp540
-rw-r--r--tests/manual/examples/opengl/qopenglwidget/glwidget.h85
-rw-r--r--tests/manual/examples/opengl/qopenglwidget/main.cpp42
-rw-r--r--tests/manual/examples/opengl/qopenglwidget/mainwindow.cpp188
-rw-r--r--tests/manual/examples/opengl/qopenglwidget/mainwindow.h39
-rw-r--r--tests/manual/examples/opengl/qopenglwidget/qopenglwidget.pro15
-rw-r--r--tests/manual/examples/opengl/qopenglwidget/qt.pngbin0 -> 2991 bytes
-rw-r--r--tests/manual/examples/opengl/qopenglwidget/texture.qrc5
-rw-r--r--tests/manual/examples/opengl/qopenglwindow/CMakeLists.txt2
-rw-r--r--tests/manual/examples/qpa/CMakeLists.txt8
-rw-r--r--tests/manual/examples/qpa/qpa.pro5
-rw-r--r--tests/manual/examples/qpa/qrasterwindow/CMakeLists.txt35
-rw-r--r--tests/manual/examples/qpa/qrasterwindow/main.cpp91
-rw-r--r--tests/manual/examples/qpa/qrasterwindow/qrasterwindow.pro4
-rw-r--r--tests/manual/examples/qpa/windows/CMakeLists.txt38
-rw-r--r--tests/manual/examples/qpa/windows/main.cpp50
-rw-r--r--tests/manual/examples/qpa/windows/window.cpp164
-rw-r--r--tests/manual/examples/qpa/windows/window.h41
-rw-r--r--tests/manual/examples/qpa/windows/windows.pro9
-rw-r--r--tests/manual/examples/vulkan/hellovulkantexture/CMakeLists.txt2
-rw-r--r--tests/manual/examples/widgets/application/CMakeLists.txt54
-rw-r--r--tests/manual/examples/widgets/application/application.pro13
-rw-r--r--tests/manual/examples/widgets/application/application.qrc10
-rw-r--r--tests/manual/examples/widgets/application/images/copy.pngbin0 -> 1338 bytes
-rw-r--r--tests/manual/examples/widgets/application/images/cut.pngbin0 -> 1323 bytes
-rw-r--r--tests/manual/examples/widgets/application/images/new.pngbin0 -> 852 bytes
-rw-r--r--tests/manual/examples/widgets/application/images/open.pngbin0 -> 2073 bytes
-rw-r--r--tests/manual/examples/widgets/application/images/paste.pngbin0 -> 1645 bytes
-rw-r--r--tests/manual/examples/widgets/application/images/save.pngbin0 -> 2699 bytes
-rw-r--r--tests/manual/examples/widgets/application/main.cpp28
-rw-r--r--tests/manual/examples/widgets/application/mainwindow.cpp306
-rw-r--r--tests/manual/examples/widgets/application/mainwindow.h53
-rw-r--r--tests/manual/examples/widgets/dialogs/classwizard/CMakeLists.txt55
-rw-r--r--tests/manual/examples/widgets/dialogs/classwizard/classwizard.cpp394
-rw-r--r--tests/manual/examples/widgets/dialogs/classwizard/classwizard.h119
-rw-r--r--tests/manual/examples/widgets/dialogs/classwizard/classwizard.pro10
-rw-r--r--tests/manual/examples/widgets/dialogs/classwizard/classwizard.qrc11
-rw-r--r--tests/manual/examples/widgets/dialogs/classwizard/images/background.pngbin0 -> 22578 bytes
-rw-r--r--tests/manual/examples/widgets/dialogs/classwizard/images/banner.pngbin0 -> 3947 bytes
-rw-r--r--tests/manual/examples/widgets/dialogs/classwizard/images/logo1.pngbin0 -> 1619 bytes
-rw-r--r--tests/manual/examples/widgets/dialogs/classwizard/images/logo2.pngbin0 -> 1619 bytes
-rw-r--r--tests/manual/examples/widgets/dialogs/classwizard/images/logo3.pngbin0 -> 1619 bytes
-rw-r--r--tests/manual/examples/widgets/dialogs/classwizard/images/watermark1.pngbin0 -> 14516 bytes
-rw-r--r--tests/manual/examples/widgets/dialogs/classwizard/images/watermark2.pngbin0 -> 14912 bytes
-rw-r--r--tests/manual/examples/widgets/dialogs/classwizard/main.cpp26
-rw-r--r--tests/manual/examples/widgets/dialogs/extension/CMakeLists.txt37
-rw-r--r--tests/manual/examples/widgets/dialogs/extension/extension.pro9
-rw-r--r--tests/manual/examples/widgets/dialogs/extension/finddialog.cpp77
-rw-r--r--tests/manual/examples/widgets/dialogs/extension/finddialog.h41
-rw-r--r--tests/manual/examples/widgets/dialogs/extension/main.cpp16
-rw-r--r--tests/manual/examples/widgets/draganddrop/fridgemagnets/CMakeLists.txt50
-rw-r--r--tests/manual/examples/widgets/draganddrop/fridgemagnets/draglabel.cpp51
-rw-r--r--tests/manual/examples/widgets/draganddrop/fridgemagnets/draglabel.h27
-rw-r--r--tests/manual/examples/widgets/draganddrop/fridgemagnets/dragwidget.cpp176
-rw-r--r--tests/manual/examples/widgets/draganddrop/fridgemagnets/dragwidget.h28
-rw-r--r--tests/manual/examples/widgets/draganddrop/fridgemagnets/fridgemagnets.pro12
-rw-r--r--tests/manual/examples/widgets/draganddrop/fridgemagnets/fridgemagnets.qrc5
-rw-r--r--tests/manual/examples/widgets/draganddrop/fridgemagnets/main.cpp23
-rw-r--r--tests/manual/examples/widgets/draganddrop/fridgemagnets/words.txt48
-rw-r--r--tests/manual/examples/widgets/draganddrop/puzzle/CMakeLists.txt51
-rw-r--r--tests/manual/examples/widgets/draganddrop/puzzle/example.jpgbin0 -> 42654 bytes
-rw-r--r--tests/manual/examples/widgets/draganddrop/puzzle/main.cpp15
-rw-r--r--tests/manual/examples/widgets/draganddrop/puzzle/mainwindow.cpp118
-rw-r--r--tests/manual/examples/widgets/draganddrop/puzzle/mainwindow.h40
-rw-r--r--tests/manual/examples/widgets/draganddrop/puzzle/pieceslist.cpp87
-rw-r--r--tests/manual/examples/widgets/draganddrop/puzzle/pieceslist.h28
-rw-r--r--tests/manual/examples/widgets/draganddrop/puzzle/puzzle.pro17
-rw-r--r--tests/manual/examples/widgets/draganddrop/puzzle/puzzle.qrc5
-rw-r--r--tests/manual/examples/widgets/draganddrop/puzzle/puzzlewidget.cpp167
-rw-r--r--tests/manual/examples/widgets/draganddrop/puzzle/puzzlewidget.h56
-rw-r--r--tests/manual/examples/widgets/effects/fademessage/CMakeLists.txt49
-rw-r--r--tests/manual/examples/widgets/effects/fademessage/README2
-rw-r--r--tests/manual/examples/widgets/effects/fademessage/background.jpgbin0 -> 159108 bytes
-rw-r--r--tests/manual/examples/widgets/effects/fademessage/fademessage.cpp91
-rw-r--r--tests/manual/examples/widgets/effects/fademessage/fademessage.h33
-rw-r--r--tests/manual/examples/widgets/effects/fademessage/fademessage.pro9
-rw-r--r--tests/manual/examples/widgets/effects/fademessage/fademessage.qrc5
-rw-r--r--tests/manual/examples/widgets/effects/fademessage/main.cpp18
-rw-r--r--tests/manual/examples/widgets/graphicsview/embeddeddialogs/CMakeLists.txt50
-rw-r--r--tests/manual/examples/widgets/graphicsview/embeddeddialogs/No-Ones-Laughing-3.jpgbin0 -> 30730 bytes
-rw-r--r--tests/manual/examples/widgets/graphicsview/embeddeddialogs/customproxy.cpp133
-rw-r--r--tests/manual/examples/widgets/graphicsview/embeddeddialogs/customproxy.h39
-rw-r--r--tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialog.cpp70
-rw-r--r--tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialog.h33
-rw-r--r--tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialog.ui88
-rw-r--r--tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialogs.pro18
-rw-r--r--tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialogs.qrc5
-rw-r--r--tests/manual/examples/widgets/graphicsview/embeddeddialogs/main.cpp42
-rw-r--r--tests/manual/examples/widgets/graphicsview/flowlayout/CMakeLists.txt38
-rw-r--r--tests/manual/examples/widgets/graphicsview/flowlayout/flowlayout.cpp170
-rw-r--r--tests/manual/examples/widgets/graphicsview/flowlayout/flowlayout.h44
-rw-r--r--tests/manual/examples/widgets/graphicsview/flowlayout/flowlayout.pro10
-rw-r--r--tests/manual/examples/widgets/graphicsview/flowlayout/main.cpp24
-rw-r--r--tests/manual/examples/widgets/graphicsview/flowlayout/window.cpp24
-rw-r--r--tests/manual/examples/widgets/graphicsview/flowlayout/window.h16
-rw-r--r--tests/manual/examples/widgets/itemviews/chart/CMakeLists.txt56
-rw-r--r--tests/manual/examples/widgets/itemviews/chart/chart.pro14
-rw-r--r--tests/manual/examples/widgets/itemviews/chart/chart.qrc5
-rw-r--r--tests/manual/examples/widgets/itemviews/chart/main.cpp14
-rw-r--r--tests/manual/examples/widgets/itemviews/chart/mainwindow.cpp136
-rw-r--r--tests/manual/examples/widgets/itemviews/chart/mainwindow.h34
-rw-r--r--tests/manual/examples/widgets/itemviews/chart/pieview.cpp506
-rw-r--r--tests/manual/examples/widgets/itemviews/chart/pieview.h66
-rw-r--r--tests/manual/examples/widgets/itemviews/chart/qtdata.cht14
-rw-r--r--tests/manual/examples/widgets/itemviews/dirview/CMakeLists.txt36
-rw-r--r--tests/manual/examples/widgets/itemviews/dirview/dirview.pro8
-rw-r--r--tests/manual/examples/widgets/itemviews/dirview/main.cpp62
-rw-r--r--tests/manual/examples/widgets/itemviews/interview/CMakeLists.txt51
-rw-r--r--tests/manual/examples/widgets/itemviews/interview/README2
-rw-r--r--tests/manual/examples/widgets/itemviews/interview/images/folder.pngbin0 -> 3910 bytes
-rw-r--r--tests/manual/examples/widgets/itemviews/interview/images/interview.pngbin0 -> 174 bytes
-rw-r--r--tests/manual/examples/widgets/itemviews/interview/images/services.pngbin0 -> 3749 bytes
-rw-r--r--tests/manual/examples/widgets/itemviews/interview/interview.pro16
-rw-r--r--tests/manual/examples/widgets/itemviews/interview/interview.qrc7
-rw-r--r--tests/manual/examples/widgets/itemviews/interview/main.cpp55
-rw-r--r--tests/manual/examples/widgets/itemviews/interview/model.cpp110
-rw-r--r--tests/manual/examples/widgets/itemviews/interview/model.h53
-rw-r--r--tests/manual/examples/widgets/itemviews/pixelator/CMakeLists.txt58
-rw-r--r--tests/manual/examples/widgets/itemviews/pixelator/imagemodel.cpp53
-rw-r--r--tests/manual/examples/widgets/itemviews/pixelator/imagemodel.h31
-rw-r--r--tests/manual/examples/widgets/itemviews/pixelator/images.qrc5
-rw-r--r--tests/manual/examples/widgets/itemviews/pixelator/images/qt.pngbin0 -> 1506 bytes
-rw-r--r--tests/manual/examples/widgets/itemviews/pixelator/main.cpp15
-rw-r--r--tests/manual/examples/widgets/itemviews/pixelator/mainwindow.cpp214
-rw-r--r--tests/manual/examples/widgets/itemviews/pixelator/mainwindow.h37
-rw-r--r--tests/manual/examples/widgets/itemviews/pixelator/pixelator.pro16
-rw-r--r--tests/manual/examples/widgets/itemviews/pixelator/pixeldelegate.cpp68
-rw-r--r--tests/manual/examples/widgets/itemviews/pixelator/pixeldelegate.h41
-rw-r--r--tests/manual/examples/widgets/itemviews/puzzle/CMakeLists.txt51
-rw-r--r--tests/manual/examples/widgets/itemviews/puzzle/example.jpgbin0 -> 42654 bytes
-rw-r--r--tests/manual/examples/widgets/itemviews/puzzle/main.cpp15
-rw-r--r--tests/manual/examples/widgets/itemviews/puzzle/mainwindow.cpp115
-rw-r--r--tests/manual/examples/widgets/itemviews/puzzle/mainwindow.h41
-rw-r--r--tests/manual/examples/widgets/itemviews/puzzle/piecesmodel.cpp168
-rw-r--r--tests/manual/examples/widgets/itemviews/puzzle/piecesmodel.h45
-rw-r--r--tests/manual/examples/widgets/itemviews/puzzle/puzzle.pro15
-rw-r--r--tests/manual/examples/widgets/itemviews/puzzle/puzzle.qrc5
-rw-r--r--tests/manual/examples/widgets/itemviews/puzzle/puzzlewidget.cpp163
-rw-r--r--tests/manual/examples/widgets/itemviews/puzzle/puzzlewidget.h56
-rw-r--r--tests/manual/examples/widgets/itemviews/simpledommodel/CMakeLists.txt40
-rw-r--r--tests/manual/examples/widgets/itemviews/simpledommodel/domitem.cpp62
-rw-r--r--tests/manual/examples/widgets/itemviews/simpledommodel/domitem.h29
-rw-r--r--tests/manual/examples/widgets/itemviews/simpledommodel/dommodel.cpp153
-rw-r--r--tests/manual/examples/widgets/itemviews/simpledommodel/dommodel.h38
-rw-r--r--tests/manual/examples/widgets/itemviews/simpledommodel/main.cpp15
-rw-r--r--tests/manual/examples/widgets/itemviews/simpledommodel/mainwindow.cpp47
-rw-r--r--tests/manual/examples/widgets/itemviews/simpledommodel/mainwindow.h33
-rw-r--r--tests/manual/examples/widgets/itemviews/simpledommodel/simpledommodel.pro14
-rw-r--r--tests/manual/examples/widgets/itemviews/simplewidgetmapper/CMakeLists.txt37
-rw-r--r--tests/manual/examples/widgets/itemviews/simplewidgetmapper/main.cpp14
-rw-r--r--tests/manual/examples/widgets/itemviews/simplewidgetmapper/simplewidgetmapper.pro10
-rw-r--r--tests/manual/examples/widgets/itemviews/simplewidgetmapper/window.cpp93
-rw-r--r--tests/manual/examples/widgets/itemviews/simplewidgetmapper/window.h47
-rw-r--r--tests/manual/examples/widgets/itemviews/storageview/CMakeLists.txt37
-rw-r--r--tests/manual/examples/widgets/itemviews/storageview/main.cpp32
-rw-r--r--tests/manual/examples/widgets/itemviews/storageview/storagemodel.cpp164
-rw-r--r--tests/manual/examples/widgets/itemviews/storageview/storagemodel.h46
-rw-r--r--tests/manual/examples/widgets/itemviews/storageview/storageview.pro12
-rw-r--r--tests/manual/examples/widgets/layouts/borderlayout/CMakeLists.txt38
-rw-r--r--tests/manual/examples/widgets/layouts/borderlayout/borderlayout.cpp171
-rw-r--r--tests/manual/examples/widgets/layouts/borderlayout/borderlayout.h50
-rw-r--r--tests/manual/examples/widgets/layouts/borderlayout/borderlayout.pro11
-rw-r--r--tests/manual/examples/widgets/layouts/borderlayout/main.cpp14
-rw-r--r--tests/manual/examples/widgets/layouts/borderlayout/window.cpp31
-rw-r--r--tests/manual/examples/widgets/layouts/borderlayout/window.h24
-rw-r--r--tests/manual/examples/widgets/layouts/dynamiclayouts/CMakeLists.txt37
-rw-r--r--tests/manual/examples/widgets/layouts/dynamiclayouts/dialog.cpp136
-rw-r--r--tests/manual/examples/widgets/layouts/dynamiclayouts/dialog.h53
-rw-r--r--tests/manual/examples/widgets/layouts/dynamiclayouts/dynamiclayouts.pro10
-rw-r--r--tests/manual/examples/widgets/layouts/dynamiclayouts/main.cpp15
-rw-r--r--tests/manual/examples/widgets/mainwindows/dockwidgets/CMakeLists.txt59
-rw-r--r--tests/manual/examples/widgets/mainwindows/dockwidgets/dockwidgets.pro12
-rw-r--r--tests/manual/examples/widgets/mainwindows/dockwidgets/dockwidgets.qrc8
-rw-r--r--tests/manual/examples/widgets/mainwindows/dockwidgets/images/new.pngbin0 -> 977 bytes
-rw-r--r--tests/manual/examples/widgets/mainwindows/dockwidgets/images/print.pngbin0 -> 1732 bytes
-rw-r--r--tests/manual/examples/widgets/mainwindows/dockwidgets/images/save.pngbin0 -> 1894 bytes
-rw-r--r--tests/manual/examples/widgets/mainwindows/dockwidgets/images/undo.pngbin0 -> 1768 bytes
-rw-r--r--tests/manual/examples/widgets/mainwindows/dockwidgets/main.cpp14
-rw-r--r--tests/manual/examples/widgets/mainwindows/dockwidgets/mainwindow.cpp298
-rw-r--r--tests/manual/examples/widgets/mainwindows/dockwidgets/mainwindow.h46
-rw-r--r--tests/manual/examples/widgets/mainwindows/mainwindow/CMakeLists.txt54
-rw-r--r--tests/manual/examples/widgets/mainwindows/mainwindow/colorswatch.cpp685
-rw-r--r--tests/manual/examples/widgets/mainwindows/mainwindow/colorswatch.h102
-rw-r--r--tests/manual/examples/widgets/mainwindows/mainwindow/main.cpp147
-rw-r--r--tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.cpp444
-rw-r--r--tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.h50
-rw-r--r--tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.pro18
-rw-r--r--tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.qrc8
-rw-r--r--tests/manual/examples/widgets/mainwindows/mainwindow/qt.pngbin0 -> 2991 bytes
-rw-r--r--tests/manual/examples/widgets/mainwindows/mainwindow/titlebarCenter.pngbin0 -> 146 bytes
-rw-r--r--tests/manual/examples/widgets/mainwindows/mainwindow/titlebarLeft.pngbin0 -> 5148 bytes
-rw-r--r--tests/manual/examples/widgets/mainwindows/mainwindow/titlebarRight.pngbin0 -> 2704 bytes
-rw-r--r--tests/manual/examples/widgets/mainwindows/mainwindow/toolbar.cpp308
-rw-r--r--tests/manual/examples/widgets/mainwindows/mainwindow/toolbar.h74
-rw-r--r--tests/manual/examples/widgets/mainwindows/mdi/CMakeLists.txt55
-rw-r--r--tests/manual/examples/widgets/mainwindows/mdi/images/copy.pngbin0 -> 1338 bytes
-rw-r--r--tests/manual/examples/widgets/mainwindows/mdi/images/cut.pngbin0 -> 1323 bytes
-rw-r--r--tests/manual/examples/widgets/mainwindows/mdi/images/new.pngbin0 -> 852 bytes
-rw-r--r--tests/manual/examples/widgets/mainwindows/mdi/images/open.pngbin0 -> 2073 bytes
-rw-r--r--tests/manual/examples/widgets/mainwindows/mdi/images/paste.pngbin0 -> 1645 bytes
-rw-r--r--tests/manual/examples/widgets/mainwindows/mdi/images/save.pngbin0 -> 2699 bytes
-rw-r--r--tests/manual/examples/widgets/mainwindows/mdi/main.cpp29
-rw-r--r--tests/manual/examples/widgets/mainwindows/mdi/mainwindow.cpp476
-rw-r--r--tests/manual/examples/widgets/mainwindows/mdi/mainwindow.h87
-rw-r--r--tests/manual/examples/widgets/mainwindows/mdi/mdi.pro13
-rw-r--r--tests/manual/examples/widgets/mainwindows/mdi/mdi.qrc10
-rw-r--r--tests/manual/examples/widgets/mainwindows/mdi/mdichild.cpp158
-rw-r--r--tests/manual/examples/widgets/mainwindows/mdi/mdichild.h39
-rw-r--r--tests/manual/examples/widgets/painting/fontsampler/CMakeLists.txt45
-rw-r--r--tests/manual/examples/widgets/painting/fontsampler/fontsampler.pro12
-rw-r--r--tests/manual/examples/widgets/painting/fontsampler/main.cpp14
-rw-r--r--tests/manual/examples/widgets/painting/fontsampler/mainwindow.cpp314
-rw-r--r--tests/manual/examples/widgets/painting/fontsampler/mainwindow.h54
-rw-r--r--tests/manual/examples/widgets/painting/fontsampler/mainwindowbase.ui142
-rw-r--r--tests/manual/examples/widgets/richtext/calendar/CMakeLists.txt37
-rw-r--r--tests/manual/examples/widgets/richtext/calendar/calendar.pro10
-rw-r--r--tests/manual/examples/widgets/richtext/calendar/main.cpp15
-rw-r--r--tests/manual/examples/widgets/richtext/calendar/mainwindow.cpp179
-rw-r--r--tests/manual/examples/widgets/richtext/calendar/mainwindow.h36
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/CMakeLists.txt105
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/example.html84
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/example.md104
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/logo32.pngbin0 -> 1410 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/checkbox-checked.pngbin0 -> 1167 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/checkbox.pngbin0 -> 779 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/editcopy.pngbin0 -> 1468 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/editcut.pngbin0 -> 1512 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/editpaste.pngbin0 -> 1906 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/editredo.pngbin0 -> 1752 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/editundo.pngbin0 -> 1746 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/exportpdf.pngbin0 -> 12637 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/filenew.pngbin0 -> 1172 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/fileopen.pngbin0 -> 2168 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/fileprint.pngbin0 -> 2087 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/filesave.pngbin0 -> 2699 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/format-indent-less.pngbin0 -> 1201 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/format-indent-more.pngbin0 -> 993 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/textbold.pngbin0 -> 1611 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/textcenter.pngbin0 -> 1404 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/textitalic.pngbin0 -> 1164 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/textjustify.pngbin0 -> 1257 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/textleft.pngbin0 -> 1235 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/textright.pngbin0 -> 1406 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/textunder.pngbin0 -> 1183 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/textundercolor.pngbin0 -> 6916 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/zoomin.pngbin0 -> 1696 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/mac/zoomout.pngbin0 -> 1662 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/checkbox-checked.pngbin0 -> 1167 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/checkbox.pngbin0 -> 779 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/editcopy.pngbin0 -> 1325 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/editcut.pngbin0 -> 1896 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/editpaste.pngbin0 -> 1482 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/editredo.pngbin0 -> 1787 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/editundo.pngbin0 -> 1768 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/exportpdf.pngbin0 -> 1215 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/filenew.pngbin0 -> 768 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/fileopen.pngbin0 -> 1662 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/fileprint.pngbin0 -> 1456 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/filesave.pngbin0 -> 1205 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/format-indent-less.pngbin0 -> 1201 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/format-indent-more.pngbin0 -> 993 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/textbold.pngbin0 -> 1134 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/textcenter.pngbin0 -> 627 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/textitalic.pngbin0 -> 829 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/textjustify.pngbin0 -> 695 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/textleft.pngbin0 -> 673 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/textright.pngbin0 -> 677 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/textunder.pngbin0 -> 971 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/textundercolor.pngbin0 -> 6916 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/zoomin.pngbin0 -> 1208 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/images/win/zoomout.pngbin0 -> 1226 bytes
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/main.cpp36
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/textedit.cpp904
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/textedit.h110
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/textedit.pro22
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/textedit.qdoc20
-rw-r--r--tests/manual/examples/widgets/richtext/textedit/textedit.qrc54
-rw-r--r--tests/manual/examples/widgets/scroller/CMakeLists.txt3
-rw-r--r--tests/manual/examples/widgets/scroller/graphicsview/CMakeLists.txt36
-rw-r--r--tests/manual/examples/widgets/scroller/graphicsview/graphicsview.pro8
-rw-r--r--tests/manual/examples/widgets/scroller/graphicsview/main.cpp255
-rw-r--r--tests/manual/examples/widgets/scroller/scroller.pro2
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/CMakeLists.txt18
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/app/CMakeLists.txt36
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/app/app.pro37
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/app/interfaces.h76
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/app/main.cpp19
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/app/mainwindow.cpp282
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/app/mainwindow.h68
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/app/paintarea.cpp152
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/app/paintarea.h54
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/app/plugindialog.cpp118
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/app/plugindialog.h38
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/plugandpaint.pro7
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/plugins/CMakeLists.txt4
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/CMakeLists.txt18
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictools.json1
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictools.pro17
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictoolsplugin.cpp150
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictoolsplugin.h54
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/CMakeLists.txt26
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafilters.json1
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafilters.pro17
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafiltersplugin.cpp82
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafiltersplugin.h28
-rw-r--r--tests/manual/examples/widgets/tools/plugandpaint/plugins/plugins.pro3
-rw-r--r--tests/manual/examples/widgets/tools/settingseditor/CMakeLists.txt20
-rw-r--r--tests/manual/examples/widgets/tools/settingseditor/inifiles/licensepage.ini46
-rw-r--r--tests/manual/examples/widgets/tools/settingseditor/inifiles/qsa.ini26
-rw-r--r--tests/manual/examples/widgets/tools/settingseditor/locationdialog.cpp192
-rw-r--r--tests/manual/examples/widgets/tools/settingseditor/locationdialog.h49
-rw-r--r--tests/manual/examples/widgets/tools/settingseditor/main.cpp17
-rw-r--r--tests/manual/examples/widgets/tools/settingseditor/mainwindow.cpp175
-rw-r--r--tests/manual/examples/widgets/tools/settingseditor/mainwindow.h44
-rw-r--r--tests/manual/examples/widgets/tools/settingseditor/settingseditor.pro18
-rw-r--r--tests/manual/examples/widgets/tools/settingseditor/settingstree.cpp231
-rw-r--r--tests/manual/examples/widgets/tools/settingseditor/settingstree.h61
-rw-r--r--tests/manual/examples/widgets/tools/settingseditor/variantdelegate.cpp377
-rw-r--r--tests/manual/examples/widgets/tools/settingseditor/variantdelegate.h53
-rw-r--r--tests/manual/examples/widgets/touch/dials/CMakeLists.txt37
-rw-r--r--tests/manual/examples/widgets/touch/dials/dials.pro8
-rw-r--r--tests/manual/examples/widgets/touch/dials/dials.ui77
-rw-r--r--tests/manual/examples/widgets/touch/dials/doc/images/touch-dials-example.pngbin0 -> 17676 bytes
-rw-r--r--tests/manual/examples/widgets/touch/dials/doc/src/touch-dials.qdoc14
-rw-r--r--tests/manual/examples/widgets/touch/dials/main.cpp21
-rw-r--r--tests/manual/examples/widgets/touch/fingerpaint/CMakeLists.txt45
-rw-r--r--tests/manual/examples/widgets/touch/fingerpaint/doc/src/fingerpaint.qdoc18
-rw-r--r--tests/manual/examples/widgets/touch/fingerpaint/fingerpaint.pro13
-rw-r--r--tests/manual/examples/widgets/touch/fingerpaint/main.cpp14
-rw-r--r--tests/manual/examples/widgets/touch/fingerpaint/mainwindow.cpp180
-rw-r--r--tests/manual/examples/widgets/touch/fingerpaint/mainwindow.h51
-rw-r--r--tests/manual/examples/widgets/touch/fingerpaint/scribblearea.cpp190
-rw-r--r--tests/manual/examples/widgets/touch/fingerpaint/scribblearea.h43
-rw-r--r--tests/manual/examples/widgets/touch/pinchzoom/CMakeLists.txt50
-rw-r--r--tests/manual/examples/widgets/touch/pinchzoom/doc/images/pinch-zoom-example.pngbin0 -> 42493 bytes
-rw-r--r--tests/manual/examples/widgets/touch/pinchzoom/doc/src/pinchzoom.qdoc14
-rw-r--r--tests/manual/examples/widgets/touch/pinchzoom/graphicsview.cpp48
-rw-r--r--tests/manual/examples/widgets/touch/pinchzoom/graphicsview.h18
-rw-r--r--tests/manual/examples/widgets/touch/pinchzoom/images/cheese.jpgbin0 -> 3029 bytes
-rw-r--r--tests/manual/examples/widgets/touch/pinchzoom/main.cpp47
-rw-r--r--tests/manual/examples/widgets/touch/pinchzoom/mice.qrc5
-rw-r--r--tests/manual/examples/widgets/touch/pinchzoom/mouse.cpp158
-rw-r--r--tests/manual/examples/widgets/touch/pinchzoom/mouse.h33
-rw-r--r--tests/manual/examples/widgets/touch/pinchzoom/pinchzoom.pro16
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/CMakeLists.txt10
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/README40
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-labeled-layout.pngbin0 -> 19114 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-labeled-screenshot.pngbin0 -> 23223 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-screenshot.pngbin0 -> 9872 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-contact.pngbin0 -> 12936 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-flowchart.pngbin0 -> 23533 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-successful.pngbin0 -> 10825 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-labeled-layout.pngbin0 -> 27103 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-signals-and-slots.pngbin0 -> 9968 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-stretch-effects.pngbin0 -> 12268 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-labeled-layout.pngbin0 -> 27467 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-linkedlist.pngbin0 -> 10209 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-screenshot.pngbin0 -> 14041 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part4-remove.pngbin0 -> 22248 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-finddialog.pngbin0 -> 10046 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-notfound.pngbin0 -> 10789 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-screenshot.pngbin0 -> 15849 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-signals-and-slots.pngbin0 -> 5542 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-load.pngbin0 -> 24797 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-save.pngbin0 -> 24747 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-screenshot.pngbin0 -> 16819 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part7-screenshot.pngbin0 -> 18369 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-screenshot.pngbin0 -> 15275 bytes
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial.qdoc948
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/addressbook.pro6
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part1/CMakeLists.txt37
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part1/addressbook.cpp30
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part1/addressbook.h29
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part1/main.cpp17
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part1/part1.pro11
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part2/CMakeLists.txt37
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part2/addressbook.cpp123
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part2/addressbook.h47
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part2/main.cpp17
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part2/part2.pro11
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part3/CMakeLists.txt37
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part3/addressbook.cpp182
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part3/addressbook.h49
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part3/main.cpp15
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part3/part3.pro11
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part4/CMakeLists.txt37
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part4/addressbook.cpp258
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part4/addressbook.h62
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part4/main.cpp15
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part4/part4.pro11
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part5/CMakeLists.txt38
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part5/addressbook.cpp283
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part5/addressbook.h65
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part5/finddialog.cpp51
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part5/finddialog.h31
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part5/main.cpp15
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part5/part5.pro13
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part6/CMakeLists.txt38
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part6/addressbook.cpp366
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part6/addressbook.h66
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part6/finddialog.cpp47
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part6/finddialog.h31
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part6/main.cpp15
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part6/part6.pro14
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part7/CMakeLists.txt38
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part7/addressbook.cpp419
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part7/addressbook.h68
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part7/finddialog.cpp47
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part7/finddialog.h31
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part7/main.cpp15
-rw-r--r--tests/manual/examples/widgets/tutorials/addressbook/part7/part7.pro14
-rw-r--r--tests/manual/examples/widgets/widgets/charactermap/CMakeLists.txt38
-rw-r--r--tests/manual/examples/widgets/widgets/charactermap/charactermap.pro12
-rw-r--r--tests/manual/examples/widgets/widgets/charactermap/characterwidget.cpp145
-rw-r--r--tests/manual/examples/widgets/widgets/charactermap/characterwidget.h50
-rw-r--r--tests/manual/examples/widgets/widgets/charactermap/main.cpp14
-rw-r--r--tests/manual/examples/widgets/widgets/charactermap/mainwindow.cpp264
-rw-r--r--tests/manual/examples/widgets/widgets/charactermap/mainwindow.h50
-rw-r--r--tests/manual/examples/widgets/widgets/digitalclock/CMakeLists.txt37
-rw-r--r--tests/manual/examples/widgets/widgets/digitalclock/digitalclock.cpp36
-rw-r--r--tests/manual/examples/widgets/widgets/digitalclock/digitalclock.h22
-rw-r--r--tests/manual/examples/widgets/widgets/digitalclock/digitalclock.pro9
-rw-r--r--tests/manual/examples/widgets/widgets/digitalclock/main.cpp14
-rw-r--r--tests/manual/examples/widgets/widgets/icons/CMakeLists.txt44
-rw-r--r--tests/manual/examples/widgets/widgets/icons/iconpreviewarea.cpp130
-rw-r--r--tests/manual/examples/widgets/widgets/icons/iconpreviewarea.h47
-rw-r--r--tests/manual/examples/widgets/widgets/icons/icons.pro20
-rw-r--r--tests/manual/examples/widgets/widgets/icons/iconsizespinbox.cpp33
-rw-r--r--tests/manual/examples/widgets/widgets/icons/iconsizespinbox.h22
-rw-r--r--tests/manual/examples/widgets/widgets/icons/imagedelegate.cpp64
-rw-r--r--tests/manual/examples/widgets/widgets/icons/imagedelegate.h31
-rw-r--r--tests/manual/examples/widgets/widgets/icons/images/designer.pngbin0 -> 3604 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/icons/images/find_disabled.pngbin0 -> 501 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/icons/images/find_normal.pngbin0 -> 838 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/icons/images/monkey_off_128x128.pngbin0 -> 7045 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/icons/images/monkey_off_16x16.pngbin0 -> 683 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/icons/images/monkey_off_32x32.pngbin0 -> 1609 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/icons/images/monkey_off_64x64.pngbin0 -> 3533 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/icons/images/monkey_on_128x128.pngbin0 -> 6909 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/icons/images/monkey_on_16x16.pngbin0 -> 681 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/icons/images/monkey_on_32x32.pngbin0 -> 1577 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/icons/images/monkey_on_64x64.pngbin0 -> 3479 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/icons/images/qt_extended_16x16.pngbin0 -> 1263 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/icons/images/qt_extended_32x32.pngbin0 -> 15518 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/icons/images/qt_extended_48x48.pngbin0 -> 789 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/icons/main.cpp34
-rw-r--r--tests/manual/examples/widgets/widgets/icons/mainwindow.cpp478
-rw-r--r--tests/manual/examples/widgets/widgets/icons/mainwindow.h74
-rw-r--r--tests/manual/examples/widgets/widgets/imageviewer/CMakeLists.txt44
-rw-r--r--tests/manual/examples/widgets/widgets/imageviewer/imageviewer.cpp364
-rw-r--r--tests/manual/examples/widgets/widgets/imageviewer/imageviewer.h74
-rw-r--r--tests/manual/examples/widgets/widgets/imageviewer/imageviewer.pro11
-rw-r--r--tests/manual/examples/widgets/widgets/imageviewer/main.cpp24
-rw-r--r--tests/manual/examples/widgets/widgets/movie/CMakeLists.txt37
-rw-r--r--tests/manual/examples/widgets/widgets/movie/animation.gifbin0 -> 42629 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/movie/main.cpp15
-rw-r--r--tests/manual/examples/widgets/widgets/movie/movie.pro12
-rw-r--r--tests/manual/examples/widgets/widgets/movie/movieplayer.cpp180
-rw-r--r--tests/manual/examples/widgets/widgets/movie/movieplayer.h59
-rw-r--r--tests/manual/examples/widgets/widgets/styles/CMakeLists.txt51
-rw-r--r--tests/manual/examples/widgets/widgets/styles/images/woodbackground.pngbin0 -> 7691 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/styles/images/woodbutton.pngbin0 -> 7689 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/styles/main.cpp17
-rw-r--r--tests/manual/examples/widgets/widgets/styles/norwegianwoodstyle.cpp310
-rw-r--r--tests/manual/examples/widgets/widgets/styles/norwegianwoodstyle.h43
-rw-r--r--tests/manual/examples/widgets/widgets/styles/styles.pro13
-rw-r--r--tests/manual/examples/widgets/widgets/styles/styles.qrc6
-rw-r--r--tests/manual/examples/widgets/widgets/styles/widgetgallery.cpp277
-rw-r--r--tests/manual/examples/widgets/widgets/styles/widgetgallery.h86
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/CMakeLists.txt84
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_checked.pngbin0 -> 263 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_checked_hover.pngbin0 -> 266 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_checked_pressed.pngbin0 -> 425 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_unchecked.pngbin0 -> 159 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_unchecked_hover.pngbin0 -> 159 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_unchecked_pressed.pngbin0 -> 320 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/down_arrow.pngbin0 -> 175 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/down_arrow_disabled.pngbin0 -> 174 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/frame.pngbin0 -> 253 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/pagefold.pngbin0 -> 1545 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/pushbutton.pngbin0 -> 533 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/pushbutton_hover.pngbin0 -> 525 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/pushbutton_pressed.pngbin0 -> 513 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_checked.pngbin0 -> 355 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_checked_hover.pngbin0 -> 532 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_checked_pressed.pngbin0 -> 599 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_unchecked.pngbin0 -> 240 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_unchecked_hover.pngbin0 -> 492 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_unchecked_pressed.pngbin0 -> 556 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/sizegrip.pngbin0 -> 129 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/spindown.pngbin0 -> 276 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/spindown_hover.pngbin0 -> 268 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/spindown_off.pngbin0 -> 249 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/spindown_pressed.pngbin0 -> 264 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/spinup.pngbin0 -> 283 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/spinup_hover.pngbin0 -> 277 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/spinup_off.pngbin0 -> 274 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/spinup_pressed.pngbin0 -> 277 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/up_arrow.pngbin0 -> 197 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/images/up_arrow_disabled.pngbin0 -> 172 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/layouts/default.ui329
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/layouts/pagefold.ui349
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/main.cpp14
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/mainwindow.cpp41
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/mainwindow.h29
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/mainwindow.ui356
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/qss/coffee.qss117
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/qss/default.qss1
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/qss/pagefold.qss299
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/stylesheet.pro15
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/stylesheet.qrc39
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/stylesheeteditor.cpp64
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/stylesheeteditor.h30
-rw-r--r--tests/manual/examples/widgets/widgets/stylesheet/stylesheeteditor.ui171
-rw-r--r--tests/manual/examples/widgets/widgets/tetrix/CMakeLists.txt39
-rw-r--r--tests/manual/examples/widgets/widgets/tetrix/main.cpp14
-rw-r--r--tests/manual/examples/widgets/widgets/tetrix/tetrix.pro13
-rw-r--r--tests/manual/examples/widgets/widgets/tetrix/tetrixboard.cpp371
-rw-r--r--tests/manual/examples/widgets/widgets/tetrix/tetrixboard.h79
-rw-r--r--tests/manual/examples/widgets/widgets/tetrix/tetrixpiece.cpp106
-rw-r--r--tests/manual/examples/widgets/widgets/tetrix/tetrixpiece.h38
-rw-r--r--tests/manual/examples/widgets/widgets/tetrix/tetrixwindow.cpp82
-rw-r--r--tests/manual/examples/widgets/widgets/tetrix/tetrixwindow.h38
-rw-r--r--tests/manual/examples/widgets/widgets/validators/CMakeLists.txt55
-rw-r--r--tests/manual/examples/widgets/widgets/validators/ledoff.pngbin0 -> 562 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/validators/ledon.pngbin0 -> 486 bytes
-rw-r--r--tests/manual/examples/widgets/widgets/validators/ledwidget.cpp25
-rw-r--r--tests/manual/examples/widgets/widgets/validators/ledwidget.h27
-rw-r--r--tests/manual/examples/widgets/widgets/validators/localeselector.cpp48
-rw-r--r--tests/manual/examples/widgets/widgets/validators/localeselector.h23
-rw-r--r--tests/manual/examples/widgets/widgets/validators/main.cpp16
-rw-r--r--tests/manual/examples/widgets/widgets/validators/validators.pro12
-rw-r--r--tests/manual/examples/widgets/widgets/validators/validators.qrc6
-rw-r--r--tests/manual/examples/widgets/widgets/validators/validators.ui468
-rw-r--r--tests/manual/examples/widgets/widgets/validators/validatorwidget.cpp75
-rw-r--r--tests/manual/examples/widgets/widgets/validators/validatorwidget.h22
-rw-r--r--tests/manual/examples/widgets/wiggly/CMakeLists.txt2
-rw-r--r--tests/manual/examples/widgets/windowcontainer/CMakeLists.txt42
-rw-r--r--tests/manual/examples/widgets/windowcontainer/windowcontainer.cpp136
-rw-r--r--tests/manual/examples/widgets/windowcontainer/windowcontainer.pro9
-rw-r--r--tests/manual/filetest/main.cpp19
-rw-r--r--tests/manual/findfiles/findfiles.qdoc2
-rw-r--r--tests/manual/findfiles/main.cpp2
-rw-r--r--tests/manual/findfiles/window.cpp2
-rw-r--r--tests/manual/findfiles/window.h2
-rw-r--r--tests/manual/fontfeatures/fontfeatures.pro17
-rw-r--r--tests/manual/fontfeatures/main.cpp14
-rw-r--r--tests/manual/fontfeatures/mainwindow.cpp225
-rw-r--r--tests/manual/fontfeatures/mainwindow.h32
-rw-r--r--tests/manual/fontfeatures/mainwindow.ui116
-rw-r--r--tests/manual/foreignwindows/main.cpp2
-rw-r--r--tests/manual/gestures/graphicsview/gestures.cpp2
-rw-r--r--tests/manual/gestures/graphicsview/gestures.h2
-rw-r--r--tests/manual/gestures/graphicsview/imageitem.cpp2
-rw-r--r--tests/manual/gestures/graphicsview/imageitem.h2
-rw-r--r--tests/manual/gestures/graphicsview/main.cpp6
-rw-r--r--tests/manual/gestures/graphicsview/mousepangesturerecognizer.cpp2
-rw-r--r--tests/manual/gestures/graphicsview/mousepangesturerecognizer.h2
-rw-r--r--tests/manual/gestures/scrollarea/CMakeLists.txt2
-rw-r--r--tests/manual/gestures/scrollarea/main.cpp4
-rw-r--r--tests/manual/gestures/scrollarea/mousepangesturerecognizer.cpp2
-rw-r--r--tests/manual/gestures/scrollarea/mousepangesturerecognizer.h2
-rw-r--r--tests/manual/graphicsframecapture/CMakeLists.txt35
-rw-r--r--tests/manual/graphicsframecapture/examplewindow.cpp116
-rw-r--r--tests/manual/graphicsframecapture/examplewindow.h31
-rw-r--r--tests/manual/graphicsframecapture/main.cpp128
-rw-r--r--tests/manual/graphicsframecapture/window.cpp256
-rw-r--r--tests/manual/graphicsframecapture/window.h55
-rw-r--r--tests/manual/highdpi/CMakeLists.txt2
-rw-r--r--tests/manual/highdpi/dprgadget/CMakeLists.txt4
-rw-r--r--tests/manual/highdpi/dprgadget/main.cpp2
-rw-r--r--tests/manual/highdpi/kitchensink/CMakeLists.txt4
-rw-r--r--tests/manual/highdpi/kitchensink/dragwidget.cpp8
-rw-r--r--tests/manual/highdpi/kitchensink/dragwidget.h2
-rw-r--r--tests/manual/highdpi/kitchensink/main.cpp2
-rw-r--r--tests/manual/highdpi/pixelgadget/CMakeLists.txt4
-rw-r--r--tests/manual/highdpi/pixelgadget/main.cpp2
-rw-r--r--tests/manual/highdpi/screengadget/CMakeLists.txt4
-rw-r--r--tests/manual/highdpi/screengadget/main.cpp2
-rw-r--r--tests/manual/iconbrowser/CMakeLists.txt64
-rw-r--r--tests/manual/iconbrowser/Main.qml31
-rw-r--r--tests/manual/iconbrowser/main.cpp570
-rw-r--r--tests/manual/inputdevices/CMakeLists.txt2
-rw-r--r--tests/manual/inputdevices/inputdevicemodel.cpp2
-rw-r--r--tests/manual/inputdevices/inputdevicemodel.h2
-rw-r--r--tests/manual/inputdevices/main.cpp2
-rw-r--r--tests/manual/inputmethodhints/inputmethodhints.cpp2
-rw-r--r--tests/manual/inputmethodhints/inputmethodhints.h2
-rw-r--r--tests/manual/inputmethodhints/main.cpp2
-rw-r--r--tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/AppIcon1024x1024.pngbin0 -> 22579 bytes
-rw-r--r--tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/AppIcon167x167.pngbin0 -> 3369 bytes
-rw-r--r--tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/AppIcon60x60@2x.png (renamed from tests/manual/ios_assets/appicon/AppIcon60x60@2x.png)bin2335 -> 2335 bytes
-rw-r--r--tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/AppIcon76x76@2x~ipad.png (renamed from tests/manual/ios_assets/appicon/AppIcon76x76@2x~ipad.png)bin3060 -> 3060 bytes
-rw-r--r--tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/Contents.json (renamed from tests/manual/ios_assets/Assets.xcassets/AppIcon.appiconset/Contents.json)4
-rw-r--r--tests/manual/ios_assets/AssetsXcode13.0.xcassets/Contents.json (renamed from tests/manual/ios_assets/Assets.xcassets/Contents.json)0
-rw-r--r--tests/manual/ios_assets/AssetsXcode13.0.xcassets/Face.imageset/Contents.json (renamed from tests/manual/ios_assets/Assets.xcassets/Face.imageset/Contents.json)0
-rw-r--r--tests/manual/ios_assets/AssetsXcode13.0.xcassets/Face.imageset/Face-16.png (renamed from tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-16.png)bin173 -> 173 bytes
-rw-r--r--tests/manual/ios_assets/AssetsXcode13.0.xcassets/Face.imageset/Face-32.png (renamed from tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-32.png)bin407 -> 407 bytes
-rw-r--r--tests/manual/ios_assets/AssetsXcode13.0.xcassets/Face.imageset/Face-48.png (renamed from tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-48.png)bin750 -> 750 bytes
-rw-r--r--tests/manual/ios_assets/AssetsXcode14.3.xcassets/AppIcon.appiconset/AppIcon1024x1024.pngbin0 -> 22579 bytes
-rw-r--r--tests/manual/ios_assets/AssetsXcode14.3.xcassets/AppIcon.appiconset/Contents.json104
-rw-r--r--tests/manual/ios_assets/AssetsXcode14.3.xcassets/Contents.json6
-rw-r--r--tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Contents.json23
-rw-r--r--tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Face-16.pngbin0 -> 173 bytes
-rw-r--r--tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Face-32.pngbin0 -> 407 bytes
-rw-r--r--tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Face-48.pngbin0 -> 750 bytes
-rw-r--r--tests/manual/ios_assets/CMakeLists.txt44
-rw-r--r--tests/manual/ios_assets/Info.ios.cmake.xcode.13.0.plist (renamed from tests/manual/ios_assets/Info.ios.cmake.plist)24
-rw-r--r--tests/manual/ios_assets/Info.ios.cmake.xcode.14.3.plist54
-rw-r--r--tests/manual/ios_assets/Info.ios.qmake.xcode.13.0.plist (renamed from tests/manual/ios_assets/Info.ios.qmake.plist)22
-rw-r--r--tests/manual/ios_assets/Info.ios.qmake.xcode.14.3.plist37
-rw-r--r--tests/manual/ios_assets/appicon/AppIcon29x29.pngbin340 -> 0 bytes
-rw-r--r--tests/manual/ios_assets/appicon/AppIcon29x29@2x.pngbin992 -> 0 bytes
-rw-r--r--tests/manual/ios_assets/appicon/AppIcon29x29@2x~ipad.pngbin992 -> 0 bytes
-rw-r--r--tests/manual/ios_assets/appicon/AppIcon29x29~ipad.pngbin340 -> 0 bytes
-rw-r--r--tests/manual/ios_assets/appicon/AppIcon40x40@2x.pngbin1444 -> 0 bytes
-rw-r--r--tests/manual/ios_assets/appicon/AppIcon40x40@2x~ipad.pngbin1444 -> 0 bytes
-rw-r--r--tests/manual/ios_assets/appicon/AppIcon40x40~ipad.pngbin585 -> 0 bytes
-rw-r--r--tests/manual/ios_assets/appicon/AppIcon50x50@2x~ipad.pngbin1913 -> 0 bytes
-rw-r--r--tests/manual/ios_assets/appicon/AppIcon50x50~ipad.pngbin794 -> 0 bytes
-rw-r--r--tests/manual/ios_assets/appicon/AppIcon57x57.pngbin967 -> 0 bytes
-rw-r--r--tests/manual/ios_assets/appicon/AppIcon57x57@2x.pngbin2200 -> 0 bytes
-rw-r--r--tests/manual/ios_assets/appicon/AppIcon72x72@2x~ipad.pngbin2897 -> 0 bytes
-rw-r--r--tests/manual/ios_assets/appicon/AppIcon72x72~ipad.pngbin1301 -> 0 bytes
-rw-r--r--tests/manual/ios_assets/appicon/AppIcon76x76~ipad.pngbin1386 -> 0 bytes
-rw-r--r--tests/manual/ios_assets/ios_assets.pro24
-rw-r--r--tests/manual/ios_assets/main.cpp2
-rw-r--r--tests/manual/ios_assets/utils.mm2
-rw-r--r--tests/manual/keyevents/CMakeLists.txt12
-rw-r--r--tests/manual/keyevents/keyevents.pro6
-rw-r--r--tests/manual/keyevents/main.cpp305
-rw-r--r--tests/manual/keypadnavigation/main.cpp2
-rw-r--r--tests/manual/lance/CMakeLists.txt2
-rw-r--r--tests/manual/lance/README4
-rw-r--r--tests/manual/lance/interactivewidget.cpp5
-rw-r--r--tests/manual/lance/interactivewidget.h2
-rw-r--r--tests/manual/lance/main.cpp2
-rw-r--r--tests/manual/lance/widgets.h2
-rw-r--r--tests/manual/manual.pro2
-rw-r--r--tests/manual/markdown/html2md.cpp2
-rw-r--r--tests/manual/network/ssl/client-auth/CMakeLists.txt24
-rw-r--r--tests/manual/network/ssl/client-auth/certs/.gitignore4
-rw-r--r--tests/manual/network/ssl/client-auth/certs/accepted-client.conf14
-rwxr-xr-xtests/manual/network/ssl/client-auth/certs/generate.sh33
-rw-r--r--tests/manual/network/ssl/client-auth/tst_manual_ssl_client_auth.cpp136
-rw-r--r--tests/manual/network_remote_stresstest/tst_network_remote_stresstest.cpp2
-rw-r--r--tests/manual/network_stresstest/minihttpserver.cpp2
-rw-r--r--tests/manual/network_stresstest/minihttpserver.h2
-rw-r--r--tests/manual/network_stresstest/tst_network_stresstest.cpp2
-rw-r--r--tests/manual/permissions/android/AndroidManifest.xml8
-rw-r--r--tests/manual/permissions/tst_qpermissions.cpp2
-rw-r--r--tests/manual/qcursor/allcursors/main.cpp2
-rw-r--r--tests/manual/qcursor/allcursors/mainwindow.cpp2
-rw-r--r--tests/manual/qcursor/allcursors/mainwindow.h2
-rw-r--r--tests/manual/qcursor/childwidget/main.cpp2
-rw-r--r--tests/manual/qcursor/childwindow/main.cpp2
-rw-r--r--tests/manual/qcursor/childwindowcontainer/main.cpp2
-rw-r--r--tests/manual/qcursor/grab_override/main.cpp2
-rw-r--r--tests/manual/qcursor/grab_override/mainwindow.cpp2
-rw-r--r--tests/manual/qcursor/grab_override/mainwindow.h2
-rw-r--r--tests/manual/qcursor/qcursorhighdpi/main.cpp2
-rw-r--r--tests/manual/qdesktopservices/tst_qdesktopservices.cpp2
-rw-r--r--tests/manual/qdnslookup/CMakeLists.txt9
-rw-r--r--tests/manual/qdnslookup/main.cpp241
-rw-r--r--tests/manual/qglyphruns/controller.cpp2
-rw-r--r--tests/manual/qglyphruns/controller.h2
-rw-r--r--tests/manual/qglyphruns/glyphruninspector.cpp2
-rw-r--r--tests/manual/qglyphruns/glyphruninspector.h2
-rw-r--r--tests/manual/qglyphruns/main.cpp2
-rw-r--r--tests/manual/qglyphruns/singleglyphrun.cpp2
-rw-r--r--tests/manual/qglyphruns/singleglyphrun.h2
-rw-r--r--tests/manual/qglyphruns/view.cpp2
-rw-r--r--tests/manual/qglyphruns/view.h2
-rw-r--r--tests/manual/qgraphicsitem/main.cpp2
-rw-r--r--tests/manual/qgraphicsitemgroup/CMakeLists.txt2
-rw-r--r--tests/manual/qgraphicsitemgroup/customitem.cpp10
-rw-r--r--tests/manual/qgraphicsitemgroup/customitem.h2
-rw-r--r--tests/manual/qgraphicsitemgroup/main.cpp2
-rw-r--r--tests/manual/qgraphicsitemgroup/widget.cpp16
-rw-r--r--tests/manual/qgraphicsitemgroup/widget.h2
-rw-r--r--tests/manual/qgraphicslayout/anchorlayout/main.cpp2
-rw-r--r--tests/manual/qgraphicslayout/flicker/main.cpp2
-rw-r--r--tests/manual/qgraphicslayout/flicker/window.cpp2
-rw-r--r--tests/manual/qgraphicslayout/flicker/window.h2
-rw-r--r--tests/manual/qgraphicslayout/weatheranchorlayout/main.cpp2
-rw-r--r--tests/manual/qhttpnetworkconnection/main.cpp2
-rw-r--r--tests/manual/qimagereader/CMakeLists.txt9
-rw-r--r--tests/manual/qimagereader/main.cpp8
-rw-r--r--tests/manual/qimagereader/qimagereader.pro1
-rw-r--r--tests/manual/qimagereader/qimagereader.qrc5
-rw-r--r--tests/manual/qlayout/gridwidget.cpp2
-rw-r--r--tests/manual/qlayout/gridwidget.h2
-rw-r--r--tests/manual/qlayout/hbwidget.cpp2
-rw-r--r--tests/manual/qlayout/hbwidget.h2
-rw-r--r--tests/manual/qlayout/main.cpp2
-rw-r--r--tests/manual/qlayout/mainwindow.cpp2
-rw-r--r--tests/manual/qlayout/mainwindow.h2
-rw-r--r--tests/manual/qlayout/vbwidget.cpp2
-rw-r--r--tests/manual/qlayout/vbwidget.h2
-rw-r--r--tests/manual/qlocale/CMakeLists.txt2
-rw-r--r--tests/manual/qlocale/calendar.cpp2
-rw-r--r--tests/manual/qlocale/calendar.h2
-rw-r--r--tests/manual/qlocale/currency.cpp2
-rw-r--r--tests/manual/qlocale/currency.h2
-rw-r--r--tests/manual/qlocale/dateformats.cpp2
-rw-r--r--tests/manual/qlocale/dateformats.h2
-rw-r--r--tests/manual/qlocale/info.cpp2
-rw-r--r--tests/manual/qlocale/info.h2
-rw-r--r--tests/manual/qlocale/languages.cpp4
-rw-r--r--tests/manual/qlocale/languages.h2
-rw-r--r--tests/manual/qlocale/main.cpp2
-rw-r--r--tests/manual/qlocale/miscellaneous.cpp2
-rw-r--r--tests/manual/qlocale/miscellaneous.h2
-rw-r--r--tests/manual/qlocale/numberformats.cpp2
-rw-r--r--tests/manual/qlocale/numberformats.h2
-rw-r--r--tests/manual/qlocale/window.cpp6
-rw-r--r--tests/manual/qlocale/window.h2
-rw-r--r--tests/manual/qmetatype/declare_metatype_noninline.cpp2
-rw-r--r--tests/manual/qmetatype/declare_metatype_noninline.h2
-rw-r--r--tests/manual/qmetatype/tst_qmetatype.cpp2
-rw-r--r--tests/manual/qmimedatabase/main.cpp4
-rw-r--r--tests/manual/qnetconmonitor/tst_qnetconmonitor.cpp2
-rw-r--r--tests/manual/qnetworkaccessmanager/qget/CMakeLists.txt3
-rw-r--r--tests/manual/qnetworkaccessmanager/qget/downloadmanager.cpp4
-rw-r--r--tests/manual/qnetworkaccessmanager/qget/qget.cpp4
-rw-r--r--tests/manual/qnetworkaccessmanager/qget/qget.h2
-rw-r--r--tests/manual/qnetworkaccessmanager/qget/transferitem.cpp2
-rw-r--r--tests/manual/qnetworkinformation/mainwindow.h2
-rw-r--r--tests/manual/qnetworkinformation/tst_qnetworkinformation.cpp2
-rw-r--r--tests/manual/qnetworkreply/main.cpp2
-rw-r--r--tests/manual/qopenglcontext/main.cpp2
-rw-r--r--tests/manual/qopenglcontext/qopenglcontextwindow.cpp2
-rw-r--r--tests/manual/qopenglcontext/qopenglcontextwindow.h2
-rw-r--r--tests/manual/qopengltextureblitter/main.cpp2
-rw-r--r--tests/manual/qopengltextureblitter/qopengltextureblitwindow.cpp2
-rw-r--r--tests/manual/qopengltextureblitter/qopengltextureblitwindow.h2
-rw-r--r--tests/manual/qopenglwidget/dockedopenglwidget/fshader.glsl2
-rw-r--r--tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.cpp2
-rw-r--r--tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.h2
-rw-r--r--tests/manual/qopenglwidget/dockedopenglwidget/main.cpp2
-rw-r--r--tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.cpp2
-rw-r--r--tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.h2
-rw-r--r--tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.cpp2
-rw-r--r--tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.h2
-rw-r--r--tests/manual/qopenglwidget/dockedopenglwidget/vshader.glsl2
-rw-r--r--tests/manual/qopenglwidget/openglwidget/CMakeLists.txt2
-rw-r--r--tests/manual/qopenglwidget/openglwidget/main.cpp15
-rw-r--r--tests/manual/qopenglwidget/openglwidget/openglwidget.cpp2
-rw-r--r--tests/manual/qopenglwidget/openglwidget/openglwidget.h2
-rw-r--r--tests/manual/qopenglwindow/multiwindow/main.cpp2
-rw-r--r--tests/manual/qprintdevice_dump/main.cpp4
-rw-r--r--tests/manual/qscreen/CMakeLists.txt2
-rw-r--r--tests/manual/qscreen/main.cpp4
-rw-r--r--tests/manual/qscreen/propertyfield.cpp2
-rw-r--r--tests/manual/qscreen/propertyfield.h2
-rw-r--r--tests/manual/qscreen/propertywatcher.cpp6
-rw-r--r--tests/manual/qscreen/propertywatcher.h2
-rw-r--r--tests/manual/qscreen_xrandr/tst_qscreen_xrandr.cpp2
-rw-r--r--tests/manual/qssloptions/main.cpp2
-rw-r--r--tests/manual/qsslsocket/main.cpp17
-rw-r--r--tests/manual/qstorageinfo/main.cpp4
-rw-r--r--tests/manual/qstorageinfo/printvolumes.cpp28
-rw-r--r--tests/manual/qsysinfo/main.cpp2
-rw-r--r--tests/manual/qt_poll/tst_qt_poll.cpp2
-rw-r--r--tests/manual/qtabbar/main.cpp2
-rw-r--r--tests/manual/qtabbar/tabbarform.cpp2
-rw-r--r--tests/manual/qtabbar/tabbarform.h2
-rw-r--r--tests/manual/qtabletevent/CMakeLists.txt2
-rw-r--r--tests/manual/qtabletevent/device_information/CMakeLists.txt28
-rw-r--r--tests/manual/qtabletevent/device_information/main.cpp2
-rw-r--r--tests/manual/qtabletevent/device_information/tabletwidget.cpp4
-rw-r--r--tests/manual/qtabletevent/device_information/tabletwidget.h2
-rw-r--r--tests/manual/qtabletevent/event_compression/main.cpp2
-rw-r--r--tests/manual/qtabletevent/event_compression/mousestatwidget.cpp2
-rw-r--r--tests/manual/qtabletevent/event_compression/mousestatwidget.h2
-rw-r--r--tests/manual/qtabletevent/regular_widgets/main.cpp2
-rw-r--r--tests/manual/qtbug-52641/main.cpp2
-rw-r--r--tests/manual/qtbug-8933/main.cpp2
-rw-r--r--tests/manual/qtbug-8933/widget.cpp2
-rw-r--r--tests/manual/qtbug-8933/widget.h2
-rw-r--r--tests/manual/qtextcursorinsert/CMakeLists.txt14
-rw-r--r--tests/manual/qtextcursorinsert/main.cpp14
-rw-r--r--tests/manual/qtextcursorinsert/widget.cpp188
-rw-r--r--tests/manual/qtextcursorinsert/widget.h38
-rw-r--r--tests/manual/qtextcursorinsert/widget.ui222
-rw-r--r--tests/manual/qtexteditlist/main.cpp2
-rw-r--r--tests/manual/qtexteditlist/widget.cpp2
-rw-r--r--tests/manual/qtexteditlist/widget.h2
-rw-r--r--tests/manual/qtexttableborders/main.cpp2
-rw-r--r--tests/manual/qtexttableborders/widget.cpp2
-rw-r--r--tests/manual/qtexttableborders/widget.h2
-rw-r--r--tests/manual/qtouchevent/main.cpp2
-rw-r--r--tests/manual/qtouchevent/touchwidget.cpp2
-rw-r--r--tests/manual/qtouchevent/touchwidget.h2
-rw-r--r--tests/manual/qvulkaninstance/main.cpp2
-rw-r--r--tests/manual/qwidget_zorder/main.cpp2
-rw-r--r--tests/manual/repaint/mainwindow/main.cpp6
-rw-r--r--tests/manual/repaint/scrollarea/main.cpp2
-rw-r--r--tests/manual/repaint/shared/shared.h18
-rw-r--r--tests/manual/repaint/splitter/main.cpp2
-rw-r--r--tests/manual/repaint/tableview/main.cpp2
-rw-r--r--tests/manual/repaint/task141091/main.cpp2
-rw-r--r--tests/manual/repaint/toplevel/main.cpp2
-rw-r--r--tests/manual/repaint/widget/main.cpp2
-rw-r--r--tests/manual/rhi/CMakeLists.txt4
-rw-r--r--tests/manual/rhi/compressedtexture_bc1/compressedtexture_bc1.cpp2
-rw-r--r--tests/manual/rhi/compressedtexture_bc1_subupload/compressedtexture_bc1_subupload.cpp2
-rwxr-xr-xtests/manual/rhi/computebuffer/buildshaders.bat2
-rw-r--r--tests/manual/rhi/computebuffer/computebuffer.cpp2
-rwxr-xr-xtests/manual/rhi/computeimage/buildshaders.bat2
-rw-r--r--tests/manual/rhi/computeimage/computeimage.cpp2
-rw-r--r--tests/manual/rhi/cubemap/buildshaders.bat2
-rw-r--r--tests/manual/rhi/cubemap/cubemap.cpp2
-rw-r--r--tests/manual/rhi/cubemap_render/buildshaders.bat2
-rw-r--r--tests/manual/rhi/cubemap_render/cubemap_render.cpp2
-rw-r--r--tests/manual/rhi/cubemap_scissor/cubemap_scissor.cpp2
-rw-r--r--tests/manual/rhi/displacement/buildshaders.bat2
-rw-r--r--tests/manual/rhi/displacement/displacement.cpp2
-rw-r--r--tests/manual/rhi/float16texture_with_compute/buildshaders.bat2
-rw-r--r--tests/manual/rhi/float16texture_with_compute/float16texture_with_compute.cpp2
-rw-r--r--tests/manual/rhi/floattexture/floattexture.cpp2
-rwxr-xr-xtests/manual/rhi/geometryshader/buildshaders.bat2
-rw-r--r--tests/manual/rhi/geometryshader/geometryshader.cpp2
-rw-r--r--tests/manual/rhi/hdr/CMakeLists.txt22
-rw-r--r--tests/manual/rhi/hdr/buildshaders.bat4
-rw-r--r--tests/manual/rhi/hdr/hdr.cpp457
-rw-r--r--tests/manual/rhi/hdr/hdrtexture.frag44
-rw-r--r--tests/manual/rhi/hdr/hdrtexture.frag.qsbbin0 -> 2742 bytes
-rw-r--r--tests/manual/rhi/hdr/hdrtexture.vert22
-rw-r--r--tests/manual/rhi/hdr/hdrtexture.vert.qsbbin0 -> 1471 bytes
-rw-r--r--tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.cpp4
-rw-r--r--tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.h2
-rw-r--r--tests/manual/rhi/hellominimalcrossgfxtriangle/main.cpp4
-rw-r--r--tests/manual/rhi/hellominimalcrossgfxtriangle/window.cpp6
-rw-r--r--tests/manual/rhi/hellominimalcrossgfxtriangle/window.h18
-rw-r--r--tests/manual/rhi/imguirenderer/CMakeLists.txt4
-rw-r--r--tests/manual/rhi/imguirenderer/imguirenderer.cpp2
-rw-r--r--tests/manual/rhi/instancing/buildshaders.bat2
-rw-r--r--tests/manual/rhi/instancing/instancing.cpp2
-rw-r--r--tests/manual/rhi/mrt/buildshaders.bat2
-rw-r--r--tests/manual/rhi/mrt/mrt.cpp2
-rw-r--r--tests/manual/rhi/msaarenderbuffer/msaarenderbuffer.cpp2
-rw-r--r--tests/manual/rhi/msaatexture/msaatexture.cpp2
-rw-r--r--tests/manual/rhi/msaatextureresolve/CMakeLists.txt41
-rw-r--r--tests/manual/rhi/msaatextureresolve/msaatextureresolve.cpp235
-rw-r--r--tests/manual/rhi/multiview/CMakeLists.txt21
-rw-r--r--tests/manual/rhi/multiview/buildshaders.bat6
-rw-r--r--tests/manual/rhi/multiview/multiview.cpp305
-rw-r--r--tests/manual/rhi/multiview/multiview.frag11
-rw-r--r--tests/manual/rhi/multiview/multiview.frag.qsbbin0 -> 2846 bytes
-rw-r--r--tests/manual/rhi/multiview/multiview.pro8
-rw-r--r--tests/manual/rhi/multiview/multiview.qrc8
-rw-r--r--tests/manual/rhi/multiview/multiview.vert21
-rw-r--r--tests/manual/rhi/multiview/multiview.vert.qsbbin0 -> 4083 bytes
-rw-r--r--tests/manual/rhi/multiview/texture.frag19
-rw-r--r--tests/manual/rhi/multiview/texture.frag.qsbbin0 -> 3595 bytes
-rw-r--r--tests/manual/rhi/multiview/texture.vert20
-rw-r--r--tests/manual/rhi/multiview/texture.vert.qsbbin0 -> 3683 bytes
-rw-r--r--tests/manual/rhi/multiwindow/multiwindow.cpp40
-rw-r--r--tests/manual/rhi/multiwindow_threaded/multiwindow_threaded.cpp39
-rw-r--r--tests/manual/rhi/multiwindow_threaded/window.cpp7
-rw-r--r--tests/manual/rhi/multiwindow_threaded/window.h3
-rw-r--r--tests/manual/rhi/noninstanced/buildshaders.bat2
-rw-r--r--tests/manual/rhi/noninstanced/noninstanced.cpp2
-rw-r--r--tests/manual/rhi/offscreen/offscreen.cpp44
-rwxr-xr-xtests/manual/rhi/polygonmode/buildshaders.bat2
-rw-r--r--tests/manual/rhi/polygonmode/polygonmode.cpp2
-rw-r--r--tests/manual/rhi/rhi.pro30
-rw-r--r--tests/manual/rhi/rhiwidgetproto/CMakeLists.txt (renamed from tests/manual/rhi/rhiwidget/CMakeLists.txt)8
-rw-r--r--tests/manual/rhi/rhiwidgetproto/examplewidget.cpp (renamed from tests/manual/rhi/rhiwidget/examplewidget.cpp)2
-rw-r--r--tests/manual/rhi/rhiwidgetproto/examplewidget.h (renamed from tests/manual/rhi/rhiwidget/examplewidget.h)4
-rw-r--r--tests/manual/rhi/rhiwidgetproto/main.cpp (renamed from tests/manual/rhi/rhiwidget/main.cpp)2
-rw-r--r--tests/manual/rhi/rhiwidgetproto/rhiwidget.cpp (renamed from tests/manual/rhi/rhiwidget/rhiwidget.cpp)11
-rw-r--r--tests/manual/rhi/rhiwidgetproto/rhiwidget.h (renamed from tests/manual/rhi/rhiwidget/rhiwidget.h)4
-rw-r--r--tests/manual/rhi/rhiwidgetproto/rhiwidget_p.h (renamed from tests/manual/rhi/rhiwidget/rhiwidget_p.h)2
-rw-r--r--tests/manual/rhi/shadowmap/buildshaders.bat2
-rw-r--r--tests/manual/rhi/shadowmap/shadowmap.cpp2
-rw-r--r--tests/manual/rhi/shared/buildshaders.bat10
-rw-r--r--tests/manual/rhi/shared/color.frag.qsbbin1135 -> 1173 bytes
-rw-r--r--tests/manual/rhi/shared/color.vert.qsbbin1235 -> 1282 bytes
-rw-r--r--tests/manual/rhi/shared/dds_bc1.h2
-rw-r--r--tests/manual/rhi/shared/examplefw.h37
-rw-r--r--tests/manual/rhi/shared/imgui/buildshaders.bat2
-rw-r--r--tests/manual/rhi/shared/imgui/imgui.frag18
-rw-r--r--tests/manual/rhi/shared/imgui/imgui.frag.qsbbin1671 -> 2596 bytes
-rw-r--r--tests/manual/rhi/shared/imgui/imgui.vert1
-rw-r--r--tests/manual/rhi/shared/imgui/imgui.vert.qsbbin1494 -> 1532 bytes
-rw-r--r--tests/manual/rhi/shared/imgui/qrhiimgui.cpp12
-rw-r--r--tests/manual/rhi/shared/imgui/qrhiimgui_p.h11
-rw-r--r--tests/manual/rhi/shared/texture.frag.qsbbin1289 -> 1341 bytes
-rw-r--r--tests/manual/rhi/shared/texture.vert.qsbbin1475 -> 1530 bytes
-rw-r--r--tests/manual/rhi/shared/texture_arr.frag.qsbbin1448 -> 1650 bytes
-rw-r--r--tests/manual/rhi/shared/texture_arr.vert.qsbbin1404 -> 1595 bytes
-rw-r--r--tests/manual/rhi/shared/texture_ms4.frag.qsbbin1725 -> 1739 bytes
-rw-r--r--tests/manual/rhi/stenciloutline/buildshaders.bat2
-rw-r--r--tests/manual/rhi/stenciloutline/stenciloutline.cpp2
-rw-r--r--tests/manual/rhi/stereo/main.cpp4
-rw-r--r--tests/manual/rhi/stereo/window.cpp71
-rw-r--r--tests/manual/rhi/stereo/window.h8
-rw-r--r--tests/manual/rhi/tessellation/buildshaders.bat2
-rw-r--r--tests/manual/rhi/tessellation/tessellation.cpp2
-rw-r--r--tests/manual/rhi/tex1d/CMakeLists.txt2
-rw-r--r--tests/manual/rhi/tex1d/buildshaders.bat2
-rw-r--r--tests/manual/rhi/tex1d/tex1d.cpp2
-rw-r--r--tests/manual/rhi/tex3d/buildshaders.bat2
-rw-r--r--tests/manual/rhi/tex3d/tex3d.cpp2
-rw-r--r--tests/manual/rhi/texturearray/texturearray.cpp2
-rw-r--r--tests/manual/rhi/texuploads/texuploads.cpp4
-rw-r--r--tests/manual/rhi/triquadcube/quadrenderer.cpp4
-rw-r--r--tests/manual/rhi/triquadcube/quadrenderer.h4
-rw-r--r--tests/manual/rhi/triquadcube/texturedcuberenderer.cpp4
-rw-r--r--tests/manual/rhi/triquadcube/texturedcuberenderer.h4
-rw-r--r--tests/manual/rhi/triquadcube/triangleoncuberenderer.cpp4
-rw-r--r--tests/manual/rhi/triquadcube/triangleoncuberenderer.h2
-rw-r--r--tests/manual/rhi/triquadcube/trianglerenderer.cpp4
-rw-r--r--tests/manual/rhi/triquadcube/trianglerenderer.h4
-rw-r--r--tests/manual/rhi/triquadcube/triquadcube.cpp22
-rw-r--r--tests/manual/shortcuts/main.cpp2
-rw-r--r--tests/manual/socketengine/main.cpp8
-rw-r--r--tests/manual/startsystemmove/main.cpp2
-rw-r--r--tests/manual/stereographicsview/main.cpp2
-rw-r--r--tests/manual/stereographicsview/mainwindow.cpp2
-rw-r--r--tests/manual/stereographicsview/mainwindow.h2
-rw-r--r--tests/manual/stereographicsview/mygraphicsview.cpp2
-rw-r--r--tests/manual/stereographicsview/mygraphicsview.h2
-rw-r--r--tests/manual/textrendering/codeeditor/codeeditor.cpp2
-rw-r--r--tests/manual/textrendering/codeeditor/codeeditor.h2
-rw-r--r--tests/manual/textrendering/codeeditor/main.cpp2
-rw-r--r--tests/manual/textrendering/glyphshaping/main.cpp4
-rw-r--r--tests/manual/textrendering/nativetext/main.cpp2
-rw-r--r--tests/manual/textrendering/textperformance/main.cpp4
-rw-r--r--tests/manual/touch/main.cpp2
-rw-r--r--tests/manual/touchGraphicsItem/main.cpp2
-rw-r--r--tests/manual/transientwindow/main.cpp2
-rw-r--r--tests/manual/transientwindow/mainwindow.cpp2
-rw-r--r--tests/manual/transientwindow/mainwindow.h2
-rw-r--r--tests/manual/triangulator/main.cpp2
-rw-r--r--tests/manual/triangulator/triviswidget.cpp2
-rw-r--r--tests/manual/triangulator/triviswidget.h2
-rw-r--r--tests/manual/unc/main.cpp2
-rw-r--r--tests/manual/wasm/CMakeLists.txt1
-rw-r--r--tests/manual/wasm/a11y/CMakeLists.txt2
-rw-r--r--tests/manual/wasm/a11y/basic_widgets/CMakeLists.txt2
-rw-r--r--tests/manual/wasm/a11y/basic_widgets/basic_widgets.html2
-rw-r--r--tests/manual/wasm/a11y/basic_widgets/basica11ywidget.cpp2
-rw-r--r--tests/manual/wasm/a11y/basic_widgets/basica11ywidget.h2
-rw-r--r--tests/manual/wasm/a11y/basic_widgets/main.cpp2
-rw-r--r--tests/manual/wasm/a11y/basic_widgets/tabswidget.cpp2
-rw-r--r--tests/manual/wasm/a11y/basic_widgets/tabswidget.h2
-rw-r--r--tests/manual/wasm/clipboard/CMakeLists.txt2
-rw-r--r--tests/manual/wasm/clipboard/main.cpp2
-rw-r--r--tests/manual/wasm/clipboard/mainwindow.cpp37
-rw-r--r--tests/manual/wasm/clipboard/mainwindow.h2
-rw-r--r--tests/manual/wasm/cursors/MainWindow.cpp2
-rw-r--r--tests/manual/wasm/cursors/MainWindow.h2
-rw-r--r--tests/manual/wasm/cursors/main.cpp2
-rw-r--r--tests/manual/wasm/eventloop/asyncify_exec/main.cpp2
-rw-r--r--tests/manual/wasm/eventloop/dialog_exec/main.cpp2
-rw-r--r--tests/manual/wasm/eventloop/eventloop_auto/CMakeLists.txt2
-rw-r--r--tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto.html2
-rw-r--r--tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto_asyncify.html2
-rw-r--r--tests/manual/wasm/eventloop/eventloop_auto/main.cpp2
-rw-r--r--tests/manual/wasm/eventloop/main_exec/main.cpp2
-rw-r--r--tests/manual/wasm/eventloop/main_noexec/main.cpp2
-rw-r--r--tests/manual/wasm/eventloop/thread_exec/main.cpp2
-rw-r--r--tests/manual/wasm/localfiles/main.cpp5
-rw-r--r--tests/manual/wasm/localfonts/CMakeLists.txt4
-rw-r--r--tests/manual/wasm/localfonts/fontloading/CMakeLists.txt20
-rw-r--r--tests/manual/wasm/localfonts/fontloading/fontloading.html167
-rw-r--r--tests/manual/wasm/localfonts/fontloading/main.cpp78
-rw-r--r--tests/manual/wasm/network/echo_client_mainthread/CMakeLists.txt2
-rw-r--r--tests/manual/wasm/network/echo_client_mainthread/main.cpp2
-rw-r--r--tests/manual/wasm/network/echo_client_secondarythread/CMakeLists.txt2
-rw-r--r--tests/manual/wasm/network/echo_client_secondarythread/main.cpp2
-rw-r--r--tests/manual/wasm/network/echo_server/CMakeLists.txt2
-rw-r--r--tests/manual/wasm/network/echo_server/main.cpp2
-rw-r--r--tests/manual/wasm/network/sockify_sockets_auto/CMakeLists.txt4
-rw-r--r--tests/manual/wasm/network/sockify_sockets_auto/main.cpp2
-rw-r--r--tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html2
-rw-r--r--tests/manual/wasm/qstdweb/CMakeLists.txt30
-rw-r--r--tests/manual/wasm/qstdweb/files_auto.html2
-rw-r--r--tests/manual/wasm/qstdweb/files_main.cpp2
-rw-r--r--tests/manual/wasm/qstdweb/iodevices_auto.html10
-rw-r--r--tests/manual/wasm/qstdweb/iodevices_main.cpp103
-rw-r--r--tests/manual/wasm/qstdweb/promise_auto.html2
-rw-r--r--tests/manual/wasm/qstdweb/promise_main.cpp2
-rw-r--r--tests/manual/wasm/qstdweb/qwasmcompositor_auto.html2
-rw-r--r--tests/manual/wasm/qstdweb/qwasmcompositor_main.cpp16
-rw-r--r--tests/manual/wasm/qtloader_integration/CMakeLists.txt45
-rw-r--r--tests/manual/wasm/qtloader_integration/main.cpp183
-rw-r--r--tests/manual/wasm/qtloader_integration/preload.json10
-rw-r--r--tests/manual/wasm/qtloader_integration/test_body.js517
-rw-r--r--tests/manual/wasm/qtloader_integration/tst_qtloader_integration.html13
-rw-r--r--tests/manual/wasm/qtwasmtestlib/README.md4
-rw-r--r--tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp2
-rw-r--r--tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.h2
-rw-r--r--tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.js12
-rw-r--r--tests/manual/wasm/qwasmwindow/CMakeLists.txt36
-rw-r--r--tests/manual/wasm/qwasmwindow/qwasmwindow.py445
-rw-r--r--tests/manual/wasm/qwasmwindow/qwasmwindow_harness.cpp149
-rw-r--r--tests/manual/wasm/qwasmwindow/qwasmwindow_harness.html66
-rwxr-xr-xtests/manual/wasm/qwasmwindow/run.sh23
-rw-r--r--tests/manual/wasm/rasterwindow/main.cpp2
-rw-r--r--tests/manual/wasm/rasterwindow/rasterwindow.cpp2
-rw-r--r--tests/manual/wasm/rasterwindow/rasterwindow.h2
-rwxr-xr-xtests/manual/wasm/shared/run.sh2
-rw-r--r--tests/manual/wasm/shared/testrunner.js91
-rw-r--r--tests/manual/widgetgrab/main.cpp2
-rw-r--r--tests/manual/widgets/itemviews/autoResizePrecision/tablehorz/testtable1.cpp2
-rw-r--r--tests/manual/widgets/itemviews/autoResizePrecision/tablevert/testtable2.cpp2
-rw-r--r--tests/manual/widgets/itemviews/autoResizePrecision/treeview/testtree.cpp2
-rw-r--r--tests/manual/widgets/itemviews/delegate/example.cpp8
-rw-r--r--tests/manual/widgets/itemviews/qconcatenatetablesproxymodel/main.cpp2
-rw-r--r--tests/manual/widgets/itemviews/qheaderview/qheaderviewtest1.cpp2
-rw-r--r--tests/manual/widgets/itemviews/qtreeview/main.cpp2
-rw-r--r--tests/manual/widgets/itemviews/qtreewidget/main.cpp2
-rw-r--r--tests/manual/widgets/itemviews/tableview-span-navigation/main.cpp2
-rw-r--r--tests/manual/widgets/kernel/CMakeLists.txt2
-rw-r--r--tests/manual/widgets/kernel/layoutreplace/main.cpp2
-rw-r--r--tests/manual/widgets/kernel/qtooltip/main.cpp2
-rw-r--r--tests/manual/widgets/kernel/setscreen/main.cpp2
-rw-r--r--tests/manual/widgets/kernel/sizeonhide/main.cpp2
-rw-r--r--tests/manual/widgets/qgraphicsview/rubberband/rubberbandtest.cpp2
-rw-r--r--tests/manual/widgets/styles/main.cpp2
-rw-r--r--tests/manual/widgets/widgets/bigmenucreator/main.cpp2
-rw-r--r--tests/manual/widgets/widgets/bigmenucreator/mainwindow.cpp2
-rw-r--r--tests/manual/widgets/widgets/bigmenucreator/mainwindow.h2
-rw-r--r--tests/manual/widgets/widgets/defaultUpMenuBar/main.cpp2
-rw-r--r--tests/manual/widgets/widgets/multiscreen-menus/main.cpp2
-rw-r--r--tests/manual/widgets/widgets/multiscreen-menus/mainwindow.cpp2
-rw-r--r--tests/manual/widgets/widgets/multiscreen-menus/mainwindow.h2
-rw-r--r--tests/manual/widgets/widgets/qmainwindow/saveStateSize/main.cpp2
-rw-r--r--tests/manual/widgets/widgets/qtabbar/stylesheet/main.cpp2
-rw-r--r--tests/manual/widgets/widgets/qtoolbutton/menuOnMultiScreens/main.cpp2
-rw-r--r--tests/manual/windowactivation/main.cpp2
-rw-r--r--tests/manual/windowchildgeometry/controllerwidget.cpp2
-rw-r--r--tests/manual/windowchildgeometry/controllerwidget.h2
-rw-r--r--tests/manual/windowchildgeometry/main.cpp2
-rw-r--r--tests/manual/windowflags/CMakeLists.txt2
-rw-r--r--tests/manual/windowflags/controllerwindow.cpp2
-rw-r--r--tests/manual/windowflags/controllerwindow.h2
-rw-r--r--tests/manual/windowflags/controls.cpp4
-rw-r--r--tests/manual/windowflags/controls.h2
-rw-r--r--tests/manual/windowflags/main.cpp2
-rw-r--r--tests/manual/windowflags/previewwindow.cpp2
-rw-r--r--tests/manual/windowflags/previewwindow.h2
-rw-r--r--tests/manual/windowgeometry/controllerwidget.cpp2
-rw-r--r--tests/manual/windowgeometry/controllerwidget.h2
-rw-r--r--tests/manual/windowgeometry/main.cpp2
-rw-r--r--tests/manual/windowmask/CMakeLists.txt14
-rw-r--r--tests/manual/windowmask/main.cpp124
-rw-r--r--tests/manual/windowmodality/main.cpp2
-rw-r--r--tests/manual/windowtransparency/windowtransparency.cpp2
-rw-r--r--tests/manual/xcb_gl_integration/main.cpp2
-rw-r--r--tests/manual/xembed/CMakeLists.txt2
-rw-r--r--tests/manual/xembed/gtk-container/CMakeLists.txt2
-rw-r--r--tests/manual/xembed/gtk-container/gtk-container.cpp2
-rw-r--r--tests/manual/xembed/qt-client-raster/main.cpp2
-rw-r--r--tests/manual/xembed/qt-client-raster/rasterwindow.cpp2
-rw-r--r--tests/manual/xembed/qt-client-raster/rasterwindow.h2
-rw-r--r--tests/manual/xembed/qt-client-widget/main.cpp2
-rw-r--r--tests/manual/xembed/qt-client-widget/window.cpp2
-rw-r--r--tests/manual/xembed/qt-client-widget/window.h2
-rw-r--r--tests/manual/xmlstreamlint/main.cpp2
1186 files changed, 37961 insertions, 1537 deletions
diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt
index 71dd87f575..f8c7ecb07f 100644
--- a/tests/manual/CMakeLists.txt
+++ b/tests/manual/CMakeLists.txt
@@ -6,18 +6,24 @@ if(UIKIT)
return()
endif()
+add_subdirectory(corelib)
add_subdirectory(filetest)
# diaglib is broken in dev due to missing
# QtOpenGL/QGLFunctions headers
# add_subdirectory(embeddedintoforeignwindow)
# add_subdirectory(foreignwindows)
add_subdirectory(gestures)
+if (QT_FEATURE_graphicsframecapture)
+ add_subdirectory(graphicsframecapture)
+endif()
add_subdirectory(highdpi)
add_subdirectory(inputmethodhints)
add_subdirectory(keypadnavigation)
-#add_subdirectory(lance) # special case qgl.h missing
+add_subdirectory(keyevents)
+#add_subdirectory(lance) # qgl.h missing
add_subdirectory(qcursor)
add_subdirectory(qdesktopservices)
+add_subdirectory(qdnslookup)
add_subdirectory(qgraphicsitem)
add_subdirectory(qgraphicsitemgroup)
add_subdirectory(qgraphicslayout/flicker)
@@ -29,11 +35,9 @@ add_subdirectory(qmimedatabase)
add_subdirectory(qnetconmonitor)
add_subdirectory(qnetworkaccessmanager/qget)
add_subdirectory(qnetworkinformation)
-#special case begin
if (QT_FEATURE_openssl AND UNIX)
add_subdirectory(qnetworkreply)
endif()
-#special case end
if(QT_FEATURE_permissions)
add_subdirectory(permissions)
endif()
@@ -64,6 +68,7 @@ endif()
add_subdirectory(xmlstreamlint)
add_subdirectory(shortcuts)
add_subdirectory(dialogs)
+add_subdirectory(windowmask)
add_subdirectory(windowtransparency)
add_subdirectory(unc)
add_subdirectory(qtabbar)
@@ -76,10 +81,10 @@ if(QT_FEATURE_openssl)
add_subdirectory(qssloptions)
endif()
if(QT_FEATURE_opengl)
- # add_subdirectory(qopengltextureblitter) special case broken in dev
+ # add_subdirectory(qopengltextureblitter) # TODO: broken in dev
endif()
if(QT_FEATURE_egl AND QT_FEATURE_opengl)
- # add_subdirectory(qopenglcontext) # special case broken in dev
+ # add_subdirectory(qopenglcontext) # TODO: broken in dev
endif()
if(QT_FEATURE_vulkan)
add_subdirectory(qvulkaninstance)
diff --git a/tests/manual/android_content_uri/CMakeLists.txt b/tests/manual/android_content_uri/CMakeLists.txt
index a8a815fd94..368e023ba6 100644
--- a/tests/manual/android_content_uri/CMakeLists.txt
+++ b/tests/manual/android_content_uri/CMakeLists.txt
@@ -1,3 +1,11 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(android_content_uri LANGUAGES CXX)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_content_uris
SOURCES
tst_content_uris.cpp
diff --git a/tests/manual/android_content_uri/tst_content_uris.cpp b/tests/manual/android_content_uri/tst_content_uris.cpp
index 089be79bc3..b9a150a447 100644
--- a/tests/manual/android_content_uri/tst_content_uris.cpp
+++ b/tests/manual/android_content_uri/tst_content_uris.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QDirIterator>
diff --git a/tests/manual/cmake/test_copy_file_if_different_command/main.cpp b/tests/manual/cmake/test_copy_file_if_different_command/main.cpp
index 2e49d48b37..eeb641a36b 100644
--- a/tests/manual/cmake/test_copy_file_if_different_command/main.cpp
+++ b/tests/manual/cmake/test_copy_file_if_different_command/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <fstream>
#include <iostream>
diff --git a/tests/manual/cocoa/appicon/main.cpp b/tests/manual/cocoa/appicon/main.cpp
index 28ff440d82..750f5e2f72 100644
--- a/tests/manual/cocoa/appicon/main.cpp
+++ b/tests/manual/cocoa/appicon/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QPushButton>
diff --git a/tests/manual/cocoa/menurama/CMakeLists.txt b/tests/manual/cocoa/menurama/CMakeLists.txt
index 469bf9fbd2..3519fadcf9 100644
--- a/tests/manual/cocoa/menurama/CMakeLists.txt
+++ b/tests/manual/cocoa/menurama/CMakeLists.txt
@@ -11,6 +11,8 @@ qt_internal_add_manual_test(Menurama
main.cpp
mainwindow.cpp mainwindow.h mainwindow.ui
menuramaapplication.cpp menuramaapplication.h
+ NO_PCH_SOURCES
+ menuramaapplication.cpp # undef QT_NO_FOREACH
LIBRARIES
Qt::Gui
Qt::Widgets
diff --git a/tests/manual/cocoa/menurama/main.cpp b/tests/manual/cocoa/menurama/main.cpp
index 4aa8b74b8f..9d3ea2fad5 100644
--- a/tests/manual/cocoa/menurama/main.cpp
+++ b/tests/manual/cocoa/menurama/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
#include "menuramaapplication.h"
diff --git a/tests/manual/cocoa/menurama/mainwindow.cpp b/tests/manual/cocoa/menurama/mainwindow.cpp
index a141c759f7..2bd761374d 100644
--- a/tests/manual/cocoa/menurama/mainwindow.cpp
+++ b/tests/manual/cocoa/menurama/mainwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
#include "ui_mainwindow.h"
diff --git a/tests/manual/cocoa/menurama/mainwindow.h b/tests/manual/cocoa/menurama/mainwindow.h
index 91ab4bfad7..bb8a2a3996 100644
--- a/tests/manual/cocoa/menurama/mainwindow.h
+++ b/tests/manual/cocoa/menurama/mainwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
diff --git a/tests/manual/cocoa/menurama/menuramaapplication.cpp b/tests/manual/cocoa/menurama/menuramaapplication.cpp
index e3f7803aa7..43b6bd8ad4 100644
--- a/tests/manual/cocoa/menurama/menuramaapplication.cpp
+++ b/tests/manual/cocoa/menurama/menuramaapplication.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "menuramaapplication.h"
diff --git a/tests/manual/cocoa/menurama/menuramaapplication.h b/tests/manual/cocoa/menurama/menuramaapplication.h
index 226bf11851..7670445024 100644
--- a/tests/manual/cocoa/menurama/menuramaapplication.h
+++ b/tests/manual/cocoa/menurama/menuramaapplication.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MENURAMAAPPLICATION_H
#define MENURAMAAPPLICATION_H
diff --git a/tests/manual/cocoa/menus/main.cpp b/tests/manual/cocoa/menus/main.cpp
index bca96f4c6e..f0da9a6ced 100644
--- a/tests/manual/cocoa/menus/main.cpp
+++ b/tests/manual/cocoa/menus/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2012 KDAB
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QMainWindow>
#include <QMenu>
diff --git a/tests/manual/cocoa/nativewidgets/main.cpp b/tests/manual/cocoa/nativewidgets/main.cpp
index 2afea341e2..cad8449069 100644
--- a/tests/manual/cocoa/nativewidgets/main.cpp
+++ b/tests/manual/cocoa/nativewidgets/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore>
#include <QtWidgets>
diff --git a/tests/manual/cocoa/popups/main.cpp b/tests/manual/cocoa/popups/main.cpp
index f54950f70a..7c28795cab 100644
--- a/tests/manual/cocoa/popups/main.cpp
+++ b/tests/manual/cocoa/popups/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
diff --git a/tests/manual/cocoa/qsystemtrayicon/main.cpp b/tests/manual/cocoa/qsystemtrayicon/main.cpp
index b75bb54f90..0346398c10 100644
--- a/tests/manual/cocoa/qsystemtrayicon/main.cpp
+++ b/tests/manual/cocoa/qsystemtrayicon/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
int main(int argc, char**argv)
diff --git a/tests/manual/cocoa/qt_on_cocoa/main.mm b/tests/manual/cocoa/qt_on_cocoa/main.mm
index bd54484409..7c48e5acbd 100644
--- a/tests/manual/cocoa/qt_on_cocoa/main.mm
+++ b/tests/manual/cocoa/qt_on_cocoa/main.mm
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "rasterwindow.h"
diff --git a/tests/manual/cocoa/qt_on_cocoa/rasterwindow.cpp b/tests/manual/cocoa/qt_on_cocoa/rasterwindow.cpp
index 72802d6a29..af84b599f5 100644
--- a/tests/manual/cocoa/qt_on_cocoa/rasterwindow.cpp
+++ b/tests/manual/cocoa/qt_on_cocoa/rasterwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "rasterwindow.h"
diff --git a/tests/manual/cocoa/qt_on_cocoa/rasterwindow.h b/tests/manual/cocoa/qt_on_cocoa/rasterwindow.h
index c964078b28..a5260449be 100644
--- a/tests/manual/cocoa/qt_on_cocoa/rasterwindow.h
+++ b/tests/manual/cocoa/qt_on_cocoa/rasterwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QRasterWindow>
#include <QImage>
diff --git a/tests/manual/cocoa/wheelevent/main.cpp b/tests/manual/cocoa/wheelevent/main.cpp
index a40f52a0b9..9d302c1d9a 100644
--- a/tests/manual/cocoa/wheelevent/main.cpp
+++ b/tests/manual/cocoa/wheelevent/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore>
#include <QtWidgets>
diff --git a/tests/manual/cocoa/wheelevent/window.cpp b/tests/manual/cocoa/wheelevent/window.cpp
index 4193fca316..cd3f1f04ac 100644
--- a/tests/manual/cocoa/wheelevent/window.cpp
+++ b/tests/manual/cocoa/wheelevent/window.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "window.h"
diff --git a/tests/manual/cocoa/wheelevent/window.h b/tests/manual/cocoa/wheelevent/window.h
index 0a97eec580..e85f70fefc 100644
--- a/tests/manual/cocoa/wheelevent/window.h
+++ b/tests/manual/cocoa/wheelevent/window.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QWindow>
#include <QImage>
diff --git a/tests/manual/corelib/CMakeLists.txt b/tests/manual/corelib/CMakeLists.txt
new file mode 100644
index 0000000000..8ed7441e77
--- /dev/null
+++ b/tests/manual/corelib/CMakeLists.txt
@@ -0,0 +1,6 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(qdatastream)
+add_subdirectory(time)
+add_subdirectory(tools)
diff --git a/tests/manual/corelib/qdatastream/CMakeLists.txt b/tests/manual/corelib/qdatastream/CMakeLists.txt
new file mode 100644
index 0000000000..5d26a862e5
--- /dev/null
+++ b/tests/manual/corelib/qdatastream/CMakeLists.txt
@@ -0,0 +1,15 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_manual_qdatastream Test:
+#####################################################################
+
+qt_internal_add_manual_test(tst_manual_qdatastream
+ SOURCES
+ tst_manualqdatastream.cpp
+ LIBRARIES
+ Qt::Core
+ ENABLE_AUTOGEN_TOOLS
+ uic
+)
diff --git a/tests/manual/corelib/qdatastream/qdatastream.pro b/tests/manual/corelib/qdatastream/qdatastream.pro
new file mode 100644
index 0000000000..9143f62851
--- /dev/null
+++ b/tests/manual/corelib/qdatastream/qdatastream.pro
@@ -0,0 +1,6 @@
+CONFIG += testcase
+
+SOURCES += tst_manualqdatastream.cpp
+QT = core testlib
+
+TARGET = tst_manual_qdatastream
diff --git a/tests/manual/corelib/qdatastream/tst_manualqdatastream.cpp b/tests/manual/corelib/qdatastream/tst_manualqdatastream.cpp
new file mode 100644
index 0000000000..37e5b80950
--- /dev/null
+++ b/tests/manual/corelib/qdatastream/tst_manualqdatastream.cpp
@@ -0,0 +1,234 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QDataStream>
+#include <QHash>
+#include <QList>
+#include <QMap>
+#include <QScopeGuard>
+#include <QSet>
+#include <QTest>
+
+// These tests are way too slow to be part of automatic unit tests
+class tst_QDataStream : public QObject
+{
+ Q_OBJECT
+
+ template <class T>
+ void fill(T &input);
+
+ void fill(QSet<qsizetype> &input);
+ void fill(QMap<qsizetype, qsizetype> &input);
+ void fill(QHash<qsizetype, qsizetype> &input);
+
+ template <class T>
+ void stream_big();
+
+public slots:
+ void initTestCase();
+
+private slots:
+ void stream_bigQString();
+ void stream_bigQByteArray();
+ void stream_bigQList();
+ void stream_bigQSet();
+ void stream_bigQMap();
+ void stream_bigQHash();
+ void stream_bigCString();
+};
+
+void tst_QDataStream::initTestCase()
+{
+ qputenv("QTEST_FUNCTION_TIMEOUT", "9000000");
+
+ QTest::addColumn<QDataStream::Version>("streamVersion");
+ QTest::addRow("current") << QDataStream::Qt_DefaultCompiledVersion;
+ QTest::addRow("Qt_6_6") << QDataStream::Qt_6_6;
+}
+
+template <class T>
+void tst_QDataStream::fill(T &input)
+{
+ constexpr qsizetype GiB = 1024 * 1024 * 1024;
+ constexpr qsizetype BaseSize = 4 * GiB + 1;
+ qDebug("Filling container with %lld entries", qint64(BaseSize));
+ QElapsedTimer timer;
+ timer.start();
+ try {
+ input.reserve(BaseSize);
+ input.resize(BaseSize, 'a');
+ } catch (const std::bad_alloc &) {
+ QSKIP("Could not allocate 4 GiB of RAM.");
+ }
+ qDebug("Created dataset in %lld ms", timer.elapsed());
+}
+
+void tst_QDataStream::fill(QSet<qsizetype> &input)
+{
+ constexpr qsizetype GiB = 1024 * 1024 * 1024;
+ constexpr qsizetype BaseSize = 4 * GiB + 1;
+ qDebug("Filling container with %lld entries", qint64(BaseSize));
+ QElapsedTimer timer;
+ timer.start();
+ try {
+ input.reserve(BaseSize);
+ for (qsizetype i = 0; i < BaseSize; ++i)
+ input.insert(i);
+ } catch (const std::bad_alloc &) {
+ QSKIP("Could not allocate 4 Gi entries.");
+ }
+ qDebug("Created dataset in %lld ms", timer.elapsed());
+}
+
+void tst_QDataStream::fill(QMap<qsizetype, qsizetype> &input)
+{
+ constexpr qsizetype GiB = 1024 * 1024 * 1024;
+ constexpr qsizetype BaseSize = 4 * GiB + 1;
+ qDebug("Filling container with %lld entries", qint64(BaseSize));
+ QElapsedTimer timer;
+ timer.start();
+ try {
+ for (qsizetype i = 0; i < BaseSize; ++i)
+ input.insert(i, i);
+ } catch (const std::bad_alloc &) {
+ QSKIP("Could not allocate 4 Gi entries.");
+ }
+ qDebug("Created dataset in %lld ms", timer.elapsed());
+}
+
+void tst_QDataStream::fill(QHash<qsizetype, qsizetype> &input)
+{
+ constexpr qsizetype GiB = 1024 * 1024 * 1024;
+ constexpr qsizetype BaseSize = 4 * GiB + 1;
+ qDebug("Filling container with %lld entries", qint64(BaseSize));
+ QElapsedTimer timer;
+ timer.start();
+ try {
+ input.reserve(BaseSize);
+ for (qsizetype i = 0; i < BaseSize; ++i)
+ input.emplace(i, i);
+ } catch (const std::bad_alloc &) {
+ QSKIP("Could not allocate 4 Gi entries.");
+ }
+ qDebug("Created dataset in %lld ms", timer.elapsed());
+}
+
+template <class T>
+void tst_QDataStream::stream_big()
+{
+#if QT_POINTER_SIZE > 4
+ QFETCH_GLOBAL(const QDataStream::Version, streamVersion);
+ QElapsedTimer timer;
+ T input;
+ fill(input);
+ QByteArray ba;
+ QDataStream inputstream(&ba, QIODevice::WriteOnly);
+ inputstream.setVersion(streamVersion);
+ timer.start();
+ try {
+ inputstream << input;
+ } catch (const std::bad_alloc &) {
+ QSKIP("Not enough memory to copy into QDataStream.");
+ }
+ qDebug("Streamed into QDataStream in %lld ms", timer.elapsed());
+ if (streamVersion < QDataStream::Qt_6_7) {
+ // old versions do not support data size more than 4 GiB
+ QCOMPARE(inputstream.status(), QDataStream::SizeLimitExceeded);
+ QVERIFY(ba.isEmpty());
+ } else {
+ T output;
+ QDataStream outputstream(ba);
+ timer.start();
+ try {
+ outputstream >> output;
+ } catch (const std::bad_alloc &) {
+ QSKIP("Not enough memory to copy out of QDataStream.");
+ }
+ qDebug("Streamed out of QDataStream in %lld ms", timer.elapsed());
+ QCOMPARE(input.size(), output.size());
+ QCOMPARE(input, output);
+ }
+#else
+ QSKIP("This test is 64-bit only.");
+#endif
+}
+
+void tst_QDataStream::stream_bigQString()
+{
+ stream_big<QString>();
+}
+
+void tst_QDataStream::stream_bigQByteArray()
+{
+ stream_big<QByteArray>();
+}
+void tst_QDataStream::stream_bigQList()
+{
+ stream_big<QList<char>>();
+}
+
+void tst_QDataStream::stream_bigQSet()
+{
+ stream_big<QSet<qsizetype>>();
+}
+
+void tst_QDataStream::stream_bigQMap()
+{
+ stream_big<QMap<qsizetype, qsizetype>>();
+}
+void tst_QDataStream::stream_bigQHash()
+{
+ stream_big<QHash<qsizetype, qsizetype>>();
+}
+
+void tst_QDataStream::stream_bigCString()
+{
+ if constexpr (sizeof(void*) == sizeof(int))
+ QSKIP("This test is 64-bit only.");
+
+ QFETCH_GLOBAL(const QDataStream::Version, streamVersion);
+ constexpr qint64 GiB = 1024 * 1024 * 1024;
+ constexpr qint64 BaseSize = 4 * GiB + 1;
+ std::string input;
+ qDebug("Creating an array with %lld entries", BaseSize);
+ QElapsedTimer timer;
+ timer.start();
+ try {
+ input.resize(BaseSize, 'a');
+ } catch (const std::bad_alloc &) {
+ QSKIP("Could not allocate 4 Gi + 2 entries.");
+ }
+ qDebug("Created dataset in %lld ms", timer.elapsed());
+ QByteArray ba;
+ QDataStream inputstream(&ba, QIODevice::WriteOnly);
+ inputstream.setVersion(streamVersion);
+ timer.start();
+ try {
+ inputstream << input.data();
+ } catch (const std::bad_alloc &) {
+ QSKIP("Not enough memory to copy into QDataStream.");
+ }
+ qDebug("Streamed into QDataStream in %lld ms", timer.elapsed());
+ if (streamVersion < QDataStream::Qt_6_7) {
+ // old versions do not support data size more than 4 GiB
+ QCOMPARE(inputstream.status(), QDataStream::SizeLimitExceeded);
+ QVERIFY(ba.isEmpty());
+ } else {
+ char *output = nullptr;
+ auto cleanup = qScopeGuard([&output] { delete [] output; });
+ QDataStream outputstream(ba);
+ timer.start();
+ try {
+ outputstream >> output;
+ } catch (const std::bad_alloc &) {
+ QSKIP("Not enough memory to copy out of QDataStream.");
+ }
+ qDebug("Streamed out of QDataStream in %lld ms", timer.elapsed());
+ QCOMPARE(qstrlen(output), input.size());
+ QVERIFY(memcmp(input.data(), output, BaseSize + 1) == 0);
+ }
+}
+
+QTEST_MAIN(tst_QDataStream)
+
+#include "tst_manualqdatastream.moc"
diff --git a/tests/manual/corelib/time/CMakeLists.txt b/tests/manual/corelib/time/CMakeLists.txt
new file mode 100644
index 0000000000..b8e6aa2f18
--- /dev/null
+++ b/tests/manual/corelib/time/CMakeLists.txt
@@ -0,0 +1,4 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(zonechange)
diff --git a/tests/manual/corelib/time/foreachzone b/tests/manual/corelib/time/foreachzone
new file mode 100755
index 0000000000..480679885a
--- /dev/null
+++ b/tests/manual/corelib/time/foreachzone
@@ -0,0 +1,30 @@
+#!/bin/sh
+# Usage: foreachzone command [args...]
+#
+# The command is run with eval, so can include embedded shell
+# metacharacters such as | and ||. It is run in a sub-shell, so can
+# change environment or cd to a different directory without
+# complicating later runs of the same command.
+#
+# It is run repeatedly, with the TZ environment variable set to each
+# timezone name in turn, excluding the copies of zoneinfo/ under its
+# posix/ and right/ sub-dirs. Symbolic links are included (as long as
+# they point to valid zone data).
+#
+# For example, in the top level of a build tree,
+# foreachzone ninja tst_qdate_check
+# will run all the QDate tests in every time zone (this may take some
+# time).
+
+DIR=/usr/share/zoneinfo
+[ -d "$DIR" ] || DIR=/usr/lib/zoneinfo
+
+find $DIR -type d \( -name posix -o -name right \) -prune -o \( -type f -o -type l \) -print \
+ | while read f
+do
+ # To filter out symlinks in zoneinfo/ itself, uncomment this line:
+ # echo "$f" | grep -wq 'zoneinfo/.*/' || [ ! -h "$f" ] || continue
+ # To skip all symlinks, omit the -L here:
+ file -L "$f" | grep -wq 'timezone data .*, version' || continue
+ ( export TZ=${f#*/zoneinfo/}; eval "$@" )
+done
diff --git a/tests/manual/corelib/time/zonechange/CMakeLists.txt b/tests/manual/corelib/time/zonechange/CMakeLists.txt
new file mode 100644
index 0000000000..474779849f
--- /dev/null
+++ b/tests/manual/corelib/time/zonechange/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_manual_test(zonechange
+ SOURCES
+ tst_zonechange.cpp
+ LIBRARIES
+ Qt::Core
+)
diff --git a/tests/manual/corelib/time/zonechange/tst_zonechange.cpp b/tests/manual/corelib/time/zonechange/tst_zonechange.cpp
new file mode 100644
index 0000000000..5ca33f1396
--- /dev/null
+++ b/tests/manual/corelib/time/zonechange/tst_zonechange.cpp
@@ -0,0 +1,60 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtCore/qcoreapplication.h>
+
+#include <QtCore/qdatetime.h>
+#if QT_CONFIG(timezone)
+#include <QtCore/qtimezone.h>
+#endif
+
+#include <chrono>
+#include <thread>
+
+using namespace std::chrono;
+
+bool distinct(const QDateTime &left, const QDateTime &right)
+{
+ if (left == right)
+ return false;
+
+ qInfo() << " Actual:" << left << "\nExpected:" << right;
+ return true;
+}
+
+// Exit status: 0 success, 1 test failed, 2 test not viable.
+int main(int argc, char **argv)
+{
+ // Other things may need this indirectly, so make sure it exists:
+ QCoreApplication ignored(argc, argv);
+ if (!qEnvironmentVariableIsEmpty("TZ")) {
+ qInfo("Environment variable TZ over-rides system setting; you need to clear it.");
+ return 2;
+ }
+
+ QDateTime date = QDateTime(QDate(2020, 2, 20), QTime(20, 20, 20));
+ QDateTime copy = date;
+ if (distinct(date, copy))
+ return 1;
+#if QT_CONFIG(timezone)
+ const auto prior = QTimeZone::systemTimeZoneId();
+#endif
+
+ qInfo("You have two minutes in which to change the system time-zone setting.");
+ std::this_thread::sleep_for(120s);
+#if QT_CONFIG(timezone)
+ if (QTimeZone::systemTimeZoneId() == prior) {
+ qInfo("Too slow.");
+ return 2;
+ }
+#endif
+
+ if (distinct(copy, date))
+ return 1;
+ QDateTime copy2 = copy.addMSecs(2);
+ QDateTime date2 = date.addMSecs(2);
+ if (distinct(copy2, date2))
+ return 1;
+
+ return 0;
+}
diff --git a/tests/manual/corelib/tools/CMakeLists.txt b/tests/manual/corelib/tools/CMakeLists.txt
new file mode 100644
index 0000000000..4300db8859
--- /dev/null
+++ b/tests/manual/corelib/tools/CMakeLists.txt
@@ -0,0 +1,12 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(customtype)
+add_subdirectory(customtypesending)
+# Needs conversion from qmake:
+# add_subdirectory(qhash)
+# add_subdirectory(qlist)
+# add_subdirectory(qmap)
+# add_subdirectory(qset)
+# add_subdirectory(qvarlengtharray)
+# add_subdirectory(qvector)
diff --git a/tests/manual/corelib/tools/customtype/CMakeLists.txt b/tests/manual/corelib/tools/customtype/CMakeLists.txt
new file mode 100644
index 0000000000..4cb1c024b3
--- /dev/null
+++ b/tests/manual/corelib/tools/customtype/CMakeLists.txt
@@ -0,0 +1,15 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+
+qt_internal_add_manual_test(customtype
+ GUI
+ SOURCES
+ main.cpp
+ message.cpp message.h
+ LIBRARIES
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
diff --git a/tests/manual/corelib/tools/customtype/customtype.pro b/tests/manual/corelib/tools/customtype/customtype.pro
new file mode 100644
index 0000000000..0a5a90f541
--- /dev/null
+++ b/tests/manual/corelib/tools/customtype/customtype.pro
@@ -0,0 +1,7 @@
+HEADERS = message.h
+SOURCES = main.cpp \
+ message.cpp
+QT += widgets
+INCLUDEPATH += .
+TARGET = customtype
+
diff --git a/tests/manual/corelib/tools/customtype/main.cpp b/tests/manual/corelib/tools/customtype/main.cpp
new file mode 100644
index 0000000000..2368db6ec0
--- /dev/null
+++ b/tests/manual/corelib/tools/customtype/main.cpp
@@ -0,0 +1,37 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QVariant>
+#include "message.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+ QStringList headers;
+ headers << "Subject: Hello World"
+ << "From: address@example.com";
+ QString body = "This is a test.\r\n";
+
+//! [printing a custom type]
+ Message message(body, headers);
+ qDebug() << "Original:" << message;
+//! [printing a custom type]
+
+//! [storing a custom value]
+ QVariant stored;
+ stored.setValue(message);
+//! [storing a custom value]
+
+ qDebug() << "Stored:" << stored;
+
+//! [retrieving a custom value]
+ Message retrieved = qvariant_cast<Message>(stored);
+ qDebug() << "Retrieved:" << retrieved;
+ retrieved = qvariant_cast<Message>(stored);
+ qDebug() << "Retrieved:" << retrieved;
+//! [retrieving a custom value]
+
+ return 0;
+}
diff --git a/tests/manual/corelib/tools/customtype/message.cpp b/tests/manual/corelib/tools/customtype/message.cpp
new file mode 100644
index 0000000000..221480d56f
--- /dev/null
+++ b/tests/manual/corelib/tools/customtype/message.cpp
@@ -0,0 +1,38 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "message.h"
+
+#include <QDebug>
+
+Message::Message(const QString &body, const QStringList &headers)
+ : m_body(body), m_headers(headers)
+{
+}
+
+//! [custom type streaming operator]
+QDebug operator<<(QDebug dbg, const Message &message)
+{
+ QDebugStateSaver saver(dbg);
+ QList<QStringView> pieces = message.body().split(u"\r\n", Qt::SkipEmptyParts);
+ if (pieces.isEmpty())
+ dbg.nospace() << "Message()";
+ else if (pieces.size() == 1)
+ dbg.nospace() << "Message(" << pieces.first() << ")";
+ else
+ dbg.nospace() << "Message(" << pieces.first() << " ...)";
+ return dbg;
+}
+//! [custom type streaming operator]
+
+//! [getter functions]
+QStringView Message::body() const
+{
+ return m_body;
+}
+
+QStringList Message::headers() const
+{
+ return m_headers;
+}
+//! [getter functions]
diff --git a/tests/manual/corelib/tools/customtype/message.h b/tests/manual/corelib/tools/customtype/message.h
new file mode 100644
index 0000000000..3b91938eb7
--- /dev/null
+++ b/tests/manual/corelib/tools/customtype/message.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+#include <QMetaType>
+#include <QStringList>
+
+//! [custom type definition]
+class Message
+{
+public:
+ Message() = default;
+ ~Message() = default;
+ Message(const Message &) = default;
+ Message &operator=(const Message &) = default;
+
+ Message(const QString &body, const QStringList &headers);
+
+ QStringView body() const;
+ QStringList headers() const;
+
+private:
+ QString m_body;
+ QStringList m_headers;
+};
+//! [custom type definition]
+
+//! [custom type meta-type declaration]
+Q_DECLARE_METATYPE(Message);
+//! [custom type meta-type declaration]
+
+//! [custom type streaming operator]
+QDebug operator<<(QDebug dbg, const Message &message);
+//! [custom type streaming operator]
+
+#endif
diff --git a/tests/manual/corelib/tools/customtypesending/CMakeLists.txt b/tests/manual/corelib/tools/customtypesending/CMakeLists.txt
new file mode 100644
index 0000000000..c22d5e23c1
--- /dev/null
+++ b/tests/manual/corelib/tools/customtypesending/CMakeLists.txt
@@ -0,0 +1,16 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+
+qt_internal_add_manual_test(customtypesending
+ GUI
+ SOURCES
+ main.cpp
+ message.cpp message.h
+ window.cpp window.h
+ LIBRARIES
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
diff --git a/tests/manual/corelib/tools/customtypesending/customtypesending.pro b/tests/manual/corelib/tools/customtypesending/customtypesending.pro
new file mode 100644
index 0000000000..d316787f76
--- /dev/null
+++ b/tests/manual/corelib/tools/customtypesending/customtypesending.pro
@@ -0,0 +1,9 @@
+HEADERS = message.h \
+ window.h
+SOURCES = main.cpp \
+ message.cpp \
+ window.cpp
+QT += widgets
+INCLUDEPATH += .
+TARGET = customtypesending
+
diff --git a/tests/manual/corelib/tools/customtypesending/main.cpp b/tests/manual/corelib/tools/customtypesending/main.cpp
new file mode 100644
index 0000000000..a8f95ffe8e
--- /dev/null
+++ b/tests/manual/corelib/tools/customtypesending/main.cpp
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QApplication>
+#include "message.h"
+#include "window.h"
+
+//! [main function]
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ QStringList headers;
+ headers << "Subject: Hello World"
+ << "From: address@example.com";
+ QString body = "This is a test.\r\n";
+ Message message(body, headers);
+
+ Window window1;
+ window1.setMessage(message);
+
+ Window window2;
+ QObject::connect(&window1, &Window::messageSent,
+ &window2, &Window::setMessage);
+ QObject::connect(&window2, &Window::messageSent,
+ &window1, &Window::setMessage);
+ window1.show();
+ window2.show();
+ return app.exec();
+}
+//! [main function]
diff --git a/tests/manual/corelib/tools/customtypesending/message.cpp b/tests/manual/corelib/tools/customtypesending/message.cpp
new file mode 100644
index 0000000000..a7a8722673
--- /dev/null
+++ b/tests/manual/corelib/tools/customtypesending/message.cpp
@@ -0,0 +1,19 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "message.h"
+
+Message::Message(const QString &body, const QStringList &headers)
+ : m_body(body), m_headers(headers)
+{
+}
+
+QString Message::body() const
+{
+ return m_body;
+}
+
+QStringList Message::headers() const
+{
+ return m_headers;
+}
diff --git a/tests/manual/corelib/tools/customtypesending/message.h b/tests/manual/corelib/tools/customtypesending/message.h
new file mode 100644
index 0000000000..b16c92f177
--- /dev/null
+++ b/tests/manual/corelib/tools/customtypesending/message.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+#include <QMetaType>
+#include <QStringList>
+
+//! [custom type definition]
+class Message
+{
+public:
+ Message() = default;
+ ~Message() = default;
+ Message(const Message &) = default;
+ Message &operator=(const Message &) = default;
+
+ Message(const QString &body, const QStringList &headers);
+
+ QString body() const;
+ QStringList headers() const;
+
+private:
+ QString m_body;
+ QStringList m_headers;
+};
+//! [custom type definition]
+
+//! [custom type meta-type declaration]
+Q_DECLARE_METATYPE(Message);
+//! [custom type meta-type declaration]
+
+#endif
diff --git a/tests/manual/corelib/tools/customtypesending/window.cpp b/tests/manual/corelib/tools/customtypesending/window.cpp
new file mode 100644
index 0000000000..fc158f10e6
--- /dev/null
+++ b/tests/manual/corelib/tools/customtypesending/window.cpp
@@ -0,0 +1,43 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtWidgets>
+#include "window.h"
+
+//! [Window constructor]
+Window::Window(QWidget *parent)
+ : QWidget(parent), editor(new QTextEdit(this))
+{
+ QPushButton *sendButton = new QPushButton(tr("&Send message"));
+
+ connect(sendButton, &QPushButton::clicked,
+ this, &Window::sendMessage);
+
+ QHBoxLayout *buttonLayout = new QHBoxLayout;
+ buttonLayout->addStretch();
+ buttonLayout->addWidget(sendButton);
+ buttonLayout->addStretch();
+
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ layout->addWidget(editor);
+ layout->addLayout(buttonLayout);
+
+ setWindowTitle(tr("Custom Type Sending"));
+}
+//! [Window constructor]
+
+//! [sending a message]
+void Window::sendMessage()
+{
+ thisMessage = Message(editor->toPlainText(), thisMessage.headers());
+ emit messageSent(thisMessage);
+}
+//! [sending a message]
+
+//! [receiving a message]
+void Window::setMessage(const Message &message)
+{
+ thisMessage = message;
+ editor->setPlainText(thisMessage.body());
+}
+//! [receiving a message]
diff --git a/tests/manual/corelib/tools/customtypesending/window.h b/tests/manual/corelib/tools/customtypesending/window.h
new file mode 100644
index 0000000000..974e7a7629
--- /dev/null
+++ b/tests/manual/corelib/tools/customtypesending/window.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include <QWidget>
+#include "message.h"
+
+QT_FORWARD_DECLARE_CLASS(QTextEdit)
+
+//! [Window class definition]
+class Window : public QWidget
+{
+ Q_OBJECT
+
+public:
+ Window(QWidget *parent = nullptr);
+
+signals:
+ void messageSent(const Message &message);
+
+public slots:
+ void setMessage(const Message &message);
+
+private slots:
+ void sendMessage();
+
+private:
+ Message thisMessage;
+ QTextEdit *editor;
+};
+//! [Window class definition]
+
+#endif
diff --git a/tests/manual/corelib/tools/qhash/main.cpp b/tests/manual/corelib/tools/qhash/main.cpp
index 1ce65dca68..6c9006ace2 100644
--- a/tests/manual/corelib/tools/qhash/main.cpp
+++ b/tests/manual/corelib/tools/qhash/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2013 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QDebug>
#include <QHash>
diff --git a/tests/manual/corelib/tools/qlist/main.cpp b/tests/manual/corelib/tools/qlist/main.cpp
index 60a8a5c4af..2f3e8c0c73 100644
--- a/tests/manual/corelib/tools/qlist/main.cpp
+++ b/tests/manual/corelib/tools/qlist/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2013 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QDebug>
#include <QList>
diff --git a/tests/manual/corelib/tools/qmap/main.cpp b/tests/manual/corelib/tools/qmap/main.cpp
index a1efdb7d09..b3c163e515 100644
--- a/tests/manual/corelib/tools/qmap/main.cpp
+++ b/tests/manual/corelib/tools/qmap/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2012 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
//#define Q_NO_DEBUGMAP_PARENT_TEST
// Comment in line above to skip the parent test.
diff --git a/tests/manual/corelib/tools/qset/main.cpp b/tests/manual/corelib/tools/qset/main.cpp
index 114f95641b..6b86d2fb06 100644
--- a/tests/manual/corelib/tools/qset/main.cpp
+++ b/tests/manual/corelib/tools/qset/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2013 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QDebug>
#include <QSet>
diff --git a/tests/manual/corelib/tools/qvarlengtharray/main.cpp b/tests/manual/corelib/tools/qvarlengtharray/main.cpp
index fdc0cbe6da..0ad8d75b6c 100644
--- a/tests/manual/corelib/tools/qvarlengtharray/main.cpp
+++ b/tests/manual/corelib/tools/qvarlengtharray/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2013 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QDebug>
#include <QVarLengthArray>
diff --git a/tests/manual/corelib/tools/qvector/main.cpp b/tests/manual/corelib/tools/qvector/main.cpp
index 13f6997351..40462f272f 100644
--- a/tests/manual/corelib/tools/qvector/main.cpp
+++ b/tests/manual/corelib/tools/qvector/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2013 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QDebug>
#include <QVector>
diff --git a/tests/manual/diaglib/debugproxystyle.cpp b/tests/manual/diaglib/debugproxystyle.cpp
index 8e9ee57413..39aad93d17 100644
--- a/tests/manual/diaglib/debugproxystyle.cpp
+++ b/tests/manual/diaglib/debugproxystyle.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "debugproxystyle.h"
#include "eventfilter.h"
diff --git a/tests/manual/diaglib/debugproxystyle.h b/tests/manual/diaglib/debugproxystyle.h
index 04c558bdb9..160693a452 100644
--- a/tests/manual/diaglib/debugproxystyle.h
+++ b/tests/manual/diaglib/debugproxystyle.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DEBUGPROXYSTYLE_H
#define DEBUGPROXYSTYLE_H
diff --git a/tests/manual/diaglib/eventfilter.cpp b/tests/manual/diaglib/eventfilter.cpp
index 6bd47f1f14..e7896c278b 100644
--- a/tests/manual/diaglib/eventfilter.cpp
+++ b/tests/manual/diaglib/eventfilter.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "eventfilter.h"
#include <QtGui/QMouseEvent>
diff --git a/tests/manual/diaglib/eventfilter.h b/tests/manual/diaglib/eventfilter.h
index b66d9ecddd..9db4918381 100644
--- a/tests/manual/diaglib/eventfilter.h
+++ b/tests/manual/diaglib/eventfilter.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef _EVENTFILTER_
#define _EVENTFILTER_
diff --git a/tests/manual/diaglib/glinfo.cpp b/tests/manual/diaglib/glinfo.cpp
index 57b4be5ad3..1abd4dd731 100644
--- a/tests/manual/diaglib/glinfo.cpp
+++ b/tests/manual/diaglib/glinfo.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "glinfo.h"
diff --git a/tests/manual/diaglib/glinfo.h b/tests/manual/diaglib/glinfo.h
index e5226df286..d633ae8a2f 100644
--- a/tests/manual/diaglib/glinfo.h
+++ b/tests/manual/diaglib/glinfo.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef _GLINFO_
#define _GLINFO_
diff --git a/tests/manual/diaglib/logwidget.cpp b/tests/manual/diaglib/logwidget.cpp
index f0cebd05d7..e49a78d454 100644
--- a/tests/manual/diaglib/logwidget.cpp
+++ b/tests/manual/diaglib/logwidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "logwidget.h"
#include <QApplication>
diff --git a/tests/manual/diaglib/logwidget.h b/tests/manual/diaglib/logwidget.h
index 8bf53418fb..921cc5be71 100644
--- a/tests/manual/diaglib/logwidget.h
+++ b/tests/manual/diaglib/logwidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef LOGWIDGET_H
#define LOGWIDGET_H
diff --git a/tests/manual/diaglib/nativewindowdump.cpp b/tests/manual/diaglib/nativewindowdump.cpp
index 8965e40292..d384f05d57 100644
--- a/tests/manual/diaglib/nativewindowdump.cpp
+++ b/tests/manual/diaglib/nativewindowdump.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "nativewindowdump.h"
diff --git a/tests/manual/diaglib/nativewindowdump.h b/tests/manual/diaglib/nativewindowdump.h
index e6d152f318..6fe2995dc8 100644
--- a/tests/manual/diaglib/nativewindowdump.h
+++ b/tests/manual/diaglib/nativewindowdump.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef _NATIVEWINDOWDUMP_
#define _NATIVEWINDOWDUMP_
diff --git a/tests/manual/diaglib/nativewindowdump_win.cpp b/tests/manual/diaglib/nativewindowdump_win.cpp
index 371a90319d..b2f8be861b 100644
--- a/tests/manual/diaglib/nativewindowdump_win.cpp
+++ b/tests/manual/diaglib/nativewindowdump_win.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "nativewindowdump.h"
#include "qwindowdump.h"
diff --git a/tests/manual/diaglib/qwidgetdump.cpp b/tests/manual/diaglib/qwidgetdump.cpp
index bfa93008be..9fd79d32d4 100644
--- a/tests/manual/diaglib/qwidgetdump.cpp
+++ b/tests/manual/diaglib/qwidgetdump.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qwidgetdump.h"
diff --git a/tests/manual/diaglib/qwidgetdump.h b/tests/manual/diaglib/qwidgetdump.h
index 9659f11773..5bac7395a8 100644
--- a/tests/manual/diaglib/qwidgetdump.h
+++ b/tests/manual/diaglib/qwidgetdump.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef _WIDGETDUMP_
#define _WIDGETDUMP_
diff --git a/tests/manual/diaglib/qwindowdump.cpp b/tests/manual/diaglib/qwindowdump.cpp
index cd7a71c11b..9c71c0ae9b 100644
--- a/tests/manual/diaglib/qwindowdump.cpp
+++ b/tests/manual/diaglib/qwindowdump.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qwindowdump.h"
diff --git a/tests/manual/diaglib/qwindowdump.h b/tests/manual/diaglib/qwindowdump.h
index 55bb82de08..1fdc1f1bcd 100644
--- a/tests/manual/diaglib/qwindowdump.h
+++ b/tests/manual/diaglib/qwindowdump.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef _WINDOWDUMP_
#define _WINDOWDUMP_
diff --git a/tests/manual/diaglib/textdump.cpp b/tests/manual/diaglib/textdump.cpp
index 7eacad64fc..0256baba51 100644
--- a/tests/manual/diaglib/textdump.cpp
+++ b/tests/manual/diaglib/textdump.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "textdump.h"
diff --git a/tests/manual/diaglib/textdump.h b/tests/manual/diaglib/textdump.h
index b21f1a0c19..0885c2d29c 100644
--- a/tests/manual/diaglib/textdump.h
+++ b/tests/manual/diaglib/textdump.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TEXTDUMP_H
#define TEXTDUMP_H
diff --git a/tests/manual/dialogs/CMakeLists.txt b/tests/manual/dialogs/CMakeLists.txt
index b64043bf20..1900b68540 100644
--- a/tests/manual/dialogs/CMakeLists.txt
+++ b/tests/manual/dialogs/CMakeLists.txt
@@ -15,6 +15,9 @@ qt_internal_add_manual_test(dialogs
messageboxpanel.cpp messageboxpanel.h
utils.cpp utils.h
wizardpanel.cpp wizardpanel.h
+ NO_PCH_SOURCES
+ filedialogpanel.cpp # undef QT_NO_FOREACH
+ utils.cpp # undef QT_NO_FOREACH
LIBRARIES
Qt::Gui
Qt::Widgets
@@ -31,6 +34,8 @@ qt_internal_extend_target(dialogs CONDITION TARGET Qt::PrintSupport
qt_internal_extend_target(dialogs CONDITION NOT (QT_FEATURE_printer EQUAL FALSE)
SOURCES
printdialogpanel.cpp printdialogpanel.h printdialogpanel.ui
+ NO_PCH_SOURCES
+ printdialogpanel.cpp # undef QT_NO_FOREACH
ENABLE_AUTOGEN_TOOLS
uic
)
diff --git a/tests/manual/dialogs/colordialogpanel.cpp b/tests/manual/dialogs/colordialogpanel.cpp
index 2508193e0a..c9052263b6 100644
--- a/tests/manual/dialogs/colordialogpanel.cpp
+++ b/tests/manual/dialogs/colordialogpanel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "colordialogpanel.h"
#include "utils.h"
@@ -52,7 +52,7 @@ public:
{
}
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
if (role == Qt::DisplayRole) {
QString name = data(index, Qt::EditRole).toString();
diff --git a/tests/manual/dialogs/colordialogpanel.h b/tests/manual/dialogs/colordialogpanel.h
index 3f61df5acd..06eff59945 100644
--- a/tests/manual/dialogs/colordialogpanel.h
+++ b/tests/manual/dialogs/colordialogpanel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef COLORDIALOGPANEL_H
#define COLORDIALOGPANEL_H
diff --git a/tests/manual/dialogs/filedialogpanel.cpp b/tests/manual/dialogs/filedialogpanel.cpp
index f7e2cfa638..2f47c5ad0a 100644
--- a/tests/manual/dialogs/filedialogpanel.cpp
+++ b/tests/manual/dialogs/filedialogpanel.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "filedialogpanel.h"
#include "utils.h"
@@ -84,6 +86,7 @@ FileDialogPanel::FileDialogPanel(QWidget *parent)
, m_resolveSymLinks(new QCheckBox(tr("Resolve symlinks")))
, m_native(new QCheckBox(tr("Use native dialog")))
, m_customDirIcons(new QCheckBox(tr("Don't use custom directory icons")))
+ , m_noIconProvider(new QCheckBox(tr("Null icon provider")))
, m_acceptMode(createCombo(this, acceptModeComboData, sizeof(acceptModeComboData)/sizeof(FlagData)))
, m_fileMode(createCombo(this, fileModeComboData, sizeof(fileModeComboData)/sizeof(FlagData)))
, m_viewMode(createCombo(this, viewModeComboData, sizeof(viewModeComboData)/sizeof(FlagData)))
@@ -111,6 +114,7 @@ FileDialogPanel::FileDialogPanel(QWidget *parent)
optionsLayout->addRow(m_resolveSymLinks);
optionsLayout->addRow(m_readOnly);
optionsLayout->addRow(m_customDirIcons);
+ optionsLayout->addRow(m_noIconProvider);
// Files
QGroupBox *filesGroupBox = new QGroupBox(tr("Files / Filters"));
@@ -415,12 +419,19 @@ void FileDialogPanel::restoreDefaults()
l->restoreDefault(&d);
}
-void FileDialogPanel::applySettings(QFileDialog *d) const
+void FileDialogPanel::applySettings(QFileDialog *d)
{
d->setAcceptMode(comboBoxValue<QFileDialog::AcceptMode>(m_acceptMode));
d->setViewMode(comboBoxValue<QFileDialog::ViewMode>(m_viewMode));
d->setFileMode(comboBoxValue<QFileDialog::FileMode>(m_fileMode));
d->setOptions(options());
+ if (m_noIconProvider->isChecked()) {
+ m_origIconProvider = d->iconProvider();
+ d->setIconProvider(nullptr);
+ } else if (m_origIconProvider) {
+ d->setIconProvider(m_origIconProvider);
+ }
+
d->setDefaultSuffix(m_defaultSuffix->text().trimmed());
const QString directory = m_directory->text().trimmed();
if (!directory.isEmpty())
diff --git a/tests/manual/dialogs/filedialogpanel.h b/tests/manual/dialogs/filedialogpanel.h
index 1cce1dfdd1..3acf193121 100644
--- a/tests/manual/dialogs/filedialogpanel.h
+++ b/tests/manual/dialogs/filedialogpanel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef FILEDIALOGPANEL_H
#define FILEDIALOGPANEL_H
@@ -9,6 +9,8 @@
#include <QPointer>
QT_BEGIN_NAMESPACE
+
+class QAbstractFileIconProvider;
class QPushButton;
class QCheckBox;
class QComboBox;
@@ -52,7 +54,7 @@ private:
QString filterString() const;
QFileDialog::Options options() const;
QStringList allowedSchemes() const;
- void applySettings(QFileDialog *d) const;
+ void applySettings(QFileDialog *d);
QFormLayout *filesLayout;
QCheckBox *m_showDirsOnly;
@@ -62,6 +64,9 @@ private:
QCheckBox *m_resolveSymLinks;
QCheckBox *m_native;
QCheckBox *m_customDirIcons;
+ QCheckBox *m_noIconProvider = nullptr;
+ QAbstractFileIconProvider *m_origIconProvider = nullptr;
+
QComboBox *m_acceptMode;
QComboBox *m_fileMode;
QComboBox *m_viewMode;
diff --git a/tests/manual/dialogs/fontdialogpanel.cpp b/tests/manual/dialogs/fontdialogpanel.cpp
index df896fcccd..4d599c456d 100644
--- a/tests/manual/dialogs/fontdialogpanel.cpp
+++ b/tests/manual/dialogs/fontdialogpanel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "fontdialogpanel.h"
#include "utils.h"
diff --git a/tests/manual/dialogs/fontdialogpanel.h b/tests/manual/dialogs/fontdialogpanel.h
index 895d741f2f..db3f2fc3f4 100644
--- a/tests/manual/dialogs/fontdialogpanel.h
+++ b/tests/manual/dialogs/fontdialogpanel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef FONTDIALOGPANEL_H
#define FONTDIALOGPANEL_H
diff --git a/tests/manual/dialogs/main.cpp b/tests/manual/dialogs/main.cpp
index 4816db125d..d8b3f3c567 100644
--- a/tests/manual/dialogs/main.cpp
+++ b/tests/manual/dialogs/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "filedialogpanel.h"
#include "colordialogpanel.h"
diff --git a/tests/manual/dialogs/messageboxpanel.cpp b/tests/manual/dialogs/messageboxpanel.cpp
index 50f386ee0c..1d03072123 100644
--- a/tests/manual/dialogs/messageboxpanel.cpp
+++ b/tests/manual/dialogs/messageboxpanel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2013 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "messageboxpanel.h"
diff --git a/tests/manual/dialogs/messageboxpanel.h b/tests/manual/dialogs/messageboxpanel.h
index 41db6cdebd..c84657562b 100644
--- a/tests/manual/dialogs/messageboxpanel.h
+++ b/tests/manual/dialogs/messageboxpanel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2013 2013 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MESSAGEBOXPANEL_H
#define MESSAGEBOXPANEL_H
diff --git a/tests/manual/dialogs/printdialogpanel.cpp b/tests/manual/dialogs/printdialogpanel.cpp
index 6fe2eeab14..716faa48c6 100644
--- a/tests/manual/dialogs/printdialogpanel.cpp
+++ b/tests/manual/dialogs/printdialogpanel.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#ifndef QT_NO_PRINTER
diff --git a/tests/manual/dialogs/printdialogpanel.h b/tests/manual/dialogs/printdialogpanel.h
index 3ae48524e8..a8d54e8aa2 100644
--- a/tests/manual/dialogs/printdialogpanel.h
+++ b/tests/manual/dialogs/printdialogpanel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef PRINTDIALOGPANEL_H
#define PRINTDIALOGPANEL_H
diff --git a/tests/manual/dialogs/utils.cpp b/tests/manual/dialogs/utils.cpp
index 9ac5f66a85..bfb61cd8df 100644
--- a/tests/manual/dialogs/utils.cpp
+++ b/tests/manual/dialogs/utils.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "utils.h"
diff --git a/tests/manual/dialogs/utils.h b/tests/manual/dialogs/utils.h
index 83362958b2..3db08ce44e 100644
--- a/tests/manual/dialogs/utils.h
+++ b/tests/manual/dialogs/utils.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef UTILS_H
#define UTILS_H
diff --git a/tests/manual/dialogs/wizardpanel.cpp b/tests/manual/dialogs/wizardpanel.cpp
index 36605de9fa..5e413960e3 100644
--- a/tests/manual/dialogs/wizardpanel.cpp
+++ b/tests/manual/dialogs/wizardpanel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "wizardpanel.h"
@@ -191,7 +191,7 @@ class WizardPage : public QWizardPage
public:
explicit WizardPage(const QString &title, QWidget *parent = nullptr);
- void initializePage();
+ void initializePage() override;
private:
WizardStyleControl *m_styleControl;
@@ -229,7 +229,7 @@ Wizard::Wizard(QWidget *parent, Qt::WindowFlags flags)
addPage(new WizardPage(tr("Page 3"), this));
}
-// A dialog using a Wizard as child widget (emulating Qt Designer).
+// A dialog using a Wizard as child widget (emulating Qt Widgets Designer).
class WizardEmbeddingDialog : public QDialog {
public:
explicit WizardEmbeddingDialog(QWidget *parent = nullptr);
diff --git a/tests/manual/dialogs/wizardpanel.h b/tests/manual/dialogs/wizardpanel.h
index e802fb10ea..42765aeaef 100644
--- a/tests/manual/dialogs/wizardpanel.h
+++ b/tests/manual/dialogs/wizardpanel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WIZARDPANEL_H
#define WIZARDPANEL_H
diff --git a/tests/manual/embeddedintoforeignwindow/CMakeLists.txt b/tests/manual/embeddedintoforeignwindow/CMakeLists.txt
index ca41eed228..a80206133b 100644
--- a/tests/manual/embeddedintoforeignwindow/CMakeLists.txt
+++ b/tests/manual/embeddedintoforeignwindow/CMakeLists.txt
@@ -13,6 +13,8 @@ qt_internal_add_manual_test(embeddedintoforeignwindow
../diaglib/textdump.cpp ../diaglib/textdump.h
itemwindow.cpp itemwindow.h
main.cpp
+ NO_PCH_SOURCES
+ itemwindow.cpp # undef QT_NO_FOREACH
DEFINES
QT_DIAG_LIB
INCLUDE_DIRECTORIES
diff --git a/tests/manual/embeddedintoforeignwindow/itemwindow.cpp b/tests/manual/embeddedintoforeignwindow/itemwindow.cpp
index 143cdea402..422e699afc 100644
--- a/tests/manual/embeddedintoforeignwindow/itemwindow.cpp
+++ b/tests/manual/embeddedintoforeignwindow/itemwindow.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "itemwindow.h"
diff --git a/tests/manual/embeddedintoforeignwindow/itemwindow.h b/tests/manual/embeddedintoforeignwindow/itemwindow.h
index a0e11ae1c0..a8db113761 100644
--- a/tests/manual/embeddedintoforeignwindow/itemwindow.h
+++ b/tests/manual/embeddedintoforeignwindow/itemwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef ITEMWINDOW_H
#define ITEMWINDOW_H
@@ -65,7 +65,8 @@ private:
#define PROPAGATE_EVENT(windowHandler, eventClass, itemHandler) \
void windowHandler(eventClass *e) override \
{ \
- foreach (Item *i, m_items) \
+ const auto copy = m_items; /* needed? */ \
+ for (Item *i : copy) \
i->itemHandler(e); \
}
diff --git a/tests/manual/embeddedintoforeignwindow/main.cpp b/tests/manual/embeddedintoforeignwindow/main.cpp
index d87962e6b5..cadd9a3bec 100644
--- a/tests/manual/embeddedintoforeignwindow/main.cpp
+++ b/tests/manual/embeddedintoforeignwindow/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "itemwindow.h"
diff --git a/tests/manual/embeddedwindows/CMakeLists.txt b/tests/manual/embeddedwindows/CMakeLists.txt
new file mode 100644
index 0000000000..814398631e
--- /dev/null
+++ b/tests/manual/embeddedwindows/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_manual_test(embeddedwindows
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Gui
+)
+
+if(QT_FEATURE_xcb)
+ target_link_libraries(embeddedwindows PRIVATE XCB::XCB)
+endif()
+
+if(APPLE)
+ enable_language(OBJCXX)
+ set_source_files_properties(main.cpp PROPERTIES LANGUAGE OBJCXX)
+ set_property(TARGET embeddedwindows PROPERTY PROPERTY MACOSX_BUNDLE TRUE)
+endif()
diff --git a/tests/manual/embeddedwindows/main.cpp b/tests/manual/embeddedwindows/main.cpp
new file mode 100644
index 0000000000..e34c7206ea
--- /dev/null
+++ b/tests/manual/embeddedwindows/main.cpp
@@ -0,0 +1,110 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtGui>
+
+#if QT_CONFIG(metal) || defined(Q_OS_WIN) || QT_CONFIG(xcb) || defined(ANDROID)
+#include "../../shared/nativewindow.h"
+#define HAVE_NATIVE_WINDOW
+#endif
+
+#include <QDebug>
+
+class TestWindow : public QRasterWindow
+{
+public:
+ using QRasterWindow::QRasterWindow;
+ TestWindow(const QBrush &brush) : m_brush(brush) {}
+
+protected:
+ void mousePressEvent(QMouseEvent *) override
+ {
+ m_pressed = true;
+ update();
+ }
+
+ void mouseReleaseEvent(QMouseEvent *) override
+ {
+ m_pressed = false;
+ update();
+ }
+
+ void paintEvent(QPaintEvent *) override
+ {
+ QPainter painter(this);
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ if (!mask().isNull())
+ painter.setClipRegion(mask());
+ painter.fillRect(QRect(0, 0, width(), height()),
+ m_pressed ? QGradient(QGradient::JuicyPeach) : m_brush);
+ }
+
+private:
+ QBrush m_brush = QGradient(QGradient::DustyGrass);
+ bool m_pressed = false;
+};
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ TestWindow window{QGradient(QGradient::WinterNeva)};
+ window.resize(500, 500);
+
+ TestWindow *opaqueChildWindow = new TestWindow;
+ opaqueChildWindow->setParent(&window);
+ opaqueChildWindow->setGeometry(50, 50, 100, 100);
+ opaqueChildWindow->showNormal();
+
+ TestWindow *maskedChildWindow = new TestWindow;
+ maskedChildWindow->setParent(&window);
+ maskedChildWindow->setGeometry(200, 50, 100, 100);
+ maskedChildWindow->setMask(QRegion(0, 0, 100, 100, QRegion::Ellipse));
+ maskedChildWindow->showNormal();
+
+ static const QColor transparentGreen = QColor(0, 255, 0, 20);
+ TestWindow *transparentChildWindow = new TestWindow(transparentGreen);
+ // The default surface format of a platform may not include
+ // an alpha, so set it explicitly.
+ QSurfaceFormat format = transparentChildWindow->format();
+ format.setAlphaBufferSize(8);
+ transparentChildWindow->setFormat(format);
+ // FIXME: Windows requires this, even for child windows
+ transparentChildWindow->setFlag(Qt::FramelessWindowHint);
+ transparentChildWindow->setParent(&window);
+ transparentChildWindow->setGeometry(350, 50, 100, 100);
+ transparentChildWindow->showNormal();
+
+#if defined(HAVE_NATIVE_WINDOW)
+ NativeWindow nativeWindow;
+ if (QWindow *foreignWindow = QWindow::fromWinId(nativeWindow)) {
+ foreignWindow->setParent(&window);
+ foreignWindow->setGeometry(50, 200, 100, 100);
+ foreignWindow->showNormal();
+ }
+
+ NativeWindow maskedNativeWindow;
+ if (QWindow *foreignWindow = QWindow::fromWinId(maskedNativeWindow)) {
+ foreignWindow->setParent(&window);
+ foreignWindow->setGeometry(200, 200, 100, 100);
+ foreignWindow->setMask(QRegion(0, 0, 100, 100, QRegion::Ellipse));
+ foreignWindow->showNormal();
+ }
+
+ NativeWindow nativeParentWindow;
+ if (QWindow *foreignWindow = QWindow::fromWinId(nativeParentWindow)) {
+ foreignWindow->setParent(&window);
+ foreignWindow->setGeometry(50, 350, 100, 100);
+ foreignWindow->showNormal();
+
+ TestWindow *maskedChildWindowOfNativeWindow = new TestWindow;
+ maskedChildWindowOfNativeWindow->setParent(foreignWindow);
+ maskedChildWindowOfNativeWindow->setGeometry(25, 25, 50, 50);
+ maskedChildWindowOfNativeWindow->showNormal();
+ }
+#endif
+
+ window.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/blurpicker/CMakeLists.txt b/tests/manual/examples/blurpicker/CMakeLists.txt
new file mode 100644
index 0000000000..b5579fc9ed
--- /dev/null
+++ b/tests/manual/examples/blurpicker/CMakeLists.txt
@@ -0,0 +1,58 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(blurpicker LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/effects/blurpicker")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(blurpicker
+ blureffect.cpp blureffect.h
+ blurpicker.cpp blurpicker.h
+ main.cpp
+)
+
+set_target_properties(blurpicker PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(blurpicker PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(blurpicker_resource_files
+ "images/accessories-calculator.png"
+ "images/accessories-text-editor.png"
+ "images/background.jpg"
+ "images/help-browser.png"
+ "images/internet-group-chat.png"
+ "images/internet-mail.png"
+ "images/internet-web-browser.png"
+ "images/office-calendar.png"
+ "images/system-users.png"
+)
+
+qt_add_resources(blurpicker "blurpicker"
+ PREFIX
+ "/"
+ FILES
+ ${blurpicker_resource_files}
+)
+
+install(TARGETS blurpicker
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/blurpicker/blureffect.cpp b/tests/manual/examples/blurpicker/blureffect.cpp
new file mode 100644
index 0000000000..2e2b8c8d6b
--- /dev/null
+++ b/tests/manual/examples/blurpicker/blureffect.cpp
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "blureffect.h"
+
+#include <QDebug>
+
+BlurEffect::BlurEffect(QGraphicsItem *item)
+ : QGraphicsBlurEffect()
+ , m_baseLine(200), item(item)
+{
+}
+
+void BlurEffect::adjustForItem()
+{
+ qreal y = m_baseLine - item->pos().y();
+ qreal radius = qBound(qreal(0.0), y / 32, qreal(16.0));
+ setBlurRadius(radius);
+}
+
+QRectF BlurEffect::boundingRect() const
+{
+ const_cast<BlurEffect *>(this)->adjustForItem();
+ return QGraphicsBlurEffect::boundingRect();
+}
+
+void BlurEffect::draw(QPainter *painter)
+{
+ adjustForItem();
+ QGraphicsBlurEffect::draw(painter);
+}
diff --git a/tests/manual/examples/blurpicker/blureffect.h b/tests/manual/examples/blurpicker/blureffect.h
new file mode 100644
index 0000000000..a39261fc5a
--- /dev/null
+++ b/tests/manual/examples/blurpicker/blureffect.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef BLUREFFECT_H
+#define BLUREFFECT_H
+
+#include <QGraphicsEffect>
+#include <QGraphicsItem>
+
+class BlurEffect: public QGraphicsBlurEffect
+{
+public:
+ BlurEffect(QGraphicsItem *item);
+
+ void setBaseLine(qreal y) { m_baseLine = y; }
+
+ QRectF boundingRect() const;
+
+ void draw(QPainter *painter) override;
+
+private:
+ void adjustForItem();
+
+private:
+ qreal m_baseLine;
+ QGraphicsItem *item;
+};
+
+#endif // BLUREFFECT_H
diff --git a/tests/manual/examples/blurpicker/blurpicker.cpp b/tests/manual/examples/blurpicker/blurpicker.cpp
new file mode 100644
index 0000000000..00269c8fe3
--- /dev/null
+++ b/tests/manual/examples/blurpicker/blurpicker.cpp
@@ -0,0 +1,122 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "blurpicker.h"
+
+#include <QtWidgets>
+#include <QtCore/qmath.h>
+#include <qmath.h>
+#include "blureffect.h"
+
+BlurPicker::BlurPicker(QWidget *parent): QGraphicsView(parent), m_index(0.0), m_animation(this, "index")
+{
+ setBackgroundBrush(QPixmap(":/images/background.jpg"));
+ setScene(new QGraphicsScene(this));
+
+ setupScene();
+ setIndex(0);
+
+ m_animation.setDuration(400);
+ m_animation.setEasingCurve(QEasingCurve::InOutSine);
+
+ setRenderHint(QPainter::Antialiasing, true);
+ setFrameStyle(QFrame::NoFrame);
+}
+
+qreal BlurPicker::index() const
+{
+ return m_index;
+}
+
+void BlurPicker::setIndex(qreal index)
+{
+ m_index = index;
+
+ qreal baseline = 0;
+ const qreal iconAngle = 2 * M_PI / m_icons.count();
+ for (int i = 0; i < m_icons.count(); ++i) {
+ QGraphicsItem *icon = m_icons[i];
+ qreal a = (i + m_index) * iconAngle;
+ qreal xs = 170 * qSin(a);
+ qreal ys = 100 * qCos(a);
+ QPointF pos(xs, ys);
+ pos = QTransform().rotate(-20).map(pos);
+ pos -= QPointF(40, 40);
+ icon->setPos(pos);
+ baseline = qMax(baseline, ys);
+ static_cast<BlurEffect *>(icon->graphicsEffect())->setBaseLine(baseline);
+ }
+
+ scene()->update();
+}
+
+void BlurPicker::setupScene()
+{
+ scene()->setSceneRect(-200, -120, 400, 240);
+
+ QStringList names;
+ names << ":/images/accessories-calculator.png";
+ names << ":/images/accessories-text-editor.png";
+ names << ":/images/help-browser.png";
+ names << ":/images/internet-group-chat.png";
+ names << ":/images/internet-mail.png";
+ names << ":/images/internet-web-browser.png";
+ names << ":/images/office-calendar.png";
+ names << ":/images/system-users.png";
+
+ for (int i = 0; i < names.count(); i++) {
+ QPixmap pixmap(names[i]);
+ QGraphicsPixmapItem *icon = scene()->addPixmap(pixmap);
+ icon->setZValue(1);
+ icon->setGraphicsEffect(new BlurEffect(icon));
+ m_icons << icon;
+ }
+
+ QGraphicsPixmapItem *bg = scene()->addPixmap(QPixmap(":/images/background.jpg"));
+ bg->setZValue(0);
+ bg->setPos(-200, -150);
+}
+
+void BlurPicker::keyPressEvent(QKeyEvent *event)
+{
+ int delta = 0;
+ switch (event->key())
+ {
+ case Qt::Key_Left:
+ delta = -1;
+ break;
+ case Qt::Key_Right:
+ delta = 1;
+ break;
+ default:
+ break;
+ }
+ if (m_animation.state() == QAbstractAnimation::Stopped && delta) {
+ m_animation.setEndValue(m_index + delta);
+ m_animation.start();
+ event->accept();
+ }
+}
+
+void BlurPicker::resizeEvent(QResizeEvent * /* event */)
+{
+}
+
+void BlurPicker::mousePressEvent(QMouseEvent *event)
+{
+ int delta = 0;
+ if (event->position().x() > (width() / 2))
+ {
+ delta = 1;
+ }
+ else
+ {
+ delta = -1;
+ }
+
+ if (m_animation.state() == QAbstractAnimation::Stopped && delta) {
+ m_animation.setEndValue(m_index + delta);
+ m_animation.start();
+ event->accept();
+ }
+}
diff --git a/tests/manual/examples/blurpicker/blurpicker.h b/tests/manual/examples/blurpicker/blurpicker.h
new file mode 100644
index 0000000000..6e4ac84e56
--- /dev/null
+++ b/tests/manual/examples/blurpicker/blurpicker.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef BLURPICKER_H
+#define BLURPICKER_H
+
+#include <QGraphicsEffect>
+#include <QGraphicsView>
+#include <QPropertyAnimation>
+
+#include "blureffect.h"
+
+class BlurPicker: public QGraphicsView
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal index READ index WRITE setIndex)
+
+public:
+ BlurPicker(QWidget *parent = nullptr);
+
+ qreal index() const;
+ void setIndex(qreal);
+
+protected:
+ void keyPressEvent(QKeyEvent *event) override;
+ void resizeEvent(QResizeEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+
+private:
+ void setupScene();
+
+private:
+ qreal m_index;
+ QList<QGraphicsItem*> m_icons;
+ QPropertyAnimation m_animation;
+};
+
+#endif // BLURPICKER_H
diff --git a/tests/manual/examples/blurpicker/blurpicker.pro b/tests/manual/examples/blurpicker/blurpicker.pro
new file mode 100644
index 0000000000..c570ac3b98
--- /dev/null
+++ b/tests/manual/examples/blurpicker/blurpicker.pro
@@ -0,0 +1,9 @@
+QT += widgets
+
+SOURCES += main.cpp blurpicker.cpp blureffect.cpp
+HEADERS += blurpicker.h blureffect.h
+RESOURCES += blurpicker.qrc
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/effects/blurpicker
+INSTALLS += target
diff --git a/tests/manual/examples/blurpicker/blurpicker.qrc b/tests/manual/examples/blurpicker/blurpicker.qrc
new file mode 100644
index 0000000000..e88eaca966
--- /dev/null
+++ b/tests/manual/examples/blurpicker/blurpicker.qrc
@@ -0,0 +1,14 @@
+<RCC>
+ <qresource prefix="/" >
+ <file>images/background.jpg</file>
+ <file>images/accessories-calculator.png</file>
+ <file>images/accessories-text-editor.png</file>
+ <file>images/help-browser.png</file>
+ <file>images/internet-group-chat.png</file>
+ <file>images/internet-mail.png</file>
+ <file>images/internet-web-browser.png</file>
+ <file>images/office-calendar.png</file>
+ <file>images/system-users.png</file>
+ </qresource>
+</RCC>
+
diff --git a/tests/manual/examples/blurpicker/images/README.txt b/tests/manual/examples/blurpicker/images/README.txt
new file mode 100644
index 0000000000..0927e177d2
--- /dev/null
+++ b/tests/manual/examples/blurpicker/images/README.txt
@@ -0,0 +1,5 @@
+The background is taken from a public domain photo at:
+http://www.photos8.com/view/computer_board2-800x600.html
+
+All other icons are from the Tango Desktop project:
+http://tango.freedesktop.org/Tango_Desktop_Project
diff --git a/tests/manual/examples/blurpicker/images/accessories-calculator.png b/tests/manual/examples/blurpicker/images/accessories-calculator.png
new file mode 100644
index 0000000000..4e7661f65c
--- /dev/null
+++ b/tests/manual/examples/blurpicker/images/accessories-calculator.png
Binary files differ
diff --git a/tests/manual/examples/blurpicker/images/accessories-text-editor.png b/tests/manual/examples/blurpicker/images/accessories-text-editor.png
new file mode 100644
index 0000000000..33bef0bc17
--- /dev/null
+++ b/tests/manual/examples/blurpicker/images/accessories-text-editor.png
Binary files differ
diff --git a/tests/manual/examples/blurpicker/images/background.jpg b/tests/manual/examples/blurpicker/images/background.jpg
new file mode 100644
index 0000000000..e75b38899d
--- /dev/null
+++ b/tests/manual/examples/blurpicker/images/background.jpg
Binary files differ
diff --git a/tests/manual/examples/blurpicker/images/help-browser.png b/tests/manual/examples/blurpicker/images/help-browser.png
new file mode 100644
index 0000000000..8ef4fae91b
--- /dev/null
+++ b/tests/manual/examples/blurpicker/images/help-browser.png
Binary files differ
diff --git a/tests/manual/examples/blurpicker/images/internet-group-chat.png b/tests/manual/examples/blurpicker/images/internet-group-chat.png
new file mode 100644
index 0000000000..dd92d93947
--- /dev/null
+++ b/tests/manual/examples/blurpicker/images/internet-group-chat.png
Binary files differ
diff --git a/tests/manual/examples/blurpicker/images/internet-mail.png b/tests/manual/examples/blurpicker/images/internet-mail.png
new file mode 100644
index 0000000000..7e6b93be83
--- /dev/null
+++ b/tests/manual/examples/blurpicker/images/internet-mail.png
Binary files differ
diff --git a/tests/manual/examples/blurpicker/images/internet-web-browser.png b/tests/manual/examples/blurpicker/images/internet-web-browser.png
new file mode 100644
index 0000000000..a979a92b4f
--- /dev/null
+++ b/tests/manual/examples/blurpicker/images/internet-web-browser.png
Binary files differ
diff --git a/tests/manual/examples/blurpicker/images/office-calendar.png b/tests/manual/examples/blurpicker/images/office-calendar.png
new file mode 100644
index 0000000000..e09590682b
--- /dev/null
+++ b/tests/manual/examples/blurpicker/images/office-calendar.png
Binary files differ
diff --git a/tests/manual/examples/blurpicker/images/system-users.png b/tests/manual/examples/blurpicker/images/system-users.png
new file mode 100644
index 0000000000..a7f630a5bd
--- /dev/null
+++ b/tests/manual/examples/blurpicker/images/system-users.png
Binary files differ
diff --git a/tests/manual/examples/blurpicker/main.cpp b/tests/manual/examples/blurpicker/main.cpp
new file mode 100644
index 0000000000..4d5bf158c9
--- /dev/null
+++ b/tests/manual/examples/blurpicker/main.cpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "blurpicker.h"
+#include <QApplication>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+ BlurPicker blurPicker;
+ blurPicker.setWindowTitle(QT_TRANSLATE_NOOP(QGraphicsView, "Application Picker"));
+
+ blurPicker.setFixedSize(400, 300);
+ blurPicker.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/corelib/permissions/CMakeLists.txt b/tests/manual/examples/corelib/permissions/CMakeLists.txt
new file mode 100644
index 0000000000..699c90647c
--- /dev/null
+++ b/tests/manual/examples/corelib/permissions/CMakeLists.txt
@@ -0,0 +1,47 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(permissions LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/permissions")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(permissions
+ MANUAL_FINALIZATION
+ main.cpp
+ android/AndroidManifest.xml
+)
+
+set_target_properties(permissions PROPERTIES
+ MACOSX_BUNDLE TRUE
+ MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist"
+ MACOSX_BUNDLE_GUI_IDENTIFIER "io.qt.examples.permissions"
+ QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android"
+)
+
+target_link_libraries(permissions PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS permissions
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
+
+if(APPLE AND NOT CMAKE_GENERATOR STREQUAL "Xcode")
+ add_custom_command(TARGET permissions
+ POST_BUILD COMMAND codesign -s - permissions.app)
+endif()
+
+qt_finalize_executable(permissions)
diff --git a/tests/manual/examples/corelib/permissions/Info.plist b/tests/manual/examples/corelib/permissions/Info.plist
new file mode 100644
index 0000000000..57625d03dc
--- /dev/null
+++ b/tests/manual/examples/corelib/permissions/Info.plist
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+
+ <key>CFBundleName</key>
+ <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
+ <key>CFBundleExecutable</key>
+ <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
+
+ <key>CFBundleVersion</key>
+ <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
+ <key>CFBundleShortVersionString</key>
+ <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
+
+ <key>LSMinimumSystemVersion</key>
+ <string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
+
+ <key>NSHumanReadableCopyright</key>
+ <string>${MACOSX_BUNDLE_COPYRIGHT}</string>
+
+ <key>CFBundleIconFile</key>
+ <string>${MACOSX_BUNDLE_ICON_FILE}</string>
+
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+
+ <key>NSSupportsAutomaticGraphicsSwitching</key>
+ <true/>
+
+ <key>NSBluetoothAlwaysUsageDescription</key>
+ <string>Testing BluetoothAlways</string>
+ <key>NSCalendarsUsageDescription</key>
+ <string>Testing Calendars</string>
+ <key>NSCameraUsageDescription</key>
+ <string>Testing Camera</string>
+ <key>NSContactsUsageDescription</key>
+ <string>Testing Contacts</string>
+ <key>NSHealthShareUsageDescription</key>
+ <string>Testing HealthShare</string>
+ <key>NSHealthUpdateUsageDescription</key>
+ <string>Testing HealthUpdate</string>
+ <key>NSLocationUsageDescription</key>
+ <string>Testing Location on macOS</string>
+ <key>NSLocationWhenInUseUsageDescription</key>
+ <string>Testing Location when in use on iOS</string>
+ <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
+ <string>Testing Location always and when in use on iOS</string>
+ <key>NSMicrophoneUsageDescription</key>
+ <string>Testing Microphone</string>
+
+</dict>
+</plist>
diff --git a/tests/manual/examples/corelib/permissions/android/AndroidManifest.xml b/tests/manual/examples/corelib/permissions/android/AndroidManifest.xml
new file mode 100644
index 0000000000..baf025d4bb
--- /dev/null
+++ b/tests/manual/examples/corelib/permissions/android/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.qtproject.example.permissions"
+ android:installLocation="auto"
+ android:versionCode="-- %%INSERT_VERSION_CODE%% --"
+ android:versionName="-- %%INSERT_VERSION_NAME%% --">
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.BLUETOOTH"
+ android:maxSdkVersion="30" />
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+ <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+ <uses-permission android:name="android.permission.READ_CALENDAR" />
+ <uses-permission android:name="android.permission.WRITE_CALENDAR" />
+ <!-- %%INSERT_PERMISSIONS -->
+ <!-- %%INSERT_FEATURES -->
+ <supports-screens
+ android:anyDensity="true"
+ android:largeScreens="true"
+ android:normalScreens="true"
+ android:smallScreens="true" />
+ <application
+ android:name="org.qtproject.qt.android.bindings.QtApplication"
+ android:hardwareAccelerated="true"
+ android:label="-- %%INSERT_APP_NAME%% --"
+ android:requestLegacyExternalStorage="true"
+ android:allowBackup="true"
+ android:fullBackupOnly="false">
+ <activity
+ android:name="org.qtproject.qt.android.bindings.QtActivity"
+ android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
+ android:launchMode="singleTop"
+ android:screenOrientation="unspecified"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+
+ <meta-data
+ android:name="android.app.lib_name"
+ android:value="-- %%INSERT_APP_LIB_NAME%% --" />
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/manual/examples/corelib/permissions/main.cpp b/tests/manual/examples/corelib/permissions/main.cpp
new file mode 100644
index 0000000000..913aed2fec
--- /dev/null
+++ b/tests/manual/examples/corelib/permissions/main.cpp
@@ -0,0 +1,87 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtCore/qmetaobject.h>
+#include <QtWidgets/qapplication.h>
+#include <QtWidgets/qwidget.h>
+#include <QtWidgets/qpushbutton.h>
+#include <QtWidgets/qlayout.h>
+#include <QtWidgets/qmessagebox.h>
+
+#if !QT_CONFIG(permissions)
+#error "This example requires the permissions feature, which is not enabled on this platform"
+#endif
+
+#include <QtCore/qpermissions.h>
+
+class PermissionWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit PermissionWidget(QWidget *parent = nullptr) : QWidget(parent)
+ {
+ QVBoxLayout *layout = new QVBoxLayout(this);
+
+ static const QPermission permissions[] = {
+ QCameraPermission{},
+ QMicrophonePermission{},
+ QBluetoothPermission{},
+ QContactsPermission{},
+ QCalendarPermission{},
+ QLocationPermission{}
+ };
+
+ for (auto permission : permissions) {
+ auto permissionName = QString::fromLatin1(permission.type().name());
+ QPushButton *button = new QPushButton(permissionName.sliced(1, permissionName.length() - 11));
+ connect(button, &QPushButton::clicked, this, &PermissionWidget::buttonClicked);
+ button->setProperty("permission", QVariant::fromValue(permission));
+ layout->addWidget(button);
+ }
+
+ QPalette pal = palette();
+ pal.setBrush(QPalette::Window, QGradient(QGradient::HappyAcid));
+ setPalette(pal);
+ }
+
+private:
+ void buttonClicked()
+ {
+ auto *button = static_cast<QPushButton*>(sender());
+
+ auto permission = button->property("permission").value<QPermission>();
+ Q_ASSERT(permission.type().isValid());
+
+ switch (qApp->checkPermission(permission)) {
+ case Qt::PermissionStatus::Undetermined:
+ qApp->requestPermission(permission, this,
+ [button](const QPermission &permission) {
+ Q_UNUSED(permission);
+ emit button->clicked(); // Try again
+ }
+ );
+ return;
+ case Qt::PermissionStatus::Denied:
+ QMessageBox::warning(this, button->text(),
+ tr("Permission is needed to use %1. Please grant permission "\
+ "to this application in the system settings.").arg(button->text()));
+ return;
+ case Qt::PermissionStatus::Granted:
+ break; // Proceed
+ }
+
+ // All good, can use the feature
+ QMessageBox::information(this, button->text(),
+ tr("Accessing %1").arg(button->text()));
+ }
+};
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ PermissionWidget widget;
+ widget.show();
+ return app.exec();
+}
+
+#include "main.moc"
diff --git a/tests/manual/examples/opengl/computegles31/CMakeLists.txt b/tests/manual/examples/opengl/computegles31/CMakeLists.txt
index 6a87d5751e..32426152aa 100644
--- a/tests/manual/examples/opengl/computegles31/CMakeLists.txt
+++ b/tests/manual/examples/opengl/computegles31/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(computegles31 LANGUAGES CXX)
diff --git a/tests/manual/examples/opengl/contextinfo/CMakeLists.txt b/tests/manual/examples/opengl/contextinfo/CMakeLists.txt
new file mode 100644
index 0000000000..ae5c10942a
--- /dev/null
+++ b/tests/manual/examples/opengl/contextinfo/CMakeLists.txt
@@ -0,0 +1,39 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(contextinfo LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/contextinfo")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(contextinfo
+ main.cpp
+ renderwindow.cpp renderwindow.h
+ widget.cpp widget.h
+)
+
+set_target_properties(contextinfo PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(contextinfo PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::OpenGL
+ Qt6::Widgets
+)
+
+install(TARGETS contextinfo
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/opengl/contextinfo/contextinfo.pro b/tests/manual/examples/opengl/contextinfo/contextinfo.pro
new file mode 100644
index 0000000000..ae8f4067b6
--- /dev/null
+++ b/tests/manual/examples/opengl/contextinfo/contextinfo.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+QT += widgets opengl
+requires(qtConfig(filedialog))
+
+SOURCES += main.cpp \
+ widget.cpp \
+ renderwindow.cpp
+
+HEADERS += widget.h \
+ renderwindow.h
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/opengl/contextinfo
+INSTALLS += target
diff --git a/tests/manual/examples/opengl/contextinfo/main.cpp b/tests/manual/examples/opengl/contextinfo/main.cpp
new file mode 100644
index 0000000000..253425ded9
--- /dev/null
+++ b/tests/manual/examples/opengl/contextinfo/main.cpp
@@ -0,0 +1,25 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+#include "widget.h"
+
+int main(int argc, char **argv)
+{
+ for (int i = 1; i < argc; ++i) {
+ if (!qstrcmp(argv[i], "-g"))
+ QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
+ else if (!qstrcmp(argv[i], "-s"))
+ QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
+ else if (!qstrcmp(argv[i], "-d"))
+ QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
+ }
+
+ QApplication app(argc, argv);
+
+ Widget w;
+ w.resize(700, 800);
+ w.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/opengl/contextinfo/renderwindow.cpp b/tests/manual/examples/opengl/contextinfo/renderwindow.cpp
new file mode 100644
index 0000000000..64c0f47db9
--- /dev/null
+++ b/tests/manual/examples/opengl/contextinfo/renderwindow.cpp
@@ -0,0 +1,191 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "renderwindow.h"
+#include <QTimer>
+#include <QMatrix4x4>
+#include <QOpenGLContext>
+#include <QtOpenGL/QOpenGLShaderProgram>
+#include <QOpenGLFunctions>
+
+RenderWindow::RenderWindow(const QSurfaceFormat &format)
+ : m_context(nullptr),
+ m_initialized(false),
+ m_forceGLSL110(false),
+ m_angle(0.0f)
+{
+ setSurfaceType(QWindow::OpenGLSurface);
+ setFormat(format);
+ m_context = new QOpenGLContext(this);
+ m_context->setFormat(requestedFormat());
+ if (!m_context->create()) {
+ delete m_context;
+ m_context = nullptr;
+ }
+}
+
+void RenderWindow::exposeEvent(QExposeEvent *)
+{
+ if (isExposed())
+ render();
+}
+
+// ES needs the precision qualifiers.
+// On desktop GL QOpenGLShaderProgram inserts dummy defines for highp/mediump/lowp.
+static const char *vertexShaderSource110 =
+ "attribute highp vec4 posAttr;\n"
+ "attribute lowp vec4 colAttr;\n"
+ "varying lowp vec4 col;\n"
+ "uniform highp mat4 matrix;\n"
+ "void main() {\n"
+ " col = colAttr;\n"
+ " gl_Position = matrix * posAttr;\n"
+ "}\n";
+
+static const char *fragmentShaderSource110 =
+ "varying lowp vec4 col;\n"
+ "void main() {\n"
+ " gl_FragColor = col;\n"
+ "}\n";
+
+static const char *vertexShaderSource =
+ "#version 150\n"
+ "in vec4 posAttr;\n"
+ "in vec4 colAttr;\n"
+ "out vec4 col;\n"
+ "uniform mat4 matrix;\n"
+ "void main() {\n"
+ " col = colAttr;\n"
+ " gl_Position = matrix * posAttr;\n"
+ "}\n";
+
+static const char *fragmentShaderSource =
+ "#version 150\n"
+ "in vec4 col;\n"
+ "out vec4 fragColor;\n"
+ "void main() {\n"
+ " fragColor = col;\n"
+ "}\n";
+
+static GLfloat vertices[] = {
+ 0.0f, 0.707f,
+ -0.5f, -0.5f,
+ 0.5f, -0.5f
+};
+
+static GLfloat colors[] = {
+ 1.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f
+};
+
+void RenderWindow::init()
+{
+ m_program = new QOpenGLShaderProgram(this);
+
+ QSurfaceFormat format = m_context->format();
+ bool useNewStyleShader = format.profile() == QSurfaceFormat::CoreProfile;
+ // Try to handle 3.0 & 3.1 that do not have the core/compatibility profile concept 3.2+ has.
+ // This may still fail since version 150 (3.2) is specified in the sources but it's worth a try.
+ if (format.renderableType() == QSurfaceFormat::OpenGL && format.majorVersion() == 3 && format.minorVersion() <= 1)
+ useNewStyleShader = !format.testOption(QSurfaceFormat::DeprecatedFunctions);
+ if (m_forceGLSL110)
+ useNewStyleShader = false;
+
+ const char *vsrc = useNewStyleShader ? vertexShaderSource : vertexShaderSource110;
+ const char *fsrc = useNewStyleShader ? fragmentShaderSource : fragmentShaderSource110;
+ qDebug("Using version %s shader", useNewStyleShader ? "150" : "110");
+
+ if (!m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vsrc)) {
+ emit error(m_program->log());
+ return;
+ }
+ if (!m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fsrc)) {
+ emit error(m_program->log());
+ return;
+ }
+ if (!m_program->link()) {
+ emit error(m_program->log());
+ return;
+ }
+
+ m_posAttr = m_program->attributeLocation("posAttr");
+ m_colAttr = m_program->attributeLocation("colAttr");
+ m_matrixUniform = m_program->uniformLocation("matrix");
+
+ m_vbo.create();
+ m_vbo.bind();
+ m_vbo.allocate(vertices, sizeof(vertices) + sizeof(colors));
+ m_vbo.write(sizeof(vertices), colors, sizeof(colors));
+ m_vbo.release();
+
+ QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
+ if (m_vao.isCreated()) // have VAO support, use it
+ setupVertexAttribs();
+}
+
+void RenderWindow::setupVertexAttribs()
+{
+ m_vbo.bind();
+ m_program->setAttributeBuffer(m_posAttr, GL_FLOAT, 0, 2);
+ m_program->setAttributeBuffer(m_colAttr, GL_FLOAT, sizeof(vertices), 3);
+ m_program->enableAttributeArray(m_posAttr);
+ m_program->enableAttributeArray(m_colAttr);
+ m_vbo.release();
+}
+
+bool RenderWindow::event(QEvent *ev)
+{
+ if (ev->type() == QEvent::UpdateRequest)
+ render();
+ return QWindow::event(ev);
+}
+
+void RenderWindow::render()
+{
+ if (!m_context->makeCurrent(this)) {
+ emit error(tr("makeCurrent() failed"));
+ return;
+ }
+
+ QOpenGLFunctions *f = m_context->functions();
+ if (!m_initialized) {
+ m_initialized = true;
+ f->glEnable(GL_DEPTH_TEST);
+ f->glClearColor(0, 0, 0, 1);
+ init();
+ emit ready();
+ }
+
+ if (!m_vbo.isCreated()) // init() failed, don't bother with trying to render
+ return;
+
+ const qreal retinaScale = devicePixelRatio();
+ f->glViewport(0, 0, width() * retinaScale, height() * retinaScale);
+ f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ m_program->bind();
+ QMatrix4x4 matrix;
+ matrix.perspective(60.0f, 4.0f / 3.0f, 0.1f, 100.0f);
+ matrix.translate(0.0f, 0.0f, -2.0f);
+ matrix.rotate(m_angle, 0.0f, 1.0f, 0.0f);
+ m_program->setUniformValue(m_matrixUniform, matrix);
+
+ if (m_vao.isCreated())
+ m_vao.bind();
+ else // no VAO support, set the vertex attribute arrays now
+ setupVertexAttribs();
+
+ f->glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ m_vao.release();
+ m_program->release();
+
+ // swapInterval is 1 by default which means that swapBuffers() will (hopefully) block
+ // and wait for vsync.
+ m_context->swapBuffers(this);
+
+ m_angle += 1.0f;
+
+ requestUpdate();
+}
diff --git a/tests/manual/examples/opengl/contextinfo/renderwindow.h b/tests/manual/examples/opengl/contextinfo/renderwindow.h
new file mode 100644
index 0000000000..964dd601a6
--- /dev/null
+++ b/tests/manual/examples/opengl/contextinfo/renderwindow.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef RENDERWINDOW_H
+#define RENDERWINDOW_H
+
+#include <QWindow>
+#include <QOpenGLVertexArrayObject>
+#include <QOpenGLBuffer>
+
+QT_FORWARD_DECLARE_CLASS(QOpenGLContext)
+QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)
+
+class RenderWindow : public QWindow
+{
+ Q_OBJECT
+
+public:
+ RenderWindow(const QSurfaceFormat &format);
+ QOpenGLContext *context() { return m_context; }
+ void exposeEvent(QExposeEvent *) override;
+ void setForceGLSL110(bool enable) { m_forceGLSL110 = enable; }
+
+signals:
+ void ready();
+ void error(const QString &msg);
+
+protected:
+ bool event(QEvent *ev) override;
+
+private slots:
+ void render();
+
+private:
+ void init();
+ void setupVertexAttribs();
+
+ QOpenGLContext *m_context;
+ bool m_initialized;
+ bool m_forceGLSL110;
+ QOpenGLShaderProgram *m_program;
+ int m_posAttr, m_colAttr, m_matrixUniform;
+ QOpenGLVertexArrayObject m_vao;
+ QOpenGLBuffer m_vbo;
+ float m_angle;
+};
+
+#endif // RENDERWINDOW_H
diff --git a/tests/manual/examples/opengl/contextinfo/widget.cpp b/tests/manual/examples/opengl/contextinfo/widget.cpp
new file mode 100644
index 0000000000..bc31497039
--- /dev/null
+++ b/tests/manual/examples/opengl/contextinfo/widget.cpp
@@ -0,0 +1,360 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "widget.h"
+#include "renderwindow.h"
+#include <QVBoxLayout>
+#include <QComboBox>
+#include <QGroupBox>
+#include <QRadioButton>
+#include <QCheckBox>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QList>
+#include <QByteArray>
+#include <QPushButton>
+#include <QTextEdit>
+#include <QSplitter>
+#include <QGuiApplication>
+#include <QSurfaceFormat>
+#include <QOpenGLContext>
+#include <QOpenGLFunctions>
+#include <QDebug>
+#include <QTextStream>
+
+struct Version {
+ const char *str;
+ int major;
+ int minor;
+};
+
+static struct Version versions[] = {
+ { "1.0", 1, 0 },
+ { "1.1", 1, 1 },
+ { "1.2", 1, 2 },
+ { "1.3", 1, 3 },
+ { "1.4", 1, 4 },
+ { "1.5", 1, 5 },
+ { "2.0", 2, 0 },
+ { "2.1", 2, 1 },
+ { "3.0", 3, 0 },
+ { "3.1", 3, 1 },
+ { "3.2", 3, 2 },
+ { "3.3", 3, 3 },
+ { "4.0", 4, 0 },
+ { "4.1", 4, 1 },
+ { "4.2", 4, 2 },
+ { "4.3", 4, 3 },
+ { "4.4", 4, 4 },
+ { "4.5", 4, 5 }
+};
+
+struct Profile {
+ const char *str;
+ QSurfaceFormat::OpenGLContextProfile profile;
+};
+
+static struct Profile profiles[] = {
+ { "none", QSurfaceFormat::NoProfile },
+ { "core", QSurfaceFormat::CoreProfile },
+ { "compatibility", QSurfaceFormat::CompatibilityProfile }
+};
+
+struct Option {
+ const char *str;
+ QSurfaceFormat::FormatOption option;
+};
+
+static struct Option options[] = {
+ { "deprecated functions (not forward compatible)", QSurfaceFormat::DeprecatedFunctions },
+ { "debug context", QSurfaceFormat::DebugContext },
+ { "stereo buffers", QSurfaceFormat::StereoBuffers },
+ // This is not a QSurfaceFormat option but is helpful to determine if the driver
+ // allows compiling old-style shaders with core profile.
+ { "force version 110 shaders", QSurfaceFormat::FormatOption(0) }
+};
+
+struct Renderable {
+ const char *str;
+ QSurfaceFormat::RenderableType renderable;
+};
+
+static struct Renderable renderables[] = {
+ { "default", QSurfaceFormat::DefaultRenderableType },
+#ifndef Q_OS_ANDROID
+ { "OpenGL", QSurfaceFormat::OpenGL },
+#endif
+ { "OpenGL ES", QSurfaceFormat::OpenGLES }
+};
+
+void Widget::addVersions(QLayout *layout)
+{
+ QHBoxLayout *hbox = new QHBoxLayout;
+ hbox->setSpacing(20);
+ QLabel *label = new QLabel(tr("Context &version: "));
+ hbox->addWidget(label);
+ m_version = new QComboBox;
+ m_version->setMinimumWidth(60);
+ label->setBuddy(m_version);
+ hbox->addWidget(m_version);
+ for (size_t i = 0; i < sizeof(versions) / sizeof(Version); ++i) {
+ m_version->addItem(QString::fromLatin1(versions[i].str));
+ if (versions[i].major == 2 && versions[i].minor == 0)
+ m_version->setCurrentIndex(m_version->count() - 1);
+ }
+
+ QPushButton *btn = new QPushButton(tr("Create context"));
+ connect(btn, &QPushButton::clicked, this, &Widget::start);
+ btn->setMinimumSize(120, 40);
+ hbox->addWidget(btn);
+
+ layout->addItem(hbox);
+}
+
+void Widget::addProfiles(QLayout *layout)
+{
+ QGroupBox *groupBox = new QGroupBox(tr("Profile"));
+ QVBoxLayout *vbox = new QVBoxLayout;
+ for (size_t i = 0; i < sizeof(profiles) / sizeof(Profile); ++i)
+ vbox->addWidget(new QRadioButton(QString::fromLatin1(profiles[i].str)));
+ static_cast<QRadioButton *>(vbox->itemAt(0)->widget())->setChecked(true);
+ groupBox->setLayout(vbox);
+ layout->addWidget(groupBox);
+ m_profiles = vbox;
+}
+
+void Widget::addOptions(QLayout *layout)
+{
+ QGroupBox *groupBox = new QGroupBox(tr("Options"));
+ QVBoxLayout *vbox = new QVBoxLayout;
+ for (size_t i = 0; i < sizeof(options) / sizeof(Option); ++i)
+ vbox->addWidget(new QCheckBox(QString::fromLatin1(options[i].str)));
+ groupBox->setLayout(vbox);
+ layout->addWidget(groupBox);
+ m_options = vbox;
+}
+
+void Widget::addRenderableTypes(QLayout *layout)
+{
+ QGroupBox *groupBox = new QGroupBox(tr("Renderable type"));
+ QVBoxLayout *vbox = new QVBoxLayout;
+ for (size_t i = 0; i < sizeof(renderables) / sizeof(Renderable); ++i)
+ vbox->addWidget(new QRadioButton(QString::fromLatin1(renderables[i].str)));
+ static_cast<QRadioButton *>(vbox->itemAt(0)->widget())->setChecked(true);
+ groupBox->setLayout(vbox);
+ layout->addWidget(groupBox);
+ m_renderables = vbox;
+}
+
+void Widget::addRenderWindow()
+{
+ m_renderWindowLayout->addWidget(m_renderWindowContainer);
+}
+
+static QWidget *widgetWithLayout(QLayout *layout)
+{
+ QWidget *w = new QWidget;
+ w->setLayout(layout);
+ return w;
+}
+
+Widget::Widget(QWidget *parent)
+ : QWidget(parent)
+{
+ QVBoxLayout *layout = new QVBoxLayout;
+ QSplitter *vsplit = new QSplitter(Qt::Vertical);
+ layout->addWidget(vsplit);
+
+ QSplitter *hsplit = new QSplitter;
+
+ QVBoxLayout *settingsLayout = new QVBoxLayout;
+ addVersions(settingsLayout);
+ addProfiles(settingsLayout);
+ addOptions(settingsLayout);
+ addRenderableTypes(settingsLayout);
+ hsplit->addWidget(widgetWithLayout(settingsLayout));
+
+ QVBoxLayout *outputLayout = new QVBoxLayout;
+ m_output = new QTextEdit;
+ m_output->setReadOnly(true);
+ outputLayout->addWidget(m_output);
+ m_extensions = new QTextEdit;
+ m_extensions->setReadOnly(true);
+ outputLayout->addWidget(m_extensions);
+ hsplit->addWidget(widgetWithLayout(outputLayout));
+
+ hsplit->setStretchFactor(0, 4);
+ hsplit->setStretchFactor(1, 6);
+ vsplit->addWidget(hsplit);
+
+ m_renderWindowLayout = new QVBoxLayout;
+ vsplit->addWidget(widgetWithLayout(m_renderWindowLayout));
+ vsplit->setStretchFactor(1, 5);
+
+ m_renderWindowContainer = new QWidget;
+ addRenderWindow();
+
+ QString description;
+ QTextStream str(&description);
+ str << "Qt " << QT_VERSION_STR << ' ' << QGuiApplication::platformName();
+ const char *openGlVariables[] =
+ {"QT_ANGLE_PLATFORM", "QT_OPENGL", "QT_OPENGL_BUGLIST", "QT_OPENGL_DLL"};
+ const size_t variableCount = sizeof(openGlVariables) / sizeof(openGlVariables[0]);
+ for (size_t v = 0; v < variableCount; ++v) {
+ if (qEnvironmentVariableIsSet(openGlVariables[v]))
+ str << ' ' << openGlVariables[v] << '=' << qgetenv(openGlVariables[v]);
+ }
+ if (QCoreApplication::testAttribute(Qt::AA_UseOpenGLES))
+ str << " Qt::AA_UseOpenGLES";
+ if (QCoreApplication::testAttribute(Qt::AA_UseSoftwareOpenGL))
+ str << " Qt::AA_UseSoftwareOpenGL";
+ if (QCoreApplication::testAttribute(Qt::AA_UseDesktopOpenGL))
+ str << " Qt::AA_UseDesktopOpenGL";
+ layout->addWidget(new QLabel(description));
+
+ setLayout(layout);
+}
+
+void Widget::start()
+{
+ QSurfaceFormat fmt;
+
+ int idx = m_version->currentIndex();
+ if (idx < 0)
+ return;
+ fmt.setVersion(versions[idx].major, versions[idx].minor);
+
+ for (size_t i = 0; i < sizeof(profiles) / sizeof(Profile); ++i)
+ if (static_cast<QRadioButton *>(m_profiles->itemAt(int(i))->widget())->isChecked()) {
+ fmt.setProfile(profiles[i].profile);
+ break;
+ }
+
+ bool forceGLSL110 = false;
+ for (size_t i = 0; i < sizeof(options) / sizeof(Option); ++i)
+ if (static_cast<QCheckBox *>(m_options->itemAt(int(i))->widget())->isChecked()) {
+ if (options[i].option)
+ fmt.setOption(options[i].option);
+ else if (i == 3)
+ forceGLSL110 = true;
+ }
+
+ for (size_t i = 0; i < sizeof(renderables) / sizeof(Renderable); ++i)
+ if (static_cast<QRadioButton *>(m_renderables->itemAt(int(i))->widget())->isChecked()) {
+ fmt.setRenderableType(renderables[i].renderable);
+ break;
+ }
+
+ // The example rendering will need a depth buffer.
+ fmt.setDepthBufferSize(16);
+
+ m_output->clear();
+ m_extensions->clear();
+ qDebug() << "Requesting surface format" << fmt;
+
+ m_renderWindowLayout->removeWidget(m_renderWindowContainer);
+ delete m_renderWindowContainer;
+
+ RenderWindow *renderWindow = new RenderWindow(fmt);
+ if (!renderWindow->context()) {
+ m_output->append(tr("Failed to create context"));
+ delete renderWindow;
+ m_renderWindowContainer = new QWidget;
+ addRenderWindow();
+ return;
+ }
+ m_surface = renderWindow;
+
+ renderWindow->setForceGLSL110(forceGLSL110);
+ connect(renderWindow, &RenderWindow::ready, this, &Widget::renderWindowReady);
+ connect(renderWindow, &RenderWindow::error, this, &Widget::renderWindowError);
+
+ m_renderWindowContainer = QWidget::createWindowContainer(renderWindow);
+ addRenderWindow();
+}
+
+void Widget::printFormat(const QSurfaceFormat &format)
+{
+ m_output->append(tr("OpenGL version: %1.%2").arg(format.majorVersion()).arg(format.minorVersion()));
+
+ for (size_t i = 0; i < sizeof(profiles) / sizeof(Profile); ++i)
+ if (profiles[i].profile == format.profile()) {
+ m_output->append(tr("Profile: %1").arg(QString::fromLatin1(profiles[i].str)));
+ break;
+ }
+
+ QString opts;
+ for (size_t i = 0; i < sizeof(options) / sizeof(Option); ++i)
+ if (format.testOption(options[i].option))
+ opts += QString::fromLatin1(options[i].str) + QLatin1Char(' ');
+ m_output->append(tr("Options: %1").arg(opts));
+
+ for (size_t i = 0; i < sizeof(renderables) / sizeof(Renderable); ++i)
+ if (renderables[i].renderable == format.renderableType()) {
+ m_output->append(tr("Renderable type: %1").arg(QString::fromLatin1(renderables[i].str)));
+ break;
+ }
+
+ m_output->append(tr("Depth buffer size: %1").arg(QString::number(format.depthBufferSize())));
+ m_output->append(tr("Stencil buffer size: %1").arg(QString::number(format.stencilBufferSize())));
+ m_output->append(tr("Samples: %1").arg(QString::number(format.samples())));
+ m_output->append(tr("Red buffer size: %1").arg(QString::number(format.redBufferSize())));
+ m_output->append(tr("Green buffer size: %1").arg(QString::number(format.greenBufferSize())));
+ m_output->append(tr("Blue buffer size: %1").arg(QString::number(format.blueBufferSize())));
+ m_output->append(tr("Alpha buffer size: %1").arg(QString::number(format.alphaBufferSize())));
+ m_output->append(tr("Swap interval: %1").arg(QString::number(format.swapInterval())));
+}
+
+void Widget::renderWindowReady()
+{
+ QOpenGLContext *context = QOpenGLContext::currentContext();
+ Q_ASSERT(context);
+
+ QString vendor, renderer, version, glslVersion;
+ const GLubyte *p;
+ QOpenGLFunctions *f = context->functions();
+ if ((p = f->glGetString(GL_VENDOR)))
+ vendor = QString::fromLatin1(reinterpret_cast<const char *>(p));
+ if ((p = f->glGetString(GL_RENDERER)))
+ renderer = QString::fromLatin1(reinterpret_cast<const char *>(p));
+ if ((p = f->glGetString(GL_VERSION)))
+ version = QString::fromLatin1(reinterpret_cast<const char *>(p));
+ if ((p = f->glGetString(GL_SHADING_LANGUAGE_VERSION)))
+ glslVersion = QString::fromLatin1(reinterpret_cast<const char *>(p));
+
+ m_output->append(tr("*** Context information ***"));
+ m_output->append(tr("Vendor: %1").arg(vendor));
+ m_output->append(tr("Renderer: %1").arg(renderer));
+ m_output->append(tr("OpenGL version: %1").arg(version));
+ m_output->append(tr("GLSL version: %1").arg(glslVersion));
+
+ m_output->append(tr("\n*** QSurfaceFormat from context ***"));
+ printFormat(context->format());
+
+ m_output->append(tr("\n*** QSurfaceFormat from window surface ***"));
+ printFormat(m_surface->format());
+
+ m_output->append(tr("\n*** Qt build information ***"));
+ const char *gltype[] = { "Desktop", "GLES 2", "GLES 1" };
+ m_output->append(tr("Qt OpenGL configuration: %1")
+ .arg(QString::fromLatin1(gltype[QOpenGLContext::openGLModuleType()])));
+#if defined(Q_OS_WIN)
+ using namespace QNativeInterface;
+ m_output->append(tr("Qt OpenGL library handle: %1")
+ .arg(QString::number(qintptr(QWGLContext::openGLModuleHandle()), 16)));
+#endif
+
+ QList<QByteArray> extensionList = context->extensions().values();
+ std::sort(extensionList.begin(), extensionList.end());
+ m_extensions->append(tr("Found %1 extensions:").arg(extensionList.count()));
+ for (const QByteArray &ext : std::as_const(extensionList))
+ m_extensions->append(QString::fromLatin1(ext));
+
+ m_output->moveCursor(QTextCursor::Start);
+ m_extensions->moveCursor(QTextCursor::Start);
+}
+
+void Widget::renderWindowError(const QString &msg)
+{
+ m_output->append(tr("An error has occurred:\n%1").arg(msg));
+}
diff --git a/tests/manual/examples/opengl/contextinfo/widget.h b/tests/manual/examples/opengl/contextinfo/widget.h
new file mode 100644
index 0000000000..e5c31fcaa7
--- /dev/null
+++ b/tests/manual/examples/opengl/contextinfo/widget.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef WIDGET_H
+#define WIDGET_H
+
+#include <QWidget>
+
+QT_FORWARD_DECLARE_CLASS(QComboBox)
+QT_FORWARD_DECLARE_CLASS(QTextEdit)
+QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
+QT_FORWARD_DECLARE_CLASS(QSurfaceFormat)
+QT_FORWARD_DECLARE_CLASS(QSurface)
+
+class Widget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit Widget(QWidget *parent = nullptr);
+
+private slots:
+ void start();
+ void renderWindowReady();
+ void renderWindowError(const QString &msg);
+
+private:
+ void addVersions(QLayout *layout);
+ void addProfiles(QLayout *layout);
+ void addOptions(QLayout *layout);
+ void addRenderableTypes(QLayout *layout);
+ void addRenderWindow();
+ void printFormat(const QSurfaceFormat &format);
+
+ QComboBox *m_version;
+ QLayout *m_profiles;
+ QLayout *m_options;
+ QLayout *m_renderables;
+ QTextEdit *m_output;
+ QTextEdit *m_extensions;
+ QVBoxLayout *m_renderWindowLayout;
+ QWidget *m_renderWindowContainer;
+ QSurface *m_surface;
+};
+
+#endif // WIDGET_H
diff --git a/tests/manual/examples/opengl/hellowindow/CMakeLists.txt b/tests/manual/examples/opengl/hellowindow/CMakeLists.txt
index 551cf9faef..3224c81ccc 100644
--- a/tests/manual/examples/opengl/hellowindow/CMakeLists.txt
+++ b/tests/manual/examples/opengl/hellowindow/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(hellowindow LANGUAGES CXX)
diff --git a/tests/manual/examples/opengl/paintedwindow/CMakeLists.txt b/tests/manual/examples/opengl/paintedwindow/CMakeLists.txt
index b5a005242b..c1bf33e73c 100644
--- a/tests/manual/examples/opengl/paintedwindow/CMakeLists.txt
+++ b/tests/manual/examples/opengl/paintedwindow/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(paintedwindow LANGUAGES CXX)
diff --git a/tests/manual/examples/opengl/qopenglwidget/CMakeLists.txt b/tests/manual/examples/opengl/qopenglwidget/CMakeLists.txt
new file mode 100644
index 0000000000..d6e9c268ff
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwidget/CMakeLists.txt
@@ -0,0 +1,53 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(qopenglwidget LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/qopenglwidget")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL OpenGLWidgets Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(qopenglwidget
+ bubble.cpp bubble.h
+ glwidget.cpp glwidget.h
+ main.cpp
+ mainwindow.cpp mainwindow.h
+)
+
+set_target_properties(qopenglwidget PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(qopenglwidget PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::OpenGL
+ Qt6::OpenGLWidgets
+ Qt6::Widgets
+)
+
+# Resources:
+set(texture_resource_files
+ "qt.png"
+)
+
+qt6_add_resources(qopenglwidget "texture"
+ PREFIX
+ "/"
+ FILES
+ ${texture_resource_files}
+)
+
+install(TARGETS qopenglwidget
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/opengl/qopenglwidget/bubble.cpp b/tests/manual/examples/opengl/qopenglwidget/bubble.cpp
new file mode 100644
index 0000000000..3c32b62113
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwidget/bubble.cpp
@@ -0,0 +1,99 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+
+#include "bubble.h"
+
+Bubble::Bubble(const QPointF &position, qreal radius, const QPointF &velocity)
+ : position(position), vel(velocity), radius(radius)
+{
+ innerColor = randomColor();
+ outerColor = randomColor();
+ updateBrush();
+}
+
+//! [0]
+void Bubble::updateCache()
+{
+ delete cache;
+ cache = new QImage(qRound(radius * 2 + 2), qRound(radius * 2 + 2), QImage::Format_ARGB32_Premultiplied);
+ cache->fill(0x00000000);
+ QPainter p(cache);
+ p.setRenderHint(QPainter::Antialiasing);
+ QPen pen(Qt::white);
+ pen.setWidth(2);
+ p.setPen(pen);
+ p.setBrush(brush);
+ p.drawEllipse(1, 1, int(2*radius), int(2*radius));
+}
+//! [0]
+
+Bubble::~Bubble()
+{
+ delete cache;
+}
+
+void Bubble::updateBrush()
+{
+ QRadialGradient gradient(QPointF(radius, radius), radius,
+ QPointF(radius*0.5, radius*0.5));
+
+ gradient.setColorAt(0, QColor(255, 255, 255, 255));
+ gradient.setColorAt(0.25, innerColor);
+ gradient.setColorAt(1, outerColor);
+ brush = QBrush(gradient);
+ updateCache();
+}
+
+//! [1]
+void Bubble::drawBubble(QPainter *painter)
+{
+ painter->save();
+ painter->translate(position.x() - radius, position.y() - radius);
+ painter->setOpacity(0.8);
+ painter->drawImage(0, 0, *cache);
+ painter->restore();
+}
+//! [1]
+
+QColor Bubble::randomColor()
+{
+ int red = int(185 + QRandomGenerator::global()->bounded(70));
+ int green = int(185 + QRandomGenerator::global()->bounded(70));
+ int blue = int(205 + QRandomGenerator::global()->bounded(50));
+ int alpha = int(91 + QRandomGenerator::global()->bounded(100));
+
+ return QColor(red, green, blue, alpha);
+}
+
+void Bubble::move(const QRect &bbox)
+{
+ position += vel;
+ qreal leftOverflow = position.x() - radius - bbox.left();
+ qreal rightOverflow = position.x() + radius - bbox.right();
+ qreal topOverflow = position.y() - radius - bbox.top();
+ qreal bottomOverflow = position.y() + radius - bbox.bottom();
+
+ if (leftOverflow < 0.0) {
+ position.setX(position.x() - 2 * leftOverflow);
+ vel.setX(-vel.x());
+ } else if (rightOverflow > 0.0) {
+ position.setX(position.x() - 2 * rightOverflow);
+ vel.setX(-vel.x());
+ }
+
+ if (topOverflow < 0.0) {
+ position.setY(position.y() - 2 * topOverflow);
+ vel.setY(-vel.y());
+ } else if (bottomOverflow > 0.0) {
+ position.setY(position.y() - 2 * bottomOverflow);
+ vel.setY(-vel.y());
+ }
+}
+
+QRectF Bubble::rect()
+{
+ return QRectF(position.x() - radius, position.y() - radius,
+ 2 * radius, 2 * radius);
+}
diff --git a/tests/manual/examples/opengl/qopenglwidget/bubble.h b/tests/manual/examples/opengl/qopenglwidget/bubble.h
new file mode 100644
index 0000000000..47355ead41
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwidget/bubble.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef BUBBLE_H
+#define BUBBLE_H
+
+#include <QBrush>
+#include <QColor>
+#include <QPointF>
+#include <QRect>
+#include <QRectF>
+
+QT_FORWARD_DECLARE_CLASS(QPainter)
+
+class Bubble
+{
+public:
+ Bubble(const QPointF &position, qreal radius, const QPointF &velocity);
+ ~Bubble();
+
+ void drawBubble(QPainter *painter);
+ void updateBrush();
+ void move(const QRect &bbox);
+ void updateCache();
+ QRectF rect();
+
+private:
+ QColor randomColor();
+
+ QBrush brush;
+ QPointF position;
+ QPointF vel;
+ qreal radius;
+ QColor innerColor;
+ QColor outerColor;
+ QImage *cache = nullptr;
+};
+
+#endif
diff --git a/tests/manual/examples/opengl/qopenglwidget/glwidget.cpp b/tests/manual/examples/opengl/qopenglwidget/glwidget.cpp
new file mode 100644
index 0000000000..e2a3ea4fc5
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwidget/glwidget.cpp
@@ -0,0 +1,540 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "glwidget.h"
+#include <QPainter>
+#include <QPaintEngine>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLTexture>
+#include <QRandomGenerator>
+#include <QCoreApplication>
+#include <qmath.h>
+
+#include "mainwindow.h"
+#include "bubble.h"
+
+const int bubbleNum = 8;
+
+#ifndef GL_SRGB8_ALPHA8
+#define GL_SRGB8_ALPHA8 0x8C43
+#endif
+
+GLWidget::GLWidget(MainWindow *maybeMainWindow, const QColor &background)
+ : m_mainWindow(maybeMainWindow),
+ m_background(background)
+{
+ setMinimumSize(300, 250);
+ if (QCoreApplication::arguments().contains(QStringLiteral("--srgb")))
+ setTextureFormat(GL_SRGB8_ALPHA8);
+}
+
+GLWidget::~GLWidget()
+{
+ reset();
+}
+
+void GLWidget::reset()
+{
+ qDeleteAll(m_bubbles);
+ // Leave everything in a state suitable for a subsequent call to
+ // initialize(). This matters when coming from the context's
+ // aboutToBeDestroyed signal, would not matter when invoked from the
+ // destructor.
+ m_bubbles.clear();
+
+ // And now release all OpenGL resources.
+ makeCurrent();
+ delete m_texture;
+ m_texture = nullptr;
+ delete m_program1;
+ m_program1 = nullptr;
+ delete m_program2;
+ m_program2 = nullptr;
+ delete m_vshader1;
+ m_vshader1 = nullptr;
+ delete m_fshader1;
+ m_fshader1 = nullptr;
+ delete m_vshader2;
+ m_vshader2 = nullptr;
+ delete m_fshader2;
+ m_fshader2 = nullptr;
+ m_vbo1.destroy();
+ m_vbo2.destroy();
+ doneCurrent();
+
+ // We are done with the current QOpenGLContext, forget it. If there is a
+ // subsequent initialize(), that will then connect to the new context.
+ QObject::disconnect(m_contextWatchConnection);
+}
+
+void GLWidget::setScaling(int scale)
+{
+ if (scale > 30)
+ m_fScale = 1 + qreal(scale - 30) / 30 * 0.25;
+ else if (scale < 30)
+ m_fScale = 1 - (qreal(30 - scale) / 30 * 0.25);
+ else
+ m_fScale = 1;
+}
+
+void GLWidget::setLogo()
+{
+ m_qtLogo = true;
+}
+
+void GLWidget::setTexture()
+{
+ m_qtLogo = false;
+}
+
+void GLWidget::setShowBubbles(bool bubbles)
+{
+ m_showBubbles = bubbles;
+}
+
+void GLWidget::paintQtLogo()
+{
+ m_program1->enableAttributeArray(m_vertexAttr1);
+ m_program1->enableAttributeArray(m_normalAttr1);
+
+ m_vbo1.bind();
+ // The data in the buffer is placed like this:
+ // vertex1.x, vertex1.y, vertex1.z, normal1.x, normal1.y, normal1.z, vertex2.x, ...
+ m_program1->setAttributeBuffer(m_vertexAttr1, GL_FLOAT, 0, 3, 6 * sizeof(GLfloat));
+ m_program1->setAttributeBuffer(m_normalAttr1, GL_FLOAT, 3 * sizeof(GLfloat), 3, 6 * sizeof(GLfloat));
+ m_vbo1.release();
+
+ glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
+
+ m_program1->disableAttributeArray(m_normalAttr1);
+ m_program1->disableAttributeArray(m_vertexAttr1);
+}
+
+void GLWidget::paintTexturedCube()
+{
+ m_texture->bind();
+
+ if (!m_vbo2.isCreated()) {
+ static GLfloat afVertices[] = {
+ -0.5, 0.5, 0.5, 0.5,-0.5,0.5,-0.5,-0.5,0.5,
+ 0.5, -0.5, 0.5, -0.5,0.5,0.5,0.5,0.5,0.5,
+ -0.5, -0.5, -0.5, 0.5,-0.5,-0.5,-0.5,0.5,-0.5,
+ 0.5, 0.5, -0.5, -0.5,0.5,-0.5,0.5,-0.5,-0.5,
+
+ 0.5, -0.5, -0.5, 0.5,-0.5,0.5,0.5,0.5,-0.5,
+ 0.5, 0.5, 0.5, 0.5,0.5,-0.5,0.5,-0.5,0.5,
+ -0.5, 0.5, -0.5, -0.5,-0.5,0.5,-0.5,-0.5,-0.5,
+ -0.5, -0.5, 0.5, -0.5,0.5,-0.5,-0.5,0.5,0.5,
+
+ 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5,
+ -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5,
+ -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5,
+ 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5
+ };
+
+ static GLfloat afTexCoord[] = {
+ 0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f,
+ 1.0f,1.0f, 0.0f,0.0f, 0.0f,1.0f,
+ 1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f,
+ 0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f,
+
+ 1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f,
+ 0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f,
+ 0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f,
+ 1.0f,1.0f, 0.0f,0.0f, 0.0f,1.0f,
+
+ 0.0f,1.0f, 1.0f,0.0f, 1.0f,1.0f,
+ 1.0f,0.0f, 0.0f,1.0f, 0.0f,0.0f,
+ 1.0f,0.0f, 1.0f,1.0f, 0.0f,0.0f,
+ 0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f
+ };
+
+ GLfloat afNormals[] = {
+
+ 0,0,-1, 0,0,-1, 0,0,-1,
+ 0,0,-1, 0,0,-1, 0,0,-1,
+ 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1,
+
+ -1,0,0, -1,0,0, -1,0,0,
+ -1,0,0, -1,0,0, -1,0,0,
+ 1,0,0, 1,0,0, 1,0,0,
+ 1,0,0, 1,0,0, 1,0,0,
+
+ 0,-1,0, 0,-1,0, 0,-1,0,
+ 0,-1,0, 0,-1,0, 0,-1,0,
+ 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0
+ };
+
+ m_vbo2.create();
+ m_vbo2.bind();
+ m_vbo2.allocate(36 * 8 * sizeof(GLfloat));
+ m_vbo2.write(0, afVertices, sizeof(afVertices));
+ m_vbo2.write(sizeof(afVertices), afTexCoord, sizeof(afTexCoord));
+ m_vbo2.write(sizeof(afVertices) + sizeof(afTexCoord), afNormals, sizeof(afNormals));
+ m_vbo2.release();
+ }
+
+ m_program2->setUniformValue(m_textureUniform2, 0); // use texture unit 0
+
+ m_program2->enableAttributeArray(m_vertexAttr2);
+ m_program2->enableAttributeArray(m_normalAttr2);
+ m_program2->enableAttributeArray(m_texCoordAttr2);
+
+ m_vbo2.bind();
+ // In the buffer we first have 36 vertices (3 floats for each), then 36 texture
+ // coordinates (2 floats for each), then 36 normals (3 floats for each).
+ m_program2->setAttributeBuffer(m_vertexAttr2, GL_FLOAT, 0, 3);
+ m_program2->setAttributeBuffer(m_texCoordAttr2, GL_FLOAT, 36 * 3 * sizeof(GLfloat), 2);
+ m_program2->setAttributeBuffer(m_normalAttr2, GL_FLOAT, 36 * 5 * sizeof(GLfloat), 3);
+ m_vbo2.release();
+
+ glDrawArrays(GL_TRIANGLES, 0, 36);
+
+ m_program2->disableAttributeArray(m_vertexAttr2);
+ m_program2->disableAttributeArray(m_normalAttr2);
+ m_program2->disableAttributeArray(m_texCoordAttr2);
+}
+
+void GLWidget::initializeGL()
+{
+ initializeOpenGLFunctions();
+
+ m_texture = new QOpenGLTexture(QImage(":/qt.png"));
+
+ m_vshader1 = new QOpenGLShader(QOpenGLShader::Vertex);
+ const char *vsrc1 =
+ "attribute highp vec4 vertex;\n"
+ "attribute mediump vec3 normal;\n"
+ "uniform mediump mat4 matrix;\n"
+ "varying mediump vec4 color;\n"
+ "void main(void)\n"
+ "{\n"
+ " vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n"
+ " float angle = max(dot(normal, toLight), 0.0);\n"
+ " vec3 col = vec3(0.40, 1.0, 0.0);\n"
+ " color = vec4(col * 0.2 + col * 0.8 * angle, 1.0);\n"
+ " color = clamp(color, 0.0, 1.0);\n"
+ " gl_Position = matrix * vertex;\n"
+ "}\n";
+ m_vshader1->compileSourceCode(vsrc1);
+
+ m_fshader1 = new QOpenGLShader(QOpenGLShader::Fragment);
+ const char *fsrc1 =
+ "varying mediump vec4 color;\n"
+ "void main(void)\n"
+ "{\n"
+ " gl_FragColor = color;\n"
+ "}\n";
+ m_fshader1->compileSourceCode(fsrc1);
+
+ m_program1 = new QOpenGLShaderProgram;
+ m_program1->addShader(m_vshader1);
+ m_program1->addShader(m_fshader1);
+ m_program1->link();
+
+ m_vertexAttr1 = m_program1->attributeLocation("vertex");
+ m_normalAttr1 = m_program1->attributeLocation("normal");
+ m_matrixUniform1 = m_program1->uniformLocation("matrix");
+
+ m_vshader2 = new QOpenGLShader(QOpenGLShader::Vertex);
+ const char *vsrc2 =
+ "attribute highp vec4 vertex;\n"
+ "attribute highp vec4 texCoord;\n"
+ "attribute mediump vec3 normal;\n"
+ "uniform mediump mat4 matrix;\n"
+ "varying highp vec4 texc;\n"
+ "varying mediump float angle;\n"
+ "void main(void)\n"
+ "{\n"
+ " vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n"
+ " angle = max(dot(normal, toLight), 0.0);\n"
+ " gl_Position = matrix * vertex;\n"
+ " texc = texCoord;\n"
+ "}\n";
+ m_vshader2->compileSourceCode(vsrc2);
+
+ m_fshader2 = new QOpenGLShader(QOpenGLShader::Fragment);
+ const char *fsrc2 =
+ "varying highp vec4 texc;\n"
+ "uniform sampler2D tex;\n"
+ "varying mediump float angle;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec3 color = texture2D(tex, texc.st).rgb;\n"
+ " color = color * 0.2 + color * 0.8 * angle;\n"
+ " gl_FragColor = vec4(clamp(color, 0.0, 1.0), 1.0);\n"
+ "}\n";
+ m_fshader2->compileSourceCode(fsrc2);
+
+ m_program2 = new QOpenGLShaderProgram;
+ m_program2->addShader(m_vshader2);
+ m_program2->addShader(m_fshader2);
+ m_program2->link();
+
+ m_vertexAttr2 = m_program2->attributeLocation("vertex");
+ m_normalAttr2 = m_program2->attributeLocation("normal");
+ m_texCoordAttr2 = m_program2->attributeLocation("texCoord");
+ m_matrixUniform2 = m_program2->uniformLocation("matrix");
+ m_textureUniform2 = m_program2->uniformLocation("tex");
+
+ m_fAngle = 0;
+ m_fScale = 1;
+
+ createGeometry();
+
+ // Use a vertex buffer object. Client-side pointers are old-school and should be avoided.
+ m_vbo1.create();
+ m_vbo1.bind();
+ // For the cube all the data belonging to the texture coordinates and
+ // normals is placed separately, after the vertices. Here, for the Qt logo,
+ // let's do something different and potentially more efficient: create a
+ // properly interleaved data set.
+ const int vertexCount = m_vertices.count();
+ QList<GLfloat> buf;
+ buf.resize(vertexCount * 3 * 2);
+ GLfloat *p = buf.data();
+ for (int i = 0; i < vertexCount; ++i) {
+ *p++ = m_vertices[i].x();
+ *p++ = m_vertices[i].y();
+ *p++ = m_vertices[i].z();
+ *p++ = m_normals[i].x();
+ *p++ = m_normals[i].y();
+ *p++ = m_normals[i].z();
+ }
+ m_vbo1.allocate(buf.constData(), buf.count() * sizeof(GLfloat));
+ m_vbo1.release();
+
+ createBubbles(bubbleNum - m_bubbles.count());
+
+ // A well-behaved QOpenGLWidget releases OpenGL resources not only upon
+ // destruction, but also when the associated OpenGL context disappears. If
+ // the widget continues to exist, the context's destruction will be
+ // followed by a call to initialize(). This is not strictly mandatory in
+ // widgets that never change their parents.
+ m_contextWatchConnection = QObject::connect(context(), &QOpenGLContext::aboutToBeDestroyed, context(), [this] { reset(); });
+}
+
+void GLWidget::paintGL()
+{
+ createBubbles(bubbleNum - m_bubbles.count());
+
+ QPainter painter;
+ painter.begin(this);
+
+ painter.beginNativePainting();
+
+ glClearColor(m_background.redF(), m_background.greenF(), m_background.blueF(), m_transparent ? 0.0f : 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glFrontFace(GL_CW);
+ glCullFace(GL_FRONT);
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_DEPTH_TEST);
+
+ QMatrix4x4 modelview;
+ modelview.rotate(m_fAngle, 0.0f, 1.0f, 0.0f);
+ modelview.rotate(m_fAngle, 1.0f, 0.0f, 0.0f);
+ modelview.rotate(m_fAngle, 0.0f, 0.0f, 1.0f);
+ modelview.scale(m_fScale);
+ modelview.translate(0.0f, -0.2f, 0.0f);
+
+ if (m_qtLogo) {
+ m_program1->bind();
+ m_program1->setUniformValue(m_matrixUniform1, modelview);
+ paintQtLogo();
+ m_program1->release();
+ } else {
+ m_program2->bind();
+ m_program2->setUniformValue(m_matrixUniform2, modelview);
+ paintTexturedCube();
+ m_program2->release();
+ }
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+
+ painter.endNativePainting();
+
+ if (m_showBubbles) {
+ for (Bubble *bubble : std::as_const(m_bubbles))
+ bubble->drawBubble(&painter);
+ }
+
+ if (const int elapsed = m_time.elapsed()) {
+ QString framesPerSecond;
+ framesPerSecond.setNum(m_frames /(elapsed / 1000.0), 'f', 2);
+ painter.setPen(m_transparent ? Qt::black : Qt::white);
+ painter.drawText(20, 40, framesPerSecond + " paintGL calls / s");
+ }
+
+ painter.end();
+
+ for (Bubble *bubble : std::as_const(m_bubbles))
+ bubble->move(rect());
+
+ if (!(m_frames % 100)) {
+ m_time.start();
+ m_frames = 0;
+ }
+ m_fAngle += 1.0f;
+ ++m_frames;
+
+ // When requested, follow the ideal way to animate: Rely on
+ // blocking swap and just schedule updates continuously.
+ if (!m_mainWindow || !m_mainWindow->timerEnabled())
+ update();
+}
+
+void GLWidget::createBubbles(int number)
+{
+ for (int i = 0; i < number; ++i) {
+ QPointF position(width()*(0.1 + QRandomGenerator::global()->bounded(0.8)),
+ height()*(0.1 + QRandomGenerator::global()->bounded(0.8)));
+ qreal radius = qMin(width(), height())*(0.0175 + QRandomGenerator::global()->bounded(0.0875));
+ QPointF velocity(width()*0.0175*(-0.5 + QRandomGenerator::global()->bounded(1.0)),
+ height()*0.0175*(-0.5 + QRandomGenerator::global()->bounded(1.0)));
+
+ m_bubbles.append(new Bubble(position, radius, velocity));
+ }
+}
+
+void GLWidget::createGeometry()
+{
+ m_vertices.clear();
+ m_normals.clear();
+
+ qreal x1 = +0.06f;
+ qreal y1 = -0.14f;
+ qreal x2 = +0.14f;
+ qreal y2 = -0.06f;
+ qreal x3 = +0.08f;
+ qreal y3 = +0.00f;
+ qreal x4 = +0.30f;
+ qreal y4 = +0.22f;
+
+ quad(x1, y1, x2, y2, y2, x2, y1, x1);
+ quad(x3, y3, x4, y4, y4, x4, y3, x3);
+
+ extrude(x1, y1, x2, y2);
+ extrude(x2, y2, y2, x2);
+ extrude(y2, x2, y1, x1);
+ extrude(y1, x1, x1, y1);
+ extrude(x3, y3, x4, y4);
+ extrude(x4, y4, y4, x4);
+ extrude(y4, x4, y3, x3);
+
+ const int NumSectors = 100;
+ const qreal sectorAngle = 2 * qreal(M_PI) / NumSectors;
+
+ for (int i = 0; i < NumSectors; ++i) {
+ qreal angle = i * sectorAngle;
+ qreal x5 = 0.30 * sin(angle);
+ qreal y5 = 0.30 * cos(angle);
+ qreal x6 = 0.20 * sin(angle);
+ qreal y6 = 0.20 * cos(angle);
+
+ angle += sectorAngle;
+ qreal x7 = 0.20 * sin(angle);
+ qreal y7 = 0.20 * cos(angle);
+ qreal x8 = 0.30 * sin(angle);
+ qreal y8 = 0.30 * cos(angle);
+
+ quad(x5, y5, x6, y6, x7, y7, x8, y8);
+
+ extrude(x6, y6, x7, y7);
+ extrude(x8, y8, x5, y5);
+ }
+
+ for (int i = 0;i < m_vertices.size();i++)
+ m_vertices[i] *= 2.0f;
+}
+
+void GLWidget::quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4)
+{
+ m_vertices << QVector3D(x1, y1, -0.05f);
+ m_vertices << QVector3D(x2, y2, -0.05f);
+ m_vertices << QVector3D(x4, y4, -0.05f);
+
+ m_vertices << QVector3D(x3, y3, -0.05f);
+ m_vertices << QVector3D(x4, y4, -0.05f);
+ m_vertices << QVector3D(x2, y2, -0.05f);
+
+ QVector3D n = QVector3D::normal
+ (QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(x4 - x1, y4 - y1, 0.0f));
+
+ m_normals << n;
+ m_normals << n;
+ m_normals << n;
+
+ m_normals << n;
+ m_normals << n;
+ m_normals << n;
+
+ m_vertices << QVector3D(x4, y4, 0.05f);
+ m_vertices << QVector3D(x2, y2, 0.05f);
+ m_vertices << QVector3D(x1, y1, 0.05f);
+
+ m_vertices << QVector3D(x2, y2, 0.05f);
+ m_vertices << QVector3D(x4, y4, 0.05f);
+ m_vertices << QVector3D(x3, y3, 0.05f);
+
+ n = QVector3D::normal
+ (QVector3D(x2 - x4, y2 - y4, 0.0f), QVector3D(x1 - x4, y1 - y4, 0.0f));
+
+ m_normals << n;
+ m_normals << n;
+ m_normals << n;
+
+ m_normals << n;
+ m_normals << n;
+ m_normals << n;
+}
+
+void GLWidget::extrude(qreal x1, qreal y1, qreal x2, qreal y2)
+{
+ m_vertices << QVector3D(x1, y1, +0.05f);
+ m_vertices << QVector3D(x2, y2, +0.05f);
+ m_vertices << QVector3D(x1, y1, -0.05f);
+
+ m_vertices << QVector3D(x2, y2, -0.05f);
+ m_vertices << QVector3D(x1, y1, -0.05f);
+ m_vertices << QVector3D(x2, y2, +0.05f);
+
+ QVector3D n = QVector3D::normal
+ (QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(0.0f, 0.0f, -0.1f));
+
+ m_normals << n;
+ m_normals << n;
+ m_normals << n;
+
+ m_normals << n;
+ m_normals << n;
+ m_normals << n;
+}
+
+void GLWidget::setTransparent(bool transparent)
+{
+ setAttribute(Qt::WA_AlwaysStackOnTop, transparent);
+ m_transparent = transparent;
+ // Call update() on the top-level window after toggling AlwayStackOnTop to make sure
+ // the entire backingstore is updated accordingly.
+ window()->update();
+}
+
+void GLWidget::resizeGL(int, int)
+{
+ if (m_mainWindow) {
+ if (!m_btn) {
+ m_btn = new QPushButton("\nAdd widget\n", this);
+ connect(m_btn, &QPushButton::clicked, this, [this] { m_mainWindow->addNew(); });
+ }
+ m_btn->move(20, 80);
+ if (!m_btn2) {
+ m_btn2 = new QPushButton("\nI prefer tabbed widgets\n", this);
+ connect(m_btn2, &QPushButton::clicked, this, [this] { m_mainWindow->showNewWindow(); });
+ }
+ m_btn2->move(20, 160);
+ }
+}
diff --git a/tests/manual/examples/opengl/qopenglwidget/glwidget.h b/tests/manual/examples/opengl/qopenglwidget/glwidget.h
new file mode 100644
index 0000000000..de9d11b94c
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwidget/glwidget.h
@@ -0,0 +1,85 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef GLWIDGET_H
+#define GLWIDGET_H
+
+#include <QOpenGLWidget>
+#include <QOpenGLFunctions>
+#include <QOpenGLBuffer>
+#include <QVector3D>
+#include <QMatrix4x4>
+#include <QElapsedTimer>
+#include <QList>
+#include <QPushButton>
+
+class Bubble;
+class MainWindow;
+
+QT_FORWARD_DECLARE_CLASS(QOpenGLTexture)
+QT_FORWARD_DECLARE_CLASS(QOpenGLShader)
+QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)
+
+class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
+{
+ Q_OBJECT
+public:
+ GLWidget(MainWindow *maybeMainWindow, const QColor &background);
+ ~GLWidget();
+
+public slots:
+ void setScaling(int scale);
+ void setLogo();
+ void setTexture();
+ void setShowBubbles(bool);
+ void setTransparent(bool transparent);
+
+protected:
+ void resizeGL(int w, int h) override;
+ void paintGL() override;
+ void initializeGL() override;
+
+private:
+ void paintTexturedCube();
+ void paintQtLogo();
+ void createGeometry();
+ void createBubbles(int number);
+ void quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4);
+ void extrude(qreal x1, qreal y1, qreal x2, qreal y2);
+ void reset();
+
+ MainWindow *m_mainWindow;
+ qreal m_fAngle = 0;
+ qreal m_fScale = 1;
+ bool m_showBubbles = true;
+ QList<QVector3D> m_vertices;
+ QList<QVector3D> m_normals;
+ bool m_qtLogo = true;
+ QList<Bubble *> m_bubbles;
+ int m_frames = 0;
+ QElapsedTimer m_time;
+ QOpenGLShader *m_vshader1 = nullptr;
+ QOpenGLShader *m_fshader1 = nullptr;
+ QOpenGLShader *m_vshader2 = nullptr;
+ QOpenGLShader *m_fshader2 = nullptr;
+ QOpenGLShaderProgram *m_program1 = nullptr;
+ QOpenGLShaderProgram *m_program2 = nullptr;
+ QOpenGLTexture *m_texture = nullptr;
+ QOpenGLBuffer m_vbo1;
+ QOpenGLBuffer m_vbo2;
+ int m_vertexAttr1 = 0;
+ int m_normalAttr1 = 0;
+ int m_matrixUniform1 = 0;
+ int m_vertexAttr2 = 0;
+ int m_normalAttr2 = 0;
+ int m_texCoordAttr2 = 0;
+ int m_matrixUniform2 = 0;
+ int m_textureUniform2 = 0;
+ bool m_transparent = false;
+ QPushButton *m_btn = nullptr;
+ QPushButton *m_btn2 = nullptr;
+ QColor m_background;
+ QMetaObject::Connection m_contextWatchConnection;
+};
+
+#endif
diff --git a/tests/manual/examples/opengl/qopenglwidget/main.cpp b/tests/manual/examples/opengl/qopenglwidget/main.cpp
new file mode 100644
index 0000000000..1614649a5a
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwidget/main.cpp
@@ -0,0 +1,42 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+#include <QMainWindow>
+#include <QColorSpace>
+#include <QSurfaceFormat>
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+#include "mainwindow.h"
+
+int main( int argc, char ** argv )
+{
+ QApplication a( argc, argv );
+
+ QCoreApplication::setApplicationName("Qt QOpenGLWidget Example");
+ QCoreApplication::setOrganizationName("QtProject");
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+ QCommandLineParser parser;
+ parser.setApplicationDescription(QCoreApplication::applicationName());
+ parser.addHelpOption();
+ parser.addVersionOption();
+ QCommandLineOption multipleSampleOption("multisample", "Multisampling");
+ parser.addOption(multipleSampleOption);
+ QCommandLineOption srgbOption("srgb", "Use sRGB Color Space");
+ parser.addOption(srgbOption);
+ parser.process(a);
+
+ QSurfaceFormat format;
+ format.setDepthBufferSize(24);
+ format.setStencilBufferSize(8);
+ if (parser.isSet(srgbOption))
+ format.setColorSpace(QColorSpace::SRgb);
+ if (parser.isSet(multipleSampleOption))
+ format.setSamples(4);
+ QSurfaceFormat::setDefaultFormat(format);
+
+ MainWindow mw;
+ mw.resize(1280, 720);
+ mw.show();
+ return a.exec();
+}
diff --git a/tests/manual/examples/opengl/qopenglwidget/mainwindow.cpp b/tests/manual/examples/opengl/qopenglwidget/mainwindow.cpp
new file mode 100644
index 0000000000..c383a692d5
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwidget/mainwindow.cpp
@@ -0,0 +1,188 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+
+#include <QApplication>
+#include <QMenuBar>
+#include <QGroupBox>
+#include <QSlider>
+#include <QLabel>
+#include <QCheckBox>
+#include <QRandomGenerator>
+#include <QSpinBox>
+#include <QScrollArea>
+#include <QTabWidget>
+#include <QTabBar>
+#include <QToolButton>
+
+#include "glwidget.h"
+
+MainWindow::MainWindow()
+ : m_nextX(1), m_nextY(1)
+{
+ GLWidget *glwidget = new GLWidget(this, qRgb(20, 20, 50));
+ m_glWidgets << glwidget;
+ QLabel *label = new QLabel(this);
+ m_timer = new QTimer(this);
+ QSlider *slider = new QSlider(this);
+ slider->setOrientation(Qt::Horizontal);
+
+ QLabel *updateLabel = new QLabel("Update interval");
+ QSpinBox *updateInterval = new QSpinBox(this);
+ updateInterval->setSuffix(" ms");
+ updateInterval->setValue(10);
+ updateInterval->setToolTip("Interval for the timer that calls update().\n"
+ "Note that on most systems the swap will block to wait for vsync\n"
+ "and therefore an interval < 16 ms will likely lead to a 60 FPS update rate.");
+ QGroupBox *updateGroupBox = new QGroupBox(this);
+ QCheckBox *timerBased = new QCheckBox("Use timer", this);
+ timerBased->setChecked(false);
+ timerBased->setToolTip("Toggles using a timer to trigger update().\n"
+ "When not set, each paintGL() schedules the next update immediately,\n"
+ "expecting the blocking swap to throttle the thread.\n"
+ "This shows how unnecessary the timer is in most cases.");
+ QCheckBox *transparent = new QCheckBox("Transparent background", this);
+ transparent->setToolTip("Toggles Qt::WA_AlwaysStackOnTop and transparent clear color for glClear().\n"
+ "Note how the button on top stacks incorrectly when enabling this.");
+ QHBoxLayout *updateLayout = new QHBoxLayout;
+ updateLayout->addWidget(updateLabel);
+ updateLayout->addWidget(updateInterval);
+ updateLayout->addWidget(timerBased);
+ updateLayout->addWidget(transparent);
+ updateGroupBox->setLayout(updateLayout);
+
+ slider->setRange(0, 50);
+ slider->setSliderPosition(30);
+ m_timer->setInterval(10);
+ label->setText("A scrollable QOpenGLWidget");
+ label->setAlignment(Qt::AlignHCenter);
+
+ QGroupBox * groupBox = new QGroupBox(this);
+ setCentralWidget(groupBox);
+ groupBox->setTitle("QOpenGLWidget Example");
+
+ m_layout = new QGridLayout(groupBox);
+
+ QScrollArea *scrollArea = new QScrollArea;
+ scrollArea->setWidget(glwidget);
+
+ m_layout->addWidget(scrollArea,1,0,8,1);
+ m_layout->addWidget(label,9,0,1,1);
+ m_layout->addWidget(updateGroupBox, 10, 0, 1, 1);
+ m_layout->addWidget(slider, 11,0,1,1);
+
+ groupBox->setLayout(m_layout);
+
+
+ QMenu *fileMenu = menuBar()->addMenu("&File");
+ fileMenu->addAction("E&xit", this, &QWidget::close);
+ QMenu *showMenu = menuBar()->addMenu("&Show");
+ showMenu->addAction("Show 3D Logo", glwidget, &GLWidget::setLogo);
+ showMenu->addAction("Show 2D Texture", glwidget, &GLWidget::setTexture);
+ QAction *showBubbles = showMenu->addAction("Show bubbles", glwidget, &GLWidget::setShowBubbles);
+ showBubbles->setCheckable(true);
+ showBubbles->setChecked(true);
+ showMenu->addAction("Open tab window", this, &MainWindow::showNewWindow);
+ QMenu *helpMenu = menuBar()->addMenu("&Help");
+ helpMenu->addAction("About Qt", qApp, &QApplication::aboutQt);
+
+ connect(m_timer, &QTimer::timeout, glwidget, QOverload<>::of(&QWidget::update));
+
+ connect(slider, &QAbstractSlider::valueChanged, glwidget, &GLWidget::setScaling);
+ connect(transparent, &QCheckBox::toggled, glwidget, &GLWidget::setTransparent);
+ connect(updateInterval, &QSpinBox::valueChanged,
+ this, &MainWindow::updateIntervalChanged);
+ connect(timerBased, &QCheckBox::toggled, this, &MainWindow::timerUsageChanged);
+ connect(timerBased, &QCheckBox::toggled, updateInterval, &QWidget::setEnabled);
+
+ if (timerBased->isChecked())
+ m_timer->start();
+ else
+ updateInterval->setEnabled(false);
+}
+
+void MainWindow::updateIntervalChanged(int value)
+{
+ m_timer->setInterval(value);
+ if (m_timer->isActive())
+ m_timer->start();
+}
+
+void MainWindow::addNew()
+{
+ if (m_nextY == 4)
+ return;
+ GLWidget *w = new GLWidget(nullptr, qRgb(QRandomGenerator::global()->bounded(256),
+ QRandomGenerator::global()->bounded(256),
+ QRandomGenerator::global()->bounded(256)));
+ m_glWidgets << w;
+ connect(m_timer, &QTimer::timeout, w, QOverload<>::of(&QWidget::update));
+ m_layout->addWidget(w, m_nextY, m_nextX, 1, 1);
+ if (m_nextX == 3) {
+ m_nextX = 1;
+ ++m_nextY;
+ } else {
+ ++m_nextX;
+ }
+}
+
+void MainWindow::timerUsageChanged(bool enabled)
+{
+ if (enabled) {
+ m_timer->start();
+ } else {
+ m_timer->stop();
+ for (QOpenGLWidget *w : std::as_const(m_glWidgets))
+ w->update();
+ }
+}
+
+void MainWindow::resizeEvent(QResizeEvent *)
+{
+ m_glWidgets[0]->setMinimumSize(size() + QSize(128, 128));
+}
+
+void MainWindow::showNewWindow()
+{
+ QTabWidget *tabs = new QTabWidget;
+ tabs->resize(800, 600);
+
+ QToolButton *tb = new QToolButton;
+ tb->setText(QLatin1String("+"));
+ tabs->addTab(new QLabel(QLatin1String("Add OpenGL widgets with +")), QString());
+ tabs->setTabEnabled(0, false);
+ tabs->tabBar()->setTabButton(0, QTabBar::RightSide, tb);
+ tabs->tabBar()->setTabsClosable(true);
+ QObject::connect(tabs->tabBar(), &QTabBar::tabCloseRequested, tabs, [tabs](int index) {
+ tabs->widget(index)->deleteLater();
+ });
+
+ const QString msgToTopLevel = QLatin1String("Break out to top-level window");
+ const QString msgFromTopLevel = QLatin1String("Move back under tab widget");
+
+ QObject::connect(tb, &QAbstractButton::clicked, tabs, [=] {
+ GLWidget *glwidget = new GLWidget(nullptr, Qt::blue);
+ glwidget->resize(tabs->size());
+ glwidget->setWindowTitle(QString::asprintf("QOpenGLWidget %p", glwidget));
+
+ QPushButton *btn = new QPushButton(msgToTopLevel, glwidget);
+ connect(btn, &QPushButton::clicked, glwidget, [=] {
+ if (glwidget->parent()) {
+ glwidget->setAttribute(Qt::WA_DeleteOnClose, true);
+ glwidget->setParent(nullptr);
+ glwidget->show();
+ btn->setText(msgFromTopLevel);
+ } else {
+ glwidget->setAttribute(Qt::WA_DeleteOnClose, false);
+ tabs->addTab(glwidget, glwidget->windowTitle());
+ btn->setText(msgToTopLevel);
+ }
+ });
+
+ tabs->setCurrentIndex(tabs->addTab(glwidget, glwidget->windowTitle()));
+ });
+
+ tabs->setAttribute(Qt::WA_DeleteOnClose);
+ tabs->show();
+}
diff --git a/tests/manual/examples/opengl/qopenglwidget/mainwindow.h b/tests/manual/examples/opengl/qopenglwidget/mainwindow.h
new file mode 100644
index 0000000000..85f11ad764
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwidget/mainwindow.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QTimer>
+#include <QGridLayout>
+
+QT_FORWARD_DECLARE_CLASS(QOpenGLWidget)
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow();
+ void addNew();
+ bool timerEnabled() const { return m_timer->isActive(); }
+
+ void resizeEvent(QResizeEvent *);
+
+public slots:
+ void showNewWindow();
+
+private slots:
+ void updateIntervalChanged(int value);
+ void timerUsageChanged(bool enabled);
+
+private:
+ QTimer *m_timer;
+ QGridLayout *m_layout;
+ int m_nextX;
+ int m_nextY;
+ QList<QOpenGLWidget *> m_glWidgets;
+};
+
+#endif
diff --git a/tests/manual/examples/opengl/qopenglwidget/qopenglwidget.pro b/tests/manual/examples/opengl/qopenglwidget/qopenglwidget.pro
new file mode 100644
index 0000000000..7ac546d7ce
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwidget/qopenglwidget.pro
@@ -0,0 +1,15 @@
+QT += widgets opengl openglwidgets
+
+SOURCES += main.cpp \
+ glwidget.cpp \
+ mainwindow.cpp \
+ bubble.cpp
+
+HEADERS += glwidget.h \
+ mainwindow.h \
+ bubble.h
+
+RESOURCES += texture.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/opengl/qopenglwidget
+INSTALLS += target
diff --git a/tests/manual/examples/opengl/qopenglwidget/qt.png b/tests/manual/examples/opengl/qopenglwidget/qt.png
new file mode 100644
index 0000000000..4f68e162de
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwidget/qt.png
Binary files differ
diff --git a/tests/manual/examples/opengl/qopenglwidget/texture.qrc b/tests/manual/examples/opengl/qopenglwidget/texture.qrc
new file mode 100644
index 0000000000..ff1d0e535f
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwidget/texture.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>qt.png</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/examples/opengl/qopenglwindow/CMakeLists.txt b/tests/manual/examples/opengl/qopenglwindow/CMakeLists.txt
index f1c407ce17..be3cb45152 100644
--- a/tests/manual/examples/opengl/qopenglwindow/CMakeLists.txt
+++ b/tests/manual/examples/opengl/qopenglwindow/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(qopenglwindow LANGUAGES CXX)
diff --git a/tests/manual/examples/qpa/CMakeLists.txt b/tests/manual/examples/qpa/CMakeLists.txt
new file mode 100644
index 0000000000..1cf5edd939
--- /dev/null
+++ b/tests/manual/examples/qpa/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+if(NOT TARGET Qt6::Gui)
+ return()
+endif()
+qt_internal_add_example(windows)
+qt_internal_add_example(qrasterwindow)
diff --git a/tests/manual/examples/qpa/qpa.pro b/tests/manual/examples/qpa/qpa.pro
new file mode 100644
index 0000000000..85b51dc5f4
--- /dev/null
+++ b/tests/manual/examples/qpa/qpa.pro
@@ -0,0 +1,5 @@
+requires(qtHaveModule(gui))
+
+TEMPLATE = subdirs
+SUBDIRS = windows \
+ qrasterwindow
diff --git a/tests/manual/examples/qpa/qrasterwindow/CMakeLists.txt b/tests/manual/examples/qpa/qrasterwindow/CMakeLists.txt
new file mode 100644
index 0000000000..3770400268
--- /dev/null
+++ b/tests/manual/examples/qpa/qrasterwindow/CMakeLists.txt
@@ -0,0 +1,35 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(qrasterwindow LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qpa/qrasterwindow")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui)
+
+qt_standard_project_setup()
+
+qt_add_executable(qrasterwindow
+ main.cpp
+)
+
+set_target_properties(qrasterwindow PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(qrasterwindow PRIVATE
+ Qt6::Core
+ Qt6::Gui
+)
+
+install(TARGETS qrasterwindow
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/qpa/qrasterwindow/main.cpp b/tests/manual/examples/qpa/qrasterwindow/main.cpp
new file mode 100644
index 0000000000..9c245bf8a1
--- /dev/null
+++ b/tests/manual/examples/qpa/qrasterwindow/main.cpp
@@ -0,0 +1,91 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QRasterWindow>
+#include <QPainter>
+#include <QPainterPath>
+#include <QGuiApplication>
+#include <QMatrix4x4>
+#include <QTimer>
+
+static QPainterPath painterPathForTriangle()
+{
+ static const QPointF bottomLeft(-1.0, -1.0);
+ static const QPointF top(0.0, 1.0);
+ static const QPointF bottomRight(1.0, -1.0);
+
+ QPainterPath path(bottomLeft);
+ path.lineTo(top);
+ path.lineTo(bottomRight);
+ path.closeSubpath();
+ return path;
+}
+
+class PaintedWindow : public QRasterWindow
+{
+ Q_OBJECT
+
+public:
+ PaintedWindow()
+ {
+ m_view.lookAt(QVector3D(3,1,1),
+ QVector3D(0,0,0),
+ QVector3D(0,1,0));
+ m_timer.setInterval(16);
+ connect(&m_timer, &QTimer::timeout, this, qOverload<>(&PaintedWindow::update));
+ m_timer.start();
+ }
+
+protected:
+ void paintEvent(QPaintEvent *)
+ {
+ QPainter p(this);
+ p.fillRect(QRect(0,0,width(),height()),Qt::gray);
+
+ p.setWorldTransform(m_window_matrix.toTransform());
+
+ QMatrix4x4 mvp = m_projection * m_view * m_model;
+ p.setTransform(mvp.toTransform(), true);
+
+ p.fillPath(painterPathForTriangle(), m_brush);
+
+ m_model.rotate(1, 0, 1, 0);
+ }
+
+ void resizeEvent(QResizeEvent *)
+ {
+ m_window_matrix = QTransform();
+ m_window_matrix.translate(width() / 2.0, height() / 2.0);
+ m_window_matrix.scale(width() / 2.0, -height() / 2.0);
+
+ m_projection.setToIdentity();
+ m_projection.perspective(45.f, qreal(width()) / qreal(height()), 0.1f, 100.f);
+
+ QLinearGradient gradient(QPointF(-1,-1), QPointF(1,1));
+ gradient.setColorAt(0, Qt::red);
+ gradient.setColorAt(1, Qt::green);
+
+ m_brush = QBrush(gradient);
+ }
+
+private:
+ QMatrix4x4 m_window_matrix;
+ QMatrix4x4 m_projection;
+ QMatrix4x4 m_view;
+ QMatrix4x4 m_model;
+ QBrush m_brush;
+ QTimer m_timer;
+};
+
+int main (int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ PaintedWindow window;
+ window.create();
+ window.show();
+
+ return app.exec();
+}
+
+#include "main.moc"
diff --git a/tests/manual/examples/qpa/qrasterwindow/qrasterwindow.pro b/tests/manual/examples/qpa/qrasterwindow/qrasterwindow.pro
new file mode 100644
index 0000000000..e5bf34f25c
--- /dev/null
+++ b/tests/manual/examples/qpa/qrasterwindow/qrasterwindow.pro
@@ -0,0 +1,4 @@
+SOURCES += main.cpp
+
+target.path = $$[QT_INSTALL_EXAMPLES]/qpa/qrasterwindow
+INSTALLS += target
diff --git a/tests/manual/examples/qpa/windows/CMakeLists.txt b/tests/manual/examples/qpa/windows/CMakeLists.txt
new file mode 100644
index 0000000000..3ced0d4b96
--- /dev/null
+++ b/tests/manual/examples/qpa/windows/CMakeLists.txt
@@ -0,0 +1,38 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(windows LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qpa/windows")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui)
+
+qt_standard_project_setup()
+
+qt_add_executable(windows
+ main.cpp
+ window.cpp window.h
+)
+
+set_target_properties(windows PROPERTIES
+ WIN32_EXECUTABLE FALSE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(windows PRIVATE
+ Qt6::Core
+ Qt6::CorePrivate
+ Qt6::Gui
+ Qt6::GuiPrivate
+)
+
+install(TARGETS windows
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/qpa/windows/main.cpp b/tests/manual/examples/qpa/windows/main.cpp
new file mode 100644
index 0000000000..b2ce40c439
--- /dev/null
+++ b/tests/manual/examples/qpa/windows/main.cpp
@@ -0,0 +1,50 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QGuiApplication>
+#include <QScreen>
+#include <QRect>
+#include <QSharedPointer>
+
+#include "window.h"
+
+int main(int argc, char **argv)
+{
+ typedef QSharedPointer<QWindow> WindowPtr;
+
+ QGuiApplication app(argc, argv);
+
+ Window a;
+ a.setFramePosition(QPoint(10, 10));
+ a.setTitle(QStringLiteral("Window A"));
+ a.setObjectName(a.title());
+ a.setVisible(true);
+
+ Window b;
+ b.setFramePosition(QPoint(100, 100));
+ b.setTitle(QStringLiteral("Window B"));
+ b.setObjectName(b.title());
+ b.setVisible(true);
+
+ Window child(&b);
+ child.setObjectName(QStringLiteral("ChildOfB"));
+ child.setVisible(true);
+
+ // create one window on each additional screen as well
+
+ QList<WindowPtr> windows;
+ const QList<QScreen *> screens = app.screens();
+ for (QScreen *screen : screens) {
+ if (screen == app.primaryScreen())
+ continue;
+ WindowPtr window(new Window(screen));
+ QRect geometry = window->geometry();
+ geometry.moveCenter(screen->availableGeometry().center());
+ window->setGeometry(geometry);
+ window->setVisible(true);
+ window->setTitle(screen->name());
+ window->setObjectName(window->title());
+ windows.push_back(window);
+ }
+ return app.exec();
+}
diff --git a/tests/manual/examples/qpa/windows/window.cpp b/tests/manual/examples/qpa/windows/window.cpp
new file mode 100644
index 0000000000..e86a4f7cfd
--- /dev/null
+++ b/tests/manual/examples/qpa/windows/window.cpp
@@ -0,0 +1,164 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "window.h"
+
+#include <private/qguiapplication_p.h>
+
+#include <QBackingStore>
+#include <QPainter>
+
+static int colorIndexId = 0;
+
+QColor colorTable[] =
+{
+ QColor("#f09f8f"),
+ QColor("#a2bff2"),
+ QColor("#c0ef8f")
+};
+
+Window::Window(QScreen *screen)
+ : QWindow(screen)
+ , m_backgroundColorIndex(colorIndexId++)
+{
+ initialize();
+}
+
+Window::Window(QWindow *parent)
+ : QWindow(parent)
+ , m_backgroundColorIndex(colorIndexId++)
+{
+ initialize();
+}
+
+void Window::initialize()
+{
+ if (parent())
+ setGeometry(QRect(160, 120, 320, 240));
+ else {
+ setFlags(flags() | Qt::WindowTitleHint | Qt::WindowSystemMenuHint
+ | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint);
+ const QSize baseSize = QSize(640, 480);
+ setGeometry(QRect(geometry().topLeft(), baseSize));
+
+ setSizeIncrement(QSize(10, 10));
+ setBaseSize(baseSize);
+ setMinimumSize(QSize(240, 160));
+ setMaximumSize(QSize(800, 600));
+ }
+
+ create();
+ m_backingStore = new QBackingStore(this);
+
+ m_image = QImage(geometry().size(), QImage::Format_RGB32);
+ m_image.fill(colorTable[m_backgroundColorIndex % (sizeof(colorTable) / sizeof(colorTable[0]))].rgba());
+
+ m_lastPos = QPoint(-1, -1);
+ m_renderTimer = 0;
+}
+
+void Window::mousePressEvent(QMouseEvent *event)
+{
+ m_lastPos = event->position().toPoint();
+}
+
+void Window::mouseMoveEvent(QMouseEvent *event)
+{
+ if (m_lastPos != QPoint(-1, -1)) {
+ QPainter p(&m_image);
+ p.setRenderHint(QPainter::Antialiasing);
+ p.drawLine(m_lastPos, event->position().toPoint());
+ m_lastPos = event->position().toPoint();
+
+ scheduleRender();
+ }
+}
+
+void Window::mouseReleaseEvent(QMouseEvent *event)
+{
+ if (m_lastPos != QPoint(-1, -1)) {
+ QPainter p(&m_image);
+ p.setRenderHint(QPainter::Antialiasing);
+ p.drawLine(m_lastPos, event->position().toPoint());
+ m_lastPos = QPoint(-1, -1);
+
+ scheduleRender();
+ }
+}
+
+void Window::exposeEvent(QExposeEvent *)
+{
+ scheduleRender();
+}
+
+void Window::resizeEvent(QResizeEvent *)
+{
+ QImage old = m_image;
+
+ int width = qMax(geometry().width(), old.width());
+ int height = qMax(geometry().height(), old.height());
+
+ if (width > old.width() || height > old.height()) {
+ m_image = QImage(width, height, QImage::Format_RGB32);
+ m_image.fill(colorTable[(m_backgroundColorIndex) % (sizeof(colorTable) / sizeof(colorTable[0]))].rgba());
+
+ QPainter p(&m_image);
+ p.drawImage(0, 0, old);
+ }
+ scheduleRender();
+}
+
+void Window::keyPressEvent(QKeyEvent *event)
+{
+ switch (event->key()) {
+ case Qt::Key_Backspace:
+ m_text.chop(1);
+ break;
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ m_text.append('\n');
+ break;
+ default:
+ m_text.append(event->text());
+ break;
+ }
+ scheduleRender();
+}
+
+void Window::scheduleRender()
+{
+ if (!m_renderTimer)
+ m_renderTimer = startTimer(1);
+}
+
+void Window::timerEvent(QTimerEvent *)
+{
+ if (isExposed())
+ render();
+ killTimer(m_renderTimer);
+ m_renderTimer = 0;
+}
+
+void Window::render()
+{
+ QRect rect(QPoint(), geometry().size());
+ m_backingStore->resize(rect.size());
+
+ m_backingStore->beginPaint(rect);
+
+ QPaintDevice *device = m_backingStore->paintDevice();
+
+ QPainter p(device);
+ p.drawImage(0, 0, m_image);
+
+ QFont font;
+ font.setPixelSize(32);
+
+ p.setFont(font);
+ p.drawText(rect, 0, m_text);
+
+ m_backingStore->endPaint();
+ m_backingStore->flush(rect);
+}
+
+
diff --git a/tests/manual/examples/qpa/windows/window.h b/tests/manual/examples/qpa/windows/window.h
new file mode 100644
index 0000000000..c7eae4028a
--- /dev/null
+++ b/tests/manual/examples/qpa/windows/window.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include <QWindow>
+#include <QImage>
+
+class Window : public QWindow
+{
+public:
+ Window(QWindow *parent = nullptr);
+ Window(QScreen *screen);
+
+protected:
+ void mousePressEvent(QMouseEvent *) override;
+ void mouseMoveEvent(QMouseEvent *) override;
+ void mouseReleaseEvent(QMouseEvent *) override;
+
+ void keyPressEvent(QKeyEvent *) override;
+
+ void exposeEvent(QExposeEvent *) override;
+ void resizeEvent(QResizeEvent *) override;
+
+ void timerEvent(QTimerEvent *) override;
+
+private:
+ void render();
+ void scheduleRender();
+ void initialize();
+
+ QString m_text;
+ QImage m_image;
+ QPoint m_lastPos;
+ int m_backgroundColorIndex;
+ QBackingStore *m_backingStore;
+ int m_renderTimer;
+};
+
+#endif // WINDOW_H
diff --git a/tests/manual/examples/qpa/windows/windows.pro b/tests/manual/examples/qpa/windows/windows.pro
new file mode 100644
index 0000000000..cf2b7ab200
--- /dev/null
+++ b/tests/manual/examples/qpa/windows/windows.pro
@@ -0,0 +1,9 @@
+CONFIG += console
+QT += gui-private core-private
+
+HEADERS += window.h
+SOURCES += window.cpp main.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/qpa/windows
+INSTALLS += target
diff --git a/tests/manual/examples/vulkan/hellovulkantexture/CMakeLists.txt b/tests/manual/examples/vulkan/hellovulkantexture/CMakeLists.txt
index e5e764dbfe..07495e1ecf 100644
--- a/tests/manual/examples/vulkan/hellovulkantexture/CMakeLists.txt
+++ b/tests/manual/examples/vulkan/hellovulkantexture/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(hellovulkantexture LANGUAGES CXX)
diff --git a/tests/manual/examples/widgets/application/CMakeLists.txt b/tests/manual/examples/widgets/application/CMakeLists.txt
new file mode 100644
index 0000000000..9b863a89fa
--- /dev/null
+++ b/tests/manual/examples/widgets/application/CMakeLists.txt
@@ -0,0 +1,54 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(application LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/mainwindows/application")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(application
+ main.cpp
+ mainwindow.cpp mainwindow.h
+)
+
+set_target_properties(application PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(application PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(application_resource_files
+ "images/copy.png"
+ "images/cut.png"
+ "images/new.png"
+ "images/open.png"
+ "images/paste.png"
+ "images/save.png"
+)
+
+qt_add_resources(application "application"
+ PREFIX
+ "/"
+ FILES
+ ${application_resource_files}
+)
+
+install(TARGETS application
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/application/application.pro b/tests/manual/examples/widgets/application/application.pro
new file mode 100644
index 0000000000..e55655a934
--- /dev/null
+++ b/tests/manual/examples/widgets/application/application.pro
@@ -0,0 +1,13 @@
+QT += widgets
+requires(qtConfig(filedialog))
+
+HEADERS = mainwindow.h
+SOURCES = main.cpp \
+ mainwindow.cpp
+#! [0]
+RESOURCES = application.qrc
+#! [0]
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/mainwindows/application
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/application/application.qrc b/tests/manual/examples/widgets/application/application.qrc
new file mode 100644
index 0000000000..0a776fab4d
--- /dev/null
+++ b/tests/manual/examples/widgets/application/application.qrc
@@ -0,0 +1,10 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>images/copy.png</file>
+ <file>images/cut.png</file>
+ <file>images/new.png</file>
+ <file>images/open.png</file>
+ <file>images/paste.png</file>
+ <file>images/save.png</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/application/images/copy.png b/tests/manual/examples/widgets/application/images/copy.png
new file mode 100644
index 0000000000..2aeb28288f
--- /dev/null
+++ b/tests/manual/examples/widgets/application/images/copy.png
Binary files differ
diff --git a/tests/manual/examples/widgets/application/images/cut.png b/tests/manual/examples/widgets/application/images/cut.png
new file mode 100644
index 0000000000..54638e9386
--- /dev/null
+++ b/tests/manual/examples/widgets/application/images/cut.png
Binary files differ
diff --git a/tests/manual/examples/widgets/application/images/new.png b/tests/manual/examples/widgets/application/images/new.png
new file mode 100644
index 0000000000..12131b0100
--- /dev/null
+++ b/tests/manual/examples/widgets/application/images/new.png
Binary files differ
diff --git a/tests/manual/examples/widgets/application/images/open.png b/tests/manual/examples/widgets/application/images/open.png
new file mode 100644
index 0000000000..45fa2883a7
--- /dev/null
+++ b/tests/manual/examples/widgets/application/images/open.png
Binary files differ
diff --git a/tests/manual/examples/widgets/application/images/paste.png b/tests/manual/examples/widgets/application/images/paste.png
new file mode 100644
index 0000000000..c14425cad1
--- /dev/null
+++ b/tests/manual/examples/widgets/application/images/paste.png
Binary files differ
diff --git a/tests/manual/examples/widgets/application/images/save.png b/tests/manual/examples/widgets/application/images/save.png
new file mode 100644
index 0000000000..e65a29d5f1
--- /dev/null
+++ b/tests/manual/examples/widgets/application/images/save.png
Binary files differ
diff --git a/tests/manual/examples/widgets/application/main.cpp b/tests/manual/examples/widgets/application/main.cpp
new file mode 100644
index 0000000000..722b8a71b8
--- /dev/null
+++ b/tests/manual/examples/widgets/application/main.cpp
@@ -0,0 +1,28 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ QCoreApplication::setOrganizationName("QtProject");
+ QCoreApplication::setApplicationName("Application Example");
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+ QCommandLineParser parser;
+ parser.setApplicationDescription(QCoreApplication::applicationName());
+ parser.addHelpOption();
+ parser.addVersionOption();
+ parser.addPositionalArgument("file", "The file to open.");
+ parser.process(app);
+
+ MainWindow mainWin;
+ if (!parser.positionalArguments().isEmpty())
+ mainWin.loadFile(parser.positionalArguments().first());
+ mainWin.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/application/mainwindow.cpp b/tests/manual/examples/widgets/application/mainwindow.cpp
new file mode 100644
index 0000000000..fcb5590751
--- /dev/null
+++ b/tests/manual/examples/widgets/application/mainwindow.cpp
@@ -0,0 +1,306 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+
+#include "mainwindow.h"
+
+MainWindow::MainWindow()
+ : textEdit(new QPlainTextEdit)
+{
+ setCentralWidget(textEdit);
+
+ createActions();
+ createStatusBar();
+
+ readSettings();
+
+ connect(textEdit->document(), &QTextDocument::contentsChanged,
+ this, &MainWindow::documentWasModified);
+
+#ifndef QT_NO_SESSIONMANAGER
+ connect(qApp, &QGuiApplication::commitDataRequest,
+ this, &MainWindow::commitData);
+#endif
+
+ setCurrentFile(QString());
+ setUnifiedTitleAndToolBarOnMac(true);
+}
+
+void MainWindow::closeEvent(QCloseEvent *event)
+{
+ if (maybeSave()) {
+ writeSettings();
+ event->accept();
+ } else {
+ event->ignore();
+ }
+}
+
+void MainWindow::newFile()
+{
+ if (maybeSave()) {
+ textEdit->clear();
+ setCurrentFile(QString());
+ }
+}
+
+void MainWindow::open()
+{
+ if (maybeSave()) {
+ QString fileName = QFileDialog::getOpenFileName(this);
+ if (!fileName.isEmpty())
+ loadFile(fileName);
+ }
+}
+
+bool MainWindow::save()
+{
+ if (curFile.isEmpty()) {
+ return saveAs();
+ } else {
+ return saveFile(curFile);
+ }
+}
+
+bool MainWindow::saveAs()
+{
+ QFileDialog dialog(this);
+ dialog.setWindowModality(Qt::WindowModal);
+ dialog.setAcceptMode(QFileDialog::AcceptSave);
+ if (dialog.exec() != QDialog::Accepted)
+ return false;
+ return saveFile(dialog.selectedFiles().first());
+}
+
+void MainWindow::about()
+{
+ QMessageBox::about(this, tr("About Application"),
+ tr("The <b>Application</b> example demonstrates how to "
+ "write modern GUI applications using Qt, with a menu bar, "
+ "toolbars, and a status bar."));
+}
+
+void MainWindow::documentWasModified()
+{
+ setWindowModified(textEdit->document()->isModified());
+}
+
+void MainWindow::createActions()
+{
+
+ QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
+ QToolBar *fileToolBar = addToolBar(tr("File"));
+ const QIcon newIcon = QIcon::fromTheme("document-new", QIcon(":/images/new.png"));
+ QAction *newAct = new QAction(newIcon, tr("&New"), this);
+ newAct->setShortcuts(QKeySequence::New);
+ newAct->setStatusTip(tr("Create a new file"));
+ connect(newAct, &QAction::triggered, this, &MainWindow::newFile);
+ fileMenu->addAction(newAct);
+ fileToolBar->addAction(newAct);
+
+ const QIcon openIcon = QIcon::fromTheme("document-open", QIcon(":/images/open.png"));
+ QAction *openAct = new QAction(openIcon, tr("&Open..."), this);
+ openAct->setShortcuts(QKeySequence::Open);
+ openAct->setStatusTip(tr("Open an existing file"));
+ connect(openAct, &QAction::triggered, this, &MainWindow::open);
+ fileMenu->addAction(openAct);
+ fileToolBar->addAction(openAct);
+
+ const QIcon saveIcon = QIcon::fromTheme("document-save", QIcon(":/images/save.png"));
+ QAction *saveAct = new QAction(saveIcon, tr("&Save"), this);
+ saveAct->setShortcuts(QKeySequence::Save);
+ saveAct->setStatusTip(tr("Save the document to disk"));
+ connect(saveAct, &QAction::triggered, this, &MainWindow::save);
+ fileMenu->addAction(saveAct);
+ fileToolBar->addAction(saveAct);
+
+ const QIcon saveAsIcon = QIcon::fromTheme("document-save-as");
+ QAction *saveAsAct = fileMenu->addAction(saveAsIcon, tr("Save &As..."), this, &MainWindow::saveAs);
+ saveAsAct->setShortcuts(QKeySequence::SaveAs);
+ saveAsAct->setStatusTip(tr("Save the document under a new name"));
+
+ fileMenu->addSeparator();
+
+ const QIcon exitIcon = QIcon::fromTheme("application-exit");
+ QAction *exitAct = fileMenu->addAction(exitIcon, tr("E&xit"), this, &QWidget::close);
+ exitAct->setShortcuts(QKeySequence::Quit);
+ exitAct->setStatusTip(tr("Exit the application"));
+
+ QMenu *editMenu = menuBar()->addMenu(tr("&Edit"));
+ QToolBar *editToolBar = addToolBar(tr("Edit"));
+
+#ifndef QT_NO_CLIPBOARD
+ const QIcon cutIcon = QIcon::fromTheme("edit-cut", QIcon(":/images/cut.png"));
+ QAction *cutAct = new QAction(cutIcon, tr("Cu&t"), this);
+ cutAct->setShortcuts(QKeySequence::Cut);
+ cutAct->setStatusTip(tr("Cut the current selection's contents to the "
+ "clipboard"));
+ connect(cutAct, &QAction::triggered, textEdit, &QPlainTextEdit::cut);
+ editMenu->addAction(cutAct);
+ editToolBar->addAction(cutAct);
+
+ const QIcon copyIcon = QIcon::fromTheme("edit-copy", QIcon(":/images/copy.png"));
+ QAction *copyAct = new QAction(copyIcon, tr("&Copy"), this);
+ copyAct->setShortcuts(QKeySequence::Copy);
+ copyAct->setStatusTip(tr("Copy the current selection's contents to the "
+ "clipboard"));
+ connect(copyAct, &QAction::triggered, textEdit, &QPlainTextEdit::copy);
+ editMenu->addAction(copyAct);
+ editToolBar->addAction(copyAct);
+
+ const QIcon pasteIcon = QIcon::fromTheme("edit-paste", QIcon(":/images/paste.png"));
+ QAction *pasteAct = new QAction(pasteIcon, tr("&Paste"), this);
+ pasteAct->setShortcuts(QKeySequence::Paste);
+ pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current "
+ "selection"));
+ connect(pasteAct, &QAction::triggered, textEdit, &QPlainTextEdit::paste);
+ editMenu->addAction(pasteAct);
+ editToolBar->addAction(pasteAct);
+
+ menuBar()->addSeparator();
+
+#endif // !QT_NO_CLIPBOARD
+
+ QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
+ QAction *aboutAct = helpMenu->addAction(tr("&About"), this, &MainWindow::about);
+ aboutAct->setStatusTip(tr("Show the application's About box"));
+
+ QAction *aboutQtAct = helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
+ aboutQtAct->setStatusTip(tr("Show the Qt library's About box"));
+
+#ifndef QT_NO_CLIPBOARD
+ cutAct->setEnabled(false);
+ copyAct->setEnabled(false);
+ connect(textEdit, &QPlainTextEdit::copyAvailable, cutAct, &QAction::setEnabled);
+ connect(textEdit, &QPlainTextEdit::copyAvailable, copyAct, &QAction::setEnabled);
+#endif // !QT_NO_CLIPBOARD
+}
+
+void MainWindow::createStatusBar()
+{
+ statusBar()->showMessage(tr("Ready"));
+}
+
+void MainWindow::readSettings()
+{
+ QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
+ const QByteArray geometry = settings.value("geometry", QByteArray()).toByteArray();
+ if (geometry.isEmpty()) {
+ const QRect availableGeometry = screen()->availableGeometry();
+ resize(availableGeometry.width() / 3, availableGeometry.height() / 2);
+ move((availableGeometry.width() - width()) / 2,
+ (availableGeometry.height() - height()) / 2);
+ } else {
+ restoreGeometry(geometry);
+ }
+}
+
+void MainWindow::writeSettings()
+{
+ QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
+ settings.setValue("geometry", saveGeometry());
+}
+
+bool MainWindow::maybeSave()
+{
+ if (!textEdit->document()->isModified())
+ return true;
+ const QMessageBox::StandardButton ret
+ = QMessageBox::warning(this, tr("Application"),
+ tr("The document has been modified.\n"
+ "Do you want to save your changes?"),
+ QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
+ switch (ret) {
+ case QMessageBox::Save:
+ return save();
+ case QMessageBox::Cancel:
+ return false;
+ default:
+ break;
+ }
+ return true;
+}
+
+void MainWindow::loadFile(const QString &fileName)
+{
+ QFile file(fileName);
+ if (!file.open(QFile::ReadOnly | QFile::Text)) {
+ QMessageBox::warning(this, tr("Application"),
+ tr("Cannot read file %1:\n%2.")
+ .arg(QDir::toNativeSeparators(fileName), file.errorString()));
+ return;
+ }
+
+ QTextStream in(&file);
+#ifndef QT_NO_CURSOR
+ QGuiApplication::setOverrideCursor(Qt::WaitCursor);
+#endif
+ textEdit->setPlainText(in.readAll());
+#ifndef QT_NO_CURSOR
+ QGuiApplication::restoreOverrideCursor();
+#endif
+
+ setCurrentFile(fileName);
+ statusBar()->showMessage(tr("File loaded"), 2000);
+}
+
+bool MainWindow::saveFile(const QString &fileName)
+{
+ QString errorMessage;
+
+ QGuiApplication::setOverrideCursor(Qt::WaitCursor);
+ QSaveFile file(fileName);
+ if (file.open(QFile::WriteOnly | QFile::Text)) {
+ QTextStream out(&file);
+ out << textEdit->toPlainText();
+ if (!file.commit()) {
+ errorMessage = tr("Cannot write file %1:\n%2.")
+ .arg(QDir::toNativeSeparators(fileName), file.errorString());
+ }
+ } else {
+ errorMessage = tr("Cannot open file %1 for writing:\n%2.")
+ .arg(QDir::toNativeSeparators(fileName), file.errorString());
+ }
+ QGuiApplication::restoreOverrideCursor();
+
+ if (!errorMessage.isEmpty()) {
+ QMessageBox::warning(this, tr("Application"), errorMessage);
+ return false;
+ }
+
+ setCurrentFile(fileName);
+ statusBar()->showMessage(tr("File saved"), 2000);
+ return true;
+}
+
+void MainWindow::setCurrentFile(const QString &fileName)
+{
+ curFile = fileName;
+ textEdit->document()->setModified(false);
+ setWindowModified(false);
+
+ QString shownName = curFile;
+ if (curFile.isEmpty())
+ shownName = "untitled.txt";
+ setWindowFilePath(shownName);
+}
+
+QString MainWindow::strippedName(const QString &fullFileName)
+{
+ return QFileInfo(fullFileName).fileName();
+}
+
+#ifndef QT_NO_SESSIONMANAGER
+void MainWindow::commitData(QSessionManager &manager)
+{
+ if (manager.allowsInteraction()) {
+ if (!maybeSave())
+ manager.cancel();
+ } else {
+ // Non-interactive: save without asking
+ if (textEdit->document()->isModified())
+ save();
+ }
+}
+#endif
diff --git a/tests/manual/examples/widgets/application/mainwindow.h b/tests/manual/examples/widgets/application/mainwindow.h
new file mode 100644
index 0000000000..f1a4e2ac4f
--- /dev/null
+++ b/tests/manual/examples/widgets/application/mainwindow.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+
+QT_BEGIN_NAMESPACE
+class QAction;
+class QMenu;
+class QPlainTextEdit;
+class QSessionManager;
+QT_END_NAMESPACE
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow();
+
+ void loadFile(const QString &fileName);
+
+protected:
+ void closeEvent(QCloseEvent *event) override;
+
+private slots:
+ void newFile();
+ void open();
+ bool save();
+ bool saveAs();
+ void about();
+ void documentWasModified();
+#ifndef QT_NO_SESSIONMANAGER
+ void commitData(QSessionManager &);
+#endif
+
+private:
+ void createActions();
+ void createStatusBar();
+ void readSettings();
+ void writeSettings();
+ bool maybeSave();
+ bool saveFile(const QString &fileName);
+ void setCurrentFile(const QString &fileName);
+ QString strippedName(const QString &fullFileName);
+
+ QPlainTextEdit *textEdit;
+ QString curFile;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/dialogs/classwizard/CMakeLists.txt b/tests/manual/examples/widgets/dialogs/classwizard/CMakeLists.txt
new file mode 100644
index 0000000000..1e31415f87
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/classwizard/CMakeLists.txt
@@ -0,0 +1,55 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(classwizard LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/dialogs/classwizard")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(classwizard
+ classwizard.cpp classwizard.h
+ main.cpp
+)
+
+set_target_properties(classwizard PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(classwizard PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(classwizard_resource_files
+ "images/background.png"
+ "images/banner.png"
+ "images/logo1.png"
+ "images/logo2.png"
+ "images/logo3.png"
+ "images/watermark1.png"
+ "images/watermark2.png"
+)
+
+qt_add_resources(classwizard "classwizard"
+ PREFIX
+ "/"
+ FILES
+ ${classwizard_resource_files}
+)
+
+install(TARGETS classwizard
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/dialogs/classwizard/classwizard.cpp b/tests/manual/examples/widgets/dialogs/classwizard/classwizard.cpp
new file mode 100644
index 0000000000..d3587ce46f
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/classwizard/classwizard.cpp
@@ -0,0 +1,394 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+
+#include "classwizard.h"
+
+//! [0] //! [1]
+ClassWizard::ClassWizard(QWidget *parent)
+ : QWizard(parent)
+{
+ addPage(new IntroPage);
+ addPage(new ClassInfoPage);
+ addPage(new CodeStylePage);
+ addPage(new OutputFilesPage);
+ addPage(new ConclusionPage);
+//! [0]
+
+ setPixmap(QWizard::BannerPixmap, QPixmap(":/images/banner.png"));
+ setPixmap(QWizard::BackgroundPixmap, QPixmap(":/images/background.png"));
+
+ setWindowTitle(tr("Class Wizard"));
+//! [2]
+}
+//! [1] //! [2]
+
+//! [3]
+void ClassWizard::accept()
+//! [3] //! [4]
+{
+ QByteArray className = field("className").toByteArray();
+ QByteArray baseClass = field("baseClass").toByteArray();
+ QByteArray macroName = field("macroName").toByteArray();
+ QByteArray baseInclude = field("baseInclude").toByteArray();
+
+ QString outputDir = field("outputDir").toString();
+ QString header = field("header").toString();
+ QString implementation = field("implementation").toString();
+//! [4]
+
+ QByteArray block;
+
+ if (field("comment").toBool()) {
+ block += "/*\n";
+ block += " " + header.toLatin1() + '\n';
+ block += "*/\n";
+ block += '\n';
+ }
+ if (field("protect").toBool()) {
+ block += "#ifndef " + macroName + '\n';
+ block += "#define " + macroName + '\n';
+ block += '\n';
+ }
+ if (field("includeBase").toBool()) {
+ block += "#include " + baseInclude + '\n';
+ block += '\n';
+ }
+
+ block += "class " + className;
+ if (!baseClass.isEmpty())
+ block += " : public " + baseClass;
+ block += '\n';
+ block += "{\n";
+
+ /* qmake ignore Q_OBJECT */
+
+ if (field("qobjectMacro").toBool()) {
+ block += " Q_OBJECT\n";
+ block += '\n';
+ }
+ block += "public:\n";
+
+ if (field("qobjectCtor").toBool()) {
+ block += " " + className + "(QObject *parent = nullptr);\n";
+ } else if (field("qwidgetCtor").toBool()) {
+ block += " " + className + "(QWidget *parent = nullptr);\n";
+ } else if (field("defaultCtor").toBool()) {
+ block += " " + className + "();\n";
+ if (field("copyCtor").toBool()) {
+ block += " " + className + "(const " + className + " &other);\n";
+ block += '\n';
+ block += " " + className + " &operator=" + "(const " + className
+ + " &other);\n";
+ }
+ }
+ block += "};\n";
+
+ if (field("protect").toBool()) {
+ block += '\n';
+ block += "#endif\n";
+ }
+
+ QFile headerFile(outputDir + '/' + header);
+ if (!headerFile.open(QFile::WriteOnly | QFile::Text)) {
+ QMessageBox::warning(nullptr, QObject::tr("Simple Wizard"),
+ QObject::tr("Cannot write file %1:\n%2")
+ .arg(headerFile.fileName())
+ .arg(headerFile.errorString()));
+ return;
+ }
+ headerFile.write(block);
+
+ block.clear();
+
+ if (field("comment").toBool()) {
+ block += "/*\n";
+ block += " " + implementation.toLatin1() + '\n';
+ block += "*/\n";
+ block += '\n';
+ }
+ block += "#include \"" + header.toLatin1() + "\"\n";
+ block += '\n';
+
+ if (field("qobjectCtor").toBool()) {
+ block += className + "::" + className + "(QObject *parent)\n";
+ block += " : " + baseClass + "(parent)\n";
+ block += "{\n";
+ block += "}\n";
+ } else if (field("qwidgetCtor").toBool()) {
+ block += className + "::" + className + "(QWidget *parent)\n";
+ block += " : " + baseClass + "(parent)\n";
+ block += "{\n";
+ block += "}\n";
+ } else if (field("defaultCtor").toBool()) {
+ block += className + "::" + className + "()\n";
+ block += "{\n";
+ block += " // missing code\n";
+ block += "}\n";
+
+ if (field("copyCtor").toBool()) {
+ block += "\n";
+ block += className + "::" + className + "(const " + className
+ + " &other)\n";
+ block += "{\n";
+ block += " *this = other;\n";
+ block += "}\n";
+ block += '\n';
+ block += className + " &" + className + "::operator=(const "
+ + className + " &other)\n";
+ block += "{\n";
+ if (!baseClass.isEmpty())
+ block += " " + baseClass + "::operator=(other);\n";
+ block += " // missing code\n";
+ block += " return *this;\n";
+ block += "}\n";
+ }
+ }
+
+ QFile implementationFile(outputDir + '/' + implementation);
+ if (!implementationFile.open(QFile::WriteOnly | QFile::Text)) {
+ QMessageBox::warning(nullptr, QObject::tr("Simple Wizard"),
+ QObject::tr("Cannot write file %1:\n%2")
+ .arg(implementationFile.fileName())
+ .arg(implementationFile.errorString()));
+ return;
+ }
+ implementationFile.write(block);
+
+//! [5]
+ QDialog::accept();
+//! [5] //! [6]
+}
+//! [6]
+
+//! [7]
+IntroPage::IntroPage(QWidget *parent)
+ : QWizardPage(parent)
+{
+ setTitle(tr("Introduction"));
+ setPixmap(QWizard::WatermarkPixmap, QPixmap(":/images/watermark1.png"));
+
+ label = new QLabel(tr("This wizard will generate a skeleton C++ class "
+ "definition, including a few functions. You simply "
+ "need to specify the class name and set a few "
+ "options to produce a header file and an "
+ "implementation file for your new C++ class."));
+ label->setWordWrap(true);
+
+ QVBoxLayout *layout = new QVBoxLayout;
+ layout->addWidget(label);
+ setLayout(layout);
+}
+//! [7]
+
+//! [8] //! [9]
+ClassInfoPage::ClassInfoPage(QWidget *parent)
+ : QWizardPage(parent)
+{
+//! [8]
+ setTitle(tr("Class Information"));
+ setSubTitle(tr("Specify basic information about the class for which you "
+ "want to generate skeleton source code files."));
+ setPixmap(QWizard::LogoPixmap, QPixmap(":/images/logo1.png"));
+
+//! [10]
+ classNameLabel = new QLabel(tr("&Class name:"));
+ classNameLineEdit = new QLineEdit;
+ classNameLabel->setBuddy(classNameLineEdit);
+
+ baseClassLabel = new QLabel(tr("B&ase class:"));
+ baseClassLineEdit = new QLineEdit;
+ baseClassLabel->setBuddy(baseClassLineEdit);
+
+ qobjectMacroCheckBox = new QCheckBox(tr("Generate Q_OBJECT &macro"));
+
+//! [10]
+ groupBox = new QGroupBox(tr("C&onstructor"));
+//! [9]
+
+ qobjectCtorRadioButton = new QRadioButton(tr("&QObject-style constructor"));
+ qwidgetCtorRadioButton = new QRadioButton(tr("Q&Widget-style constructor"));
+ defaultCtorRadioButton = new QRadioButton(tr("&Default constructor"));
+ copyCtorCheckBox = new QCheckBox(tr("&Generate copy constructor and "
+ "operator="));
+
+ defaultCtorRadioButton->setChecked(true);
+
+ connect(defaultCtorRadioButton, &QAbstractButton::toggled,
+ copyCtorCheckBox, &QWidget::setEnabled);
+
+//! [11] //! [12]
+ registerField("className*", classNameLineEdit);
+ registerField("baseClass", baseClassLineEdit);
+ registerField("qobjectMacro", qobjectMacroCheckBox);
+//! [11]
+ registerField("qobjectCtor", qobjectCtorRadioButton);
+ registerField("qwidgetCtor", qwidgetCtorRadioButton);
+ registerField("defaultCtor", defaultCtorRadioButton);
+ registerField("copyCtor", copyCtorCheckBox);
+
+ QVBoxLayout *groupBoxLayout = new QVBoxLayout;
+//! [12]
+ groupBoxLayout->addWidget(qobjectCtorRadioButton);
+ groupBoxLayout->addWidget(qwidgetCtorRadioButton);
+ groupBoxLayout->addWidget(defaultCtorRadioButton);
+ groupBoxLayout->addWidget(copyCtorCheckBox);
+ groupBox->setLayout(groupBoxLayout);
+
+ QGridLayout *layout = new QGridLayout;
+ layout->addWidget(classNameLabel, 0, 0);
+ layout->addWidget(classNameLineEdit, 0, 1);
+ layout->addWidget(baseClassLabel, 1, 0);
+ layout->addWidget(baseClassLineEdit, 1, 1);
+ layout->addWidget(qobjectMacroCheckBox, 2, 0, 1, 2);
+ layout->addWidget(groupBox, 3, 0, 1, 2);
+ setLayout(layout);
+//! [13]
+}
+//! [13]
+
+//! [14]
+CodeStylePage::CodeStylePage(QWidget *parent)
+ : QWizardPage(parent)
+{
+ setTitle(tr("Code Style Options"));
+ setSubTitle(tr("Choose the formatting of the generated code."));
+ setPixmap(QWizard::LogoPixmap, QPixmap(":/images/logo2.png"));
+
+ commentCheckBox = new QCheckBox(tr("&Start generated files with a "
+//! [14]
+ "comment"));
+ commentCheckBox->setChecked(true);
+
+ protectCheckBox = new QCheckBox(tr("&Protect header file against multiple "
+ "inclusions"));
+ protectCheckBox->setChecked(true);
+
+ macroNameLabel = new QLabel(tr("&Macro name:"));
+ macroNameLineEdit = new QLineEdit;
+ macroNameLabel->setBuddy(macroNameLineEdit);
+
+ includeBaseCheckBox = new QCheckBox(tr("&Include base class definition"));
+ baseIncludeLabel = new QLabel(tr("Base class include:"));
+ baseIncludeLineEdit = new QLineEdit;
+ baseIncludeLabel->setBuddy(baseIncludeLineEdit);
+
+ connect(protectCheckBox, &QAbstractButton::toggled,
+ macroNameLabel, &QWidget::setEnabled);
+ connect(protectCheckBox, &QAbstractButton::toggled,
+ macroNameLineEdit, &QWidget::setEnabled);
+ connect(includeBaseCheckBox, &QAbstractButton::toggled,
+ baseIncludeLabel, &QWidget::setEnabled);
+ connect(includeBaseCheckBox, &QAbstractButton::toggled,
+ baseIncludeLineEdit, &QWidget::setEnabled);
+
+ registerField("comment", commentCheckBox);
+ registerField("protect", protectCheckBox);
+ registerField("macroName", macroNameLineEdit);
+ registerField("includeBase", includeBaseCheckBox);
+ registerField("baseInclude", baseIncludeLineEdit);
+
+ QGridLayout *layout = new QGridLayout;
+ layout->setColumnMinimumWidth(0, 20);
+ layout->addWidget(commentCheckBox, 0, 0, 1, 3);
+ layout->addWidget(protectCheckBox, 1, 0, 1, 3);
+ layout->addWidget(macroNameLabel, 2, 1);
+ layout->addWidget(macroNameLineEdit, 2, 2);
+ layout->addWidget(includeBaseCheckBox, 3, 0, 1, 3);
+ layout->addWidget(baseIncludeLabel, 4, 1);
+ layout->addWidget(baseIncludeLineEdit, 4, 2);
+//! [15]
+ setLayout(layout);
+}
+//! [15]
+
+//! [16]
+void CodeStylePage::initializePage()
+{
+ QString className = field("className").toString();
+ macroNameLineEdit->setText(className.toUpper() + "_H");
+
+ QString baseClass = field("baseClass").toString();
+
+ includeBaseCheckBox->setChecked(!baseClass.isEmpty());
+ includeBaseCheckBox->setEnabled(!baseClass.isEmpty());
+ baseIncludeLabel->setEnabled(!baseClass.isEmpty());
+ baseIncludeLineEdit->setEnabled(!baseClass.isEmpty());
+
+ QRegularExpression rx("Q[A-Z].*");
+ if (baseClass.isEmpty()) {
+ baseIncludeLineEdit->clear();
+ } else if (rx.match(baseClass).hasMatch()) {
+ baseIncludeLineEdit->setText('<' + baseClass + '>');
+ } else {
+ baseIncludeLineEdit->setText('"' + baseClass.toLower() + ".h\"");
+ }
+}
+//! [16]
+
+OutputFilesPage::OutputFilesPage(QWidget *parent)
+ : QWizardPage(parent)
+{
+ setTitle(tr("Output Files"));
+ setSubTitle(tr("Specify where you want the wizard to put the generated "
+ "skeleton code."));
+ setPixmap(QWizard::LogoPixmap, QPixmap(":/images/logo3.png"));
+
+ outputDirLabel = new QLabel(tr("&Output directory:"));
+ outputDirLineEdit = new QLineEdit;
+ outputDirLabel->setBuddy(outputDirLineEdit);
+
+ headerLabel = new QLabel(tr("&Header file name:"));
+ headerLineEdit = new QLineEdit;
+ headerLabel->setBuddy(headerLineEdit);
+
+ implementationLabel = new QLabel(tr("&Implementation file name:"));
+ implementationLineEdit = new QLineEdit;
+ implementationLabel->setBuddy(implementationLineEdit);
+
+ registerField("outputDir*", outputDirLineEdit);
+ registerField("header*", headerLineEdit);
+ registerField("implementation*", implementationLineEdit);
+
+ QGridLayout *layout = new QGridLayout;
+ layout->addWidget(outputDirLabel, 0, 0);
+ layout->addWidget(outputDirLineEdit, 0, 1);
+ layout->addWidget(headerLabel, 1, 0);
+ layout->addWidget(headerLineEdit, 1, 1);
+ layout->addWidget(implementationLabel, 2, 0);
+ layout->addWidget(implementationLineEdit, 2, 1);
+ setLayout(layout);
+}
+
+//! [17]
+void OutputFilesPage::initializePage()
+{
+ QString className = field("className").toString();
+ headerLineEdit->setText(className.toLower() + ".h");
+ implementationLineEdit->setText(className.toLower() + ".cpp");
+ outputDirLineEdit->setText(QDir::toNativeSeparators(QDir::tempPath()));
+}
+//! [17]
+
+ConclusionPage::ConclusionPage(QWidget *parent)
+ : QWizardPage(parent)
+{
+ setTitle(tr("Conclusion"));
+ setPixmap(QWizard::WatermarkPixmap, QPixmap(":/images/watermark2.png"));
+
+ label = new QLabel;
+ label->setWordWrap(true);
+
+ QVBoxLayout *layout = new QVBoxLayout;
+ layout->addWidget(label);
+ setLayout(layout);
+}
+
+void ConclusionPage::initializePage()
+{
+ QString finishText = wizard()->buttonText(QWizard::FinishButton);
+ finishText.remove('&');
+ label->setText(tr("Click %1 to generate the class skeleton.")
+ .arg(finishText));
+}
diff --git a/tests/manual/examples/widgets/dialogs/classwizard/classwizard.h b/tests/manual/examples/widgets/dialogs/classwizard/classwizard.h
new file mode 100644
index 0000000000..61f63b5035
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/classwizard/classwizard.h
@@ -0,0 +1,119 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef CLASSWIZARD_H
+#define CLASSWIZARD_H
+
+#include <QWizard>
+
+QT_BEGIN_NAMESPACE
+class QCheckBox;
+class QGroupBox;
+class QLabel;
+class QLineEdit;
+class QRadioButton;
+QT_END_NAMESPACE
+
+//! [0]
+class ClassWizard : public QWizard
+{
+ Q_OBJECT
+
+public:
+ ClassWizard(QWidget *parent = nullptr);
+
+ void accept() override;
+};
+//! [0]
+
+//! [1]
+class IntroPage : public QWizardPage
+{
+ Q_OBJECT
+
+public:
+ IntroPage(QWidget *parent = nullptr);
+
+private:
+ QLabel *label;
+};
+//! [1]
+
+//! [2]
+class ClassInfoPage : public QWizardPage
+{
+ Q_OBJECT
+
+public:
+ ClassInfoPage(QWidget *parent = nullptr);
+
+private:
+ QLabel *classNameLabel;
+ QLabel *baseClassLabel;
+ QLineEdit *classNameLineEdit;
+ QLineEdit *baseClassLineEdit;
+ QCheckBox *qobjectMacroCheckBox;
+ QGroupBox *groupBox;
+ QRadioButton *qobjectCtorRadioButton;
+ QRadioButton *qwidgetCtorRadioButton;
+ QRadioButton *defaultCtorRadioButton;
+ QCheckBox *copyCtorCheckBox;
+};
+//! [2]
+
+//! [3]
+class CodeStylePage : public QWizardPage
+{
+ Q_OBJECT
+
+public:
+ CodeStylePage(QWidget *parent = nullptr);
+
+protected:
+ void initializePage() override;
+
+private:
+ QCheckBox *commentCheckBox;
+ QCheckBox *protectCheckBox;
+ QCheckBox *includeBaseCheckBox;
+ QLabel *macroNameLabel;
+ QLabel *baseIncludeLabel;
+ QLineEdit *macroNameLineEdit;
+ QLineEdit *baseIncludeLineEdit;
+};
+//! [3]
+
+class OutputFilesPage : public QWizardPage
+{
+ Q_OBJECT
+
+public:
+ OutputFilesPage(QWidget *parent = nullptr);
+
+protected:
+ void initializePage() override;
+
+private:
+ QLabel *outputDirLabel;
+ QLabel *headerLabel;
+ QLabel *implementationLabel;
+ QLineEdit *outputDirLineEdit;
+ QLineEdit *headerLineEdit;
+ QLineEdit *implementationLineEdit;
+};
+
+class ConclusionPage : public QWizardPage
+{
+ Q_OBJECT
+
+public:
+ ConclusionPage(QWidget *parent = nullptr);
+
+protected:
+ void initializePage() override;
+
+private:
+ QLabel *label;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/dialogs/classwizard/classwizard.pro b/tests/manual/examples/widgets/dialogs/classwizard/classwizard.pro
new file mode 100644
index 0000000000..3ec321f4e8
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/classwizard/classwizard.pro
@@ -0,0 +1,10 @@
+QT += widgets
+
+HEADERS = classwizard.h
+SOURCES = classwizard.cpp \
+ main.cpp
+RESOURCES = classwizard.qrc
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/dialogs/classwizard
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/dialogs/classwizard/classwizard.qrc b/tests/manual/examples/widgets/dialogs/classwizard/classwizard.qrc
new file mode 100644
index 0000000000..41a5ddc7d1
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/classwizard/classwizard.qrc
@@ -0,0 +1,11 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>images/background.png</file>
+ <file>images/banner.png</file>
+ <file>images/logo1.png</file>
+ <file>images/logo2.png</file>
+ <file>images/logo3.png</file>
+ <file>images/watermark1.png</file>
+ <file>images/watermark2.png</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/dialogs/classwizard/images/background.png b/tests/manual/examples/widgets/dialogs/classwizard/images/background.png
new file mode 100644
index 0000000000..44c7badb85
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/classwizard/images/background.png
Binary files differ
diff --git a/tests/manual/examples/widgets/dialogs/classwizard/images/banner.png b/tests/manual/examples/widgets/dialogs/classwizard/images/banner.png
new file mode 100644
index 0000000000..3169152b8e
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/classwizard/images/banner.png
Binary files differ
diff --git a/tests/manual/examples/widgets/dialogs/classwizard/images/logo1.png b/tests/manual/examples/widgets/dialogs/classwizard/images/logo1.png
new file mode 100644
index 0000000000..f9b594aafc
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/classwizard/images/logo1.png
Binary files differ
diff --git a/tests/manual/examples/widgets/dialogs/classwizard/images/logo2.png b/tests/manual/examples/widgets/dialogs/classwizard/images/logo2.png
new file mode 100644
index 0000000000..5dcbd4669d
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/classwizard/images/logo2.png
Binary files differ
diff --git a/tests/manual/examples/widgets/dialogs/classwizard/images/logo3.png b/tests/manual/examples/widgets/dialogs/classwizard/images/logo3.png
new file mode 100644
index 0000000000..9fd3ea2358
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/classwizard/images/logo3.png
Binary files differ
diff --git a/tests/manual/examples/widgets/dialogs/classwizard/images/watermark1.png b/tests/manual/examples/widgets/dialogs/classwizard/images/watermark1.png
new file mode 100644
index 0000000000..0091f5c17a
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/classwizard/images/watermark1.png
Binary files differ
diff --git a/tests/manual/examples/widgets/dialogs/classwizard/images/watermark2.png b/tests/manual/examples/widgets/dialogs/classwizard/images/watermark2.png
new file mode 100644
index 0000000000..3b88f2e360
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/classwizard/images/watermark2.png
Binary files differ
diff --git a/tests/manual/examples/widgets/dialogs/classwizard/main.cpp b/tests/manual/examples/widgets/dialogs/classwizard/main.cpp
new file mode 100644
index 0000000000..a58db7f54f
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/classwizard/main.cpp
@@ -0,0 +1,26 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+#include <QTranslator>
+#include <QLocale>
+#include <QLibraryInfo>
+
+#include "classwizard.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+#ifndef QT_NO_TRANSLATION
+ QString translatorFileName = QLatin1String("qtbase_");
+ translatorFileName += QLocale::system().name();
+ QTranslator *translator = new QTranslator(&app);
+ if (translator->load(translatorFileName, QLibraryInfo::path(QLibraryInfo::TranslationsPath)))
+ app.installTranslator(translator);
+#endif
+
+ ClassWizard wizard;
+ wizard.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/dialogs/extension/CMakeLists.txt b/tests/manual/examples/widgets/dialogs/extension/CMakeLists.txt
new file mode 100644
index 0000000000..1e6da9187f
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/extension/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(extension LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/dialogs/extension")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(extension
+ finddialog.cpp finddialog.h
+ main.cpp
+)
+
+set_target_properties(extension PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(extension PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS extension
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/dialogs/extension/extension.pro b/tests/manual/examples/widgets/dialogs/extension/extension.pro
new file mode 100644
index 0000000000..f51052cd56
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/extension/extension.pro
@@ -0,0 +1,9 @@
+QT += widgets
+
+HEADERS = finddialog.h
+SOURCES = finddialog.cpp \
+ main.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/dialogs/extension
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/dialogs/extension/finddialog.cpp b/tests/manual/examples/widgets/dialogs/extension/finddialog.cpp
new file mode 100644
index 0000000000..f6b5943467
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/extension/finddialog.cpp
@@ -0,0 +1,77 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+
+#include "finddialog.h"
+
+//! [0]
+FindDialog::FindDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ label = new QLabel(tr("Find &what:"));
+ lineEdit = new QLineEdit;
+ label->setBuddy(lineEdit);
+
+ caseCheckBox = new QCheckBox(tr("Match &case"));
+ fromStartCheckBox = new QCheckBox(tr("Search from &start"));
+ fromStartCheckBox->setChecked(true);
+
+//! [1]
+ findButton = new QPushButton(tr("&Find"));
+ findButton->setDefault(true);
+
+ moreButton = new QPushButton(tr("&More"));
+ moreButton->setCheckable(true);
+//! [0]
+ moreButton->setAutoDefault(false);
+
+//! [1]
+
+//! [2]
+ extension = new QWidget;
+
+ wholeWordsCheckBox = new QCheckBox(tr("&Whole words"));
+ backwardCheckBox = new QCheckBox(tr("Search &backward"));
+ searchSelectionCheckBox = new QCheckBox(tr("Search se&lection"));
+//! [2]
+
+//! [3]
+ buttonBox = new QDialogButtonBox(Qt::Vertical);
+ buttonBox->addButton(findButton, QDialogButtonBox::ActionRole);
+ buttonBox->addButton(moreButton, QDialogButtonBox::ActionRole);
+
+ connect(moreButton, &QAbstractButton::toggled, extension, &QWidget::setVisible);
+
+ QVBoxLayout *extensionLayout = new QVBoxLayout;
+ extensionLayout->setContentsMargins(QMargins());
+ extensionLayout->addWidget(wholeWordsCheckBox);
+ extensionLayout->addWidget(backwardCheckBox);
+ extensionLayout->addWidget(searchSelectionCheckBox);
+ extension->setLayout(extensionLayout);
+//! [3]
+
+//! [4]
+ QHBoxLayout *topLeftLayout = new QHBoxLayout;
+ topLeftLayout->addWidget(label);
+ topLeftLayout->addWidget(lineEdit);
+
+ QVBoxLayout *leftLayout = new QVBoxLayout;
+ leftLayout->addLayout(topLeftLayout);
+ leftLayout->addWidget(caseCheckBox);
+ leftLayout->addWidget(fromStartCheckBox);
+
+ QGridLayout *mainLayout = new QGridLayout;
+ mainLayout->setSizeConstraint(QLayout::SetFixedSize);
+ mainLayout->addLayout(leftLayout, 0, 0);
+ mainLayout->addWidget(buttonBox, 0, 1);
+ mainLayout->addWidget(extension, 1, 0, 1, 2);
+ mainLayout->setRowStretch(2, 1);
+
+ setLayout(mainLayout);
+
+ setWindowTitle(tr("Extension"));
+//! [4] //! [5]
+ extension->hide();
+}
+//! [5]
diff --git a/tests/manual/examples/widgets/dialogs/extension/finddialog.h b/tests/manual/examples/widgets/dialogs/extension/finddialog.h
new file mode 100644
index 0000000000..61bc442f46
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/extension/finddialog.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef FINDDIALOG_H
+#define FINDDIALOG_H
+
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+class QCheckBox;
+class QDialogButtonBox;
+class QGroupBox;
+class QLabel;
+class QLineEdit;
+class QPushButton;
+QT_END_NAMESPACE
+
+//! [0]
+class FindDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ FindDialog(QWidget *parent = nullptr);
+
+private:
+ QLabel *label;
+ QLineEdit *lineEdit;
+ QCheckBox *caseCheckBox;
+ QCheckBox *fromStartCheckBox;
+ QCheckBox *wholeWordsCheckBox;
+ QCheckBox *searchSelectionCheckBox;
+ QCheckBox *backwardCheckBox;
+ QDialogButtonBox *buttonBox;
+ QPushButton *findButton;
+ QPushButton *moreButton;
+ QWidget *extension;
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/dialogs/extension/main.cpp b/tests/manual/examples/widgets/dialogs/extension/main.cpp
new file mode 100644
index 0000000000..730a0347f4
--- /dev/null
+++ b/tests/manual/examples/widgets/dialogs/extension/main.cpp
@@ -0,0 +1,16 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "finddialog.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ FindDialog dialog;
+
+ dialog.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/draganddrop/fridgemagnets/CMakeLists.txt b/tests/manual/examples/widgets/draganddrop/fridgemagnets/CMakeLists.txt
new file mode 100644
index 0000000000..966406969c
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/fridgemagnets/CMakeLists.txt
@@ -0,0 +1,50 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(fridgemagnets LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/draganddrop/fridgemagnets")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(fridgemagnets
+ draglabel.cpp draglabel.h
+ dragwidget.cpp dragwidget.h
+ main.cpp
+)
+
+set_target_properties(fridgemagnets PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(fridgemagnets PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(fridgemagnets_resource_files
+ "words.txt"
+)
+
+qt_add_resources(fridgemagnets "fridgemagnets"
+ PREFIX
+ "/dictionary"
+ FILES
+ ${fridgemagnets_resource_files}
+)
+
+install(TARGETS fridgemagnets
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/draganddrop/fridgemagnets/draglabel.cpp b/tests/manual/examples/widgets/draganddrop/fridgemagnets/draglabel.cpp
new file mode 100644
index 0000000000..952faae8fe
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/fridgemagnets/draglabel.cpp
@@ -0,0 +1,51 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "draglabel.h"
+
+#include <QtWidgets>
+
+//! [0]
+DragLabel::DragLabel(const QString &text, QWidget *parent)
+ : QLabel(parent)
+{
+ QFontMetrics metric(font());
+ QSize size = metric.size(Qt::TextSingleLine, text);
+
+ QImage image(size.width() + 12, size.height() + 12, QImage::Format_ARGB32_Premultiplied);
+ image.fill(qRgba(0, 0, 0, 0));
+
+ QFont font;
+ font.setStyleStrategy(QFont::ForceOutline);
+//! [0]
+
+//! [1]
+ QLinearGradient gradient(0, 0, 0, image.height()-1);
+ gradient.setColorAt(0.0, Qt::white);
+ gradient.setColorAt(0.2, QColor(200, 200, 255));
+ gradient.setColorAt(0.8, QColor(200, 200, 255));
+ gradient.setColorAt(1.0, QColor(127, 127, 200));
+
+ QPainter painter;
+ painter.begin(&image);
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.setBrush(gradient);
+ painter.drawRoundedRect(QRectF(0.5, 0.5, image.width()-1, image.height()-1),
+ 25, 25, Qt::RelativeSize);
+
+ painter.setFont(font);
+ painter.setBrush(Qt::black);
+ painter.drawText(QRect(QPoint(6, 6), size), Qt::AlignCenter, text);
+ painter.end();
+//! [1]
+
+//! [2]
+ setPixmap(QPixmap::fromImage(image));
+ m_labelText = text;
+}
+//! [2]
+
+QString DragLabel::labelText() const
+{
+ return m_labelText;
+}
diff --git a/tests/manual/examples/widgets/draganddrop/fridgemagnets/draglabel.h b/tests/manual/examples/widgets/draganddrop/fridgemagnets/draglabel.h
new file mode 100644
index 0000000000..e734a9ff78
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/fridgemagnets/draglabel.h
@@ -0,0 +1,27 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef DRAGLABEL_H
+#define DRAGLABEL_H
+
+#include <QLabel>
+
+QT_BEGIN_NAMESPACE
+class QDragEnterEvent;
+class QDragMoveEvent;
+class QFrame;
+QT_END_NAMESPACE
+
+//! [0]
+class DragLabel : public QLabel
+{
+public:
+ DragLabel(const QString &text, QWidget *parent);
+ QString labelText() const;
+
+private:
+ QString m_labelText;
+};
+//! [0]
+
+#endif // DRAGLABEL_H
diff --git a/tests/manual/examples/widgets/draganddrop/fridgemagnets/dragwidget.cpp b/tests/manual/examples/widgets/draganddrop/fridgemagnets/dragwidget.cpp
new file mode 100644
index 0000000000..a883e7c58a
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/fridgemagnets/dragwidget.cpp
@@ -0,0 +1,176 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "draglabel.h"
+#include "dragwidget.h"
+
+#include <QtWidgets>
+
+static inline QString fridgetMagnetsMimeType() { return QStringLiteral("application/x-fridgemagnet"); }
+
+//! [0]
+DragWidget::DragWidget(QWidget *parent)
+ : QWidget(parent)
+{
+ QFile dictionaryFile(QStringLiteral(":/dictionary/words.txt"));
+ dictionaryFile.open(QFile::ReadOnly);
+ QTextStream inputStream(&dictionaryFile);
+//! [0]
+
+//! [1]
+ int x = 5;
+ int y = 5;
+
+ while (!inputStream.atEnd()) {
+ QString word;
+ inputStream >> word;
+ if (!word.isEmpty()) {
+ DragLabel *wordLabel = new DragLabel(word, this);
+ wordLabel->move(x, y);
+ wordLabel->show();
+ wordLabel->setAttribute(Qt::WA_DeleteOnClose);
+ x += wordLabel->width() + 2;
+ if (x >= 245) {
+ x = 5;
+ y += wordLabel->height() + 2;
+ }
+ }
+ }
+//! [1]
+
+//! [2]
+ QPalette newPalette = palette();
+ newPalette.setColor(QPalette::Window, Qt::white);
+ setPalette(newPalette);
+
+ setMinimumSize(400, qMax(200, y));
+ setWindowTitle(tr("Fridge Magnets"));
+//! [2] //! [3]
+ setAcceptDrops(true);
+}
+//! [3]
+
+//! [4]
+void DragWidget::dragEnterEvent(QDragEnterEvent *event)
+{
+//! [4] //! [5]
+ if (event->mimeData()->hasFormat(fridgetMagnetsMimeType())) {
+ if (children().contains(event->source())) {
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ } else {
+ event->acceptProposedAction();
+//! [5] //! [6]
+ }
+//! [6] //! [7]
+ } else if (event->mimeData()->hasText()) {
+ event->acceptProposedAction();
+ } else {
+ event->ignore();
+ }
+}
+//! [7]
+
+//! [8]
+void DragWidget::dragMoveEvent(QDragMoveEvent *event)
+{
+ if (event->mimeData()->hasFormat(fridgetMagnetsMimeType())) {
+ if (children().contains(event->source())) {
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ } else {
+ event->acceptProposedAction();
+ }
+ } else if (event->mimeData()->hasText()) {
+ event->acceptProposedAction();
+ } else {
+ event->ignore();
+ }
+}
+//! [8]
+
+//! [9]
+void DragWidget::dropEvent(QDropEvent *event)
+{
+ if (event->mimeData()->hasFormat(fridgetMagnetsMimeType())) {
+ const QMimeData *mime = event->mimeData();
+//! [9] //! [10]
+ QByteArray itemData = mime->data(fridgetMagnetsMimeType());
+ QDataStream dataStream(&itemData, QIODevice::ReadOnly);
+
+ QString text;
+ QPoint offset;
+ dataStream >> text >> offset;
+//! [10]
+//! [11]
+ DragLabel *newLabel = new DragLabel(text, this);
+ newLabel->move(event->position().toPoint() - offset);
+ newLabel->show();
+ newLabel->setAttribute(Qt::WA_DeleteOnClose);
+
+ if (event->source() == this) {
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ } else {
+ event->acceptProposedAction();
+ }
+//! [11] //! [12]
+ } else if (event->mimeData()->hasText()) {
+ QStringList pieces = event->mimeData()->text().split(
+ QRegularExpression(QStringLiteral("\\s+")), Qt::SkipEmptyParts);
+ QPoint position = event->position().toPoint();
+
+ for (const QString &piece : pieces) {
+ DragLabel *newLabel = new DragLabel(piece, this);
+ newLabel->move(position);
+ newLabel->show();
+ newLabel->setAttribute(Qt::WA_DeleteOnClose);
+
+ position += QPoint(newLabel->width(), 0);
+ }
+
+ event->acceptProposedAction();
+ } else {
+ event->ignore();
+ }
+}
+//! [12]
+
+//! [13]
+void DragWidget::mousePressEvent(QMouseEvent *event)
+{
+//! [13]
+//! [14]
+ DragLabel *child = static_cast<DragLabel*>(childAt(event->position().toPoint()));
+ if (!child)
+ return;
+
+ QPoint hotSpot = event->position().toPoint() - child->pos();
+
+ QByteArray itemData;
+ QDataStream dataStream(&itemData, QIODevice::WriteOnly);
+ dataStream << child->labelText() << QPoint(hotSpot);
+//! [14]
+
+//! [15]
+ QMimeData *mimeData = new QMimeData;
+ mimeData->setData(fridgetMagnetsMimeType(), itemData);
+ mimeData->setText(child->labelText());
+//! [15]
+
+//! [16]
+ QDrag *drag = new QDrag(this);
+ drag->setMimeData(mimeData);
+ drag->setPixmap(child->pixmap());
+ drag->setHotSpot(hotSpot);
+
+ child->hide();
+//! [16]
+
+//! [17]
+ if (drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::CopyAction) == Qt::MoveAction)
+ child->close();
+ else
+ child->show();
+}
+//! [17]
diff --git a/tests/manual/examples/widgets/draganddrop/fridgemagnets/dragwidget.h b/tests/manual/examples/widgets/draganddrop/fridgemagnets/dragwidget.h
new file mode 100644
index 0000000000..9614a3b76a
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/fridgemagnets/dragwidget.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef DRAGWIDGET_H
+#define DRAGWIDGET_H
+
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QDragEnterEvent;
+class QDropEvent;
+QT_END_NAMESPACE
+
+//! [0]
+class DragWidget : public QWidget
+{
+public:
+ explicit DragWidget(QWidget *parent = nullptr);
+
+protected:
+ void dragEnterEvent(QDragEnterEvent *event) override;
+ void dragMoveEvent(QDragMoveEvent *event) override;
+ void dropEvent(QDropEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+};
+//! [0]
+
+#endif // DRAGWIDGET_H
diff --git a/tests/manual/examples/widgets/draganddrop/fridgemagnets/fridgemagnets.pro b/tests/manual/examples/widgets/draganddrop/fridgemagnets/fridgemagnets.pro
new file mode 100644
index 0000000000..2c3165d1c5
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/fridgemagnets/fridgemagnets.pro
@@ -0,0 +1,12 @@
+QT += widgets
+
+HEADERS = draglabel.h \
+ dragwidget.h
+RESOURCES = fridgemagnets.qrc
+SOURCES = draglabel.cpp \
+ dragwidget.cpp \
+ main.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/draganddrop/fridgemagnets
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/draganddrop/fridgemagnets/fridgemagnets.qrc b/tests/manual/examples/widgets/draganddrop/fridgemagnets/fridgemagnets.qrc
new file mode 100644
index 0000000000..b72217d701
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/fridgemagnets/fridgemagnets.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/dictionary">
+ <file>words.txt</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/draganddrop/fridgemagnets/main.cpp b/tests/manual/examples/widgets/draganddrop/fridgemagnets/main.cpp
new file mode 100644
index 0000000000..d3ffc82650
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/fridgemagnets/main.cpp
@@ -0,0 +1,23 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "dragwidget.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+#ifdef QT_KEYPAD_NAVIGATION
+ QApplication::setNavigationMode(Qt::NavigationModeCursorAuto);
+#endif
+ DragWidget window;
+
+ bool smallScreen = QApplication::arguments().contains(QStringLiteral("-small-screen"));
+ if (smallScreen)
+ window.showFullScreen();
+ else
+ window.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/draganddrop/fridgemagnets/words.txt b/tests/manual/examples/widgets/draganddrop/fridgemagnets/words.txt
new file mode 100644
index 0000000000..a7e1632b09
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/fridgemagnets/words.txt
@@ -0,0 +1,48 @@
+Colorless
+green
+ideas
+sleep
+furiously
+A
+colorless
+green
+idea
+is
+a
+new
+untried
+idea
+that
+is
+without
+vividness
+dull
+and
+unexciting
+To
+sleep
+furiously
+may
+seem
+a
+puzzling
+turn
+of
+phrase
+but
+the
+mind
+in
+sleep
+often
+indeed
+moves
+furiously
+with
+ideas
+and
+images
+flickering
+in
+and
+out
diff --git a/tests/manual/examples/widgets/draganddrop/puzzle/CMakeLists.txt b/tests/manual/examples/widgets/draganddrop/puzzle/CMakeLists.txt
new file mode 100644
index 0000000000..a4c098e45b
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/puzzle/CMakeLists.txt
@@ -0,0 +1,51 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(puzzle LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/draganddrop_puzzle")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(draganddrop_puzzle
+ main.cpp
+ mainwindow.cpp mainwindow.h
+ pieceslist.cpp pieceslist.h
+ puzzlewidget.cpp puzzlewidget.h
+)
+
+set_target_properties(draganddrop_puzzle PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(draganddrop_puzzle PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(puzzle_resource_files
+ "example.jpg"
+)
+
+qt_add_resources(draganddrop_puzzle "puzzle"
+ PREFIX
+ "/images"
+ FILES
+ ${puzzle_resource_files}
+)
+
+install(TARGETS draganddrop_puzzle
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/draganddrop/puzzle/example.jpg b/tests/manual/examples/widgets/draganddrop/puzzle/example.jpg
new file mode 100644
index 0000000000..023203c57a
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/puzzle/example.jpg
Binary files differ
diff --git a/tests/manual/examples/widgets/draganddrop/puzzle/main.cpp b/tests/manual/examples/widgets/draganddrop/puzzle/main.cpp
new file mode 100644
index 0000000000..32e219256a
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/puzzle/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ MainWindow window;
+ window.loadImage(QStringLiteral(":/images/example.jpg"));
+ window.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/draganddrop/puzzle/mainwindow.cpp b/tests/manual/examples/widgets/draganddrop/puzzle/mainwindow.cpp
new file mode 100644
index 0000000000..b34bc24201
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/puzzle/mainwindow.cpp
@@ -0,0 +1,118 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+#include "pieceslist.h"
+#include "puzzlewidget.h"
+
+#include <QtWidgets>
+#include <stdlib.h>
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent)
+{
+ setupMenus();
+ setupWidgets();
+
+ setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
+ setWindowTitle(tr("Puzzle"));
+}
+
+void MainWindow::openImage()
+{
+ const QString directory =
+ QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).value(0, QDir::homePath());
+ QFileDialog dialog(this, tr("Open Image"), directory);
+ dialog.setAcceptMode(QFileDialog::AcceptOpen);
+ dialog.setFileMode(QFileDialog::ExistingFile);
+ QStringList mimeTypeFilters;
+ for (const QByteArray &mimeTypeName : QImageReader::supportedMimeTypes())
+ mimeTypeFilters.append(mimeTypeName);
+ mimeTypeFilters.sort();
+ dialog.setMimeTypeFilters(mimeTypeFilters);
+ dialog.selectMimeTypeFilter("image/jpeg");
+ if (dialog.exec() == QDialog::Accepted)
+ loadImage(dialog.selectedFiles().constFirst());
+}
+
+void MainWindow::loadImage(const QString &fileName)
+{
+ QPixmap newImage;
+ if (!newImage.load(fileName)) {
+ QMessageBox::warning(this, tr("Open Image"),
+ tr("The image file could not be loaded."),
+ QMessageBox::Close);
+ return;
+ }
+ puzzleImage = newImage;
+ setupPuzzle();
+}
+
+void MainWindow::setCompleted()
+{
+ QMessageBox::information(this, tr("Puzzle Completed"),
+ tr("Congratulations! You have completed the puzzle!\n"
+ "Click OK to start again."),
+ QMessageBox::Ok);
+
+ setupPuzzle();
+}
+
+void MainWindow::setupPuzzle()
+{
+ int size = qMin(puzzleImage.width(), puzzleImage.height());
+ puzzleImage = puzzleImage.copy((puzzleImage.width() - size) / 2,
+ (puzzleImage.height() - size) / 2, size, size).scaled(puzzleWidget->width(),
+ puzzleWidget->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+
+ piecesList->clear();
+
+ for (int y = 0; y < 5; ++y) {
+ for (int x = 0; x < 5; ++x) {
+ int pieceSize = puzzleWidget->pieceSize();
+ QPixmap pieceImage = puzzleImage.copy(x * pieceSize, y * pieceSize, pieceSize, pieceSize);
+ piecesList->addPiece(pieceImage, QPoint(x, y));
+ }
+ }
+
+ for (int i = 0; i < piecesList->count(); ++i) {
+ if (QRandomGenerator::global()->bounded(2) == 1) {
+ QListWidgetItem *item = piecesList->takeItem(i);
+ piecesList->insertItem(0, item);
+ }
+ }
+
+ puzzleWidget->clear();
+}
+
+void MainWindow::setupMenus()
+{
+ QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
+
+ QAction *openAction = fileMenu->addAction(tr("&Open..."), this, &MainWindow::openImage);
+ openAction->setShortcuts(QKeySequence::Open);
+
+ QAction *exitAction = fileMenu->addAction(tr("E&xit"), qApp, &QCoreApplication::quit);
+ exitAction->setShortcuts(QKeySequence::Quit);
+
+ QMenu *gameMenu = menuBar()->addMenu(tr("&Game"));
+
+ gameMenu->addAction(tr("&Restart"), this, &MainWindow::setupPuzzle);
+}
+
+void MainWindow::setupWidgets()
+{
+ QFrame *frame = new QFrame;
+ QHBoxLayout *frameLayout = new QHBoxLayout(frame);
+ puzzleWidget = new PuzzleWidget(400);
+
+ piecesList = new PiecesList(puzzleWidget->pieceSize(), this);
+
+
+ connect(puzzleWidget, &PuzzleWidget::puzzleCompleted,
+ this, &MainWindow::setCompleted, Qt::QueuedConnection);
+
+ frameLayout->addWidget(piecesList);
+ frameLayout->addWidget(puzzleWidget);
+ setCentralWidget(frame);
+}
diff --git a/tests/manual/examples/widgets/draganddrop/puzzle/mainwindow.h b/tests/manual/examples/widgets/draganddrop/puzzle/mainwindow.h
new file mode 100644
index 0000000000..83a441c722
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/puzzle/mainwindow.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QPixmap>
+
+class PiecesList;
+class PuzzleWidget;
+QT_BEGIN_NAMESPACE
+class QListWidgetItem;
+QT_END_NAMESPACE
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QWidget *parent = nullptr);
+ void loadImage(const QString &path);
+
+public slots:
+ void openImage();
+ void setupPuzzle();
+
+private slots:
+ void setCompleted();
+
+private:
+ void setupMenus();
+ void setupWidgets();
+
+ QPixmap puzzleImage;
+ PiecesList *piecesList;
+ PuzzleWidget *puzzleWidget;
+};
+
+#endif // MAINWINDOW_H
diff --git a/tests/manual/examples/widgets/draganddrop/puzzle/pieceslist.cpp b/tests/manual/examples/widgets/draganddrop/puzzle/pieceslist.cpp
new file mode 100644
index 0000000000..0c7e771e42
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/puzzle/pieceslist.cpp
@@ -0,0 +1,87 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "pieceslist.h"
+
+#include <QDrag>
+#include <QDragEnterEvent>
+#include <QMimeData>
+
+PiecesList::PiecesList(int pieceSize, QWidget *parent)
+ : QListWidget(parent), m_PieceSize(pieceSize)
+{
+ setDragEnabled(true);
+ setViewMode(QListView::IconMode);
+ setIconSize(QSize(m_PieceSize, m_PieceSize));
+ setSpacing(10);
+ setAcceptDrops(true);
+ setDropIndicatorShown(true);
+}
+
+void PiecesList::dragEnterEvent(QDragEnterEvent *event)
+{
+ if (event->mimeData()->hasFormat(PiecesList::puzzleMimeType()))
+ event->accept();
+ else
+ event->ignore();
+}
+
+void PiecesList::dragMoveEvent(QDragMoveEvent *event)
+{
+ if (event->mimeData()->hasFormat(PiecesList::puzzleMimeType())) {
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ } else {
+ event->ignore();
+ }
+}
+
+void PiecesList::dropEvent(QDropEvent *event)
+{
+ if (event->mimeData()->hasFormat(PiecesList::puzzleMimeType())) {
+ QByteArray pieceData = event->mimeData()->data(PiecesList::puzzleMimeType());
+ QDataStream dataStream(&pieceData, QIODevice::ReadOnly);
+ QPixmap pixmap;
+ QPoint location;
+ dataStream >> pixmap >> location;
+
+ addPiece(pixmap, location);
+
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ } else {
+ event->ignore();
+ }
+}
+
+void PiecesList::addPiece(const QPixmap &pixmap, const QPoint &location)
+{
+ QListWidgetItem *pieceItem = new QListWidgetItem(this);
+ pieceItem->setIcon(QIcon(pixmap));
+ pieceItem->setData(Qt::UserRole, QVariant(pixmap));
+ pieceItem->setData(Qt::UserRole+1, location);
+ pieceItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
+}
+
+void PiecesList::startDrag(Qt::DropActions /*supportedActions*/)
+{
+ QListWidgetItem *item = currentItem();
+
+ QByteArray itemData;
+ QDataStream dataStream(&itemData, QIODevice::WriteOnly);
+ QPixmap pixmap = qvariant_cast<QPixmap>(item->data(Qt::UserRole));
+ QPoint location = item->data(Qt::UserRole+1).toPoint();
+
+ dataStream << pixmap << location;
+
+ QMimeData *mimeData = new QMimeData;
+ mimeData->setData(PiecesList::puzzleMimeType(), itemData);
+
+ QDrag *drag = new QDrag(this);
+ drag->setMimeData(mimeData);
+ drag->setHotSpot(QPoint(pixmap.width()/2, pixmap.height()/2));
+ drag->setPixmap(pixmap);
+
+ if (drag->exec(Qt::MoveAction) == Qt::MoveAction)
+ delete takeItem(row(item));
+}
diff --git a/tests/manual/examples/widgets/draganddrop/puzzle/pieceslist.h b/tests/manual/examples/widgets/draganddrop/puzzle/pieceslist.h
new file mode 100644
index 0000000000..4c617e7006
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/puzzle/pieceslist.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PIECESLIST_H
+#define PIECESLIST_H
+
+#include <QListWidget>
+
+class PiecesList : public QListWidget
+{
+ Q_OBJECT
+
+public:
+ explicit PiecesList(int pieceSize, QWidget *parent = nullptr);
+ void addPiece(const QPixmap &pixmap, const QPoint &location);
+
+ static QString puzzleMimeType() { return QStringLiteral("image/x-puzzle-piece"); }
+
+protected:
+ void dragEnterEvent(QDragEnterEvent *event) override;
+ void dragMoveEvent(QDragMoveEvent *event) override;
+ void dropEvent(QDropEvent *event) override;
+ void startDrag(Qt::DropActions supportedActions) override;
+
+ int m_PieceSize;
+};
+
+#endif // PIECESLIST_H
diff --git a/tests/manual/examples/widgets/draganddrop/puzzle/puzzle.pro b/tests/manual/examples/widgets/draganddrop/puzzle/puzzle.pro
new file mode 100644
index 0000000000..c462ba1bb8
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/puzzle/puzzle.pro
@@ -0,0 +1,17 @@
+QT += widgets
+requires(qtConfig(filedialog))
+
+HEADERS = mainwindow.h \
+ pieceslist.h \
+ puzzlewidget.h
+RESOURCES = puzzle.qrc
+SOURCES = main.cpp \
+ mainwindow.cpp \
+ pieceslist.cpp \
+ puzzlewidget.cpp
+
+QMAKE_PROJECT_NAME = dndpuzzle
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/draganddrop/puzzle
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/draganddrop/puzzle/puzzle.qrc b/tests/manual/examples/widgets/draganddrop/puzzle/puzzle.qrc
new file mode 100644
index 0000000000..4076cec026
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/puzzle/puzzle.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/images">
+ <file>example.jpg</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/draganddrop/puzzle/puzzlewidget.cpp b/tests/manual/examples/widgets/draganddrop/puzzle/puzzlewidget.cpp
new file mode 100644
index 0000000000..7c3f12f7d8
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/puzzle/puzzlewidget.cpp
@@ -0,0 +1,167 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "puzzlewidget.h"
+#include "pieceslist.h"
+
+#include <QDrag>
+#include <QDragEnterEvent>
+#include <QMimeData>
+#include <QPainter>
+
+PuzzleWidget::PuzzleWidget(int imageSize, QWidget *parent)
+ : QWidget(parent), m_ImageSize(imageSize)
+{
+ setAcceptDrops(true);
+ setMinimumSize(m_ImageSize, m_ImageSize);
+ setMaximumSize(m_ImageSize, m_ImageSize);
+}
+
+void PuzzleWidget::clear()
+{
+ pieces.clear();
+ highlightedRect = QRect();
+ inPlace = 0;
+ update();
+}
+
+void PuzzleWidget::dragEnterEvent(QDragEnterEvent *event)
+{
+ if (event->mimeData()->hasFormat(PiecesList::puzzleMimeType()))
+ event->accept();
+ else
+ event->ignore();
+}
+
+void PuzzleWidget::dragLeaveEvent(QDragLeaveEvent *event)
+{
+ QRect updateRect = highlightedRect;
+ highlightedRect = QRect();
+ update(updateRect);
+ event->accept();
+}
+
+void PuzzleWidget::dragMoveEvent(QDragMoveEvent *event)
+{
+ QRect updateRect = highlightedRect.united(targetSquare(event->position().toPoint()));
+
+ if (event->mimeData()->hasFormat(PiecesList::puzzleMimeType())
+ && findPiece(targetSquare(event->position().toPoint())) == -1) {
+
+ highlightedRect = targetSquare(event->position().toPoint());
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ } else {
+ highlightedRect = QRect();
+ event->ignore();
+ }
+
+ update(updateRect);
+}
+
+void PuzzleWidget::dropEvent(QDropEvent *event)
+{
+ if (event->mimeData()->hasFormat(PiecesList::puzzleMimeType())
+ && findPiece(targetSquare(event->position().toPoint())) == -1) {
+
+ QByteArray pieceData = event->mimeData()->data(PiecesList::puzzleMimeType());
+ QDataStream dataStream(&pieceData, QIODevice::ReadOnly);
+ Piece piece;
+ piece.rect = targetSquare(event->position().toPoint());
+ dataStream >> piece.pixmap >> piece.location;
+
+ pieces.append(piece);
+
+ highlightedRect = QRect();
+ update(piece.rect);
+
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+
+ if (piece.location == piece.rect.topLeft() / pieceSize()) {
+ inPlace++;
+ if (inPlace == 25)
+ emit puzzleCompleted();
+ }
+ } else {
+ highlightedRect = QRect();
+ event->ignore();
+ }
+}
+
+int PuzzleWidget::findPiece(const QRect &pieceRect) const
+{
+ for (int i = 0, size = pieces.size(); i < size; ++i) {
+ if (pieces.at(i).rect == pieceRect)
+ return i;
+ }
+ return -1;
+}
+
+void PuzzleWidget::mousePressEvent(QMouseEvent *event)
+{
+ QRect square = targetSquare(event->position().toPoint());
+ const int found = findPiece(square);
+
+ if (found == -1)
+ return;
+
+ Piece piece = pieces.takeAt(found);
+
+ if (piece.location == square.topLeft() / pieceSize())
+ inPlace--;
+
+ update(square);
+
+ QByteArray itemData;
+ QDataStream dataStream(&itemData, QIODevice::WriteOnly);
+
+ dataStream << piece.pixmap << piece.location;
+
+ QMimeData *mimeData = new QMimeData;
+ mimeData->setData(PiecesList::puzzleMimeType(), itemData);
+
+ QDrag *drag = new QDrag(this);
+ drag->setMimeData(mimeData);
+ drag->setHotSpot(event->position().toPoint() - square.topLeft());
+ drag->setPixmap(piece.pixmap);
+
+ if (drag->exec(Qt::MoveAction) != Qt::MoveAction) {
+ pieces.insert(found, piece);
+ update(targetSquare(event->position().toPoint()));
+
+ if (piece.location == square.topLeft() / pieceSize())
+ inPlace++;
+ }
+}
+
+void PuzzleWidget::paintEvent(QPaintEvent *event)
+{
+ QPainter painter(this);
+ painter.fillRect(event->rect(), Qt::white);
+
+ if (highlightedRect.isValid()) {
+ painter.setBrush(QColor("#ffcccc"));
+ painter.setPen(Qt::NoPen);
+ painter.drawRect(highlightedRect.adjusted(0, 0, -1, -1));
+ }
+
+ for (const Piece &piece : pieces)
+ painter.drawPixmap(piece.rect, piece.pixmap);
+}
+
+const QRect PuzzleWidget::targetSquare(const QPoint &position) const
+{
+ QPoint topLeft = QPoint(position.x() / pieceSize(), position.y() / pieceSize()) * pieceSize();
+ return QRect(topLeft, QSize(pieceSize(), pieceSize()));
+}
+
+int PuzzleWidget::pieceSize() const
+{
+ return m_ImageSize / 5;
+}
+
+int PuzzleWidget::imageSize() const
+{
+ return m_ImageSize;
+}
diff --git a/tests/manual/examples/widgets/draganddrop/puzzle/puzzlewidget.h b/tests/manual/examples/widgets/draganddrop/puzzle/puzzlewidget.h
new file mode 100644
index 0000000000..d1c00872ec
--- /dev/null
+++ b/tests/manual/examples/widgets/draganddrop/puzzle/puzzlewidget.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PUZZLEWIDGET_H
+#define PUZZLEWIDGET_H
+
+#include <QPoint>
+#include <QPixmap>
+#include <QList>
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QDragEnterEvent;
+class QDropEvent;
+class QMouseEvent;
+QT_END_NAMESPACE
+
+class PuzzleWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit PuzzleWidget(int imageSize, QWidget *parent = nullptr);
+ void clear();
+
+ int pieceSize() const;
+ int imageSize() const;
+
+signals:
+ void puzzleCompleted();
+
+protected:
+ void dragEnterEvent(QDragEnterEvent *event) override;
+ void dragLeaveEvent(QDragLeaveEvent *event) override;
+ void dragMoveEvent(QDragMoveEvent *event) override;
+ void dropEvent(QDropEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void paintEvent(QPaintEvent *event) override;
+
+private:
+ struct Piece {
+ QPixmap pixmap;
+ QRect rect;
+ QPoint location;
+ };
+
+ int findPiece(const QRect &pieceRect) const;
+ const QRect targetSquare(const QPoint &position) const;
+
+ QList<Piece> pieces;
+ QRect highlightedRect;
+ int inPlace;
+ int m_ImageSize;
+};
+
+#endif // PUZZLEWIDGET_H
diff --git a/tests/manual/examples/widgets/effects/fademessage/CMakeLists.txt b/tests/manual/examples/widgets/effects/fademessage/CMakeLists.txt
new file mode 100644
index 0000000000..13139fe0ad
--- /dev/null
+++ b/tests/manual/examples/widgets/effects/fademessage/CMakeLists.txt
@@ -0,0 +1,49 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(fademessage LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/effects/fademessage")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(fademessage
+ fademessage.cpp fademessage.h
+ main.cpp
+)
+
+set_target_properties(fademessage PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(fademessage PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(fademessage_resource_files
+ "background.jpg"
+)
+
+qt_add_resources(fademessage "fademessage"
+ PREFIX
+ "/"
+ FILES
+ ${fademessage_resource_files}
+)
+
+install(TARGETS fademessage
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/effects/fademessage/README b/tests/manual/examples/widgets/effects/fademessage/README
new file mode 100644
index 0000000000..f639e76508
--- /dev/null
+++ b/tests/manual/examples/widgets/effects/fademessage/README
@@ -0,0 +1,2 @@
+The background is taken from a public domain photo at:
+http://www.photos8.com/view/windows_problem_blue-800x600.html
diff --git a/tests/manual/examples/widgets/effects/fademessage/background.jpg b/tests/manual/examples/widgets/effects/fademessage/background.jpg
new file mode 100644
index 0000000000..9884233a29
--- /dev/null
+++ b/tests/manual/examples/widgets/effects/fademessage/background.jpg
Binary files differ
diff --git a/tests/manual/examples/widgets/effects/fademessage/fademessage.cpp b/tests/manual/examples/widgets/effects/fademessage/fademessage.cpp
new file mode 100644
index 0000000000..3d8385020b
--- /dev/null
+++ b/tests/manual/examples/widgets/effects/fademessage/fademessage.cpp
@@ -0,0 +1,91 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "fademessage.h"
+
+#include <QtWidgets>
+
+FadeMessage::FadeMessage(QWidget *parent): QGraphicsView(parent)
+{
+ setScene(&m_scene);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+ setupScene();
+
+ m_animation = new QPropertyAnimation(m_effect, "strength", this);
+ m_animation->setDuration(500);
+ m_animation->setEasingCurve(QEasingCurve::InOutSine);
+ m_animation->setStartValue(0);
+ m_animation->setEndValue(1);
+
+ setRenderHint(QPainter::Antialiasing, true);
+ setFrameStyle(QFrame::NoFrame);
+}
+
+void FadeMessage::togglePopup()
+{
+ if (m_message->isVisible()) {
+ m_message->setVisible(false);
+ m_animation->setDirection(QAbstractAnimation::Backward);
+ } else {
+ m_message->setVisible(true);
+ m_animation->setDirection(QAbstractAnimation::Forward);
+ }
+ m_animation->start();
+}
+
+void FadeMessage::setupScene()
+{
+ QGraphicsRectItem *parent = m_scene.addRect(0, 0, 800, 600);
+ parent->setPen(Qt::NoPen);
+ parent->setZValue(0);
+
+ QGraphicsPixmapItem *bg = m_scene.addPixmap(QPixmap(":/background.jpg"));
+ bg->setParentItem(parent);
+ bg->setZValue(-1);
+
+ for (int i = 1; i < 5; ++i)
+ for (int j = 2; j < 5; ++j) {
+ QGraphicsRectItem *item = m_scene.addRect(i * 50, (j - 1) * 50, 38, 38);
+ item->setParentItem(parent);
+ item->setZValue(1);
+ int hue = 12 * (i * 5 + j);
+ item->setBrush(QColor::fromHsv(hue, 128, 128));
+ }
+
+ QFont font;
+ font.setPointSize(font.pointSize() * 2);
+ font.setBold(true);
+ QFontMetrics fontMetrics(font);
+ int fh = fontMetrics.height();
+
+ QString sceneText = "Qt Everywhere!";
+ int sceneTextWidth = fontMetrics.horizontalAdvance(sceneText);
+
+ QGraphicsRectItem *block = m_scene.addRect(50, 300, sceneTextWidth, fh + 3);
+ block->setPen(Qt::NoPen);
+ block->setBrush(QColor(102, 153, 51));
+
+ QGraphicsTextItem *text = m_scene.addText(sceneText, font);
+ text->setDefaultTextColor(Qt::white);
+ text->setPos(50, 300);
+ block->setZValue(2);
+ block->hide();
+
+ text->setParentItem(block);
+ m_message = block;
+
+ m_effect = new QGraphicsColorizeEffect;
+ m_effect->setColor(QColor(122, 193, 66));
+ m_effect->setStrength(0);
+ m_effect->setEnabled(true);
+ parent->setGraphicsEffect(m_effect);
+
+ QPushButton *press = new QPushButton;
+ press->setText(tr("Press me"));
+ connect(press, &QAbstractButton::clicked, this, &FadeMessage::togglePopup);
+ m_scene.addWidget(press);
+
+ press->move(300, 500);
+}
diff --git a/tests/manual/examples/widgets/effects/fademessage/fademessage.h b/tests/manual/examples/widgets/effects/fademessage/fademessage.h
new file mode 100644
index 0000000000..49923016cd
--- /dev/null
+++ b/tests/manual/examples/widgets/effects/fademessage/fademessage.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef FADEMESSAGE_H
+#define FADEMESSAGE_H
+
+#include <QGraphicsEffect>
+#include <QGraphicsView>
+#include <QPropertyAnimation>
+
+#include "fademessage.h"
+
+class FadeMessage: public QGraphicsView
+{
+ Q_OBJECT
+
+public:
+ FadeMessage(QWidget *parent = nullptr);
+
+private:
+ void setupScene();
+
+private slots:
+ void togglePopup();
+
+private:
+ QGraphicsScene m_scene;
+ QGraphicsColorizeEffect *m_effect;
+ QGraphicsItem *m_message;
+ QPropertyAnimation *m_animation;
+};
+
+#endif // FADEMESSAGE_H
diff --git a/tests/manual/examples/widgets/effects/fademessage/fademessage.pro b/tests/manual/examples/widgets/effects/fademessage/fademessage.pro
new file mode 100644
index 0000000000..0beff1999a
--- /dev/null
+++ b/tests/manual/examples/widgets/effects/fademessage/fademessage.pro
@@ -0,0 +1,9 @@
+QT += widgets
+
+SOURCES += main.cpp fademessage.cpp
+HEADERS += fademessage.h
+RESOURCES += fademessage.qrc
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/effects/fademessage
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/effects/fademessage/fademessage.qrc b/tests/manual/examples/widgets/effects/fademessage/fademessage.qrc
new file mode 100644
index 0000000000..9efea6a67d
--- /dev/null
+++ b/tests/manual/examples/widgets/effects/fademessage/fademessage.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/" >
+ <file>background.jpg</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/effects/fademessage/main.cpp b/tests/manual/examples/widgets/effects/fademessage/main.cpp
new file mode 100644
index 0000000000..66504bc432
--- /dev/null
+++ b/tests/manual/examples/widgets/effects/fademessage/main.cpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "fademessage.h"
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+ FadeMessage widget;
+ widget.setWindowTitle(QT_TRANSLATE_NOOP(QGraphicsView, "Popup Message with Effect"));
+ widget.setFixedSize(400, 600);
+ widget.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/graphicsview/embeddeddialogs/CMakeLists.txt b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/CMakeLists.txt
new file mode 100644
index 0000000000..c25544166f
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/CMakeLists.txt
@@ -0,0 +1,50 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(embeddeddialogs LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/graphicsview/embeddeddialogs")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(embeddeddialogs
+ customproxy.cpp customproxy.h
+ embeddeddialog.cpp embeddeddialog.h embeddeddialog.ui
+ main.cpp
+)
+
+set_target_properties(embeddeddialogs PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(embeddeddialogs PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(embeddeddialogs_resource_files
+ "No-Ones-Laughing-3.jpg"
+)
+
+qt_add_resources(embeddeddialogs "embeddeddialogs"
+ PREFIX
+ "/"
+ FILES
+ ${embeddeddialogs_resource_files}
+)
+
+install(TARGETS embeddeddialogs
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/graphicsview/embeddeddialogs/No-Ones-Laughing-3.jpg b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/No-Ones-Laughing-3.jpg
new file mode 100644
index 0000000000..445567fbda
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/No-Ones-Laughing-3.jpg
Binary files differ
diff --git a/tests/manual/examples/widgets/graphicsview/embeddeddialogs/customproxy.cpp b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/customproxy.cpp
new file mode 100644
index 0000000000..57a104f57d
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/customproxy.cpp
@@ -0,0 +1,133 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "customproxy.h"
+
+#include <QGraphicsScene>
+#include <QPainter>
+#include <QStyleOptionGraphicsItem>
+
+CustomProxy::CustomProxy(QGraphicsItem *parent, Qt::WindowFlags wFlags)
+ : QGraphicsProxyWidget(parent, wFlags), timeLine(new QTimeLine(250, this))
+{
+ connect(timeLine, &QTimeLine::valueChanged,
+ this, &CustomProxy::updateStep);
+ connect(timeLine, &QTimeLine::stateChanged,
+ this, &CustomProxy::stateChanged);
+}
+
+QRectF CustomProxy::boundingRect() const
+{
+ return QGraphicsProxyWidget::boundingRect().adjusted(0, 0, 10, 10);
+}
+
+void CustomProxy::paintWindowFrame(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ const QColor color(0, 0, 0, 64);
+
+ QRectF r = windowFrameRect();
+ QRectF right(r.right(), r.top() + 10, 10, r.height() - 10);
+ QRectF bottom(r.left() + 10, r.bottom(), r.width(), 10);
+ bool intersectsRight = right.intersects(option->exposedRect);
+ bool intersectsBottom = bottom.intersects(option->exposedRect);
+ if (intersectsRight && intersectsBottom) {
+ QPainterPath path;
+ path.addRect(right);
+ path.addRect(bottom);
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(color);
+ painter->drawPath(path);
+ } else if (intersectsBottom) {
+ painter->fillRect(bottom, color);
+ } else if (intersectsRight) {
+ painter->fillRect(right, color);
+ }
+
+ QGraphicsProxyWidget::paintWindowFrame(painter, option, widget);
+}
+
+void CustomProxy::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
+{
+ QGraphicsProxyWidget::hoverEnterEvent(event);
+ scene()->setActiveWindow(this);
+ if (qFuzzyCompare(timeLine->currentValue(), 1))
+ zoomIn();
+}
+
+void CustomProxy::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
+{
+ QGraphicsProxyWidget::hoverLeaveEvent(event);
+ if (!popupShown
+ && (timeLine->direction() != QTimeLine::Backward || qFuzzyIsNull(timeLine->currentValue()))) {
+ zoomOut();
+ }
+}
+
+bool CustomProxy::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
+{
+ if (watched->isWindow()
+ && (event->type() == QEvent::UngrabMouse || event->type() == QEvent::GrabMouse)) {
+ popupShown = watched->isVisible();
+ if (!popupShown && !isUnderMouse())
+ zoomOut();
+ }
+ return QGraphicsProxyWidget::sceneEventFilter(watched, event);
+}
+
+QVariant CustomProxy::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ if (change == ItemChildAddedChange || change == ItemChildRemovedChange) {
+ if (change == ItemChildAddedChange) {
+ currentPopup = qvariant_cast<QGraphicsItem *>(value);
+ currentPopup->setCacheMode(ItemCoordinateCache);
+ if (scene())
+ currentPopup->installSceneEventFilter(this);
+ } else if (scene()) {
+ currentPopup->removeSceneEventFilter(this);
+ currentPopup = nullptr;
+ }
+ } else if (currentPopup && change == ItemSceneHasChanged) {
+ currentPopup->installSceneEventFilter(this);
+ }
+ return QGraphicsProxyWidget::itemChange(change, value);
+}
+
+void CustomProxy::updateStep(qreal step)
+{
+ QRectF r = boundingRect();
+ setTransform(QTransform()
+ .translate(r.width() / 2, r.height() / 2)
+ .rotate(step * 30, Qt::XAxis)
+ .rotate(step * 10, Qt::YAxis)
+ .rotate(step * 5, Qt::ZAxis)
+ .scale(1 + 1.5 * step, 1 + 1.5 * step)
+ .translate(-r.width() / 2, -r.height() / 2));
+}
+
+void CustomProxy::stateChanged(QTimeLine::State state)
+{
+ if (state == QTimeLine::Running) {
+ if (timeLine->direction() == QTimeLine::Forward)
+ setCacheMode(ItemCoordinateCache);
+ } else if (state == QTimeLine::NotRunning) {
+ if (timeLine->direction() == QTimeLine::Backward)
+ setCacheMode(DeviceCoordinateCache);
+ }
+}
+
+void CustomProxy::zoomIn()
+{
+ if (timeLine->direction() != QTimeLine::Forward)
+ timeLine->setDirection(QTimeLine::Forward);
+ if (timeLine->state() == QTimeLine::NotRunning)
+ timeLine->start();
+}
+
+void CustomProxy::zoomOut()
+{
+ if (timeLine->direction() != QTimeLine::Backward)
+ timeLine->setDirection(QTimeLine::Backward);
+ if (timeLine->state() == QTimeLine::NotRunning)
+ timeLine->start();
+}
diff --git a/tests/manual/examples/widgets/graphicsview/embeddeddialogs/customproxy.h b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/customproxy.h
new file mode 100644
index 0000000000..444d39e73e
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/customproxy.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef CUSTOMPROXY_H
+#define CUSTOMPROXY_H
+
+#include <QTimeLine>
+#include <QGraphicsProxyWidget>
+
+class CustomProxy : public QGraphicsProxyWidget
+{
+ Q_OBJECT
+
+public:
+ explicit CustomProxy(QGraphicsItem *parent = nullptr, Qt::WindowFlags wFlags = { });
+
+ QRectF boundingRect() const override;
+ void paintWindowFrame(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget) override;
+
+protected:
+ void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
+ bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override;
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
+
+private slots:
+ void updateStep(qreal step);
+ void stateChanged(QTimeLine::State);
+ void zoomIn();
+ void zoomOut();
+
+private:
+ QTimeLine *timeLine;
+ QGraphicsItem *currentPopup = nullptr;
+ bool popupShown = false;
+};
+
+#endif // CUSTOMPROXY_H
diff --git a/tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialog.cpp b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialog.cpp
new file mode 100644
index 0000000000..43d7fbf1e9
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialog.cpp
@@ -0,0 +1,70 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "embeddeddialog.h"
+#include "ui_embeddeddialog.h"
+
+#include <QStyleFactory>
+
+EmbeddedDialog::EmbeddedDialog(QWidget *parent)
+ : QDialog(parent)
+ , ui(new Ui::EmbeddedDialog)
+{
+ ui->setupUi(this);
+ ui->layoutDirection->setCurrentIndex(layoutDirection() != Qt::LeftToRight);
+
+ const QStringList styleKeys = QStyleFactory::keys();
+ for (const QString &styleName : styleKeys) {
+ ui->style->addItem(styleName);
+ if (style()->objectName().toLower() == styleName.toLower())
+ ui->style->setCurrentIndex(ui->style->count() - 1);
+ }
+
+ connect(ui->layoutDirection, &QComboBox::activated,
+ this, &EmbeddedDialog::layoutDirectionChanged);
+ connect(ui->spacing, &QSlider::valueChanged,
+ this, &EmbeddedDialog::spacingChanged);
+ connect(ui->fontComboBox, &QFontComboBox::currentFontChanged,
+ this, &EmbeddedDialog::fontChanged);
+ connect(ui->style, &QComboBox::textActivated,
+ this, &EmbeddedDialog::styleChanged);
+}
+
+EmbeddedDialog::~EmbeddedDialog()
+{
+ delete ui;
+}
+
+void EmbeddedDialog::layoutDirectionChanged(int index)
+{
+ setLayoutDirection(index == 0 ? Qt::LeftToRight : Qt::RightToLeft);
+}
+
+void EmbeddedDialog::spacingChanged(int spacing)
+{
+ layout()->setSpacing(spacing);
+ adjustSize();
+}
+
+void EmbeddedDialog::fontChanged(const QFont &font)
+{
+ setFont(font);
+}
+
+static void setStyleHelper(QWidget *widget, QStyle *style)
+{
+ widget->setStyle(style);
+ widget->setPalette(style->standardPalette());
+ const QObjectList children = widget->children();
+ for (QObject *child : children) {
+ if (QWidget *childWidget = qobject_cast<QWidget *>(child))
+ setStyleHelper(childWidget, style);
+ }
+}
+
+void EmbeddedDialog::styleChanged(const QString &styleName)
+{
+ QStyle *style = QStyleFactory::create(styleName);
+ if (style)
+ setStyleHelper(this, style);
+}
diff --git a/tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialog.h b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialog.h
new file mode 100644
index 0000000000..d4c561799e
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialog.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef EMBEDDEDDIALOG_H
+#define EMBEDDEDDIALOG_H
+
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+namespace Ui {
+class EmbeddedDialog;
+}
+QT_END_NAMESPACE
+
+class EmbeddedDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ EmbeddedDialog(QWidget *parent = nullptr);
+ ~EmbeddedDialog();
+
+private slots:
+ void layoutDirectionChanged(int index);
+ void spacingChanged(int spacing);
+ void fontChanged(const QFont &font);
+ void styleChanged(const QString &styleName);
+
+private:
+ Ui::EmbeddedDialog *ui;
+};
+
+#endif // EMBEDDEDDIALOG_H
diff --git a/tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialog.ui b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialog.ui
new file mode 100644
index 0000000000..82aa5d7be3
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialog.ui
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>EmbeddedDialog</class>
+ <widget class="QDialog" name="EmbeddedDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>407</width>
+ <height>134</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Embedded Dialog</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Layout Direction:</string>
+ </property>
+ <property name="buddy">
+ <cstring>layoutDirection</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="layoutDirection">
+ <item>
+ <property name="text">
+ <string>Left to Right</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Right to Left</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Select Font:</string>
+ </property>
+ <property name="buddy">
+ <cstring>fontComboBox</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QFontComboBox" name="fontComboBox"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Style:</string>
+ </property>
+ <property name="buddy">
+ <cstring>style</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="style"/>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Layout spacing:</string>
+ </property>
+ <property name="buddy">
+ <cstring>spacing</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QSlider" name="spacing">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialogs.pro b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialogs.pro
new file mode 100644
index 0000000000..60c507fc3d
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialogs.pro
@@ -0,0 +1,18 @@
+QT += widgets
+requires(qtConfig(fontcombobox))
+
+SOURCES += main.cpp
+SOURCES += customproxy.cpp embeddeddialog.cpp
+HEADERS += customproxy.h embeddeddialog.h
+
+FORMS += embeddeddialog.ui
+RESOURCES += embeddeddialogs.qrc
+
+build_all:!build_pass {
+ CONFIG -= build_all
+ CONFIG += release
+}
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/graphicsview/embeddeddialogs
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialogs.qrc b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialogs.qrc
new file mode 100644
index 0000000000..33be5038da
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/embeddeddialogs.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource>
+ <file>No-Ones-Laughing-3.jpg</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/graphicsview/embeddeddialogs/main.cpp b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/main.cpp
new file mode 100644
index 0000000000..700c6b1222
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/embeddeddialogs/main.cpp
@@ -0,0 +1,42 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "customproxy.h"
+#include "embeddeddialog.h"
+
+#include <QApplication>
+#include <QGraphicsScene>
+#include <QGraphicsView>
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ QGraphicsScene scene;
+ scene.setStickyFocus(true);
+ const int gridSize = 10;
+
+ for (int y = 0; y < gridSize; ++y) {
+ for (int x = 0; x < gridSize; ++x) {
+ CustomProxy *proxy = new CustomProxy(nullptr, Qt::Window);
+ proxy->setWidget(new EmbeddedDialog);
+
+ QRectF rect = proxy->boundingRect();
+
+ proxy->setPos(x * rect.width() * 1.05, y * rect.height() * 1.05);
+ proxy->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
+
+ scene.addItem(proxy);
+ }
+ }
+ scene.setSceneRect(scene.itemsBoundingRect());
+
+ QGraphicsView view(&scene);
+ view.scale(0.5, 0.5);
+ view.setRenderHints(view.renderHints() | QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
+ view.setBackgroundBrush(QPixmap(":/No-Ones-Laughing-3.jpg"));
+ view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
+ view.show();
+ view.setWindowTitle("Embedded Dialogs Example");
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/graphicsview/flowlayout/CMakeLists.txt b/tests/manual/examples/widgets/graphicsview/flowlayout/CMakeLists.txt
new file mode 100644
index 0000000000..66ad4707b5
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/flowlayout/CMakeLists.txt
@@ -0,0 +1,38 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(flowlayout LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/graphicsview_flowlayout")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(graphicsview_flowlayout
+ flowlayout.cpp flowlayout.h
+ main.cpp
+ window.cpp window.h
+)
+
+set_target_properties(graphicsview_flowlayout PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(graphicsview_flowlayout PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS graphicsview_flowlayout
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/graphicsview/flowlayout/flowlayout.cpp b/tests/manual/examples/widgets/graphicsview/flowlayout/flowlayout.cpp
new file mode 100644
index 0000000000..ab5f4717b3
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/flowlayout/flowlayout.cpp
@@ -0,0 +1,170 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "flowlayout.h"
+
+#include <QtMath>
+
+FlowLayout::FlowLayout(QGraphicsLayoutItem *parent) : QGraphicsLayout(parent)
+{
+ QSizePolicy sp = sizePolicy();
+ sp.setHeightForWidth(true);
+ setSizePolicy(sp);
+}
+
+void FlowLayout::insertItem(int index, QGraphicsLayoutItem *item)
+{
+ item->setParentLayoutItem(this);
+ if (index > m_items.count() || index < 0)
+ index = m_items.count();
+ m_items.insert(index, item);
+ invalidate();
+}
+
+int FlowLayout::count() const
+{
+ return m_items.count();
+}
+
+QGraphicsLayoutItem *FlowLayout::itemAt(int index) const
+{
+ return m_items.value(index);
+}
+
+void FlowLayout::removeAt(int index)
+{
+ m_items.removeAt(index);
+ invalidate();
+}
+
+qreal FlowLayout::spacing(Qt::Orientation o) const
+{
+ return m_spacing[int(o) - 1];
+}
+
+void FlowLayout::setSpacing(Qt::Orientations o, qreal spacing)
+{
+ if (o & Qt::Horizontal)
+ m_spacing[0] = spacing;
+ if (o & Qt::Vertical)
+ m_spacing[1] = spacing;
+}
+
+void FlowLayout::setGeometry(const QRectF &geom)
+{
+ QGraphicsLayout::setGeometry(geom);
+ doLayout(geom, true);
+}
+
+qreal FlowLayout::doLayout(const QRectF &geom, bool applyNewGeometry) const
+{
+ qreal left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ const qreal maxw = geom.width() - left - right;
+
+ qreal x = 0;
+ qreal y = 0;
+ qreal maxRowHeight = 0;
+ QSizeF pref;
+ for (QGraphicsLayoutItem *item : m_items) {
+ pref = item->effectiveSizeHint(Qt::PreferredSize);
+ maxRowHeight = qMax(maxRowHeight, pref.height());
+
+ qreal next_x;
+ next_x = x + pref.width();
+ if (next_x > maxw) {
+ if (qFuzzyIsNull(x)) {
+ pref.setWidth(maxw);
+ } else {
+ x = 0;
+ next_x = pref.width();
+ }
+ y += maxRowHeight + spacing(Qt::Vertical);
+ maxRowHeight = 0;
+ }
+
+ if (applyNewGeometry)
+ item->setGeometry(QRectF(QPointF(left + x, top + y), pref));
+ x = next_x + spacing(Qt::Horizontal);
+ }
+ maxRowHeight = qMax(maxRowHeight, pref.height());
+ return top + y + maxRowHeight + bottom;
+}
+
+QSizeF FlowLayout::minSize(const QSizeF &constraint) const
+{
+ QSizeF size(0, 0);
+ qreal left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ if (constraint.width() >= 0) { // height for width
+ const qreal height = doLayout(QRectF(QPointF(0,0), constraint), false);
+ size = QSizeF(constraint.width(), height);
+ } else if (constraint.height() >= 0) { // width for height?
+ // not supported
+ } else {
+ for (const QGraphicsLayoutItem *item : std::as_const(m_items))
+ size = size.expandedTo(item->effectiveSizeHint(Qt::MinimumSize));
+ size += QSizeF(left + right, top + bottom);
+ }
+ return size;
+}
+
+QSizeF FlowLayout::prefSize() const
+{
+ qreal left, right;
+ getContentsMargins(&left, nullptr, &right, nullptr);
+
+ qreal maxh = 0;
+ qreal totalWidth = 0;
+ for (const QGraphicsLayoutItem *item : std::as_const(m_items)) {
+ if (totalWidth > 0)
+ totalWidth += spacing(Qt::Horizontal);
+ QSizeF pref = item->effectiveSizeHint(Qt::PreferredSize);
+ totalWidth += pref.width();
+ maxh = qMax(maxh, pref.height());
+ }
+ maxh += spacing(Qt::Vertical);
+
+ const qreal goldenAspectRatio = 1.61803399;
+ qreal w = qSqrt(totalWidth * maxh * goldenAspectRatio) + left + right;
+
+ return minSize(QSizeF(w, -1));
+}
+
+QSizeF FlowLayout::maxSize() const
+{
+ qreal totalWidth = 0;
+ qreal totalHeight = 0;
+ for (const QGraphicsLayoutItem *item : std::as_const(m_items)) {
+ if (totalWidth > 0)
+ totalWidth += spacing(Qt::Horizontal);
+ if (totalHeight > 0)
+ totalHeight += spacing(Qt::Vertical);
+ QSizeF pref = item->effectiveSizeHint(Qt::PreferredSize);
+ totalWidth += pref.width();
+ totalHeight += pref.height();
+ }
+
+ qreal left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ return QSizeF(left + totalWidth + right, top + totalHeight + bottom);
+}
+
+QSizeF FlowLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ QSizeF sh = constraint;
+ switch (which) {
+ case Qt::PreferredSize:
+ sh = prefSize();
+ break;
+ case Qt::MinimumSize:
+ sh = minSize(constraint);
+ break;
+ case Qt::MaximumSize:
+ sh = maxSize();
+ break;
+ default:
+ break;
+ }
+ return sh;
+}
diff --git a/tests/manual/examples/widgets/graphicsview/flowlayout/flowlayout.h b/tests/manual/examples/widgets/graphicsview/flowlayout/flowlayout.h
new file mode 100644
index 0000000000..028394827a
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/flowlayout/flowlayout.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef FLOWLAYOUT_H
+#define FLOWLAYOUT_H
+
+#include <QGraphicsLayout>
+
+class FlowLayout : public QGraphicsLayout
+{
+public:
+ FlowLayout(QGraphicsLayoutItem *parent = nullptr);
+ inline void addItem(QGraphicsLayoutItem *item);
+ void insertItem(int index, QGraphicsLayoutItem *item);
+ void setSpacing(Qt::Orientations o, qreal spacing);
+ qreal spacing(Qt::Orientation o) const;
+
+ // inherited functions
+ void setGeometry(const QRectF &geom) override;
+
+ int count() const override;
+ QGraphicsLayoutItem *itemAt(int index) const override;
+ void removeAt(int index) override;
+
+protected:
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const override;
+
+private:
+ qreal doLayout(const QRectF &geom, bool applyNewGeometry) const;
+ QSizeF minSize(const QSizeF &constraint) const;
+ QSizeF prefSize() const;
+ QSizeF maxSize() const;
+
+ QList<QGraphicsLayoutItem *> m_items;
+ qreal m_spacing[2] = {6, 6};
+};
+
+
+inline void FlowLayout::addItem(QGraphicsLayoutItem *item)
+{
+ insertItem(-1, item);
+}
+
+#endif // FLOWLAYOUT_H
diff --git a/tests/manual/examples/widgets/graphicsview/flowlayout/flowlayout.pro b/tests/manual/examples/widgets/graphicsview/flowlayout/flowlayout.pro
new file mode 100644
index 0000000000..7830e3a008
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/flowlayout/flowlayout.pro
@@ -0,0 +1,10 @@
+QT += widgets
+
+QMAKE_PROJECT_NAME = flowlayout_graphicsview
+
+HEADERS += flowlayout.h window.h
+SOURCES += flowlayout.cpp main.cpp window.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/graphicsview/flowlayout
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/graphicsview/flowlayout/main.cpp b/tests/manual/examples/widgets/graphicsview/flowlayout/main.cpp
new file mode 100644
index 0000000000..118b404870
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/flowlayout/main.cpp
@@ -0,0 +1,24 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//! [1]
+#include "window.h"
+
+#include <QApplication>
+#include <QGraphicsView>
+
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ QGraphicsScene scene;
+ QGraphicsView view(&scene);
+ Window *w = new Window;
+ scene.addItem(w);
+
+ view.resize(400, 300);
+ view.show();
+
+ return app.exec();
+}
+//! [1]
diff --git a/tests/manual/examples/widgets/graphicsview/flowlayout/window.cpp b/tests/manual/examples/widgets/graphicsview/flowlayout/window.cpp
new file mode 100644
index 0000000000..ed9a5720d3
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/flowlayout/window.cpp
@@ -0,0 +1,24 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "window.h"
+#include "flowlayout.h"
+
+#include <QGraphicsProxyWidget>
+#include <QLabel>
+
+Window::Window(QGraphicsItem *parent) : QGraphicsWidget(parent, Qt::Window)
+{
+ FlowLayout *lay = new FlowLayout;
+ const QString sentence(QLatin1String("I am not bothered by the fact that I am unknown."
+ " I am bothered when I do not know others. (Confucius)"));
+ const QList<QStringView> words = QStringView{ sentence }.split(QLatin1Char(' '), Qt::SkipEmptyParts);
+ for (const QStringView &word : words) {
+ QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(this);
+ QLabel *label = new QLabel(word.toString());
+ label->setFrameStyle(QFrame::Box | QFrame::Plain);
+ proxy->setWidget(label);
+ lay->addItem(proxy);
+ }
+ setLayout(lay);
+}
diff --git a/tests/manual/examples/widgets/graphicsview/flowlayout/window.h b/tests/manual/examples/widgets/graphicsview/flowlayout/window.h
new file mode 100644
index 0000000000..2fc5570406
--- /dev/null
+++ b/tests/manual/examples/widgets/graphicsview/flowlayout/window.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include <QGraphicsWidget>
+
+class Window : public QGraphicsWidget
+{
+ Q_OBJECT
+public:
+ Window(QGraphicsItem *parent = nullptr);
+};
+
+#endif // WINDOW_H
diff --git a/tests/manual/examples/widgets/itemviews/chart/CMakeLists.txt b/tests/manual/examples/widgets/itemviews/chart/CMakeLists.txt
new file mode 100644
index 0000000000..57e52d84fc
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/chart/CMakeLists.txt
@@ -0,0 +1,56 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(chart LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/itemviews/chart")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(chart
+ main.cpp
+ mainwindow.cpp mainwindow.h
+ pieview.cpp pieview.h
+)
+
+set_target_properties(chart PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(chart PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(chart_resource_files
+ "qtdata.cht"
+)
+
+qt_add_resources(chart "chart"
+ PREFIX
+ "/Charts"
+ FILES
+ ${chart_resource_files}
+)
+
+if(UNIX AND NOT APPLE AND NOT HAIKU AND NOT INTEGRITY AND NOT VXWORKS)
+ target_link_libraries(chart PRIVATE
+ m
+ )
+endif()
+
+install(TARGETS chart
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/itemviews/chart/chart.pro b/tests/manual/examples/widgets/itemviews/chart/chart.pro
new file mode 100644
index 0000000000..323f6202e4
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/chart/chart.pro
@@ -0,0 +1,14 @@
+QT += widgets
+requires(qtConfig(filedialog))
+
+HEADERS = mainwindow.h \
+ pieview.h
+RESOURCES = chart.qrc
+SOURCES = main.cpp \
+ mainwindow.cpp \
+ pieview.cpp
+unix:!mac:!vxworks:!integrity:!haiku:LIBS += -lm
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/itemviews/chart
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/itemviews/chart/chart.qrc b/tests/manual/examples/widgets/itemviews/chart/chart.qrc
new file mode 100644
index 0000000000..7401d4d2f8
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/chart/chart.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/Charts" >
+ <file>qtdata.cht</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/itemviews/chart/main.cpp b/tests/manual/examples/widgets/itemviews/chart/main.cpp
new file mode 100644
index 0000000000..7d7cf3e573
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/chart/main.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ MainWindow window;
+ window.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/itemviews/chart/mainwindow.cpp b/tests/manual/examples/widgets/itemviews/chart/mainwindow.cpp
new file mode 100644
index 0000000000..450f2e3a56
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/chart/mainwindow.cpp
@@ -0,0 +1,136 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "pieview.h"
+#include "mainwindow.h"
+
+#include <QtWidgets>
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent)
+{
+ QMenu *fileMenu = new QMenu(tr("&File"), this);
+ QAction *openAction = fileMenu->addAction(tr("&Open..."));
+ openAction->setShortcuts(QKeySequence::Open);
+ QAction *saveAction = fileMenu->addAction(tr("&Save As..."));
+ saveAction->setShortcuts(QKeySequence::SaveAs);
+ QAction *quitAction = fileMenu->addAction(tr("E&xit"));
+ quitAction->setShortcuts(QKeySequence::Quit);
+
+ setupModel();
+ setupViews();
+
+ connect(openAction, &QAction::triggered, this, &MainWindow::openFile);
+ connect(saveAction, &QAction::triggered, this, &MainWindow::saveFile);
+ connect(quitAction, &QAction::triggered, qApp, &QCoreApplication::quit);
+
+ menuBar()->addMenu(fileMenu);
+ statusBar();
+
+ loadFile(":/Charts/qtdata.cht");
+
+ setWindowTitle(tr("Chart"));
+ resize(870, 550);
+}
+
+void MainWindow::setupModel()
+{
+ model = new QStandardItemModel(8, 2, this);
+ model->setHeaderData(0, Qt::Horizontal, tr("Label"));
+ model->setHeaderData(1, Qt::Horizontal, tr("Quantity"));
+}
+
+void MainWindow::setupViews()
+{
+ QSplitter *splitter = new QSplitter;
+ QTableView *table = new QTableView;
+ pieChart = new PieView;
+ splitter->addWidget(table);
+ splitter->addWidget(pieChart);
+ splitter->setStretchFactor(0, 0);
+ splitter->setStretchFactor(1, 1);
+
+ table->setModel(model);
+ pieChart->setModel(model);
+
+ QItemSelectionModel *selectionModel = new QItemSelectionModel(model);
+ table->setSelectionModel(selectionModel);
+ pieChart->setSelectionModel(selectionModel);
+
+ QHeaderView *headerView = table->horizontalHeader();
+ headerView->setStretchLastSection(true);
+
+ setCentralWidget(splitter);
+}
+
+void MainWindow::openFile()
+{
+ const QString fileName =
+ QFileDialog::getOpenFileName(this, tr("Choose a data file"), "", "*.cht");
+ if (!fileName.isEmpty())
+ loadFile(fileName);
+}
+
+void MainWindow::loadFile(const QString &fileName)
+{
+ QFile file(fileName);
+ if (!file.open(QFile::ReadOnly | QFile::Text))
+ return;
+
+ QTextStream stream(&file);
+
+ model->removeRows(0, model->rowCount(QModelIndex()), QModelIndex());
+
+ int row = 0;
+ while (!stream.atEnd()) {
+ const QString line = stream.readLine();
+ if (!line.isEmpty()) {
+ model->insertRows(row, 1, QModelIndex());
+
+ const QStringList pieces = line.split(QLatin1Char(','), Qt::SkipEmptyParts);
+ if (pieces.size() < 3)
+ continue;
+ model->setData(model->index(row, 0, QModelIndex()),
+ pieces.value(0));
+ model->setData(model->index(row, 1, QModelIndex()),
+ pieces.value(1));
+ model->setData(model->index(row, 0, QModelIndex()),
+ QColor(pieces.value(2)), Qt::DecorationRole);
+ row++;
+ }
+ };
+
+ file.close();
+ statusBar()->showMessage(tr("Loaded %1").arg(fileName), 2000);
+}
+
+void MainWindow::saveFile()
+{
+ QString fileName = QFileDialog::getSaveFileName(this,
+ tr("Save file as"), "", "*.cht");
+
+ if (fileName.isEmpty())
+ return;
+
+ QFile file(fileName);
+ if (!file.open(QFile::WriteOnly | QFile::Text))
+ return;
+
+ QTextStream stream(&file);
+ for (int row = 0; row < model->rowCount(QModelIndex()); ++row) {
+
+ QStringList pieces;
+
+ pieces.append(model->data(model->index(row, 0, QModelIndex()),
+ Qt::DisplayRole).toString());
+ pieces.append(model->data(model->index(row, 1, QModelIndex()),
+ Qt::DisplayRole).toString());
+ pieces.append(model->data(model->index(row, 0, QModelIndex()),
+ Qt::DecorationRole).toString());
+
+ stream << pieces.join(',') << "\n";
+ }
+
+ file.close();
+ statusBar()->showMessage(tr("Saved %1").arg(fileName), 2000);
+}
diff --git a/tests/manual/examples/widgets/itemviews/chart/mainwindow.h b/tests/manual/examples/widgets/itemviews/chart/mainwindow.h
new file mode 100644
index 0000000000..f2a639c952
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/chart/mainwindow.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+
+QT_BEGIN_NAMESPACE
+class QAbstractItemModel;
+class QAbstractItemView;
+QT_END_NAMESPACE
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow(QWidget *parent = nullptr);
+
+private slots:
+ void openFile();
+ void saveFile();
+
+private:
+ void setupModel();
+ void setupViews();
+ void loadFile(const QString &path);
+
+ QAbstractItemModel *model = nullptr;
+ QAbstractItemView *pieChart = nullptr;
+};
+
+#endif // MAINWINDOW_H
diff --git a/tests/manual/examples/widgets/itemviews/chart/pieview.cpp b/tests/manual/examples/widgets/itemviews/chart/pieview.cpp
new file mode 100644
index 0000000000..e681207f38
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/chart/pieview.cpp
@@ -0,0 +1,506 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "pieview.h"
+
+#include <QtWidgets>
+
+PieView::PieView(QWidget *parent)
+ : QAbstractItemView(parent)
+{
+ horizontalScrollBar()->setRange(0, 0);
+ verticalScrollBar()->setRange(0, 0);
+}
+
+void PieView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
+ const QList<int> &roles)
+{
+ QAbstractItemView::dataChanged(topLeft, bottomRight, roles);
+
+ if (!roles.contains(Qt::DisplayRole))
+ return;
+
+ validItems = 0;
+ totalValue = 0.0;
+
+ for (int row = 0; row < model()->rowCount(rootIndex()); ++row) {
+
+ QModelIndex index = model()->index(row, 1, rootIndex());
+ double value = model()->data(index, Qt::DisplayRole).toDouble();
+
+ if (value > 0.0) {
+ totalValue += value;
+ validItems++;
+ }
+ }
+ viewport()->update();
+}
+
+bool PieView::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event)
+{
+ if (index.column() == 0)
+ return QAbstractItemView::edit(index, trigger, event);
+ else
+ return false;
+}
+
+/*
+ Returns the item that covers the coordinate given in the view.
+*/
+
+QModelIndex PieView::indexAt(const QPoint &point) const
+{
+ if (validItems == 0)
+ return QModelIndex();
+
+ // Transform the view coordinates into contents widget coordinates.
+ int wx = point.x() + horizontalScrollBar()->value();
+ int wy = point.y() + verticalScrollBar()->value();
+
+ if (wx < totalSize) {
+ double cx = wx - totalSize / 2;
+ double cy = totalSize / 2 - wy; // positive cy for items above the center
+
+ // Determine the distance from the center point of the pie chart.
+ double d = std::sqrt(std::pow(cx, 2) + std::pow(cy, 2));
+
+ if (d == 0 || d > pieSize / 2)
+ return QModelIndex();
+
+ // Determine the angle of the point.
+ double angle = qRadiansToDegrees(std::atan2(cy, cx));
+ if (angle < 0)
+ angle = 360 + angle;
+
+ // Find the relevant slice of the pie.
+ double startAngle = 0.0;
+
+ for (int row = 0; row < model()->rowCount(rootIndex()); ++row) {
+
+ QModelIndex index = model()->index(row, 1, rootIndex());
+ double value = model()->data(index).toDouble();
+
+ if (value > 0.0) {
+ double sliceAngle = 360 * value / totalValue;
+
+ if (angle >= startAngle && angle < (startAngle + sliceAngle))
+ return model()->index(row, 1, rootIndex());
+
+ startAngle += sliceAngle;
+ }
+ }
+ } else {
+ QStyleOptionViewItem option;
+ initViewItemOption(&option);
+ double itemHeight = QFontMetrics(option.font).height();
+ int listItem = int((wy - margin) / itemHeight);
+ int validRow = 0;
+
+ for (int row = 0; row < model()->rowCount(rootIndex()); ++row) {
+
+ QModelIndex index = model()->index(row, 1, rootIndex());
+ if (model()->data(index).toDouble() > 0.0) {
+
+ if (listItem == validRow)
+ return model()->index(row, 0, rootIndex());
+
+ // Update the list index that corresponds to the next valid row.
+ ++validRow;
+ }
+ }
+ }
+
+ return QModelIndex();
+}
+
+bool PieView::isIndexHidden(const QModelIndex & /*index*/) const
+{
+ return false;
+}
+
+/*
+ Returns the rectangle of the item at position \a index in the
+ model. The rectangle is in contents coordinates.
+*/
+
+QRect PieView::itemRect(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QRect();
+
+ // Check whether the index's row is in the list of rows represented
+ // by slices.
+ QModelIndex valueIndex;
+
+ if (index.column() != 1)
+ valueIndex = model()->index(index.row(), 1, rootIndex());
+ else
+ valueIndex = index;
+
+ if (model()->data(valueIndex).toDouble() <= 0.0)
+ return QRect();
+
+ int listItem = 0;
+ for (int row = index.row()-1; row >= 0; --row) {
+ if (model()->data(model()->index(row, 1, rootIndex())).toDouble() > 0.0)
+ listItem++;
+ }
+
+ switch (index.column()) {
+ case 0: {
+ QStyleOptionViewItem option;
+ initViewItemOption(&option);
+ const qreal itemHeight = QFontMetricsF(option.font).height();
+ return QRect(totalSize,
+ qRound(margin + listItem * itemHeight),
+ totalSize - margin, qRound(itemHeight));
+ }
+ case 1:
+ return viewport()->rect();
+ }
+ return QRect();
+}
+
+QRegion PieView::itemRegion(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QRegion();
+
+ if (index.column() != 1)
+ return itemRect(index);
+
+ if (model()->data(index).toDouble() <= 0.0)
+ return QRegion();
+
+ double startAngle = 0.0;
+ for (int row = 0; row < model()->rowCount(rootIndex()); ++row) {
+
+ QModelIndex sliceIndex = model()->index(row, 1, rootIndex());
+ double value = model()->data(sliceIndex).toDouble();
+
+ if (value > 0.0) {
+ double angle = 360 * value / totalValue;
+
+ if (sliceIndex == index) {
+ QPainterPath slicePath;
+ slicePath.moveTo(totalSize / 2, totalSize / 2);
+ slicePath.arcTo(margin, margin, margin + pieSize, margin + pieSize,
+ startAngle, angle);
+ slicePath.closeSubpath();
+
+ return QRegion(slicePath.toFillPolygon().toPolygon());
+ }
+
+ startAngle += angle;
+ }
+ }
+
+ return QRegion();
+}
+
+int PieView::horizontalOffset() const
+{
+ return horizontalScrollBar()->value();
+}
+
+void PieView::mousePressEvent(QMouseEvent *event)
+{
+ QAbstractItemView::mousePressEvent(event);
+ origin = event->position().toPoint();
+ if (!rubberBand)
+ rubberBand = new QRubberBand(QRubberBand::Rectangle, viewport());
+ rubberBand->setGeometry(QRect(origin, QSize()));
+ rubberBand->show();
+}
+
+void PieView::mouseMoveEvent(QMouseEvent *event)
+{
+ if (rubberBand)
+ rubberBand->setGeometry(QRect(origin, event->position().toPoint()).normalized());
+ QAbstractItemView::mouseMoveEvent(event);
+}
+
+void PieView::mouseReleaseEvent(QMouseEvent *event)
+{
+ QAbstractItemView::mouseReleaseEvent(event);
+ if (rubberBand)
+ rubberBand->hide();
+ viewport()->update();
+}
+
+QModelIndex PieView::moveCursor(QAbstractItemView::CursorAction cursorAction,
+ Qt::KeyboardModifiers /*modifiers*/)
+{
+ QModelIndex current = currentIndex();
+
+ switch (cursorAction) {
+ case MoveLeft:
+ case MoveUp:
+ if (current.row() > 0)
+ current = model()->index(current.row() - 1, current.column(),
+ rootIndex());
+ else
+ current = model()->index(0, current.column(), rootIndex());
+ break;
+ case MoveRight:
+ case MoveDown:
+ if (current.row() < rows(current) - 1)
+ current = model()->index(current.row() + 1, current.column(),
+ rootIndex());
+ else
+ current = model()->index(rows(current) - 1, current.column(),
+ rootIndex());
+ break;
+ default:
+ break;
+ }
+
+ viewport()->update();
+ return current;
+}
+
+void PieView::paintEvent(QPaintEvent *event)
+{
+ QItemSelectionModel *selections = selectionModel();
+ QStyleOptionViewItem option;
+ initViewItemOption(&option);
+
+ QBrush background = option.palette.base();
+ QPen foreground(option.palette.color(QPalette::WindowText));
+
+ QPainter painter(viewport());
+ painter.setRenderHint(QPainter::Antialiasing);
+
+ painter.fillRect(event->rect(), background);
+ painter.setPen(foreground);
+
+ // Viewport rectangles
+ QRect pieRect = QRect(margin, margin, pieSize, pieSize);
+
+ if (validItems <= 0)
+ return;
+
+ painter.save();
+ painter.translate(pieRect.x() - horizontalScrollBar()->value(),
+ pieRect.y() - verticalScrollBar()->value());
+ painter.drawEllipse(0, 0, pieSize, pieSize);
+ double startAngle = 0.0;
+ int row;
+
+ for (row = 0; row < model()->rowCount(rootIndex()); ++row) {
+ QModelIndex index = model()->index(row, 1, rootIndex());
+ double value = model()->data(index).toDouble();
+
+ if (value > 0.0) {
+ double angle = 360 * value / totalValue;
+
+ QModelIndex colorIndex = model()->index(row, 0, rootIndex());
+ QColor color = QColor(model()->data(colorIndex, Qt::DecorationRole).toString());
+
+ if (currentIndex() == index)
+ painter.setBrush(QBrush(color, Qt::Dense4Pattern));
+ else if (selections->isSelected(index))
+ painter.setBrush(QBrush(color, Qt::Dense3Pattern));
+ else
+ painter.setBrush(QBrush(color));
+
+ painter.drawPie(0, 0, pieSize, pieSize, int(startAngle*16), int(angle*16));
+
+ startAngle += angle;
+ }
+ }
+ painter.restore();
+
+ for (row = 0; row < model()->rowCount(rootIndex()); ++row) {
+ QModelIndex index = model()->index(row, 1, rootIndex());
+ double value = model()->data(index).toDouble();
+
+ if (value > 0.0) {
+ QModelIndex labelIndex = model()->index(row, 0, rootIndex());
+
+ QStyleOptionViewItem option;
+ initViewItemOption(&option);
+
+ option.rect = visualRect(labelIndex);
+ if (selections->isSelected(labelIndex))
+ option.state |= QStyle::State_Selected;
+ if (currentIndex() == labelIndex)
+ option.state |= QStyle::State_HasFocus;
+ itemDelegate()->paint(&painter, option, labelIndex);
+ }
+ }
+}
+
+void PieView::resizeEvent(QResizeEvent * /* event */)
+{
+ updateGeometries();
+}
+
+int PieView::rows(const QModelIndex &index) const
+{
+ return model()->rowCount(model()->parent(index));
+}
+
+void PieView::rowsInserted(const QModelIndex &parent, int start, int end)
+{
+ for (int row = start; row <= end; ++row) {
+ QModelIndex index = model()->index(row, 1, rootIndex());
+ double value = model()->data(index).toDouble();
+
+ if (value > 0.0) {
+ totalValue += value;
+ ++validItems;
+ }
+ }
+
+ QAbstractItemView::rowsInserted(parent, start, end);
+}
+
+void PieView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
+{
+ for (int row = start; row <= end; ++row) {
+ QModelIndex index = model()->index(row, 1, rootIndex());
+ double value = model()->data(index).toDouble();
+ if (value > 0.0) {
+ totalValue -= value;
+ --validItems;
+ }
+ }
+
+ QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);
+}
+
+void PieView::scrollContentsBy(int dx, int dy)
+{
+ viewport()->scroll(dx, dy);
+}
+
+void PieView::scrollTo(const QModelIndex &index, ScrollHint)
+{
+ QRect area = viewport()->rect();
+ QRect rect = visualRect(index);
+
+ if (rect.left() < area.left()) {
+ horizontalScrollBar()->setValue(
+ horizontalScrollBar()->value() + rect.left() - area.left());
+ } else if (rect.right() > area.right()) {
+ horizontalScrollBar()->setValue(
+ horizontalScrollBar()->value() + qMin(
+ rect.right() - area.right(), rect.left() - area.left()));
+ }
+
+ if (rect.top() < area.top()) {
+ verticalScrollBar()->setValue(
+ verticalScrollBar()->value() + rect.top() - area.top());
+ } else if (rect.bottom() > area.bottom()) {
+ verticalScrollBar()->setValue(
+ verticalScrollBar()->value() + qMin(
+ rect.bottom() - area.bottom(), rect.top() - area.top()));
+ }
+
+ update();
+}
+
+/*
+ Find the indices corresponding to the extent of the selection.
+*/
+
+void PieView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
+{
+ // Use content widget coordinates because we will use the itemRegion()
+ // function to check for intersections.
+
+ QRect contentsRect = rect.translated(
+ horizontalScrollBar()->value(),
+ verticalScrollBar()->value()).normalized();
+
+ int rows = model()->rowCount(rootIndex());
+ int columns = model()->columnCount(rootIndex());
+ QModelIndexList indexes;
+
+ for (int row = 0; row < rows; ++row) {
+ for (int column = 0; column < columns; ++column) {
+ QModelIndex index = model()->index(row, column, rootIndex());
+ QRegion region = itemRegion(index);
+ if (region.intersects(contentsRect))
+ indexes.append(index);
+ }
+ }
+
+ if (indexes.size() > 0) {
+ int firstRow = indexes.at(0).row();
+ int lastRow = firstRow;
+ int firstColumn = indexes.at(0).column();
+ int lastColumn = firstColumn;
+
+ for (int i = 1; i < indexes.size(); ++i) {
+ firstRow = qMin(firstRow, indexes.at(i).row());
+ lastRow = qMax(lastRow, indexes.at(i).row());
+ firstColumn = qMin(firstColumn, indexes.at(i).column());
+ lastColumn = qMax(lastColumn, indexes.at(i).column());
+ }
+
+ QItemSelection selection(
+ model()->index(firstRow, firstColumn, rootIndex()),
+ model()->index(lastRow, lastColumn, rootIndex()));
+ selectionModel()->select(selection, command);
+ } else {
+ QModelIndex noIndex;
+ QItemSelection selection(noIndex, noIndex);
+ selectionModel()->select(selection, command);
+ }
+
+ update();
+}
+
+void PieView::updateGeometries()
+{
+ horizontalScrollBar()->setPageStep(viewport()->width());
+ horizontalScrollBar()->setRange(0, qMax(0, 2 * totalSize - viewport()->width()));
+ verticalScrollBar()->setPageStep(viewport()->height());
+ verticalScrollBar()->setRange(0, qMax(0, totalSize - viewport()->height()));
+}
+
+int PieView::verticalOffset() const
+{
+ return verticalScrollBar()->value();
+}
+
+/*
+ Returns the position of the item in viewport coordinates.
+*/
+
+QRect PieView::visualRect(const QModelIndex &index) const
+{
+ QRect rect = itemRect(index);
+ if (!rect.isValid())
+ return rect;
+
+ return QRect(rect.left() - horizontalScrollBar()->value(),
+ rect.top() - verticalScrollBar()->value(),
+ rect.width(), rect.height());
+}
+
+/*
+ Returns a region corresponding to the selection in viewport coordinates.
+*/
+
+QRegion PieView::visualRegionForSelection(const QItemSelection &selection) const
+{
+ int ranges = selection.count();
+
+ if (ranges == 0)
+ return QRect();
+
+ QRegion region;
+ for (int i = 0; i < ranges; ++i) {
+ const QItemSelectionRange &range = selection.at(i);
+ for (int row = range.top(); row <= range.bottom(); ++row) {
+ for (int col = range.left(); col <= range.right(); ++col) {
+ QModelIndex index = model()->index(row, col, rootIndex());
+ region += visualRect(index);
+ }
+ }
+ }
+ return region;
+}
diff --git a/tests/manual/examples/widgets/itemviews/chart/pieview.h b/tests/manual/examples/widgets/itemviews/chart/pieview.h
new file mode 100644
index 0000000000..19c4a18ed0
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/chart/pieview.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PIEVIEW_H
+#define PIEVIEW_H
+
+#include <QAbstractItemView>
+
+//! [0]
+class PieView : public QAbstractItemView
+{
+ Q_OBJECT
+
+public:
+ PieView(QWidget *parent = nullptr);
+
+ QRect visualRect(const QModelIndex &index) const override;
+ void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override;
+ QModelIndex indexAt(const QPoint &point) const override;
+
+protected slots:
+ void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
+ const QList<int> &roles = QList<int>()) override;
+ void rowsInserted(const QModelIndex &parent, int start, int end) override;
+ void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) override;
+
+protected:
+ bool edit(const QModelIndex &index, EditTrigger trigger, QEvent *event) override;
+ QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction,
+ Qt::KeyboardModifiers modifiers) override;
+
+ int horizontalOffset() const override;
+ int verticalOffset() const override;
+
+ bool isIndexHidden(const QModelIndex &index) const override;
+
+ void setSelection(const QRect&, QItemSelectionModel::SelectionFlags command) override;
+
+ void mousePressEvent(QMouseEvent *event) override;
+
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+
+ void paintEvent(QPaintEvent *event) override;
+ void resizeEvent(QResizeEvent *event) override;
+ void scrollContentsBy(int dx, int dy) override;
+
+ QRegion visualRegionForSelection(const QItemSelection &selection) const override;
+
+private:
+ QRect itemRect(const QModelIndex &item) const;
+ QRegion itemRegion(const QModelIndex &index) const;
+ int rows(const QModelIndex &index = QModelIndex()) const;
+ void updateGeometries() override;
+
+ int margin = 0;
+ int totalSize = 300;
+ int pieSize = totalSize - 2 * margin;
+ int validItems = 0;
+ double totalValue = 0.0;
+ QRubberBand *rubberBand = nullptr;
+ QPoint origin;
+};
+//! [0]
+
+#endif // PIEVIEW_H
diff --git a/tests/manual/examples/widgets/itemviews/chart/qtdata.cht b/tests/manual/examples/widgets/itemviews/chart/qtdata.cht
new file mode 100644
index 0000000000..6386246c4b
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/chart/qtdata.cht
@@ -0,0 +1,14 @@
+Scientific Research,21,#99e600
+Engineering & Design,18,#99cc00
+Automotive,14,#99b300
+Aerospace,13,#9f991a
+Automation & Machine Tools,13,#a48033
+Medical & Bioinformatics,13,#a9664d
+Imaging & Special Effects,12,#ae4d66
+Defense,11,#b33380
+Test & Measurement Systems,9,#a64086
+Oil & Gas,9,#994d8d
+Entertainment & Broadcasting,7,#8d5a93
+Financial,6,#806699
+Consumer Electronics,4,#8073a6
+Other,38,#8080b3
diff --git a/tests/manual/examples/widgets/itemviews/dirview/CMakeLists.txt b/tests/manual/examples/widgets/itemviews/dirview/CMakeLists.txt
new file mode 100644
index 0000000000..e7261fc4e6
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/dirview/CMakeLists.txt
@@ -0,0 +1,36 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(dirview LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/itemviews/dirview")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(dirview
+ main.cpp
+)
+
+set_target_properties(dirview PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(dirview PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS dirview
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/itemviews/dirview/dirview.pro b/tests/manual/examples/widgets/itemviews/dirview/dirview.pro
new file mode 100644
index 0000000000..981a64a7d6
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/dirview/dirview.pro
@@ -0,0 +1,8 @@
+QT += widgets
+requires(qtConfig(treeview))
+
+SOURCES = main.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/itemviews/dirview
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/itemviews/dirview/main.cpp b/tests/manual/examples/widgets/itemviews/dirview/main.cpp
new file mode 100644
index 0000000000..5db9338b67
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/dirview/main.cpp
@@ -0,0 +1,62 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+#include <QFileSystemModel>
+#include <QFileIconProvider>
+#include <QScreen>
+#include <QScroller>
+#include <QTreeView>
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+ QCommandLineParser parser;
+ parser.setApplicationDescription("Qt Dir View Example");
+ parser.addHelpOption();
+ parser.addVersionOption();
+ QCommandLineOption dontUseCustomDirectoryIconsOption("c", "Set QFileSystemModel::DontUseCustomDirectoryIcons");
+ parser.addOption(dontUseCustomDirectoryIconsOption);
+ QCommandLineOption dontWatchOption("w", "Set QFileSystemModel::DontWatch");
+ parser.addOption(dontWatchOption);
+ parser.addPositionalArgument("directory", "The directory to start in.");
+ parser.process(app);
+ const QString rootPath = parser.positionalArguments().isEmpty()
+ ? QString() : parser.positionalArguments().first();
+
+ QFileSystemModel model;
+ QFileIconProvider iconProvider;
+ model.setIconProvider(&iconProvider);
+ model.setRootPath("");
+ if (parser.isSet(dontUseCustomDirectoryIconsOption))
+ model.setOption(QFileSystemModel::DontUseCustomDirectoryIcons);
+ if (parser.isSet(dontWatchOption))
+ model.setOption(QFileSystemModel::DontWatchForChanges);
+ QTreeView tree;
+ tree.setModel(&model);
+ if (!rootPath.isEmpty()) {
+ const QModelIndex rootIndex = model.index(QDir::cleanPath(rootPath));
+ if (rootIndex.isValid())
+ tree.setRootIndex(rootIndex);
+ }
+
+ // Demonstrating look and feel features
+ tree.setAnimated(false);
+ tree.setIndentation(20);
+ tree.setSortingEnabled(true);
+ const QSize availableSize = tree.screen()->availableGeometry().size();
+ tree.resize(availableSize / 2);
+ tree.setColumnWidth(0, tree.width() / 3);
+
+ // Make it flickable on touchscreens
+ QScroller::grabGesture(&tree, QScroller::TouchGesture);
+
+ tree.setWindowTitle(QObject::tr("Dir View"));
+ tree.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/itemviews/interview/CMakeLists.txt b/tests/manual/examples/widgets/itemviews/interview/CMakeLists.txt
new file mode 100644
index 0000000000..8bab5798ad
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/interview/CMakeLists.txt
@@ -0,0 +1,51 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(interview LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/itemviews/interview")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(interview
+ main.cpp
+ model.cpp model.h
+)
+
+set_target_properties(interview PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(interview PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(interview_resource_files
+ "images/folder.png"
+ "images/interview.png"
+ "images/services.png"
+)
+
+qt_add_resources(interview "interview"
+ PREFIX
+ "/"
+ FILES
+ ${interview_resource_files}
+)
+
+install(TARGETS interview
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/itemviews/interview/README b/tests/manual/examples/widgets/itemviews/interview/README
new file mode 100644
index 0000000000..50894428f6
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/interview/README
@@ -0,0 +1,2 @@
+The interview example shows the same model and selection being shared
+between three different views.
diff --git a/tests/manual/examples/widgets/itemviews/interview/images/folder.png b/tests/manual/examples/widgets/itemviews/interview/images/folder.png
new file mode 100644
index 0000000000..589fd2df59
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/interview/images/folder.png
Binary files differ
diff --git a/tests/manual/examples/widgets/itemviews/interview/images/interview.png b/tests/manual/examples/widgets/itemviews/interview/images/interview.png
new file mode 100644
index 0000000000..0c3d690258
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/interview/images/interview.png
Binary files differ
diff --git a/tests/manual/examples/widgets/itemviews/interview/images/services.png b/tests/manual/examples/widgets/itemviews/interview/images/services.png
new file mode 100644
index 0000000000..6b2ad969d4
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/interview/images/services.png
Binary files differ
diff --git a/tests/manual/examples/widgets/itemviews/interview/interview.pro b/tests/manual/examples/widgets/itemviews/interview/interview.pro
new file mode 100644
index 0000000000..6d64f23eb9
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/interview/interview.pro
@@ -0,0 +1,16 @@
+TEMPLATE = app
+QT += widgets
+requires(qtConfig(treeview))
+
+HEADERS += model.h
+SOURCES += model.cpp main.cpp
+RESOURCES += interview.qrc
+
+build_all:!build_pass {
+ CONFIG -= build_all
+ CONFIG += release
+}
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/itemviews/interview
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/itemviews/interview/interview.qrc b/tests/manual/examples/widgets/itemviews/interview/interview.qrc
new file mode 100644
index 0000000000..b28ea34d8a
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/interview/interview.qrc
@@ -0,0 +1,7 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/">
+ <file>images/folder.png</file>
+ <file>images/services.png</file>
+ <file>images/interview.png</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/itemviews/interview/main.cpp b/tests/manual/examples/widgets/itemviews/interview/main.cpp
new file mode 100644
index 0000000000..3446ff1dc4
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/interview/main.cpp
@@ -0,0 +1,55 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "model.h"
+
+#include <QApplication>
+#include <QHeaderView>
+#include <QListView>
+#include <QSplitter>
+#include <QTableView>
+#include <QTreeView>
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ QSplitter page;
+
+ QAbstractItemModel *data = new Model(1000, 10, &page);
+ QItemSelectionModel *selections = new QItemSelectionModel(data);
+
+ QTableView *table = new QTableView;
+ table->setModel(data);
+ table->setSelectionModel(selections);
+ table->horizontalHeader()->setSectionsMovable(true);
+ table->verticalHeader()->setSectionsMovable(true);
+ // Set StaticContents to enable minimal repaints on resizes.
+ table->viewport()->setAttribute(Qt::WA_StaticContents);
+ page.addWidget(table);
+
+ QTreeView *tree = new QTreeView;
+ tree->setModel(data);
+ tree->setSelectionModel(selections);
+ tree->setUniformRowHeights(true);
+ tree->header()->setStretchLastSection(false);
+ tree->viewport()->setAttribute(Qt::WA_StaticContents);
+ // Disable the focus rect to get minimal repaints when scrolling on Mac.
+ tree->setAttribute(Qt::WA_MacShowFocusRect, false);
+ page.addWidget(tree);
+
+ QListView *list = new QListView;
+ list->setModel(data);
+ list->setSelectionModel(selections);
+ list->setViewMode(QListView::IconMode);
+ list->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ list->setAlternatingRowColors(false);
+ list->viewport()->setAttribute(Qt::WA_StaticContents);
+ list->setAttribute(Qt::WA_MacShowFocusRect, false);
+ page.addWidget(list);
+
+ page.setWindowIcon(QPixmap(":/images/interview.png"));
+ page.setWindowTitle("Interview");
+ page.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/itemviews/interview/model.cpp b/tests/manual/examples/widgets/itemviews/interview/model.cpp
new file mode 100644
index 0000000000..9050ae6cc1
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/interview/model.cpp
@@ -0,0 +1,110 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "model.h"
+
+#include <QPixmap>
+
+Model::Model(int rows, int columns, QObject *parent)
+ : QAbstractItemModel(parent),
+ services(QPixmap(":/images/services.png")),
+ rc(rows),
+ cc(columns),
+ tree(new QList<Node>(rows, Node()))
+{
+
+}
+
+Model::~Model()
+{
+ delete tree;
+}
+
+QModelIndex Model::index(int row, int column, const QModelIndex &parent) const
+{
+ if (row < rc && row >= 0 && column < cc && column >= 0) {
+ Node *parentNode = static_cast<Node*>(parent.internalPointer());
+ Node *childNode = node(row, parentNode);
+ if (childNode)
+ return createIndex(row, column, childNode);
+ }
+ return QModelIndex();
+}
+
+QModelIndex Model::parent(const QModelIndex &child) const
+{
+ if (child.isValid()) {
+ Node *childNode = static_cast<Node*>(child.internalPointer());
+ Node *parentNode = parent(childNode);
+ if (parentNode)
+ return createIndex(row(parentNode), 0, parentNode);
+ }
+ return QModelIndex();
+}
+
+int Model::rowCount(const QModelIndex &parent) const
+{
+ return (parent.isValid() && parent.column() != 0) ? 0 : rc;
+}
+
+int Model::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ return cc;
+}
+
+QVariant Model::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+ if (role == Qt::DisplayRole)
+ return QVariant("Item " + QString::number(index.row()) + ':' + QString::number(index.column()));
+ if (role == Qt::DecorationRole) {
+ if (index.column() == 0)
+ return iconProvider.icon(QFileIconProvider::Folder);
+ return iconProvider.icon(QFileIconProvider::File);
+ }
+ return QVariant();
+}
+
+QVariant Model::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (role == Qt::DisplayRole)
+ return QString::number(section);
+ if (role == Qt::DecorationRole)
+ return QVariant::fromValue(services);
+ return QAbstractItemModel::headerData(section, orientation, role);
+}
+
+bool Model::hasChildren(const QModelIndex &parent) const
+{
+ if (parent.isValid() && parent.column() != 0)
+ return false;
+ return rc > 0 && cc > 0;
+}
+
+Qt::ItemFlags Model::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return {};
+ return Qt::ItemIsDragEnabled|QAbstractItemModel::flags(index);
+}
+
+Model::Node *Model::node(int row, Node *parent) const
+{
+ if (parent && !parent->children)
+ parent->children = new QList<Node>(rc, Node(parent));
+ QList<Node> *v = parent ? parent->children : tree;
+ return const_cast<Node*>(&(v->at(row)));
+}
+
+Model::Node *Model::parent(Node *child) const
+{
+ return child ? child->parent : nullptr;
+}
+
+int Model::row(Node *node) const
+{
+ const Node *first = node->parent ? &(node->parent->children->at(0)) : &(tree->at(0));
+ return node - first;
+}
diff --git a/tests/manual/examples/widgets/itemviews/interview/model.h b/tests/manual/examples/widgets/itemviews/interview/model.h
new file mode 100644
index 0000000000..87ee740792
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/interview/model.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MODEL_H
+#define MODEL_H
+
+#include <QAbstractItemModel>
+#include <QFileIconProvider>
+#include <QIcon>
+#include <QList>
+
+class Model : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ Model(int rows, int columns, QObject *parent = nullptr);
+ ~Model();
+
+ QModelIndex index(int row, int column, const QModelIndex &parent) const override;
+ QModelIndex parent(const QModelIndex &child) const override;
+
+ int rowCount(const QModelIndex &parent) const override;
+ int columnCount(const QModelIndex &parent) const override;
+
+ QVariant data(const QModelIndex &index, int role) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+
+ bool hasChildren(const QModelIndex &parent) const override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+
+private:
+
+ struct Node
+ {
+ Node(Node *parent = nullptr) : parent(parent), children(nullptr) {}
+ ~Node() { delete children; }
+ Node *parent;
+ QList<Node> *children;
+ };
+
+ Node *node(int row, Node *parent) const;
+ Node *parent(Node *child) const;
+ int row(Node *node) const;
+
+ QIcon services;
+ int rc;
+ int cc;
+ QList<Node> *tree;
+ QFileIconProvider iconProvider;
+};
+
+#endif // MODEL_H
diff --git a/tests/manual/examples/widgets/itemviews/pixelator/CMakeLists.txt b/tests/manual/examples/widgets/itemviews/pixelator/CMakeLists.txt
new file mode 100644
index 0000000000..0a2671b13c
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/pixelator/CMakeLists.txt
@@ -0,0 +1,58 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(pixelator LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/itemviews/pixelator")
+
+find_package(Qt6
+ REQUIRED COMPONENTS Core Gui Widgets
+ OPTIONAL_COMPONENTS PrintSupport
+)
+
+qt_standard_project_setup()
+
+qt_add_executable(pixelator
+ imagemodel.cpp imagemodel.h
+ main.cpp
+ mainwindow.cpp mainwindow.h
+ pixeldelegate.cpp pixeldelegate.h
+)
+
+set_target_properties(pixelator PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(pixelator PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+if(TARGET Qt6::PrintSupport)
+ target_link_libraries(pixelator PRIVATE Qt6::PrintSupport)
+endif()
+
+# Resources:
+set(images_resource_files
+ "images/qt.png"
+)
+
+qt_add_resources(pixelator "images"
+ PREFIX
+ "/"
+ FILES
+ ${images_resource_files}
+)
+
+install(TARGETS pixelator
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/itemviews/pixelator/imagemodel.cpp b/tests/manual/examples/widgets/itemviews/pixelator/imagemodel.cpp
new file mode 100644
index 0000000000..6b5b866522
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/pixelator/imagemodel.cpp
@@ -0,0 +1,53 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "imagemodel.h"
+
+//! [0]
+ImageModel::ImageModel(QObject *parent)
+ : QAbstractTableModel(parent)
+{
+}
+//! [0]
+
+//! [1]
+void ImageModel::setImage(const QImage &image)
+{
+ beginResetModel();
+ modelImage = image;
+ endResetModel();
+}
+//! [1]
+
+//! [2]
+int ImageModel::rowCount(const QModelIndex & /* parent */) const
+{
+ return modelImage.height();
+}
+
+int ImageModel::columnCount(const QModelIndex & /* parent */) const
+//! [2] //! [3]
+{
+ return modelImage.width();
+}
+//! [3]
+
+//! [4]
+QVariant ImageModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || role != Qt::DisplayRole)
+ return QVariant();
+ return qGray(modelImage.pixel(index.column(), index.row()));
+}
+//! [4]
+
+//! [5]
+QVariant ImageModel::headerData(int /* section */,
+ Qt::Orientation /* orientation */,
+ int role) const
+{
+ if (role == Qt::SizeHintRole)
+ return QSize(1, 1);
+ return QVariant();
+}
+//! [5]
diff --git a/tests/manual/examples/widgets/itemviews/pixelator/imagemodel.h b/tests/manual/examples/widgets/itemviews/pixelator/imagemodel.h
new file mode 100644
index 0000000000..050ac6b790
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/pixelator/imagemodel.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef IMAGEMODEL_H
+#define IMAGEMODEL_H
+
+#include <QAbstractTableModel>
+#include <QImage>
+
+//! [0]
+class ImageModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ ImageModel(QObject *parent = nullptr);
+
+ void setImage(const QImage &image);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+
+private:
+ QImage modelImage;
+};
+//! [0]
+
+#endif // IMAGEMODEL_H
diff --git a/tests/manual/examples/widgets/itemviews/pixelator/images.qrc b/tests/manual/examples/widgets/itemviews/pixelator/images.qrc
new file mode 100644
index 0000000000..c105e13895
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/pixelator/images.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>images/qt.png</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/itemviews/pixelator/images/qt.png b/tests/manual/examples/widgets/itemviews/pixelator/images/qt.png
new file mode 100644
index 0000000000..dd197cb59c
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/pixelator/images/qt.png
Binary files differ
diff --git a/tests/manual/examples/widgets/itemviews/pixelator/main.cpp b/tests/manual/examples/widgets/itemviews/pixelator/main.cpp
new file mode 100644
index 0000000000..26783b02fc
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/pixelator/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ MainWindow window;
+ window.show();
+ window.openImage(":/images/qt.png");
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/itemviews/pixelator/mainwindow.cpp b/tests/manual/examples/widgets/itemviews/pixelator/mainwindow.cpp
new file mode 100644
index 0000000000..42f0a43844
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/pixelator/mainwindow.cpp
@@ -0,0 +1,214 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+#include "imagemodel.h"
+#include "pixeldelegate.h"
+
+#include <QtWidgets>
+#if defined(QT_PRINTSUPPORT_LIB)
+#include <QtPrintSupport/qtprintsupportglobal.h>
+#if QT_CONFIG(printdialog)
+#include <QPrinter>
+#include <QPrintDialog>
+#endif
+#endif
+
+//! [0]
+MainWindow::MainWindow()
+{
+//! [0]
+ currentPath = QDir::homePath();
+ model = new ImageModel(this);
+
+ QWidget *centralWidget = new QWidget;
+
+//! [1]
+ view = new QTableView;
+ view->setShowGrid(false);
+ view->horizontalHeader()->hide();
+ view->verticalHeader()->hide();
+ view->horizontalHeader()->setMinimumSectionSize(1);
+ view->verticalHeader()->setMinimumSectionSize(1);
+ view->setModel(model);
+//! [1]
+
+//! [2]
+ PixelDelegate *delegate = new PixelDelegate(this);
+ view->setItemDelegate(delegate);
+//! [2]
+
+//! [3]
+ QLabel *pixelSizeLabel = new QLabel(tr("Pixel size:"));
+ QSpinBox *pixelSizeSpinBox = new QSpinBox;
+ pixelSizeSpinBox->setMinimum(4);
+ pixelSizeSpinBox->setMaximum(32);
+ pixelSizeSpinBox->setValue(12);
+//! [3]
+
+ QMenu *fileMenu = new QMenu(tr("&File"), this);
+ QAction *openAction = fileMenu->addAction(tr("&Open..."));
+ openAction->setShortcuts(QKeySequence::Open);
+
+ printAction = fileMenu->addAction(tr("&Print..."));
+ printAction->setEnabled(false);
+ printAction->setShortcut(QKeySequence::Print);
+
+ QAction *quitAction = fileMenu->addAction(tr("E&xit"));
+ quitAction->setShortcuts(QKeySequence::Quit);
+
+ QMenu *helpMenu = new QMenu(tr("&Help"), this);
+ QAction *aboutAction = helpMenu->addAction(tr("&About"));
+
+ menuBar()->addMenu(fileMenu);
+ menuBar()->addSeparator();
+ menuBar()->addMenu(helpMenu);
+
+ connect(openAction, &QAction::triggered, this, &MainWindow::chooseImage);
+ connect(printAction, &QAction::triggered, this, &MainWindow::printImage);
+ connect(quitAction, &QAction::triggered, qApp, &QCoreApplication::quit);
+ connect(aboutAction, &QAction::triggered, this, &MainWindow::showAboutBox);
+//! [4]
+ connect(pixelSizeSpinBox, &QSpinBox::valueChanged,
+ delegate, &PixelDelegate::setPixelSize);
+ connect(pixelSizeSpinBox, &QSpinBox::valueChanged,
+ this, &MainWindow::updateView);
+//! [4]
+
+ QHBoxLayout *controlsLayout = new QHBoxLayout;
+ controlsLayout->addWidget(pixelSizeLabel);
+ controlsLayout->addWidget(pixelSizeSpinBox);
+ controlsLayout->addStretch(1);
+
+ QVBoxLayout *mainLayout = new QVBoxLayout;
+ mainLayout->addWidget(view);
+ mainLayout->addLayout(controlsLayout);
+ centralWidget->setLayout(mainLayout);
+
+ setCentralWidget(centralWidget);
+
+ setWindowTitle(tr("Pixelator"));
+ resize(640, 480);
+//! [5]
+}
+//! [5]
+
+void MainWindow::chooseImage()
+{
+ QString fileName = QFileDialog::getOpenFileName(this,
+ tr("Choose an image"), currentPath, "*");
+
+ if (!fileName.isEmpty())
+ openImage(fileName);
+}
+
+void MainWindow::openImage(const QString &fileName)
+{
+ QImage image;
+
+ if (image.load(fileName)) {
+ model->setImage(image);
+ if (!fileName.startsWith(":/")) {
+ currentPath = fileName;
+ setWindowTitle(tr("%1 - Pixelator").arg(currentPath));
+ }
+
+ printAction->setEnabled(true);
+ updateView();
+ }
+}
+
+void MainWindow::printImage()
+{
+#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog)
+ if (model->rowCount(QModelIndex())*model->columnCount(QModelIndex()) > 90000) {
+ QMessageBox::StandardButton answer;
+ answer = QMessageBox::question(this, tr("Large Image Size"),
+ tr("The printed image may be very large. Are you sure that "
+ "you want to print it?"),
+ QMessageBox::Yes | QMessageBox::No);
+ if (answer == QMessageBox::No)
+ return;
+ }
+
+ QPrinter printer(QPrinter::HighResolution);
+
+ QPrintDialog dlg(&printer, this);
+ dlg.setWindowTitle(tr("Print Image"));
+
+ if (dlg.exec() != QDialog::Accepted) {
+ return;
+ }
+
+ QPainter painter;
+ painter.begin(&printer);
+
+ int rows = model->rowCount(QModelIndex());
+ int columns = model->columnCount(QModelIndex());
+ int sourceWidth = (columns + 1) * ItemSize;
+ int sourceHeight = (rows + 1) * ItemSize;
+
+ painter.save();
+
+ double xscale = printer.pageRect(QPrinter::DevicePixel).width() / double(sourceWidth);
+ double yscale = printer.pageRect(QPrinter::DevicePixel).height() / double(sourceHeight);
+ double scale = qMin(xscale, yscale);
+
+ painter.translate(printer.paperRect(QPrinter::DevicePixel).x() + printer.pageRect(QPrinter::DevicePixel).width() / 2,
+ printer.paperRect(QPrinter::DevicePixel).y() + printer.pageRect(QPrinter::DevicePixel).height() / 2);
+ painter.scale(scale, scale);
+ painter.translate(-sourceWidth / 2, -sourceHeight / 2);
+
+ QStyleOptionViewItem option;
+ QModelIndex parent = QModelIndex();
+
+ QProgressDialog progress(tr("Printing..."), tr("Cancel"), 0, rows, this);
+ progress.setWindowModality(Qt::ApplicationModal);
+ float y = ItemSize / 2;
+
+ for (int row = 0; row < rows; ++row) {
+ progress.setValue(row);
+ qApp->processEvents();
+ if (progress.wasCanceled())
+ break;
+
+ float x = ItemSize / 2;
+
+ for (int column = 0; column < columns; ++column) {
+ option.rect = QRect(int(x), int(y), ItemSize, ItemSize);
+ view->itemDelegate()->paint(&painter, option,
+ model->index(row, column, parent));
+ x = x + ItemSize;
+ }
+ y = y + ItemSize;
+ }
+ progress.setValue(rows);
+
+ painter.restore();
+ painter.end();
+
+ if (progress.wasCanceled()) {
+ QMessageBox::information(this, tr("Printing canceled"),
+ tr("The printing process was canceled."), QMessageBox::Cancel);
+ }
+#else
+ QMessageBox::information(this, tr("Printing canceled"),
+ tr("Printing is not supported on this Qt build"), QMessageBox::Cancel);
+#endif
+}
+
+void MainWindow::showAboutBox()
+{
+ QMessageBox::about(this, tr("About the Pixelator example"),
+ tr("This example demonstrates how a standard view and a custom\n"
+ "delegate can be used to produce a specialized representation\n"
+ "of data in a simple custom model."));
+}
+
+//! [6]
+void MainWindow::updateView()
+{
+ view->resizeColumnsToContents();
+ view->resizeRowsToContents();
+}
+//! [6]
diff --git a/tests/manual/examples/widgets/itemviews/pixelator/mainwindow.h b/tests/manual/examples/widgets/itemviews/pixelator/mainwindow.h
new file mode 100644
index 0000000000..9929e2f5af
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/pixelator/mainwindow.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+
+class ImageModel;
+QT_BEGIN_NAMESPACE
+class QAction;
+class QTableView;
+QT_END_NAMESPACE
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow();
+
+ void openImage(const QString &fileName);
+
+public slots:
+ void chooseImage();
+ void printImage();
+ void showAboutBox();
+ void updateView();
+
+private:
+ ImageModel *model;
+ QAction *printAction;
+ QString currentPath;
+ QTableView *view;
+};
+
+#endif // MAINWINDOW_H
diff --git a/tests/manual/examples/widgets/itemviews/pixelator/pixelator.pro b/tests/manual/examples/widgets/itemviews/pixelator/pixelator.pro
new file mode 100644
index 0000000000..421f626e28
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/pixelator/pixelator.pro
@@ -0,0 +1,16 @@
+QT += widgets
+requires(qtConfig(tableview))
+qtHaveModule(printsupport): QT += printsupport
+
+HEADERS = imagemodel.h \
+ mainwindow.h \
+ pixeldelegate.h
+SOURCES = imagemodel.cpp \
+ main.cpp \
+ mainwindow.cpp \
+ pixeldelegate.cpp
+RESOURCES += images.qrc
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/itemviews/pixelator
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/itemviews/pixelator/pixeldelegate.cpp b/tests/manual/examples/widgets/itemviews/pixelator/pixeldelegate.cpp
new file mode 100644
index 0000000000..3a104cd2c2
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/pixelator/pixeldelegate.cpp
@@ -0,0 +1,68 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "pixeldelegate.h"
+
+#include <QPainter>
+
+//! [0]
+PixelDelegate::PixelDelegate(QObject *parent)
+ : QAbstractItemDelegate(parent), pixelSize(12)
+{}
+//! [0]
+
+//! [1]
+void PixelDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+//! [2]
+ if (option.state & QStyle::State_Selected)
+ painter->fillRect(option.rect, option.palette.highlight());
+//! [1]
+
+//! [3]
+ const int size = qMin(option.rect.width(), option.rect.height());
+//! [3] //! [4]
+ const int brightness = index.model()->data(index, Qt::DisplayRole).toInt();
+ const double radius = (size / 2.0) - (brightness / 255.0 * size / 2.0);
+ if (qFuzzyIsNull(radius))
+ return;
+//! [4]
+
+//! [5]
+ painter->save();
+//! [5] //! [6]
+ painter->setRenderHint(QPainter::Antialiasing, true);
+//! [6] //! [7]
+ painter->setPen(Qt::NoPen);
+//! [7] //! [8]
+ if (option.state & QStyle::State_Selected)
+//! [8] //! [9]
+ painter->setBrush(option.palette.highlightedText());
+ else
+//! [2]
+ painter->setBrush(option.palette.text());
+//! [9]
+
+//! [10]
+ painter->drawEllipse(QRectF(option.rect.x() + option.rect.width() / 2 - radius,
+ option.rect.y() + option.rect.height() / 2 - radius,
+ 2 * radius, 2 * radius));
+ painter->restore();
+}
+//! [10]
+
+//! [11]
+QSize PixelDelegate::sizeHint(const QStyleOptionViewItem & /* option */,
+ const QModelIndex & /* index */) const
+{
+ return QSize(pixelSize, pixelSize);
+}
+//! [11]
+
+//! [12]
+void PixelDelegate::setPixelSize(int size)
+{
+ pixelSize = size;
+}
+//! [12]
diff --git a/tests/manual/examples/widgets/itemviews/pixelator/pixeldelegate.h b/tests/manual/examples/widgets/itemviews/pixelator/pixeldelegate.h
new file mode 100644
index 0000000000..a4c435ce69
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/pixelator/pixeldelegate.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PIXELDELEGATE_H
+#define PIXELDELEGATE_H
+
+#include <QAbstractItemDelegate>
+#include <QModelIndex>
+#include <QSize>
+
+QT_BEGIN_NAMESPACE
+class QAbstractItemModel;
+class QObject;
+class QPainter;
+QT_END_NAMESPACE
+
+static constexpr int ItemSize = 256;
+
+//! [0]
+class PixelDelegate : public QAbstractItemDelegate
+{
+ Q_OBJECT
+
+public:
+ PixelDelegate(QObject *parent = nullptr);
+
+ void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+
+ QSize sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+
+public slots:
+ void setPixelSize(int size);
+
+private:
+ int pixelSize;
+};
+//! [0]
+
+#endif // PIXELDELEGATE_H
diff --git a/tests/manual/examples/widgets/itemviews/puzzle/CMakeLists.txt b/tests/manual/examples/widgets/itemviews/puzzle/CMakeLists.txt
new file mode 100644
index 0000000000..21989f1a83
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/puzzle/CMakeLists.txt
@@ -0,0 +1,51 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(puzzle LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/itemviews/puzzle")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(itemviews_puzzle
+ main.cpp
+ mainwindow.cpp mainwindow.h
+ piecesmodel.cpp piecesmodel.h
+ puzzlewidget.cpp puzzlewidget.h
+)
+
+set_target_properties(itemviews_puzzle PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(itemviews_puzzle PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(puzzle_resource_files
+ "example.jpg"
+)
+
+qt_add_resources(itemviews_puzzle "puzzle"
+ PREFIX
+ "/images"
+ FILES
+ ${puzzle_resource_files}
+)
+
+install(TARGETS itemviews_puzzle
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/itemviews/puzzle/example.jpg b/tests/manual/examples/widgets/itemviews/puzzle/example.jpg
new file mode 100644
index 0000000000..023203c57a
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/puzzle/example.jpg
Binary files differ
diff --git a/tests/manual/examples/widgets/itemviews/puzzle/main.cpp b/tests/manual/examples/widgets/itemviews/puzzle/main.cpp
new file mode 100644
index 0000000000..32e219256a
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/puzzle/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ MainWindow window;
+ window.loadImage(QStringLiteral(":/images/example.jpg"));
+ window.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/itemviews/puzzle/mainwindow.cpp b/tests/manual/examples/widgets/itemviews/puzzle/mainwindow.cpp
new file mode 100644
index 0000000000..f4b221df20
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/puzzle/mainwindow.cpp
@@ -0,0 +1,115 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+#include "piecesmodel.h"
+#include "puzzlewidget.h"
+
+#include <QtWidgets>
+#include <stdlib.h>
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent)
+{
+ setupMenus();
+ setupWidgets();
+ model = new PiecesModel(puzzleWidget->pieceSize(), this);
+ piecesList->setModel(model);
+
+ setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
+ setWindowTitle(tr("Puzzle"));
+}
+
+void MainWindow::openImage()
+{
+ const QString directory =
+ QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).value(0, QDir::homePath());
+ QFileDialog dialog(this, tr("Open Image"), directory);
+ dialog.setAcceptMode(QFileDialog::AcceptOpen);
+ dialog.setFileMode(QFileDialog::ExistingFile);
+ QStringList mimeTypeFilters;
+ for (const QByteArray &mimeTypeName : QImageReader::supportedMimeTypes())
+ mimeTypeFilters.append(mimeTypeName);
+ mimeTypeFilters.sort();
+ dialog.setMimeTypeFilters(mimeTypeFilters);
+ dialog.selectMimeTypeFilter("image/jpeg");
+ if (dialog.exec() == QDialog::Accepted)
+ loadImage(dialog.selectedFiles().constFirst());
+}
+
+void MainWindow::loadImage(const QString &fileName)
+{
+ QPixmap newImage;
+ if (!newImage.load(fileName)) {
+ QMessageBox::warning(this, tr("Open Image"),
+ tr("The image file could not be loaded."),
+ QMessageBox::Close);
+ return;
+ }
+ puzzleImage = newImage;
+ setupPuzzle();
+}
+
+void MainWindow::setCompleted()
+{
+ QMessageBox::information(this, tr("Puzzle Completed"),
+ tr("Congratulations! You have completed the puzzle!\n"
+ "Click OK to start again."),
+ QMessageBox::Ok);
+
+ setupPuzzle();
+}
+
+void MainWindow::setupPuzzle()
+{
+ int size = qMin(puzzleImage.width(), puzzleImage.height());
+ puzzleImage = puzzleImage.copy((puzzleImage.width() - size) / 2,
+ (puzzleImage.height() - size) / 2, size, size).scaled(puzzleWidget->imageSize(),
+ puzzleWidget->imageSize(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+
+ model->addPieces(puzzleImage);
+ puzzleWidget->clear();
+}
+
+void MainWindow::setupMenus()
+{
+ QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
+
+ QAction *openAction = fileMenu->addAction(tr("&Open..."), this, &MainWindow::openImage);
+ openAction->setShortcuts(QKeySequence::Open);
+
+ QAction *exitAction = fileMenu->addAction(tr("E&xit"), qApp, &QCoreApplication::quit);
+ exitAction->setShortcuts(QKeySequence::Quit);
+
+ QMenu *gameMenu = menuBar()->addMenu(tr("&Game"));
+
+ gameMenu->addAction(tr("&Restart"), this, &MainWindow::setupPuzzle);
+}
+
+void MainWindow::setupWidgets()
+{
+ QFrame *frame = new QFrame;
+ QHBoxLayout *frameLayout = new QHBoxLayout(frame);
+
+ puzzleWidget = new PuzzleWidget(400);
+
+ piecesList = new QListView;
+ piecesList->setDragEnabled(true);
+ piecesList->setViewMode(QListView::IconMode);
+ piecesList->setIconSize(QSize(puzzleWidget->pieceSize() - 20, puzzleWidget->pieceSize() - 20));
+ piecesList->setGridSize(QSize(puzzleWidget->pieceSize(), puzzleWidget->pieceSize()));
+ piecesList->setSpacing(10);
+ piecesList->setMovement(QListView::Snap);
+ piecesList->setAcceptDrops(true);
+ piecesList->setDropIndicatorShown(true);
+
+ PiecesModel *model = new PiecesModel(puzzleWidget->pieceSize(), this);
+ piecesList->setModel(model);
+
+ connect(puzzleWidget, &PuzzleWidget::puzzleCompleted,
+ this, &MainWindow::setCompleted, Qt::QueuedConnection);
+
+ frameLayout->addWidget(piecesList);
+ frameLayout->addWidget(puzzleWidget);
+ setCentralWidget(frame);
+}
diff --git a/tests/manual/examples/widgets/itemviews/puzzle/mainwindow.h b/tests/manual/examples/widgets/itemviews/puzzle/mainwindow.h
new file mode 100644
index 0000000000..56a59b805c
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/puzzle/mainwindow.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QPixmap>
+
+class PuzzleWidget;
+class PiecesModel;
+QT_BEGIN_NAMESPACE
+class QListView;
+QT_END_NAMESPACE
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QWidget *parent = nullptr);
+
+public slots:
+ void openImage();
+ void loadImage(const QString &path);
+ void setupPuzzle();
+
+private slots:
+ void setCompleted();
+
+private:
+ void setupMenus();
+ void setupWidgets();
+
+ QPixmap puzzleImage;
+ QListView *piecesList;
+ PuzzleWidget *puzzleWidget;
+ PiecesModel *model;
+};
+
+#endif // MAINWINDOW_H
diff --git a/tests/manual/examples/widgets/itemviews/puzzle/piecesmodel.cpp b/tests/manual/examples/widgets/itemviews/puzzle/piecesmodel.cpp
new file mode 100644
index 0000000000..8e3ccf4aa3
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/puzzle/piecesmodel.cpp
@@ -0,0 +1,168 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "piecesmodel.h"
+
+#include <QIcon>
+#include <QMimeData>
+#include <QRandomGenerator>
+
+PiecesModel::PiecesModel(int pieceSize, QObject *parent)
+ : QAbstractListModel(parent), m_PieceSize(pieceSize)
+{
+}
+
+QVariant PiecesModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (role == Qt::DecorationRole)
+ return QIcon(pixmaps.value(index.row()).scaled(m_PieceSize, m_PieceSize,
+ Qt::KeepAspectRatio, Qt::SmoothTransformation));
+ else if (role == Qt::UserRole)
+ return pixmaps.value(index.row());
+ else if (role == Qt::UserRole + 1)
+ return locations.value(index.row());
+
+ return QVariant();
+}
+
+void PiecesModel::addPiece(const QPixmap &pixmap, const QPoint &location)
+{
+ int row;
+ if (QRandomGenerator::global()->bounded(2) == 1)
+ row = 0;
+ else
+ row = pixmaps.size();
+
+ beginInsertRows(QModelIndex(), row, row);
+ pixmaps.insert(row, pixmap);
+ locations.insert(row, location);
+ endInsertRows();
+}
+
+Qt::ItemFlags PiecesModel::flags(const QModelIndex &index) const
+{
+ if (index.isValid())
+ return (QAbstractListModel::flags(index)|Qt::ItemIsDragEnabled);
+
+ return Qt::ItemIsDropEnabled;
+}
+
+bool PiecesModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ if (parent.isValid())
+ return false;
+
+ if (row >= pixmaps.size() || row + count <= 0)
+ return false;
+
+ int beginRow = qMax(0, row);
+ int endRow = qMin(row + count - 1, pixmaps.size() - 1);
+
+ beginRemoveRows(parent, beginRow, endRow);
+
+ while (beginRow <= endRow) {
+ pixmaps.removeAt(beginRow);
+ locations.removeAt(beginRow);
+ ++beginRow;
+ }
+
+ endRemoveRows();
+ return true;
+}
+
+QStringList PiecesModel::mimeTypes() const
+{
+ QStringList types;
+ types << "image/x-puzzle-piece";
+ return types;
+}
+
+QMimeData *PiecesModel::mimeData(const QModelIndexList &indexes) const
+{
+ QMimeData *mimeData = new QMimeData();
+ QByteArray encodedData;
+
+ QDataStream stream(&encodedData, QDataStream::WriteOnly);
+
+ for (const QModelIndex &index : indexes) {
+ if (index.isValid()) {
+ QPixmap pixmap = qvariant_cast<QPixmap>(data(index, Qt::UserRole));
+ QPoint location = data(index, Qt::UserRole+1).toPoint();
+ stream << pixmap << location;
+ }
+ }
+
+ mimeData->setData("image/x-puzzle-piece", encodedData);
+ return mimeData;
+}
+
+bool PiecesModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent)
+{
+ if (!data->hasFormat("image/x-puzzle-piece"))
+ return false;
+
+ if (action == Qt::IgnoreAction)
+ return true;
+
+ if (column > 0)
+ return false;
+
+ int endRow;
+
+ if (!parent.isValid()) {
+ if (row < 0)
+ endRow = pixmaps.size();
+ else
+ endRow = qMin(row, pixmaps.size());
+ } else {
+ endRow = parent.row();
+ }
+
+ QByteArray encodedData = data->data("image/x-puzzle-piece");
+ QDataStream stream(&encodedData, QDataStream::ReadOnly);
+
+ while (!stream.atEnd()) {
+ QPixmap pixmap;
+ QPoint location;
+ stream >> pixmap >> location;
+
+ beginInsertRows(QModelIndex(), endRow, endRow);
+ pixmaps.insert(endRow, pixmap);
+ locations.insert(endRow, location);
+ endInsertRows();
+
+ ++endRow;
+ }
+
+ return true;
+}
+
+int PiecesModel::rowCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : pixmaps.size();
+}
+
+Qt::DropActions PiecesModel::supportedDropActions() const
+{
+ return Qt::CopyAction | Qt::MoveAction;
+}
+
+void PiecesModel::addPieces(const QPixmap &pixmap)
+{
+ if (!pixmaps.isEmpty()) {
+ beginRemoveRows(QModelIndex(), 0, pixmaps.size() - 1);
+ pixmaps.clear();
+ locations.clear();
+ endRemoveRows();
+ }
+ for (int y = 0; y < 5; ++y) {
+ for (int x = 0; x < 5; ++x) {
+ QPixmap pieceImage = pixmap.copy(x*m_PieceSize, y*m_PieceSize, m_PieceSize, m_PieceSize);
+ addPiece(pieceImage, QPoint(x, y));
+ }
+ }
+}
diff --git a/tests/manual/examples/widgets/itemviews/puzzle/piecesmodel.h b/tests/manual/examples/widgets/itemviews/puzzle/piecesmodel.h
new file mode 100644
index 0000000000..878ed73a70
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/puzzle/piecesmodel.h
@@ -0,0 +1,45 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PIECESLIST_H
+#define PIECESLIST_H
+
+#include <QAbstractListModel>
+#include <QPixmap>
+#include <QPoint>
+#include <QStringList>
+#include <QList>
+
+QT_BEGIN_NAMESPACE
+class QMimeData;
+QT_END_NAMESPACE
+
+class PiecesModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ explicit PiecesModel(int pieceSize, QObject *parent = nullptr);
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+ bool removeRows(int row, int count, const QModelIndex &parent) override;
+
+ bool dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent) override;
+ QMimeData *mimeData(const QModelIndexList &indexes) const override;
+ QStringList mimeTypes() const override;
+ int rowCount(const QModelIndex &parent) const override;
+ Qt::DropActions supportedDropActions() const override;
+
+ void addPiece(const QPixmap &pixmap, const QPoint &location);
+ void addPieces(const QPixmap &pixmap);
+
+private:
+ QList<QPoint> locations;
+ QList<QPixmap> pixmaps;
+
+ int m_PieceSize;
+};
+
+#endif // PIECESLIST_H
diff --git a/tests/manual/examples/widgets/itemviews/puzzle/puzzle.pro b/tests/manual/examples/widgets/itemviews/puzzle/puzzle.pro
new file mode 100644
index 0000000000..dcc27aae6a
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/puzzle/puzzle.pro
@@ -0,0 +1,15 @@
+QT += widgets
+requires(qtConfig(listview))
+
+HEADERS = mainwindow.h \
+ piecesmodel.h \
+ puzzlewidget.h
+RESOURCES = puzzle.qrc
+SOURCES = main.cpp \
+ mainwindow.cpp \
+ piecesmodel.cpp \
+ puzzlewidget.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/itemviews/puzzle
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/itemviews/puzzle/puzzle.qrc b/tests/manual/examples/widgets/itemviews/puzzle/puzzle.qrc
new file mode 100644
index 0000000000..4076cec026
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/puzzle/puzzle.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/images">
+ <file>example.jpg</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/itemviews/puzzle/puzzlewidget.cpp b/tests/manual/examples/widgets/itemviews/puzzle/puzzlewidget.cpp
new file mode 100644
index 0000000000..15aa6ac94f
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/puzzle/puzzlewidget.cpp
@@ -0,0 +1,163 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "puzzlewidget.h"
+
+#include <QtWidgets>
+
+PuzzleWidget::PuzzleWidget(int imageSize, QWidget *parent)
+ : QWidget(parent), m_ImageSize(imageSize)
+{
+ setAcceptDrops(true);
+ setMinimumSize(m_ImageSize, m_ImageSize);
+ setMaximumSize(m_ImageSize, m_ImageSize);
+}
+
+void PuzzleWidget::clear()
+{
+ pieces.clear();
+ highlightedRect = QRect();
+ inPlace = 0;
+ update();
+}
+
+void PuzzleWidget::dragEnterEvent(QDragEnterEvent *event)
+{
+ if (event->mimeData()->hasFormat("image/x-puzzle-piece"))
+ event->accept();
+ else
+ event->ignore();
+}
+
+void PuzzleWidget::dragLeaveEvent(QDragLeaveEvent *event)
+{
+ QRect updateRect = highlightedRect;
+ highlightedRect = QRect();
+ update(updateRect);
+ event->accept();
+}
+
+void PuzzleWidget::dragMoveEvent(QDragMoveEvent *event)
+{
+ QRect updateRect = highlightedRect.united(targetSquare(event->position().toPoint()));
+
+ if (event->mimeData()->hasFormat("image/x-puzzle-piece")
+ && findPiece(targetSquare(event->position().toPoint())) == -1) {
+
+ highlightedRect = targetSquare(event->position().toPoint());
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ } else {
+ highlightedRect = QRect();
+ event->ignore();
+ }
+
+ update(updateRect);
+}
+
+void PuzzleWidget::dropEvent(QDropEvent *event)
+{
+ if (event->mimeData()->hasFormat("image/x-puzzle-piece")
+ && findPiece(targetSquare(event->position().toPoint())) == -1) {
+
+ QByteArray pieceData = event->mimeData()->data("image/x-puzzle-piece");
+ QDataStream dataStream(&pieceData, QIODevice::ReadOnly);
+ Piece piece;
+ piece.rect = targetSquare(event->position().toPoint());
+ dataStream >> piece.pixmap >> piece.location;
+
+ pieces.append(piece);
+
+ highlightedRect = QRect();
+ update(piece.rect);
+
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+
+ if (piece.location == piece.rect.topLeft() / pieceSize()) {
+ inPlace++;
+ if (inPlace == 25)
+ emit puzzleCompleted();
+ }
+ } else {
+ highlightedRect = QRect();
+ event->ignore();
+ }
+}
+
+int PuzzleWidget::findPiece(const QRect &pieceRect) const
+{
+ for (int i = 0, size = pieces.size(); i < size; ++i) {
+ if (pieces.at(i).rect == pieceRect)
+ return i;
+ }
+ return -1;
+}
+
+void PuzzleWidget::mousePressEvent(QMouseEvent *event)
+{
+ QRect square = targetSquare(event->position().toPoint());
+ int found = findPiece(square);
+
+ if (found == -1)
+ return;
+
+ Piece piece = pieces.takeAt(found);
+
+ if (piece.location == square.topLeft() / pieceSize())
+ inPlace--;
+
+ update(square);
+
+ QByteArray itemData;
+ QDataStream dataStream(&itemData, QIODevice::WriteOnly);
+
+ dataStream << piece.pixmap << piece.location;
+
+ QMimeData *mimeData = new QMimeData;
+ mimeData->setData("image/x-puzzle-piece", itemData);
+
+ QDrag *drag = new QDrag(this);
+ drag->setMimeData(mimeData);
+ drag->setHotSpot(event->position().toPoint() - square.topLeft());
+ drag->setPixmap(piece.pixmap);
+
+ if (drag->exec(Qt::MoveAction) == Qt::IgnoreAction) {
+ pieces.insert(found, piece);
+ update(targetSquare(event->position().toPoint()));
+
+ if (piece.location == QPoint(square.x() / pieceSize(), square.y() / pieceSize()))
+ inPlace++;
+ }
+}
+
+void PuzzleWidget::paintEvent(QPaintEvent *event)
+{
+ QPainter painter(this);
+ painter.fillRect(event->rect(), Qt::white);
+
+ if (highlightedRect.isValid()) {
+ painter.setBrush(QColor("#ffcccc"));
+ painter.setPen(Qt::NoPen);
+ painter.drawRect(highlightedRect.adjusted(0, 0, -1, -1));
+ }
+
+ for (const Piece &piece : pieces)
+ painter.drawPixmap(piece.rect, piece.pixmap);
+}
+
+const QRect PuzzleWidget::targetSquare(const QPoint &position) const
+{
+ QPoint topLeft = QPoint(position.x() / pieceSize(), position.y() / pieceSize()) * pieceSize();
+ return QRect(topLeft, QSize(pieceSize(), pieceSize()));
+}
+
+int PuzzleWidget::pieceSize() const
+{
+ return m_ImageSize / 5;
+}
+
+int PuzzleWidget::imageSize() const
+{
+ return m_ImageSize;
+}
diff --git a/tests/manual/examples/widgets/itemviews/puzzle/puzzlewidget.h b/tests/manual/examples/widgets/itemviews/puzzle/puzzlewidget.h
new file mode 100644
index 0000000000..d1c00872ec
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/puzzle/puzzlewidget.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PUZZLEWIDGET_H
+#define PUZZLEWIDGET_H
+
+#include <QPoint>
+#include <QPixmap>
+#include <QList>
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QDragEnterEvent;
+class QDropEvent;
+class QMouseEvent;
+QT_END_NAMESPACE
+
+class PuzzleWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit PuzzleWidget(int imageSize, QWidget *parent = nullptr);
+ void clear();
+
+ int pieceSize() const;
+ int imageSize() const;
+
+signals:
+ void puzzleCompleted();
+
+protected:
+ void dragEnterEvent(QDragEnterEvent *event) override;
+ void dragLeaveEvent(QDragLeaveEvent *event) override;
+ void dragMoveEvent(QDragMoveEvent *event) override;
+ void dropEvent(QDropEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void paintEvent(QPaintEvent *event) override;
+
+private:
+ struct Piece {
+ QPixmap pixmap;
+ QRect rect;
+ QPoint location;
+ };
+
+ int findPiece(const QRect &pieceRect) const;
+ const QRect targetSquare(const QPoint &position) const;
+
+ QList<Piece> pieces;
+ QRect highlightedRect;
+ int inPlace;
+ int m_ImageSize;
+};
+
+#endif // PUZZLEWIDGET_H
diff --git a/tests/manual/examples/widgets/itemviews/simpledommodel/CMakeLists.txt b/tests/manual/examples/widgets/itemviews/simpledommodel/CMakeLists.txt
new file mode 100644
index 0000000000..20d0aeedde
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/simpledommodel/CMakeLists.txt
@@ -0,0 +1,40 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(simpledommodel LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/itemviews/simpledommodel")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Xml)
+
+qt_standard_project_setup()
+
+qt_add_executable(simpledommodel
+ domitem.cpp domitem.h
+ dommodel.cpp dommodel.h
+ main.cpp
+ mainwindow.cpp mainwindow.h
+)
+
+set_target_properties(simpledommodel PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(simpledommodel PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+ Qt6::Xml
+)
+
+install(TARGETS simpledommodel
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/itemviews/simpledommodel/domitem.cpp b/tests/manual/examples/widgets/itemviews/simpledommodel/domitem.cpp
new file mode 100644
index 0000000000..b3e197b3db
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/simpledommodel/domitem.cpp
@@ -0,0 +1,62 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "domitem.h"
+
+#include <QtXml>
+
+//! [0]
+DomItem::DomItem(const QDomNode &node, int row, DomItem *parent)
+ : domNode(node),
+//! [0]
+ // Record the item's location within its parent.
+//! [1]
+ parentItem(parent),
+ rowNumber(row)
+{}
+//! [1]
+
+//! [2]
+DomItem::~DomItem()
+{
+ qDeleteAll(childItems);
+}
+//! [2]
+
+//! [3]
+QDomNode DomItem::node() const
+{
+ return domNode;
+}
+//! [3]
+
+//! [4]
+DomItem *DomItem::parent()
+{
+ return parentItem;
+}
+//! [4]
+
+//! [5]
+DomItem *DomItem::child(int i)
+{
+ DomItem *childItem = childItems.value(i);
+ if (childItem)
+ return childItem;
+
+ // if child does not yet exist, create it
+ if (i >= 0 && i < domNode.childNodes().count()) {
+ QDomNode childNode = domNode.childNodes().item(i);
+ childItem = new DomItem(childNode, i, this);
+ childItems[i] = childItem;
+ }
+ return childItem;
+}
+//! [5]
+
+//! [6]
+int DomItem::row() const
+{
+ return rowNumber;
+}
+//! [6]
diff --git a/tests/manual/examples/widgets/itemviews/simpledommodel/domitem.h b/tests/manual/examples/widgets/itemviews/simpledommodel/domitem.h
new file mode 100644
index 0000000000..9b02d8e88c
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/simpledommodel/domitem.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef DOMITEM_H
+#define DOMITEM_H
+
+#include <QDomNode>
+#include <QHash>
+
+//! [0]
+class DomItem
+{
+public:
+ DomItem(const QDomNode &node, int row, DomItem *parent = nullptr);
+ ~DomItem();
+ DomItem *child(int i);
+ DomItem *parent();
+ QDomNode node() const;
+ int row() const;
+
+private:
+ QDomNode domNode;
+ QHash<int, DomItem *> childItems;
+ DomItem *parentItem;
+ int rowNumber;
+};
+//! [0]
+
+#endif // DOMITEM_H
diff --git a/tests/manual/examples/widgets/itemviews/simpledommodel/dommodel.cpp b/tests/manual/examples/widgets/itemviews/simpledommodel/dommodel.cpp
new file mode 100644
index 0000000000..17f05c8be3
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/simpledommodel/dommodel.cpp
@@ -0,0 +1,153 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "dommodel.h"
+#include "domitem.h"
+
+#include <QtXml>
+
+//! [0]
+DomModel::DomModel(const QDomDocument &document, QObject *parent)
+ : QAbstractItemModel(parent),
+ domDocument(document),
+ rootItem(new DomItem(domDocument, 0))
+{
+}
+//! [0]
+
+//! [1]
+DomModel::~DomModel()
+{
+ delete rootItem;
+}
+//! [1]
+
+//! [2]
+int DomModel::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ return 3;
+}
+//! [2]
+
+//! [3]
+QVariant DomModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ const DomItem *item = static_cast<DomItem*>(index.internalPointer());
+
+ const QDomNode node = item->node();
+//! [3] //! [4]
+
+ switch (index.column()) {
+ case 0:
+ return node.nodeName();
+ case 1:
+ {
+ const QDomNamedNodeMap attributeMap = node.attributes();
+ QStringList attributes;
+ for (int i = 0; i < attributeMap.count(); ++i) {
+ QDomNode attribute = attributeMap.item(i);
+ attributes << attribute.nodeName() + "=\""
+ + attribute.nodeValue() + '"';
+ }
+ return attributes.join(' ');
+ }
+ case 2:
+ return node.nodeValue().split('\n').join(' ');
+ default:
+ break;
+ }
+ return QVariant();
+}
+//! [4]
+
+//! [5]
+Qt::ItemFlags DomModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+
+ return QAbstractItemModel::flags(index);
+}
+//! [5]
+
+//! [6]
+QVariant DomModel::headerData(int section, Qt::Orientation orientation,
+ int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ switch (section) {
+ case 0:
+ return tr("Name");
+ case 1:
+ return tr("Attributes");
+ case 2:
+ return tr("Value");
+ default:
+ break;
+ }
+ }
+ return QVariant();
+}
+//! [6]
+
+//! [7]
+QModelIndex DomModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ DomItem *parentItem;
+
+ if (!parent.isValid())
+ parentItem = rootItem;
+ else
+ parentItem = static_cast<DomItem*>(parent.internalPointer());
+//! [7]
+
+//! [8]
+ DomItem *childItem = parentItem->child(row);
+ if (childItem)
+ return createIndex(row, column, childItem);
+ return QModelIndex();
+}
+//! [8]
+
+//! [9]
+QModelIndex DomModel::parent(const QModelIndex &child) const
+{
+ if (!child.isValid())
+ return QModelIndex();
+
+ DomItem *childItem = static_cast<DomItem*>(child.internalPointer());
+ DomItem *parentItem = childItem->parent();
+
+ if (!parentItem || parentItem == rootItem)
+ return QModelIndex();
+
+ return createIndex(parentItem->row(), 0, parentItem);
+}
+//! [9]
+
+//! [10]
+int DomModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.column() > 0)
+ return 0;
+
+ DomItem *parentItem;
+
+ if (!parent.isValid())
+ parentItem = rootItem;
+ else
+ parentItem = static_cast<DomItem*>(parent.internalPointer());
+
+ return parentItem->node().childNodes().count();
+}
+//! [10]
diff --git a/tests/manual/examples/widgets/itemviews/simpledommodel/dommodel.h b/tests/manual/examples/widgets/itemviews/simpledommodel/dommodel.h
new file mode 100644
index 0000000000..109ab33e58
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/simpledommodel/dommodel.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef DOMMODEL_H
+#define DOMMODEL_H
+
+#include <QAbstractItemModel>
+#include <QDomDocument>
+#include <QModelIndex>
+
+class DomItem;
+
+//! [0]
+class DomModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ explicit DomModel(const QDomDocument &document, QObject *parent = nullptr);
+ ~DomModel();
+
+ QVariant data(const QModelIndex &index, int role) const override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const override;
+ QModelIndex index(int row, int column,
+ const QModelIndex &parent = QModelIndex()) const override;
+ QModelIndex parent(const QModelIndex &child) const override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+
+private:
+ QDomDocument domDocument;
+ DomItem *rootItem;
+};
+//! [0]
+
+#endif // DOMMODEL_H
diff --git a/tests/manual/examples/widgets/itemviews/simpledommodel/main.cpp b/tests/manual/examples/widgets/itemviews/simpledommodel/main.cpp
new file mode 100644
index 0000000000..2ea03356f2
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/simpledommodel/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ MainWindow window;
+ window.resize(640, 480);
+ window.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/itemviews/simpledommodel/mainwindow.cpp b/tests/manual/examples/widgets/itemviews/simpledommodel/mainwindow.cpp
new file mode 100644
index 0000000000..ad64863fbb
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/simpledommodel/mainwindow.cpp
@@ -0,0 +1,47 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+#include "dommodel.h"
+
+#include <QDomDocument>
+#include <QTreeView>
+#include <QMenuBar>
+#include <QFileDialog>
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent),
+ model(new DomModel(QDomDocument(), this)),
+ view(new QTreeView(this))
+{
+ fileMenu = menuBar()->addMenu(tr("&File"));
+ fileMenu->addAction(tr("&Open..."), QKeySequence::Open, this, &MainWindow::openFile);
+ fileMenu->addAction(tr("E&xit"), QKeySequence::Quit, this, &QWidget::close);
+
+ view->setModel(model);
+
+ setCentralWidget(view);
+ setWindowTitle(tr("Simple DOM Model"));
+}
+
+void MainWindow::openFile()
+{
+ QString filePath = QFileDialog::getOpenFileName(this, tr("Open File"),
+ xmlPath, tr("XML files (*.xml);;HTML files (*.html);;"
+ "SVG files (*.svg);;User Interface files (*.ui)"));
+
+ if (!filePath.isEmpty()) {
+ QFile file(filePath);
+ if (file.open(QIODevice::ReadOnly)) {
+ QDomDocument document;
+ if (document.setContent(&file)) {
+ DomModel *newModel = new DomModel(document, this);
+ view->setModel(newModel);
+ delete model;
+ model = newModel;
+ xmlPath = filePath;
+ }
+ file.close();
+ }
+ }
+}
diff --git a/tests/manual/examples/widgets/itemviews/simpledommodel/mainwindow.h b/tests/manual/examples/widgets/itemviews/simpledommodel/mainwindow.h
new file mode 100644
index 0000000000..a82f3956c8
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/simpledommodel/mainwindow.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QString>
+
+class DomModel;
+QT_BEGIN_NAMESPACE
+class QMenu;
+class QTreeView;
+QT_END_NAMESPACE
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow(QWidget *parent = nullptr);
+
+public slots:
+ void openFile();
+
+private:
+ DomModel *model;
+ QMenu *fileMenu;
+ QString xmlPath;
+ QTreeView *view;
+};
+
+#endif // MAINWINDOW_H
diff --git a/tests/manual/examples/widgets/itemviews/simpledommodel/simpledommodel.pro b/tests/manual/examples/widgets/itemviews/simpledommodel/simpledommodel.pro
new file mode 100644
index 0000000000..3d45920e36
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/simpledommodel/simpledommodel.pro
@@ -0,0 +1,14 @@
+HEADERS = domitem.h \
+ dommodel.h \
+ mainwindow.h
+SOURCES = domitem.cpp \
+ dommodel.cpp \
+ main.cpp \
+ mainwindow.cpp
+QT += xml widgets
+requires(qtConfig(filedialog))
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/itemviews/simpledommodel
+INSTALLS += target
+
diff --git a/tests/manual/examples/widgets/itemviews/simplewidgetmapper/CMakeLists.txt b/tests/manual/examples/widgets/itemviews/simplewidgetmapper/CMakeLists.txt
new file mode 100644
index 0000000000..c62dbc0306
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/simplewidgetmapper/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(simplewidgetmapper LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/itemviews/simplewidgetmapper")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(simplewidgetmapper
+ main.cpp
+ window.cpp window.h
+)
+
+set_target_properties(simplewidgetmapper PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(simplewidgetmapper PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS simplewidgetmapper
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/itemviews/simplewidgetmapper/main.cpp b/tests/manual/examples/widgets/itemviews/simplewidgetmapper/main.cpp
new file mode 100644
index 0000000000..2709c948f9
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/simplewidgetmapper/main.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "window.h"
+
+#include <QApplication>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ Window window;
+ window.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/itemviews/simplewidgetmapper/simplewidgetmapper.pro b/tests/manual/examples/widgets/itemviews/simplewidgetmapper/simplewidgetmapper.pro
new file mode 100644
index 0000000000..f86a16bd3f
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/simplewidgetmapper/simplewidgetmapper.pro
@@ -0,0 +1,10 @@
+QT += widgets
+requires(qtConfig(datawidgetmapper))
+
+HEADERS = window.h
+SOURCES = main.cpp \
+ window.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/itemviews/simplewidgetmapper
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/itemviews/simplewidgetmapper/window.cpp b/tests/manual/examples/widgets/itemviews/simplewidgetmapper/window.cpp
new file mode 100644
index 0000000000..f7ef05dbd5
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/simplewidgetmapper/window.cpp
@@ -0,0 +1,93 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "window.h"
+
+#include <QtWidgets>
+
+//! [Set up widgets]
+Window::Window(QWidget *parent)
+ : QWidget(parent)
+{
+ setupModel();
+
+ nameLabel = new QLabel(tr("Na&me:"));
+ nameEdit = new QLineEdit();
+ addressLabel = new QLabel(tr("&Address:"));
+ addressEdit = new QTextEdit();
+ ageLabel = new QLabel(tr("A&ge (in years):"));
+ ageSpinBox = new QSpinBox();
+ nextButton = new QPushButton(tr("&Next"));
+ previousButton = new QPushButton(tr("&Previous"));
+
+ nameLabel->setBuddy(nameEdit);
+ addressLabel->setBuddy(addressEdit);
+ ageLabel->setBuddy(ageSpinBox);
+//! [Set up widgets]
+
+//! [Set up the mapper]
+ mapper = new QDataWidgetMapper(this);
+ mapper->setModel(model);
+ mapper->addMapping(nameEdit, 0);
+ mapper->addMapping(addressEdit, 1);
+ mapper->addMapping(ageSpinBox, 2);
+
+ connect(previousButton, &QAbstractButton::clicked, mapper, &QDataWidgetMapper::toPrevious);
+ connect(nextButton, &QAbstractButton::clicked, mapper, &QDataWidgetMapper::toNext);
+ connect(mapper, &QDataWidgetMapper::currentIndexChanged, this, &Window::updateButtons);
+//! [Set up the mapper]
+
+//! [Set up the layout]
+ QGridLayout *layout = new QGridLayout();
+ layout->addWidget(nameLabel, 0, 0, 1, 1);
+ layout->addWidget(nameEdit, 0, 1, 1, 1);
+ layout->addWidget(previousButton, 0, 2, 1, 1);
+ layout->addWidget(addressLabel, 1, 0, 1, 1);
+ layout->addWidget(addressEdit, 1, 1, 2, 1);
+ layout->addWidget(nextButton, 1, 2, 1, 1);
+ layout->addWidget(ageLabel, 3, 0, 1, 1);
+ layout->addWidget(ageSpinBox, 3, 1, 1, 1);
+ setLayout(layout);
+
+ setWindowTitle(tr("Simple Widget Mapper"));
+ mapper->toFirst();
+}
+//! [Set up the layout]
+
+//! [Set up the model]
+void Window::setupModel()
+{
+ model = new QStandardItemModel(5, 3, this);
+
+ QStringList names;
+ names << "Alice" << "Bob" << "Carol" << "Donald" << "Emma";
+
+ QStringList addresses;
+ addresses << "<qt>123 Main Street<br/>Market Town</qt>"
+ << "<qt>PO Box 32<br/>Mail Handling Service"
+ "<br/>Service City</qt>"
+ << "<qt>The Lighthouse<br/>Remote Island</qt>"
+ << "<qt>47338 Park Avenue<br/>Big City</qt>"
+ << "<qt>Research Station<br/>Base Camp<br/>Big Mountain</qt>";
+
+ QStringList ages;
+ ages << "20" << "31" << "32" << "19" << "26";
+
+ for (int row = 0; row < 5; ++row) {
+ QStandardItem *item = new QStandardItem(names[row]);
+ model->setItem(row, 0, item);
+ item = new QStandardItem(addresses[row]);
+ model->setItem(row, 1, item);
+ item = new QStandardItem(ages[row]);
+ model->setItem(row, 2, item);
+ }
+}
+//! [Set up the model]
+
+//! [Slot for updating the buttons]
+void Window::updateButtons(int row)
+{
+ previousButton->setEnabled(row > 0);
+ nextButton->setEnabled(row < model->rowCount() - 1);
+}
+//! [Slot for updating the buttons]
diff --git a/tests/manual/examples/widgets/itemviews/simplewidgetmapper/window.h b/tests/manual/examples/widgets/itemviews/simplewidgetmapper/window.h
new file mode 100644
index 0000000000..1502c00df1
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/simplewidgetmapper/window.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QDataWidgetMapper;
+class QLabel;
+class QLineEdit;
+class QPushButton;
+class QSpinBox;
+class QStandardItemModel;
+class QTextEdit;
+QT_END_NAMESPACE
+
+//! [Window definition]
+class Window : public QWidget
+{
+ Q_OBJECT
+
+public:
+ Window(QWidget *parent = nullptr);
+
+private slots:
+ void updateButtons(int row);
+
+private:
+ void setupModel();
+
+ QLabel *nameLabel;
+ QLabel *addressLabel;
+ QLabel *ageLabel;
+ QLineEdit *nameEdit;
+ QTextEdit *addressEdit;
+ QSpinBox *ageSpinBox;
+ QPushButton *nextButton;
+ QPushButton *previousButton;
+
+ QStandardItemModel *model;
+ QDataWidgetMapper *mapper;
+};
+//! [Window definition]
+
+#endif // WINDOW_H
diff --git a/tests/manual/examples/widgets/itemviews/storageview/CMakeLists.txt b/tests/manual/examples/widgets/itemviews/storageview/CMakeLists.txt
new file mode 100644
index 0000000000..36c37d037f
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/storageview/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(storageview LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/itemviews/storageview")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(storageview
+ main.cpp
+ storagemodel.cpp storagemodel.h
+)
+
+set_target_properties(storageview PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(storageview PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS storageview
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/itemviews/storageview/main.cpp b/tests/manual/examples/widgets/itemviews/storageview/main.cpp
new file mode 100644
index 0000000000..3bd5392736
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/storageview/main.cpp
@@ -0,0 +1,32 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Ivan Komissarov
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+#include <QShortcut>
+#include <QTreeView>
+
+#include "storagemodel.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+
+ QTreeView view;
+ view.resize(640, 480);
+ view.setWindowTitle("Storage View");
+ view.setSelectionBehavior(QAbstractItemView::SelectRows);
+
+ StorageModel *model = new StorageModel(&view);
+ model->refresh();
+ QShortcut *refreshShortcut = new QShortcut(QKeySequence::Refresh, &view);
+ QObject::connect(refreshShortcut, &QShortcut::activated, model, &StorageModel::refresh);
+ view.setModel(model);
+
+ int columnCount = view.model()->columnCount();
+ for (int c = 0; c < columnCount; ++c)
+ view.resizeColumnToContents(c);
+ view.show();
+
+ return a.exec();
+}
diff --git a/tests/manual/examples/widgets/itemviews/storageview/storagemodel.cpp b/tests/manual/examples/widgets/itemviews/storageview/storagemodel.cpp
new file mode 100644
index 0000000000..194f8723f6
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/storageview/storagemodel.cpp
@@ -0,0 +1,164 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Ivan Komissarov
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "storagemodel.h"
+
+#include <QDir>
+#include <QLocale>
+
+void StorageModel::refresh()
+{
+ beginResetModel();
+ m_volumes = QStorageInfo::mountedVolumes();
+ std::sort(m_volumes.begin(), m_volumes.end(),
+ [](const QStorageInfo &st1, const QStorageInfo &st2) {
+ static const QString rootSortString = QStringLiteral(" ");
+ return (st1.isRoot() ? rootSortString : st1.rootPath())
+ < (st2.isRoot() ? rootSortString : st2.rootPath());
+ });
+ endResetModel();
+}
+
+int StorageModel::columnCount(const QModelIndex &/*parent*/) const
+{
+ return ColumnCount;
+}
+
+int StorageModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+ return m_volumes.count();
+}
+
+Qt::ItemFlags StorageModel::flags(const QModelIndex &index) const
+{
+ Qt::ItemFlags result = QAbstractTableModel::flags(index);
+ switch (index.column()) {
+ case ColumnAvailable:
+ case ColumnIsReady:
+ case ColumnIsReadOnly:
+ case ColumnIsValid:
+ result |= Qt::ItemIsUserCheckable;
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+QVariant StorageModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (role == Qt::DisplayRole) {
+ const QStorageInfo &volume = m_volumes.at(index.row());
+ switch (index.column()) {
+ case ColumnRootPath:
+ return QDir::toNativeSeparators(volume.rootPath());
+ case ColumnName:
+ return volume.name();
+ case ColumnDevice:
+ return volume.device();
+ case ColumnFileSystemName:
+ return volume.fileSystemType();
+ case ColumnTotal:
+ return QLocale().formattedDataSize(volume.bytesTotal());
+ case ColumnFree:
+ return QLocale().formattedDataSize(volume.bytesFree());
+ case ColumnAvailable:
+ return QLocale().formattedDataSize(volume.bytesAvailable());
+ default:
+ break;
+ }
+ } else if (role == Qt::CheckStateRole) {
+ const QStorageInfo &volume = m_volumes.at(index.row());
+ switch (index.column()) {
+ case ColumnIsReady:
+ return volume.isReady();
+ case ColumnIsReadOnly:
+ return volume.isReadOnly();
+ case ColumnIsValid:
+ return volume.isValid();
+ default:
+ break;
+ }
+ } else if (role == Qt::TextAlignmentRole) {
+ switch (index.column()) {
+ case ColumnTotal:
+ case ColumnFree:
+ case ColumnAvailable:
+ return Qt::AlignTrailing;
+ default:
+ break;
+ }
+ return Qt::AlignLeading;
+ } else if (role == Qt::ToolTipRole) {
+ QLocale locale;
+ const QStorageInfo &volume = m_volumes.at(index.row());
+ return tr("Root path : %1\n"
+ "Name: %2\n"
+ "Display Name: %3\n"
+ "Device: %4\n"
+ "FileSystem: %5\n"
+ "Total size: %6\n"
+ "Free size: %7\n"
+ "Available size: %8\n"
+ "Is Ready: %9\n"
+ "Is Read-only: %10\n"
+ "Is Valid: %11\n"
+ "Is Root: %12"
+ ).
+ arg(QDir::toNativeSeparators(volume.rootPath())).
+ arg(volume.name()).
+ arg(volume.displayName()).
+ arg(QString::fromUtf8(volume.device())).
+ arg(QString::fromUtf8(volume.fileSystemType())).
+ arg(locale.formattedDataSize(volume.bytesTotal())).
+ arg(locale.formattedDataSize(volume.bytesFree())).
+ arg(locale.formattedDataSize(volume.bytesAvailable())).
+ arg(volume.isReady() ? tr("true") : tr("false")).
+ arg(volume.isReadOnly() ? tr("true") : tr("false")).
+ arg(volume.isValid() ? tr("true") : tr("false")).
+ arg(volume.isRoot() ? tr("true") : tr("false"));
+ }
+ return QVariant();
+}
+
+QVariant StorageModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation != Qt::Horizontal)
+ return QVariant();
+
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ switch (section) {
+ case ColumnRootPath:
+ return tr("Root Path");
+ case ColumnName:
+ return tr("Volume Name");
+ case ColumnDevice:
+ return tr("Device");
+ case ColumnFileSystemName:
+ return tr("File System");
+ case ColumnTotal:
+ return tr("Total");
+ case ColumnFree:
+ return tr("Free");
+ case ColumnAvailable:
+ return tr("Available");
+ case ColumnIsReady:
+ return tr("Ready");
+ case ColumnIsReadOnly:
+ return tr("Read-only");
+ case ColumnIsValid:
+ return tr("Valid");
+ default:
+ break;
+ }
+
+ return QVariant();
+}
diff --git a/tests/manual/examples/widgets/itemviews/storageview/storagemodel.h b/tests/manual/examples/widgets/itemviews/storageview/storagemodel.h
new file mode 100644
index 0000000000..f8cc3289c8
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/storageview/storagemodel.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Ivan Komissarov
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef STORAGEMODEL_H
+#define STORAGEMODEL_H
+
+#include <QAbstractTableModel>
+#include <QStorageInfo>
+
+class StorageModel : public QAbstractTableModel
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(StorageModel)
+public:
+ enum Column {
+ ColumnRootPath = 0,
+ ColumnName,
+ ColumnDevice,
+ ColumnFileSystemName,
+ ColumnTotal,
+ ColumnFree,
+ ColumnAvailable,
+ ColumnIsReady,
+ ColumnIsReadOnly,
+ ColumnIsValid,
+ ColumnCount
+ };
+
+ using QAbstractTableModel::QAbstractTableModel;
+
+ int columnCount(const QModelIndex &parent) const override;
+ int rowCount(const QModelIndex &parent) const override;
+
+ QVariant data(const QModelIndex &index, int role) const override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+
+public slots:
+ void refresh();
+
+private:
+ QList<QStorageInfo> m_volumes;
+};
+
+#endif // STORAGEMODEL_H
diff --git a/tests/manual/examples/widgets/itemviews/storageview/storageview.pro b/tests/manual/examples/widgets/itemviews/storageview/storageview.pro
new file mode 100644
index 0000000000..2fdb78e7b8
--- /dev/null
+++ b/tests/manual/examples/widgets/itemviews/storageview/storageview.pro
@@ -0,0 +1,12 @@
+QT += core gui widgets
+requires(qtConfig(treeview))
+TARGET = storageview
+TEMPLATE = app
+SOURCES += storagemodel.cpp \
+ main.cpp
+HEADERS += \
+ storagemodel.h
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/itemviews/storageview
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/layouts/borderlayout/CMakeLists.txt b/tests/manual/examples/widgets/layouts/borderlayout/CMakeLists.txt
new file mode 100644
index 0000000000..d217e825d5
--- /dev/null
+++ b/tests/manual/examples/widgets/layouts/borderlayout/CMakeLists.txt
@@ -0,0 +1,38 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(borderlayout LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/layouts/borderlayout")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(borderlayout
+ borderlayout.cpp borderlayout.h
+ main.cpp
+ window.cpp window.h
+)
+
+set_target_properties(borderlayout PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(borderlayout PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS borderlayout
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/layouts/borderlayout/borderlayout.cpp b/tests/manual/examples/widgets/layouts/borderlayout/borderlayout.cpp
new file mode 100644
index 0000000000..4dad24da0d
--- /dev/null
+++ b/tests/manual/examples/widgets/layouts/borderlayout/borderlayout.cpp
@@ -0,0 +1,171 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "borderlayout.h"
+
+BorderLayout::BorderLayout(QWidget *parent, const QMargins &margins, int spacing)
+ : QLayout(parent)
+{
+ setContentsMargins(margins);
+ setSpacing(spacing);
+}
+
+BorderLayout::BorderLayout(int spacing)
+{
+ setSpacing(spacing);
+}
+
+
+BorderLayout::~BorderLayout()
+{
+ QLayoutItem *l;
+ while ((l = takeAt(0)))
+ delete l;
+}
+
+void BorderLayout::addItem(QLayoutItem *item)
+{
+ add(item, West);
+}
+
+void BorderLayout::addWidget(QWidget *widget, Position position)
+{
+ add(new QWidgetItem(widget), position);
+}
+
+Qt::Orientations BorderLayout::expandingDirections() const
+{
+ return Qt::Horizontal | Qt::Vertical;
+}
+
+bool BorderLayout::hasHeightForWidth() const
+{
+ return false;
+}
+
+int BorderLayout::count() const
+{
+ return list.size();
+}
+
+QLayoutItem *BorderLayout::itemAt(int index) const
+{
+ ItemWrapper *wrapper = list.value(index);
+ return wrapper ? wrapper->item : nullptr;
+}
+
+QSize BorderLayout::minimumSize() const
+{
+ return calculateSize(MinimumSize);
+}
+
+void BorderLayout::setGeometry(const QRect &rect)
+{
+ ItemWrapper *center = nullptr;
+ int eastWidth = 0;
+ int westWidth = 0;
+ int northHeight = 0;
+ int southHeight = 0;
+ int centerHeight = 0;
+ int i;
+
+ QLayout::setGeometry(rect);
+
+ for (i = 0; i < list.size(); ++i) {
+ ItemWrapper *wrapper = list.at(i);
+ QLayoutItem *item = wrapper->item;
+ Position position = wrapper->position;
+
+ if (position == North) {
+ item->setGeometry(QRect(rect.x(), northHeight, rect.width(),
+ item->sizeHint().height()));
+
+ northHeight += item->geometry().height() + spacing();
+ } else if (position == South) {
+ item->setGeometry(QRect(item->geometry().x(),
+ item->geometry().y(), rect.width(),
+ item->sizeHint().height()));
+
+ southHeight += item->geometry().height() + spacing();
+
+ item->setGeometry(QRect(rect.x(),
+ rect.y() + rect.height() - southHeight + spacing(),
+ item->geometry().width(),
+ item->geometry().height()));
+ } else if (position == Center) {
+ center = wrapper;
+ }
+ }
+
+ centerHeight = rect.height() - northHeight - southHeight;
+
+ for (i = 0; i < list.size(); ++i) {
+ ItemWrapper *wrapper = list.at(i);
+ QLayoutItem *item = wrapper->item;
+ Position position = wrapper->position;
+
+ if (position == West) {
+ item->setGeometry(QRect(rect.x() + westWidth, northHeight,
+ item->sizeHint().width(), centerHeight));
+
+ westWidth += item->geometry().width() + spacing();
+ } else if (position == East) {
+ item->setGeometry(QRect(item->geometry().x(), item->geometry().y(),
+ item->sizeHint().width(), centerHeight));
+
+ eastWidth += item->geometry().width() + spacing();
+
+ item->setGeometry(QRect(
+ rect.x() + rect.width() - eastWidth + spacing(),
+ northHeight, item->geometry().width(),
+ item->geometry().height()));
+ }
+ }
+
+ if (center)
+ center->item->setGeometry(QRect(westWidth, northHeight,
+ rect.width() - eastWidth - westWidth,
+ centerHeight));
+}
+
+QSize BorderLayout::sizeHint() const
+{
+ return calculateSize(SizeHint);
+}
+
+QLayoutItem *BorderLayout::takeAt(int index)
+{
+ if (index >= 0 && index < list.size()) {
+ ItemWrapper *layoutStruct = list.takeAt(index);
+ return layoutStruct->item;
+ }
+ return nullptr;
+}
+
+void BorderLayout::add(QLayoutItem *item, Position position)
+{
+ list.append(new ItemWrapper(item, position));
+}
+
+QSize BorderLayout::calculateSize(SizeType sizeType) const
+{
+ QSize totalSize;
+
+ for (int i = 0; i < list.size(); ++i) {
+ ItemWrapper *wrapper = list.at(i);
+ Position position = wrapper->position;
+ QSize itemSize;
+
+ if (sizeType == MinimumSize)
+ itemSize = wrapper->item->minimumSize();
+ else // (sizeType == SizeHint)
+ itemSize = wrapper->item->sizeHint();
+
+ if (position == North || position == South || position == Center)
+ totalSize.rheight() += itemSize.height();
+
+ if (position == West || position == East || position == Center)
+ totalSize.rwidth() += itemSize.width();
+ }
+ return totalSize;
+}
diff --git a/tests/manual/examples/widgets/layouts/borderlayout/borderlayout.h b/tests/manual/examples/widgets/layouts/borderlayout/borderlayout.h
new file mode 100644
index 0000000000..c1d3ae7204
--- /dev/null
+++ b/tests/manual/examples/widgets/layouts/borderlayout/borderlayout.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef BORDERLAYOUT_H
+#define BORDERLAYOUT_H
+
+#include <QLayout>
+#include <QRect>
+
+class BorderLayout : public QLayout
+{
+public:
+ enum Position { West, North, South, East, Center };
+
+ explicit BorderLayout(QWidget *parent, const QMargins &margins = QMargins(), int spacing = -1);
+ BorderLayout(int spacing = -1);
+ ~BorderLayout();
+
+ void addItem(QLayoutItem *item) override;
+ void addWidget(QWidget *widget, Position position);
+ Qt::Orientations expandingDirections() const override;
+ bool hasHeightForWidth() const override;
+ int count() const override;
+ QLayoutItem *itemAt(int index) const override;
+ QSize minimumSize() const override;
+ void setGeometry(const QRect &rect) override;
+ QSize sizeHint() const override;
+ QLayoutItem *takeAt(int index) override;
+
+ void add(QLayoutItem *item, Position position);
+
+private:
+ struct ItemWrapper
+ {
+ ItemWrapper(QLayoutItem *i, Position p) {
+ item = i;
+ position = p;
+ }
+
+ QLayoutItem *item;
+ Position position;
+ };
+
+ enum SizeType { MinimumSize, SizeHint };
+ QSize calculateSize(SizeType sizeType) const;
+
+ QList<ItemWrapper *> list;
+};
+
+#endif // BORDERLAYOUT_H
diff --git a/tests/manual/examples/widgets/layouts/borderlayout/borderlayout.pro b/tests/manual/examples/widgets/layouts/borderlayout/borderlayout.pro
new file mode 100644
index 0000000000..53c9647bd7
--- /dev/null
+++ b/tests/manual/examples/widgets/layouts/borderlayout/borderlayout.pro
@@ -0,0 +1,11 @@
+QT += widgets
+
+HEADERS = borderlayout.h \
+ window.h
+SOURCES = borderlayout.cpp \
+ main.cpp \
+ window.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/layouts/borderlayout
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/layouts/borderlayout/main.cpp b/tests/manual/examples/widgets/layouts/borderlayout/main.cpp
new file mode 100644
index 0000000000..27409403a5
--- /dev/null
+++ b/tests/manual/examples/widgets/layouts/borderlayout/main.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "window.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ Window window;
+ window.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/layouts/borderlayout/window.cpp b/tests/manual/examples/widgets/layouts/borderlayout/window.cpp
new file mode 100644
index 0000000000..ea3b6a6fe8
--- /dev/null
+++ b/tests/manual/examples/widgets/layouts/borderlayout/window.cpp
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "borderlayout.h"
+#include "window.h"
+#include <QTextBrowser>
+#include <QLabel>
+
+Window::Window()
+{
+ QTextBrowser *centralWidget = new QTextBrowser;
+ centralWidget->setPlainText(tr("Central widget"));
+
+ BorderLayout *layout = new BorderLayout;
+ layout->addWidget(centralWidget, BorderLayout::Center);
+ layout->addWidget(createLabel("North"), BorderLayout::North);
+ layout->addWidget(createLabel("West"), BorderLayout::West);
+ layout->addWidget(createLabel("East 1"), BorderLayout::East);
+ layout->addWidget(createLabel("East 2") , BorderLayout::East);
+ layout->addWidget(createLabel("South"), BorderLayout::South);
+ setLayout(layout);
+
+ setWindowTitle(tr("Border Layout"));
+}
+
+QLabel *Window::createLabel(const QString &text)
+{
+ QLabel *label = new QLabel(text);
+ label->setFrameStyle(QFrame::Box | QFrame::Raised);
+ return label;
+}
diff --git a/tests/manual/examples/widgets/layouts/borderlayout/window.h b/tests/manual/examples/widgets/layouts/borderlayout/window.h
new file mode 100644
index 0000000000..9e97d0e8fb
--- /dev/null
+++ b/tests/manual/examples/widgets/layouts/borderlayout/window.h
@@ -0,0 +1,24 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QLabel;
+QT_END_NAMESPACE
+
+class Window : public QWidget
+{
+ Q_OBJECT
+
+public:
+ Window();
+
+private:
+ QLabel *createLabel(const QString &text);
+};
+
+#endif // WINDOW_H
diff --git a/tests/manual/examples/widgets/layouts/dynamiclayouts/CMakeLists.txt b/tests/manual/examples/widgets/layouts/dynamiclayouts/CMakeLists.txt
new file mode 100644
index 0000000000..26978a901d
--- /dev/null
+++ b/tests/manual/examples/widgets/layouts/dynamiclayouts/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(dynamiclayouts LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/layouts/dynamiclayouts")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(dynamiclayouts
+ dialog.cpp dialog.h
+ main.cpp
+)
+
+set_target_properties(dynamiclayouts PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(dynamiclayouts PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS dynamiclayouts
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/layouts/dynamiclayouts/dialog.cpp b/tests/manual/examples/widgets/layouts/dynamiclayouts/dialog.cpp
new file mode 100644
index 0000000000..28b4fc2f7f
--- /dev/null
+++ b/tests/manual/examples/widgets/layouts/dynamiclayouts/dialog.cpp
@@ -0,0 +1,136 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+
+#include "dialog.h"
+
+Dialog::Dialog(QWidget *parent)
+ : QDialog(parent)
+{
+ createRotatableGroupBox();
+ createOptionsGroupBox();
+ createButtonBox();
+
+ mainLayout = new QGridLayout;
+ mainLayout->addWidget(rotatableGroupBox, 0, 0);
+ mainLayout->addWidget(optionsGroupBox, 1, 0);
+ mainLayout->addWidget(buttonBox, 2, 0);
+ setLayout(mainLayout);
+
+ mainLayout->setSizeConstraint(QLayout::SetMinimumSize);
+
+ setWindowTitle(tr("Dynamic Layouts"));
+}
+
+void Dialog::buttonsOrientationChanged(int index)
+{
+ mainLayout->setSizeConstraint(QLayout::SetNoConstraint);
+ setMinimumSize(0, 0);
+
+ Qt::Orientation orientation = Qt::Orientation(
+ buttonsOrientationComboBox->itemData(index).toInt());
+
+ if (orientation == buttonBox->orientation())
+ return;
+
+ mainLayout->removeWidget(buttonBox);
+
+ int spacing = mainLayout->spacing();
+
+ QSize oldSizeHint = buttonBox->sizeHint() + QSize(spacing, spacing);
+ buttonBox->setOrientation(orientation);
+ QSize newSizeHint = buttonBox->sizeHint() + QSize(spacing, spacing);
+
+ if (orientation == Qt::Horizontal) {
+ mainLayout->addWidget(buttonBox, 2, 0);
+ resize(size() + QSize(-oldSizeHint.width(), newSizeHint.height()));
+ } else {
+ mainLayout->addWidget(buttonBox, 0, 3, 2, 1);
+ resize(size() + QSize(newSizeHint.width(), -oldSizeHint.height()));
+ }
+
+ mainLayout->setSizeConstraint(QLayout::SetDefaultConstraint);
+}
+
+void Dialog::rotateWidgets()
+{
+ Q_ASSERT(rotatableWidgets.count() % 2 == 0);
+
+ for (QWidget *widget : std::as_const(rotatableWidgets))
+ rotatableLayout->removeWidget(widget);
+
+ rotatableWidgets.enqueue(rotatableWidgets.dequeue());
+
+ const int n = rotatableWidgets.count();
+ for (int i = 0; i < n / 2; ++i) {
+ rotatableLayout->addWidget(rotatableWidgets[n - i - 1], 0, i);
+ rotatableLayout->addWidget(rotatableWidgets[i], 1, i);
+ }
+}
+
+void Dialog::help()
+{
+ QMessageBox::information(this, tr("Dynamic Layouts Help"),
+ tr("This example shows how to change layouts "
+ "dynamically."));
+}
+
+void Dialog::createRotatableGroupBox()
+{
+ rotatableGroupBox = new QGroupBox(tr("Rotatable Widgets"));
+
+ rotatableWidgets.enqueue(new QSpinBox);
+ rotatableWidgets.enqueue(new QSlider);
+ rotatableWidgets.enqueue(new QDial);
+ rotatableWidgets.enqueue(new QProgressBar);
+
+ int n = rotatableWidgets.count();
+ for (int i = 0; i < n; ++i) {
+ connect(rotatableWidgets[i], SIGNAL(valueChanged(int)),
+ rotatableWidgets[(i + 1) % n], SLOT(setValue(int)));
+ }
+
+ rotatableLayout = new QGridLayout;
+ rotatableGroupBox->setLayout(rotatableLayout);
+
+ rotateWidgets();
+}
+
+void Dialog::createOptionsGroupBox()
+{
+ optionsGroupBox = new QGroupBox(tr("Options"));
+
+ buttonsOrientationLabel = new QLabel(tr("Orientation of buttons:"));
+
+ buttonsOrientationComboBox = new QComboBox;
+ buttonsOrientationComboBox->addItem(tr("Horizontal"), Qt::Horizontal);
+ buttonsOrientationComboBox->addItem(tr("Vertical"), Qt::Vertical);
+
+ connect(buttonsOrientationComboBox,
+ &QComboBox::currentIndexChanged,
+ this,
+ &Dialog::buttonsOrientationChanged);
+
+ optionsLayout = new QGridLayout;
+ optionsLayout->addWidget(buttonsOrientationLabel, 0, 0);
+ optionsLayout->addWidget(buttonsOrientationComboBox, 0, 1);
+ optionsLayout->setColumnStretch(2, 1);
+ optionsGroupBox->setLayout(optionsLayout);
+}
+
+void Dialog::createButtonBox()
+{
+ buttonBox = new QDialogButtonBox;
+
+ closeButton = buttonBox->addButton(QDialogButtonBox::Close);
+ helpButton = buttonBox->addButton(QDialogButtonBox::Help);
+ rotateWidgetsButton = buttonBox->addButton(tr("Rotate &Widgets"),
+ QDialogButtonBox::ActionRole);
+
+ connect(rotateWidgetsButton, &QPushButton::clicked, this, &Dialog::rotateWidgets);
+ connect(closeButton, &QPushButton::clicked, this, &Dialog::close);
+ connect(helpButton, &QPushButton::clicked, this, &Dialog::help);
+}
+
+
diff --git a/tests/manual/examples/widgets/layouts/dynamiclayouts/dialog.h b/tests/manual/examples/widgets/layouts/dynamiclayouts/dialog.h
new file mode 100644
index 0000000000..89424cee8a
--- /dev/null
+++ b/tests/manual/examples/widgets/layouts/dynamiclayouts/dialog.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef DIALOG_H
+#define DIALOG_H
+
+#include <QDialog>
+#include <QQueue>
+
+QT_BEGIN_NAMESPACE
+class QComboBox;
+class QDialogButtonBox;
+class QGridLayout;
+class QGroupBox;
+class QLabel;
+class QPushButton;
+QT_END_NAMESPACE
+
+class Dialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ Dialog(QWidget *parent = nullptr);
+
+private slots:
+ void buttonsOrientationChanged(int index);
+ void rotateWidgets();
+ void help();
+
+private:
+ void createRotatableGroupBox();
+ void createOptionsGroupBox();
+ void createButtonBox();
+
+ QGroupBox *rotatableGroupBox;
+ QQueue<QWidget *> rotatableWidgets;
+
+ QGroupBox *optionsGroupBox;
+ QLabel *buttonsOrientationLabel;
+ QComboBox *buttonsOrientationComboBox;
+
+ QDialogButtonBox *buttonBox;
+ QPushButton *closeButton;
+ QPushButton *helpButton;
+ QPushButton *rotateWidgetsButton;
+
+ QGridLayout *mainLayout;
+ QGridLayout *rotatableLayout;
+ QGridLayout *optionsLayout;
+};
+
+#endif // DIALOG_H
diff --git a/tests/manual/examples/widgets/layouts/dynamiclayouts/dynamiclayouts.pro b/tests/manual/examples/widgets/layouts/dynamiclayouts/dynamiclayouts.pro
new file mode 100644
index 0000000000..dcd4288698
--- /dev/null
+++ b/tests/manual/examples/widgets/layouts/dynamiclayouts/dynamiclayouts.pro
@@ -0,0 +1,10 @@
+QT += widgets
+requires(qtConfig(combobox))
+
+HEADERS = dialog.h
+SOURCES = dialog.cpp \
+ main.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/layouts/dynamiclayouts
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/layouts/dynamiclayouts/main.cpp b/tests/manual/examples/widgets/layouts/dynamiclayouts/main.cpp
new file mode 100644
index 0000000000..865e0d1579
--- /dev/null
+++ b/tests/manual/examples/widgets/layouts/dynamiclayouts/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "dialog.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ Dialog dialog;
+ dialog.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/CMakeLists.txt b/tests/manual/examples/widgets/mainwindows/dockwidgets/CMakeLists.txt
new file mode 100644
index 0000000000..fd8444ae65
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/CMakeLists.txt
@@ -0,0 +1,59 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(dockwidgets LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/mainwindows/dockwidgets")
+
+find_package(Qt6
+ REQUIRED COMPONENTS Core Gui Widgets
+ OPTIONAL_COMPONENTS PrintSupport
+)
+
+qt_standard_project_setup()
+
+qt_add_executable(dockwidgets
+ main.cpp
+ mainwindow.cpp mainwindow.h
+)
+
+set_target_properties(dockwidgets PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(dockwidgets PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+if (TARGET Qt6::PrintSupport)
+ target_link_libraries(dockwidgets PRIVATE Qt6::PrintSupport)
+endif()
+
+# Resources:
+set(dockwidgets_resource_files
+ "images/new.png"
+ "images/print.png"
+ "images/save.png"
+ "images/undo.png"
+)
+
+qt_add_resources(dockwidgets "dockwidgets"
+ PREFIX
+ "/"
+ FILES
+ ${dockwidgets_resource_files}
+)
+
+install(TARGETS dockwidgets
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/dockwidgets.pro b/tests/manual/examples/widgets/mainwindows/dockwidgets/dockwidgets.pro
new file mode 100644
index 0000000000..3acded5ed3
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/dockwidgets.pro
@@ -0,0 +1,12 @@
+QT += widgets
+requires(qtConfig(listwidget))
+qtHaveModule(printsupport): QT += printsupport
+
+HEADERS = mainwindow.h
+SOURCES = main.cpp \
+ mainwindow.cpp
+RESOURCES = dockwidgets.qrc
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/mainwindows/dockwidgets
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/dockwidgets.qrc b/tests/manual/examples/widgets/mainwindows/dockwidgets/dockwidgets.qrc
new file mode 100644
index 0000000000..968feac7ea
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/dockwidgets.qrc
@@ -0,0 +1,8 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>images/new.png</file>
+ <file>images/print.png</file>
+ <file>images/save.png</file>
+ <file>images/undo.png</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/images/new.png b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/new.png
new file mode 100644
index 0000000000..dd795cfffc
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/new.png
Binary files differ
diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/images/print.png b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/print.png
new file mode 100644
index 0000000000..2afb769ee2
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/print.png
Binary files differ
diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/images/save.png b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/save.png
new file mode 100644
index 0000000000..46eac82ad1
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/save.png
Binary files differ
diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/images/undo.png b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/undo.png
new file mode 100644
index 0000000000..eee23d24a3
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/images/undo.png
Binary files differ
diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/main.cpp b/tests/manual/examples/widgets/mainwindows/dockwidgets/main.cpp
new file mode 100644
index 0000000000..431d7dae98
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/main.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ MainWindow mainWin;
+ mainWin.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/mainwindow.cpp b/tests/manual/examples/widgets/mainwindows/dockwidgets/mainwindow.cpp
new file mode 100644
index 0000000000..3493a66a67
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/mainwindow.cpp
@@ -0,0 +1,298 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [0]
+#include <QtWidgets>
+#if defined(QT_PRINTSUPPORT_LIB)
+#include <QtPrintSupport/qtprintsupportglobal.h>
+#if QT_CONFIG(printdialog)
+#include <QtPrintSupport>
+#endif
+#endif
+
+#include "mainwindow.h"
+//! [0]
+
+//! [1]
+MainWindow::MainWindow()
+ : textEdit(new QTextEdit)
+{
+ setCentralWidget(textEdit);
+
+ createActions();
+ createStatusBar();
+ createDockWindows();
+
+ setWindowTitle(tr("Dock Widgets"));
+
+ newLetter();
+ setUnifiedTitleAndToolBarOnMac(true);
+}
+//! [1]
+
+//! [2]
+void MainWindow::newLetter()
+{
+ textEdit->clear();
+
+ QTextCursor cursor(textEdit->textCursor());
+ cursor.movePosition(QTextCursor::Start);
+ QTextFrame *topFrame = cursor.currentFrame();
+ QTextFrameFormat topFrameFormat = topFrame->frameFormat();
+ topFrameFormat.setPadding(16);
+ topFrame->setFrameFormat(topFrameFormat);
+
+ QTextCharFormat textFormat;
+ QTextCharFormat boldFormat;
+ boldFormat.setFontWeight(QFont::Bold);
+ QTextCharFormat italicFormat;
+ italicFormat.setFontItalic(true);
+
+ QTextTableFormat tableFormat;
+ tableFormat.setBorder(1);
+ tableFormat.setCellPadding(16);
+ tableFormat.setAlignment(Qt::AlignRight);
+ cursor.insertTable(1, 1, tableFormat);
+ cursor.insertText("The Firm", boldFormat);
+ cursor.insertBlock();
+ cursor.insertText("321 City Street", textFormat);
+ cursor.insertBlock();
+ cursor.insertText("Industry Park");
+ cursor.insertBlock();
+ cursor.insertText("Some Country");
+ cursor.setPosition(topFrame->lastPosition());
+ cursor.insertText(QDate::currentDate().toString("d MMMM yyyy"), textFormat);
+ cursor.insertBlock();
+ cursor.insertBlock();
+ cursor.insertText("Dear ", textFormat);
+ cursor.insertText("NAME", italicFormat);
+ cursor.insertText(",", textFormat);
+ for (int i = 0; i < 3; ++i)
+ cursor.insertBlock();
+ cursor.insertText(tr("Yours sincerely,"), textFormat);
+ for (int i = 0; i < 3; ++i)
+ cursor.insertBlock();
+ cursor.insertText("The Boss", textFormat);
+ cursor.insertBlock();
+ cursor.insertText("ADDRESS", italicFormat);
+}
+//! [2]
+
+//! [3]
+void MainWindow::print()
+{
+#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog)
+ QTextDocument *document = textEdit->document();
+ QPrinter printer;
+
+ QPrintDialog dlg(&printer, this);
+ if (dlg.exec() != QDialog::Accepted) {
+ return;
+ }
+
+ document->print(&printer);
+ statusBar()->showMessage(tr("Ready"), 2000);
+#endif
+}
+//! [3]
+
+//! [4]
+void MainWindow::save()
+{
+ QMimeDatabase mimeDatabase;
+ QString fileName = QFileDialog::getSaveFileName(this,
+ tr("Choose a file name"), ".",
+ mimeDatabase.mimeTypeForName("text/html").filterString());
+ if (fileName.isEmpty())
+ return;
+ QFile file(fileName);
+ if (!file.open(QFile::WriteOnly | QFile::Text)) {
+ QMessageBox::warning(this, tr("Dock Widgets"),
+ tr("Cannot write file %1:\n%2.")
+ .arg(QDir::toNativeSeparators(fileName), file.errorString()));
+ return;
+ }
+
+ QTextStream out(&file);
+ QGuiApplication::setOverrideCursor(Qt::WaitCursor);
+ out << textEdit->toHtml();
+ QGuiApplication::restoreOverrideCursor();
+
+ statusBar()->showMessage(tr("Saved '%1'").arg(fileName), 2000);
+}
+//! [4]
+
+//! [5]
+void MainWindow::undo()
+{
+ QTextDocument *document = textEdit->document();
+ document->undo();
+}
+//! [5]
+
+//! [6]
+void MainWindow::insertCustomer(const QString &customer)
+{
+ if (customer.isEmpty())
+ return;
+ QStringList customerList = customer.split(", ");
+ QTextDocument *document = textEdit->document();
+ QTextCursor cursor = document->find("NAME");
+ if (!cursor.isNull()) {
+ cursor.beginEditBlock();
+ cursor.insertText(customerList.at(0));
+ QTextCursor oldcursor = cursor;
+ cursor = document->find("ADDRESS");
+ if (!cursor.isNull()) {
+ for (int i = 1; i < customerList.size(); ++i) {
+ cursor.insertBlock();
+ cursor.insertText(customerList.at(i));
+ }
+ cursor.endEditBlock();
+ }
+ else
+ oldcursor.endEditBlock();
+ }
+}
+//! [6]
+
+//! [7]
+void MainWindow::addParagraph(const QString &paragraph)
+{
+ if (paragraph.isEmpty())
+ return;
+ QTextDocument *document = textEdit->document();
+ QTextCursor cursor = document->find(tr("Yours sincerely,"));
+ if (cursor.isNull())
+ return;
+ cursor.beginEditBlock();
+ cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor, 2);
+ cursor.insertBlock();
+ cursor.insertText(paragraph);
+ cursor.insertBlock();
+ cursor.endEditBlock();
+
+}
+//! [7]
+
+void MainWindow::about()
+{
+ QMessageBox::about(this, tr("About Dock Widgets"),
+ tr("The <b>Dock Widgets</b> example demonstrates how to "
+ "use Qt's dock widgets. You can enter your own text, "
+ "click a customer to add a customer name and "
+ "address, and click standard paragraphs to add them."));
+}
+
+void MainWindow::createActions()
+{
+ QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
+ QToolBar *fileToolBar = addToolBar(tr("File"));
+
+ const QIcon newIcon = QIcon::fromTheme("document-new", QIcon(":/images/new.png"));
+ QAction *newLetterAct = new QAction(newIcon, tr("&New Letter"), this);
+ newLetterAct->setShortcuts(QKeySequence::New);
+ newLetterAct->setStatusTip(tr("Create a new form letter"));
+ connect(newLetterAct, &QAction::triggered, this, &MainWindow::newLetter);
+ fileMenu->addAction(newLetterAct);
+ fileToolBar->addAction(newLetterAct);
+
+ const QIcon saveIcon = QIcon::fromTheme("document-save", QIcon(":/images/save.png"));
+ QAction *saveAct = new QAction(saveIcon, tr("&Save..."), this);
+ saveAct->setShortcuts(QKeySequence::Save);
+ saveAct->setStatusTip(tr("Save the current form letter"));
+ connect(saveAct, &QAction::triggered, this, &MainWindow::save);
+ fileMenu->addAction(saveAct);
+ fileToolBar->addAction(saveAct);
+
+ const QIcon printIcon = QIcon::fromTheme("document-print", QIcon(":/images/print.png"));
+ QAction *printAct = new QAction(printIcon, tr("&Print..."), this);
+ printAct->setShortcuts(QKeySequence::Print);
+ printAct->setStatusTip(tr("Print the current form letter"));
+ connect(printAct, &QAction::triggered, this, &MainWindow::print);
+ fileMenu->addAction(printAct);
+ fileToolBar->addAction(printAct);
+
+ fileMenu->addSeparator();
+
+ QAction *quitAct = fileMenu->addAction(tr("&Quit"), qApp, &QCoreApplication::quit);
+ quitAct->setShortcuts(QKeySequence::Quit);
+ quitAct->setStatusTip(tr("Quit the application"));
+
+ QMenu *editMenu = menuBar()->addMenu(tr("&Edit"));
+ QToolBar *editToolBar = addToolBar(tr("Edit"));
+ const QIcon undoIcon = QIcon::fromTheme("edit-undo", QIcon(":/images/undo.png"));
+ QAction *undoAct = new QAction(undoIcon, tr("&Undo"), this);
+ undoAct->setShortcuts(QKeySequence::Undo);
+ undoAct->setStatusTip(tr("Undo the last editing action"));
+ connect(undoAct, &QAction::triggered, this, &MainWindow::undo);
+ editMenu->addAction(undoAct);
+ editToolBar->addAction(undoAct);
+
+ viewMenu = menuBar()->addMenu(tr("&View"));
+
+ menuBar()->addSeparator();
+
+ QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
+
+ QAction *aboutAct = helpMenu->addAction(tr("&About"), this, &MainWindow::about);
+ aboutAct->setStatusTip(tr("Show the application's About box"));
+
+ QAction *aboutQtAct = helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
+ aboutQtAct->setStatusTip(tr("Show the Qt library's About box"));
+}
+
+//! [8]
+void MainWindow::createStatusBar()
+{
+ statusBar()->showMessage(tr("Ready"));
+}
+//! [8]
+
+//! [9]
+void MainWindow::createDockWindows()
+{
+ QDockWidget *dock = new QDockWidget(tr("Customers"), this);
+ dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+ customerList = new QListWidget(dock);
+ customerList->addItems(QStringList()
+ << "John Doe, Harmony Enterprises, 12 Lakeside, Ambleton"
+ << "Jane Doe, Memorabilia, 23 Watersedge, Beaton"
+ << "Tammy Shea, Tiblanka, 38 Sea Views, Carlton"
+ << "Tim Sheen, Caraba Gifts, 48 Ocean Way, Deal"
+ << "Sol Harvey, Chicos Coffee, 53 New Springs, Eccleston"
+ << "Sally Hobart, Tiroli Tea, 67 Long River, Fedula");
+ dock->setWidget(customerList);
+ addDockWidget(Qt::RightDockWidgetArea, dock);
+ viewMenu->addAction(dock->toggleViewAction());
+
+ dock = new QDockWidget(tr("Paragraphs"), this);
+ paragraphsList = new QListWidget(dock);
+ paragraphsList->addItems(QStringList()
+ << "Thank you for your payment which we have received today."
+ << "Your order has been dispatched and should be with you "
+ "within 28 days."
+ << "We have dispatched those items that were in stock. The "
+ "rest of your order will be dispatched once all the "
+ "remaining items have arrived at our warehouse. No "
+ "additional shipping charges will be made."
+ << "You made a small overpayment (less than $5) which we "
+ "will keep on account for you, or return at your request."
+ << "You made a small underpayment (less than $1), but we have "
+ "sent your order anyway. We'll add this underpayment to "
+ "your next bill."
+ << "Unfortunately you did not send enough money. Please remit "
+ "an additional $. Your order will be dispatched as soon as "
+ "the complete amount has been received."
+ << "You made an overpayment (more than $5). Do you wish to "
+ "buy more items, or should we return the excess to you?");
+ dock->setWidget(paragraphsList);
+ addDockWidget(Qt::RightDockWidgetArea, dock);
+ viewMenu->addAction(dock->toggleViewAction());
+
+ connect(customerList, &QListWidget::currentTextChanged,
+ this, &MainWindow::insertCustomer);
+ connect(paragraphsList, &QListWidget::currentTextChanged,
+ this, &MainWindow::addParagraph);
+}
+//! [9]
diff --git a/tests/manual/examples/widgets/mainwindows/dockwidgets/mainwindow.h b/tests/manual/examples/widgets/mainwindows/dockwidgets/mainwindow.h
new file mode 100644
index 0000000000..67890e8a61
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/dockwidgets/mainwindow.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+
+QT_BEGIN_NAMESPACE
+class QAction;
+class QListWidget;
+class QMenu;
+class QTextEdit;
+QT_END_NAMESPACE
+
+//! [0]
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow();
+
+private slots:
+ void newLetter();
+ void save();
+ void print();
+ void undo();
+ void about();
+ void insertCustomer(const QString &customer);
+ void addParagraph(const QString &paragraph);
+
+private:
+ void createActions();
+ void createStatusBar();
+ void createDockWindows();
+
+ QTextEdit *textEdit;
+ QListWidget *customerList;
+ QListWidget *paragraphsList;
+
+ QMenu *viewMenu;
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/CMakeLists.txt b/tests/manual/examples/widgets/mainwindows/mainwindow/CMakeLists.txt
new file mode 100644
index 0000000000..bab95a9145
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mainwindow/CMakeLists.txt
@@ -0,0 +1,54 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(mainwindow LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/mainwindows/mainwindow")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(mainwindow
+ colorswatch.cpp colorswatch.h
+ main.cpp
+ mainwindow.cpp mainwindow.h
+ toolbar.cpp toolbar.h
+)
+
+set_target_properties(mainwindow PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(mainwindow PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(mainwindow_resource_files
+ "qt.png"
+ "titlebarCenter.png"
+ "titlebarLeft.png"
+ "titlebarRight.png"
+)
+
+qt_add_resources(mainwindow "mainwindow"
+ PREFIX
+ "/res"
+ FILES
+ ${mainwindow_resource_files}
+)
+
+install(TARGETS mainwindow
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/colorswatch.cpp b/tests/manual/examples/widgets/mainwindows/mainwindow/colorswatch.cpp
new file mode 100644
index 0000000000..678f00054c
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mainwindow/colorswatch.cpp
@@ -0,0 +1,685 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "colorswatch.h"
+
+#include <QActionGroup>
+#include <QtEvents>
+#include <QFrame>
+#include <QMainWindow>
+#include <QMenu>
+#include <QPainter>
+#include <QImage>
+#include <QColor>
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QGridLayout>
+#include <QSignalBlocker>
+#include <QSpinBox>
+#include <QLabel>
+#include <QPainterPath>
+#include <QPushButton>
+#include <QHBoxLayout>
+#include <QBitmap>
+#include <QtDebug>
+
+#undef DEBUG_SIZEHINTS
+
+QColor bgColorForName(const QString &name)
+{
+ if (name == "Black")
+ return QColor("#D8D8D8");
+ if (name == "White")
+ return QColor("#F1F1F1");
+ if (name == "Red")
+ return QColor("#F1D8D8");
+ if (name == "Green")
+ return QColor("#D8E4D8");
+ if (name == "Blue")
+ return QColor("#D8D8F1");
+ if (name == "Yellow")
+ return QColor("#F1F0D8");
+ return QColor(name).lighter(110);
+}
+
+QColor fgColorForName(const QString &name)
+{
+ if (name == "Black")
+ return QColor("#6C6C6C");
+ if (name == "White")
+ return QColor("#F8F8F8");
+ if (name == "Red")
+ return QColor("#F86C6C");
+ if (name == "Green")
+ return QColor("#6CB26C");
+ if (name == "Blue")
+ return QColor("#6C6CF8");
+ if (name == "Yellow")
+ return QColor("#F8F76C");
+ return QColor(name);
+}
+
+class ColorDock : public QFrame
+{
+ Q_OBJECT
+public:
+ explicit ColorDock(const QString &c, QWidget *parent);
+
+ QSize sizeHint() const override { return szHint; }
+ QSize minimumSizeHint() const override { return minSzHint; }
+
+ void setCustomSizeHint(const QSize &size);
+
+public slots:
+ void changeSizeHints();
+
+protected:
+ void paintEvent(QPaintEvent *) override;
+
+private:
+ const QString color;
+ QSize szHint;
+ QSize minSzHint;
+};
+
+ColorDock::ColorDock(const QString &c, QWidget *parent)
+ : QFrame(parent)
+ , color(c)
+ , szHint(-1, -1)
+ , minSzHint(125, 75)
+{
+ QFont font = this->font();
+ font.setPointSize(8);
+ setFont(font);
+}
+
+void ColorDock::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ p.setRenderHint(QPainter::Antialiasing);
+ p.fillRect(rect(), bgColorForName(color));
+
+ p.save();
+
+ extern void render_qt_text(QPainter *, int, int, const QColor &);
+ render_qt_text(&p, width(), height(), fgColorForName(color));
+
+ p.restore();
+
+#ifdef DEBUG_SIZEHINTS
+ p.setRenderHint(QPainter::Antialiasing, false);
+
+ QSize sz = size();
+ QSize szHint = sizeHint();
+ QSize minSzHint = minimumSizeHint();
+ QSize maxSz = maximumSize();
+ QString text = QString::fromLatin1("sz: %1x%2\nszHint: %3x%4\nminSzHint: %5x%6\n"
+ "maxSz: %8x%9")
+ .arg(sz.width()).arg(sz.height())
+ .arg(szHint.width()).arg(szHint.height())
+ .arg(minSzHint.width()).arg(minSzHint.height())
+ .arg(maxSz.width()).arg(maxSz.height());
+
+ QRect r = fontMetrics().boundingRect(rect(), Qt::AlignLeft|Qt::AlignTop, text);
+ r.adjust(-2, -2, 1, 1);
+ p.translate(4, 4);
+ QColor bg = Qt::yellow;
+ bg.setAlpha(120);
+ p.setBrush(bg);
+ p.setPen(Qt::black);
+ p.drawRect(r);
+ p.drawText(rect(), Qt::AlignLeft|Qt::AlignTop, text);
+#endif // DEBUG_SIZEHINTS
+}
+
+static QSpinBox *createSpinBox(int value, QWidget *parent, int max = 1000)
+{
+ QSpinBox *result = new QSpinBox(parent);
+ result->setMinimum(-1);
+ result->setMaximum(max);
+ result->setValue(value);
+ return result;
+}
+
+void ColorDock::changeSizeHints()
+{
+ QDialog dialog(this);
+ dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ dialog.setWindowTitle(color);
+
+ QVBoxLayout *topLayout = new QVBoxLayout(&dialog);
+
+ QGridLayout *inputLayout = new QGridLayout();
+ topLayout->addLayout(inputLayout);
+
+ inputLayout->addWidget(new QLabel(tr("Size Hint:"), &dialog), 0, 0);
+ inputLayout->addWidget(new QLabel(tr("Min Size Hint:"), &dialog), 1, 0);
+ inputLayout->addWidget(new QLabel(tr("Max Size:"), &dialog), 2, 0);
+ inputLayout->addWidget(new QLabel(tr("Dock Widget Max Size:"), &dialog), 3, 0);
+
+ QSpinBox *szHintW = createSpinBox(szHint.width(), &dialog);
+ inputLayout->addWidget(szHintW, 0, 1);
+ QSpinBox *szHintH = createSpinBox(szHint.height(), &dialog);
+ inputLayout->addWidget(szHintH, 0, 2);
+
+ QSpinBox *minSzHintW = createSpinBox(minSzHint.width(), &dialog);
+ inputLayout->addWidget(minSzHintW, 1, 1);
+ QSpinBox *minSzHintH = createSpinBox(minSzHint.height(), &dialog);
+ inputLayout->addWidget(minSzHintH, 1, 2);
+
+ QSize maxSz = maximumSize();
+ QSpinBox *maxSzW = createSpinBox(maxSz.width(), &dialog, QWIDGETSIZE_MAX);
+ inputLayout->addWidget(maxSzW, 2, 1);
+ QSpinBox *maxSzH = createSpinBox(maxSz.height(), &dialog, QWIDGETSIZE_MAX);
+ inputLayout->addWidget(maxSzH, 2, 2);
+
+ QSize dwMaxSz = parentWidget()->maximumSize();
+ QSpinBox *dwMaxSzW = createSpinBox(dwMaxSz.width(), &dialog, QWIDGETSIZE_MAX);
+ inputLayout->addWidget(dwMaxSzW, 3, 1);
+ QSpinBox *dwMaxSzH = createSpinBox(dwMaxSz.height(), &dialog, QWIDGETSIZE_MAX);
+ inputLayout->addWidget(dwMaxSzH, 3, 2);
+
+ inputLayout->setColumnStretch(1, 1);
+ inputLayout->setColumnStretch(2, 1);
+
+ topLayout->addStretch();
+
+ QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
+ connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
+ connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
+
+ topLayout->addWidget(buttonBox);
+
+ if (dialog.exec() != QDialog::Accepted)
+ return;
+
+ szHint = QSize(szHintW->value(), szHintH->value());
+ minSzHint = QSize(minSzHintW->value(), minSzHintH->value());
+ maxSz = QSize(maxSzW->value(), maxSzH->value());
+ setMaximumSize(maxSz);
+ dwMaxSz = QSize(dwMaxSzW->value(), dwMaxSzH->value());
+ parentWidget()->setMaximumSize(dwMaxSz);
+ updateGeometry();
+ update();
+}
+
+void ColorDock::setCustomSizeHint(const QSize &size)
+{
+ if (szHint != size) {
+ szHint = size;
+ updateGeometry();
+ }
+}
+
+ColorSwatch::ColorSwatch(const QString &colorName, QMainWindow *parent, Qt::WindowFlags flags)
+ : QDockWidget(parent, flags), mainWindow(parent)
+{
+ setObjectName(colorName + QLatin1String(" Dock Widget"));
+ setWindowTitle(objectName() + QLatin1String(" [*]"));
+
+ ColorDock *swatch = new ColorDock(colorName, this);
+ swatch->setFrameStyle(QFrame::Box | QFrame::Sunken);
+
+ setWidget(swatch);
+
+ closableAction = new QAction(tr("Closable"), this);
+ closableAction->setCheckable(true);
+ connect(closableAction, &QAction::triggered, this, &ColorSwatch::changeClosable);
+
+ movableAction = new QAction(tr("Movable"), this);
+ movableAction->setCheckable(true);
+ connect(movableAction, &QAction::triggered, this, &ColorSwatch::changeMovable);
+
+ floatableAction = new QAction(tr("Floatable"), this);
+ floatableAction->setCheckable(true);
+ connect(floatableAction, &QAction::triggered, this, &ColorSwatch::changeFloatable);
+
+ verticalTitleBarAction = new QAction(tr("Vertical title bar"), this);
+ verticalTitleBarAction->setCheckable(true);
+ connect(verticalTitleBarAction, &QAction::triggered,
+ this, &ColorSwatch::changeVerticalTitleBar);
+
+ floatingAction = new QAction(tr("Floating"), this);
+ floatingAction->setCheckable(true);
+ connect(floatingAction, &QAction::triggered, this, &ColorSwatch::changeFloating);
+
+ allowedAreasActions = new QActionGroup(this);
+ allowedAreasActions->setExclusive(false);
+
+ allowLeftAction = new QAction(tr("Allow on Left"), this);
+ allowLeftAction->setCheckable(true);
+ connect(allowLeftAction, &QAction::triggered, this, &ColorSwatch::allowLeft);
+
+ allowRightAction = new QAction(tr("Allow on Right"), this);
+ allowRightAction->setCheckable(true);
+ connect(allowRightAction, &QAction::triggered, this, &ColorSwatch::allowRight);
+
+ allowTopAction = new QAction(tr("Allow on Top"), this);
+ allowTopAction->setCheckable(true);
+ connect(allowTopAction, &QAction::triggered, this, &ColorSwatch::allowTop);
+
+ allowBottomAction = new QAction(tr("Allow on Bottom"), this);
+ allowBottomAction->setCheckable(true);
+ connect(allowBottomAction, &QAction::triggered, this, &ColorSwatch::allowBottom);
+
+ allowedAreasActions->addAction(allowLeftAction);
+ allowedAreasActions->addAction(allowRightAction);
+ allowedAreasActions->addAction(allowTopAction);
+ allowedAreasActions->addAction(allowBottomAction);
+
+ areaActions = new QActionGroup(this);
+ areaActions->setExclusive(true);
+
+ leftAction = new QAction(tr("Place on Left") , this);
+ leftAction->setCheckable(true);
+ connect(leftAction, &QAction::triggered, this, &ColorSwatch::placeLeft);
+
+ rightAction = new QAction(tr("Place on Right") , this);
+ rightAction->setCheckable(true);
+ connect(rightAction, &QAction::triggered, this, &ColorSwatch::placeRight);
+
+ topAction = new QAction(tr("Place on Top") , this);
+ topAction->setCheckable(true);
+ connect(topAction, &QAction::triggered, this, &ColorSwatch::placeTop);
+
+ bottomAction = new QAction(tr("Place on Bottom") , this);
+ bottomAction->setCheckable(true);
+ connect(bottomAction, &QAction::triggered, this, &ColorSwatch::placeBottom);
+
+ areaActions->addAction(leftAction);
+ areaActions->addAction(rightAction);
+ areaActions->addAction(topAction);
+ areaActions->addAction(bottomAction);
+
+ connect(movableAction, &QAction::triggered, areaActions, &QActionGroup::setEnabled);
+
+ connect(movableAction, &QAction::triggered, allowedAreasActions, &QActionGroup::setEnabled);
+
+ connect(floatableAction, &QAction::triggered, floatingAction, &QAction::setEnabled);
+
+ connect(floatingAction, &QAction::triggered, floatableAction, &QAction::setDisabled);
+ connect(movableAction, &QAction::triggered, floatableAction, &QAction::setEnabled);
+
+ tabMenu = new QMenu(this);
+ tabMenu->setTitle(tr("Tab into"));
+ connect(tabMenu, &QMenu::triggered, this, &ColorSwatch::tabInto);
+
+ splitHMenu = new QMenu(this);
+ splitHMenu->setTitle(tr("Split horizontally into"));
+ connect(splitHMenu, &QMenu::triggered, this, &ColorSwatch::splitInto);
+
+ splitVMenu = new QMenu(this);
+ splitVMenu->setTitle(tr("Split vertically into"));
+ connect(splitVMenu, &QMenu::triggered, this, &ColorSwatch::splitInto);
+
+ QAction *windowModifiedAction = new QAction(tr("Modified"), this);
+ windowModifiedAction->setCheckable(true);
+ windowModifiedAction->setChecked(false);
+ connect(windowModifiedAction, &QAction::toggled, this, &QWidget::setWindowModified);
+
+ menu = new QMenu(colorName, this);
+ menu->addAction(toggleViewAction());
+ menu->addAction(tr("Raise"), this, &QWidget::raise);
+ menu->addAction(tr("Change Size Hints..."), swatch, &ColorDock::changeSizeHints);
+
+ menu->addSeparator();
+ menu->addAction(closableAction);
+ menu->addAction(movableAction);
+ menu->addAction(floatableAction);
+ menu->addAction(floatingAction);
+ menu->addAction(verticalTitleBarAction);
+ menu->addSeparator();
+ menu->addActions(allowedAreasActions->actions());
+ menu->addSeparator();
+ menu->addActions(areaActions->actions());
+ menu->addSeparator();
+ menu->addMenu(splitHMenu);
+ menu->addMenu(splitVMenu);
+ menu->addMenu(tabMenu);
+ menu->addSeparator();
+ menu->addAction(windowModifiedAction);
+
+ connect(menu, &QMenu::aboutToShow, this, &ColorSwatch::updateContextMenu);
+
+ if (colorName == QLatin1String("Black")) {
+ leftAction->setShortcut(Qt::CTRL | Qt::Key_W);
+ rightAction->setShortcut(Qt::CTRL | Qt::Key_E);
+ toggleViewAction()->setShortcut(Qt::CTRL | Qt::Key_R);
+ }
+}
+
+void ColorSwatch::updateContextMenu()
+{
+ const Qt::DockWidgetArea area = mainWindow->dockWidgetArea(this);
+ const Qt::DockWidgetAreas areas = allowedAreas();
+
+ closableAction->setChecked(features() & QDockWidget::DockWidgetClosable);
+ if (windowType() == Qt::Drawer) {
+ floatableAction->setEnabled(false);
+ floatingAction->setEnabled(false);
+ movableAction->setEnabled(false);
+ verticalTitleBarAction->setChecked(false);
+ } else {
+ floatableAction->setChecked(features() & QDockWidget::DockWidgetFloatable);
+ floatingAction->setChecked(isWindow());
+ // done after floating, to get 'floatable' correctly initialized
+ movableAction->setChecked(features() & QDockWidget::DockWidgetMovable);
+ verticalTitleBarAction
+ ->setChecked(features() & QDockWidget::DockWidgetVerticalTitleBar);
+ }
+
+ allowLeftAction->setChecked(isAreaAllowed(Qt::LeftDockWidgetArea));
+ allowRightAction->setChecked(isAreaAllowed(Qt::RightDockWidgetArea));
+ allowTopAction->setChecked(isAreaAllowed(Qt::TopDockWidgetArea));
+ allowBottomAction->setChecked(isAreaAllowed(Qt::BottomDockWidgetArea));
+
+ if (allowedAreasActions->isEnabled()) {
+ allowLeftAction->setEnabled(area != Qt::LeftDockWidgetArea);
+ allowRightAction->setEnabled(area != Qt::RightDockWidgetArea);
+ allowTopAction->setEnabled(area != Qt::TopDockWidgetArea);
+ allowBottomAction->setEnabled(area != Qt::BottomDockWidgetArea);
+ }
+
+ {
+ const QSignalBlocker blocker(leftAction);
+ leftAction->setChecked(area == Qt::LeftDockWidgetArea);
+ }
+ {
+ const QSignalBlocker blocker(rightAction);
+ rightAction->setChecked(area == Qt::RightDockWidgetArea);
+ }
+ {
+ const QSignalBlocker blocker(topAction);
+ topAction->setChecked(area == Qt::TopDockWidgetArea);
+ }
+ {
+ const QSignalBlocker blocker(bottomAction);
+ bottomAction->setChecked(area == Qt::BottomDockWidgetArea);
+ }
+
+ if (areaActions->isEnabled()) {
+ leftAction->setEnabled(areas & Qt::LeftDockWidgetArea);
+ rightAction->setEnabled(areas & Qt::RightDockWidgetArea);
+ topAction->setEnabled(areas & Qt::TopDockWidgetArea);
+ bottomAction->setEnabled(areas & Qt::BottomDockWidgetArea);
+ }
+
+ tabMenu->clear();
+ splitHMenu->clear();
+ splitVMenu->clear();
+ const QList<ColorSwatch *> dockList = mainWindow->findChildren<ColorSwatch*>();
+ for (const ColorSwatch *dock : dockList) {
+ tabMenu->addAction(dock->objectName());
+ splitHMenu->addAction(dock->objectName());
+ splitVMenu->addAction(dock->objectName());
+ }
+}
+
+static ColorSwatch *findByName(const QMainWindow *mainWindow, const QString &name)
+{
+ const QList<ColorSwatch *> dockList = mainWindow->findChildren<ColorSwatch*>();
+ for (ColorSwatch *dock : dockList) {
+ if (name == dock->objectName())
+ return dock;
+ }
+ return nullptr;
+}
+
+void ColorSwatch::splitInto(QAction *action)
+{
+ ColorSwatch *target = findByName(mainWindow, action->text());
+ if (!target)
+ return;
+
+ const Qt::Orientation o = action->parent() == splitHMenu
+ ? Qt::Horizontal : Qt::Vertical;
+ mainWindow->splitDockWidget(target, this, o);
+}
+
+void ColorSwatch::tabInto(QAction *action)
+{
+ if (ColorSwatch *target = findByName(mainWindow, action->text()))
+ mainWindow->tabifyDockWidget(target, this);
+}
+
+#ifndef QT_NO_CONTEXTMENU
+void ColorSwatch::contextMenuEvent(QContextMenuEvent *event)
+{
+ event->accept();
+ menu->popup(event->globalPos());
+}
+#endif // QT_NO_CONTEXTMENU
+
+void ColorSwatch::resizeEvent(QResizeEvent *e)
+{
+ if (BlueTitleBar *btb = qobject_cast<BlueTitleBar*>(titleBarWidget()))
+ btb->updateMask();
+
+ QDockWidget::resizeEvent(e);
+}
+
+void ColorSwatch::allow(Qt::DockWidgetArea area, bool a)
+{
+ Qt::DockWidgetAreas areas = allowedAreas();
+ areas = a ? areas | area : areas & ~area;
+ setAllowedAreas(areas);
+
+ if (areaActions->isEnabled()) {
+ leftAction->setEnabled(areas & Qt::LeftDockWidgetArea);
+ rightAction->setEnabled(areas & Qt::RightDockWidgetArea);
+ topAction->setEnabled(areas & Qt::TopDockWidgetArea);
+ bottomAction->setEnabled(areas & Qt::BottomDockWidgetArea);
+ }
+}
+
+void ColorSwatch::place(Qt::DockWidgetArea area, bool p)
+{
+ if (!p)
+ return;
+
+ mainWindow->addDockWidget(area, this);
+
+ if (allowedAreasActions->isEnabled()) {
+ allowLeftAction->setEnabled(area != Qt::LeftDockWidgetArea);
+ allowRightAction->setEnabled(area != Qt::RightDockWidgetArea);
+ allowTopAction->setEnabled(area != Qt::TopDockWidgetArea);
+ allowBottomAction->setEnabled(area != Qt::BottomDockWidgetArea);
+ }
+}
+
+void ColorSwatch::setCustomSizeHint(const QSize &size)
+{
+ if (ColorDock *dock = qobject_cast<ColorDock*>(widget()))
+ dock->setCustomSizeHint(size);
+}
+
+void ColorSwatch::changeClosable(bool on)
+{ setFeatures(on ? features() | DockWidgetClosable : features() & ~DockWidgetClosable); }
+
+void ColorSwatch::changeMovable(bool on)
+{ setFeatures(on ? features() | DockWidgetMovable : features() & ~DockWidgetMovable); }
+
+void ColorSwatch::changeFloatable(bool on)
+{ setFeatures(on ? features() | DockWidgetFloatable : features() & ~DockWidgetFloatable); }
+
+void ColorSwatch::changeFloating(bool floating)
+{ setFloating(floating); }
+
+void ColorSwatch::allowLeft(bool a)
+{ allow(Qt::LeftDockWidgetArea, a); }
+
+void ColorSwatch::allowRight(bool a)
+{ allow(Qt::RightDockWidgetArea, a); }
+
+void ColorSwatch::allowTop(bool a)
+{ allow(Qt::TopDockWidgetArea, a); }
+
+void ColorSwatch::allowBottom(bool a)
+{ allow(Qt::BottomDockWidgetArea, a); }
+
+void ColorSwatch::placeLeft(bool p)
+{ place(Qt::LeftDockWidgetArea, p); }
+
+void ColorSwatch::placeRight(bool p)
+{ place(Qt::RightDockWidgetArea, p); }
+
+void ColorSwatch::placeTop(bool p)
+{ place(Qt::TopDockWidgetArea, p); }
+
+void ColorSwatch::placeBottom(bool p)
+{ place(Qt::BottomDockWidgetArea, p); }
+
+void ColorSwatch::changeVerticalTitleBar(bool on)
+{
+ setFeatures(on ? features() | DockWidgetVerticalTitleBar
+ : features() & ~DockWidgetVerticalTitleBar);
+}
+
+QSize BlueTitleBar::minimumSizeHint() const
+{
+ QDockWidget *dw = qobject_cast<QDockWidget*>(parentWidget());
+ Q_ASSERT(dw);
+ QSize result(leftPm.width() + rightPm.width(), centerPm.height());
+ if (dw->features() & QDockWidget::DockWidgetVerticalTitleBar)
+ result.transpose();
+ return result;
+}
+
+BlueTitleBar::BlueTitleBar(QWidget *parent)
+ : QWidget(parent)
+ , leftPm(QPixmap(":/res/titlebarLeft.png"))
+ , centerPm(QPixmap(":/res/titlebarCenter.png"))
+ , rightPm(QPixmap(":/res/titlebarRight.png"))
+{
+}
+
+void BlueTitleBar::paintEvent(QPaintEvent*)
+{
+ QPainter painter(this);
+ QRect rect = this->rect();
+
+ QDockWidget *dw = qobject_cast<QDockWidget*>(parentWidget());
+ Q_ASSERT(dw);
+
+ if (dw->features() & QDockWidget::DockWidgetVerticalTitleBar) {
+ QSize s = rect.size();
+ s.transpose();
+ rect.setSize(s);
+
+ painter.translate(rect.left(), rect.top() + rect.width());
+ painter.rotate(-90);
+ painter.translate(-rect.left(), -rect.top());
+ }
+
+ painter.drawPixmap(rect.topLeft(), leftPm);
+ painter.drawPixmap(rect.topRight() - QPoint(rightPm.width() - 1, 0), rightPm);
+ QBrush brush(centerPm);
+ painter.fillRect(rect.left() + leftPm.width(), rect.top(),
+ rect.width() - leftPm.width() - rightPm.width(),
+ centerPm.height(), centerPm);
+}
+
+void BlueTitleBar::mouseReleaseEvent(QMouseEvent *event)
+{
+ QPoint pos = event->position().toPoint();
+
+ QRect rect = this->rect();
+
+ QDockWidget *dw = qobject_cast<QDockWidget*>(parentWidget());
+ Q_ASSERT(dw);
+
+ if (dw->features() & QDockWidget::DockWidgetVerticalTitleBar) {
+ QPoint p = pos;
+ pos.setX(rect.left() + rect.bottom() - p.y());
+ pos.setY(rect.top() + p.x() - rect.left());
+
+ QSize s = rect.size();
+ s.transpose();
+ rect.setSize(s);
+ }
+
+ const int buttonRight = 7;
+ const int buttonWidth = 20;
+ int right = rect.right() - pos.x();
+ int button = (right - buttonRight)/buttonWidth;
+ switch (button) {
+ case 0:
+ event->accept();
+ dw->close();
+ break;
+ case 1:
+ event->accept();
+ dw->setFloating(!dw->isFloating());
+ break;
+ case 2: {
+ event->accept();
+ QDockWidget::DockWidgetFeatures features = dw->features();
+ if (features & QDockWidget::DockWidgetVerticalTitleBar)
+ features &= ~QDockWidget::DockWidgetVerticalTitleBar;
+ else
+ features |= QDockWidget::DockWidgetVerticalTitleBar;
+ dw->setFeatures(features);
+ break;
+ }
+ default:
+ event->ignore();
+ break;
+ }
+}
+
+void BlueTitleBar::updateMask()
+{
+ QDockWidget *dw = qobject_cast<QDockWidget*>(parent());
+ Q_ASSERT(dw);
+
+ QRect rect = dw->rect();
+ QBitmap bitmap(dw->size());
+
+ {
+ QPainter painter(&bitmap);
+
+ // initialize to transparent
+ painter.fillRect(rect, Qt::color0);
+
+ QRect contents = rect;
+ contents.setTopLeft(geometry().bottomLeft());
+ contents.setRight(geometry().right());
+ contents.setBottom(contents.bottom()-y());
+ painter.fillRect(contents, Qt::color1);
+
+ // let's paint the titlebar
+ QRect titleRect = this->geometry();
+
+ if (dw->features() & QDockWidget::DockWidgetVerticalTitleBar) {
+ QSize s = rect.size();
+ s.transpose();
+ rect.setSize(s);
+
+ QSize s2 = size();
+ s2.transpose();
+ titleRect.setSize(s2);
+
+ painter.translate(rect.left(), rect.top() + rect.width());
+ painter.rotate(-90);
+ painter.translate(-rect.left(), -rect.top());
+ }
+
+ contents.setTopLeft(titleRect.bottomLeft());
+ contents.setRight(titleRect.right());
+ contents.setBottom(rect.bottom()-y());
+
+ QRect rect = titleRect;
+
+ painter.drawPixmap(rect.topLeft(), leftPm.mask());
+ painter.fillRect(rect.left() + leftPm.width(), rect.top(),
+ rect.width() - leftPm.width() - rightPm.width(),
+ centerPm.height(), Qt::color1);
+ painter.drawPixmap(rect.topRight() - QPoint(rightPm.width() - 1, 0), rightPm.mask());
+
+ painter.fillRect(contents, Qt::color1);
+ }
+
+ dw->setMask(bitmap);
+}
+
+#include "colorswatch.moc"
diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/colorswatch.h b/tests/manual/examples/widgets/mainwindows/mainwindow/colorswatch.h
new file mode 100644
index 0000000000..5b144bd428
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mainwindow/colorswatch.h
@@ -0,0 +1,102 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef COLORSWATCH_H
+#define COLORSWATCH_H
+
+#include <QDockWidget>
+
+QT_FORWARD_DECLARE_CLASS(QAction)
+QT_FORWARD_DECLARE_CLASS(QActionGroup)
+QT_FORWARD_DECLARE_CLASS(QMenu)
+
+class ColorSwatch : public QDockWidget
+{
+ Q_OBJECT
+
+public:
+ explicit ColorSwatch(const QString &colorName, QMainWindow *parent = nullptr, Qt::WindowFlags flags = { });
+
+ void setCustomSizeHint(const QSize &size);
+ QMenu *colorSwatchMenu() const { return menu; }
+
+protected:
+#ifndef QT_NO_CONTEXTMENU
+ void contextMenuEvent(QContextMenuEvent *event) override;
+#endif // QT_NO_CONTEXTMENU
+ void resizeEvent(QResizeEvent *e) override;
+
+private slots:
+ void changeClosable(bool on);
+ void changeMovable(bool on);
+ void changeFloatable(bool on);
+ void changeFloating(bool on);
+ void changeVerticalTitleBar(bool on);
+ void updateContextMenu();
+
+ void allowLeft(bool a);
+ void allowRight(bool a);
+ void allowTop(bool a);
+ void allowBottom(bool a);
+
+ void placeLeft(bool p);
+ void placeRight(bool p);
+ void placeTop(bool p);
+ void placeBottom(bool p);
+
+ void splitInto(QAction *action);
+ void tabInto(QAction *action);
+
+private:
+ void allow(Qt::DockWidgetArea area, bool allow);
+ void place(Qt::DockWidgetArea area, bool place);
+
+ QAction *closableAction;
+ QAction *movableAction;
+ QAction *floatableAction;
+ QAction *floatingAction;
+ QAction *verticalTitleBarAction;
+
+ QActionGroup *allowedAreasActions;
+ QAction *allowLeftAction;
+ QAction *allowRightAction;
+ QAction *allowTopAction;
+ QAction *allowBottomAction;
+
+ QActionGroup *areaActions;
+ QAction *leftAction;
+ QAction *rightAction;
+ QAction *topAction;
+ QAction *bottomAction;
+
+ QMenu *tabMenu;
+ QMenu *splitHMenu;
+ QMenu *splitVMenu;
+ QMenu *menu;
+
+ QMainWindow *mainWindow;
+};
+
+class BlueTitleBar : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit BlueTitleBar(QWidget *parent = nullptr);
+
+ QSize sizeHint() const override { return minimumSizeHint(); }
+ QSize minimumSizeHint() const override;
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+
+public slots:
+ void updateMask();
+
+private:
+ const QPixmap leftPm;
+ const QPixmap centerPm;
+ const QPixmap rightPm;
+};
+
+#endif // COLORSWATCH_H
diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/main.cpp b/tests/manual/examples/widgets/mainwindows/mainwindow/main.cpp
new file mode 100644
index 0000000000..a64d2f5843
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mainwindow/main.cpp
@@ -0,0 +1,147 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+
+#include <QApplication>
+#include <QPainterPath>
+#include <QPainter>
+#include <QMap>
+#include <QDebug>
+
+void render_qt_text(QPainter *painter, int w, int h, const QColor &color)
+{
+ QPainterPath path;
+ path.moveTo(-0.083695, 0.283849);
+ path.cubicTo(-0.049581, 0.349613, -0.012720, 0.397969, 0.026886, 0.428917);
+ path.cubicTo(0.066493, 0.459865, 0.111593, 0.477595, 0.162186, 0.482108);
+ path.lineTo(0.162186, 0.500000);
+ path.cubicTo(0.115929, 0.498066, 0.066565, 0.487669, 0.014094, 0.468810);
+ path.cubicTo(-0.038378, 0.449952, -0.088103, 0.423839, -0.135082, 0.390474);
+ path.cubicTo(-0.182061, 0.357108, -0.222608, 0.321567, -0.256722, 0.283849);
+ path.cubicTo(-0.304712, 0.262250, -0.342874, 0.239362, -0.371206, 0.215184);
+ path.cubicTo(-0.411969, 0.179078, -0.443625, 0.134671, -0.466175, 0.081963);
+ path.cubicTo(-0.488725, 0.029255, -0.500000, -0.033043, -0.500000, -0.104932);
+ path.cubicTo(-0.500000, -0.218407, -0.467042, -0.312621, -0.401127, -0.387573);
+ path.cubicTo(-0.335212, -0.462524, -0.255421, -0.500000, -0.161752, -0.500000);
+ path.cubicTo(-0.072998, -0.500000, 0.003903, -0.462444, 0.068951, -0.387331);
+ path.cubicTo(0.133998, -0.312218, 0.166522, -0.217440, 0.166522, -0.102998);
+ path.cubicTo(0.166522, -0.010155, 0.143394, 0.071325, 0.097138, 0.141441);
+ path.cubicTo(0.050882, 0.211557, -0.009396, 0.259026, -0.083695, 0.283849);
+ path.moveTo(-0.167823, -0.456963);
+ path.cubicTo(-0.228823, -0.456963, -0.277826, -0.432624, -0.314831, -0.383946);
+ path.cubicTo(-0.361665, -0.323340, -0.385082, -0.230335, -0.385082, -0.104932);
+ path.cubicTo(-0.385082, 0.017569, -0.361376, 0.112025, -0.313964, 0.178433);
+ path.cubicTo(-0.277248, 0.229368, -0.228534, 0.254836, -0.167823, 0.254836);
+ path.cubicTo(-0.105088, 0.254836, -0.054496, 0.229368, -0.016045, 0.178433);
+ path.cubicTo(0.029055, 0.117827, 0.051605, 0.028691, 0.051605, -0.088975);
+ path.cubicTo(0.051605, -0.179562, 0.039318, -0.255803, 0.014744, -0.317698);
+ path.cubicTo(-0.004337, -0.365409, -0.029705, -0.400548, -0.061362, -0.423114);
+ path.cubicTo(-0.093018, -0.445680, -0.128505, -0.456963, -0.167823, -0.456963);
+ path.moveTo(0.379011, -0.404739);
+ path.lineTo(0.379011, -0.236460);
+ path.lineTo(0.486123, -0.236460);
+ path.lineTo(0.486123, -0.197292);
+ path.lineTo(0.379011, -0.197292);
+ path.lineTo(0.379011, 0.134913);
+ path.cubicTo(0.379011, 0.168117, 0.383276, 0.190442, 0.391804, 0.201886);
+ path.cubicTo(0.400332, 0.213330, 0.411246, 0.219052, 0.424545, 0.219052);
+ path.cubicTo(0.435531, 0.219052, 0.446227, 0.215264, 0.456635, 0.207689);
+ path.cubicTo(0.467042, 0.200113, 0.474993, 0.188910, 0.480486, 0.174081);
+ path.lineTo(0.500000, 0.174081);
+ path.cubicTo(0.488436, 0.210509, 0.471957, 0.237911, 0.450564, 0.256286);
+ path.cubicTo(0.429170, 0.274662, 0.407054, 0.283849, 0.384215, 0.283849);
+ path.cubicTo(0.368893, 0.283849, 0.353859, 0.279094, 0.339115, 0.269584);
+ path.cubicTo(0.324371, 0.260074, 0.313530, 0.246534, 0.306592, 0.228965);
+ path.cubicTo(0.299653, 0.211396, 0.296184, 0.184075, 0.296184, 0.147002);
+ path.lineTo(0.296184, -0.197292);
+ path.lineTo(0.223330, -0.197292);
+ path.lineTo(0.223330, -0.215667);
+ path.cubicTo(0.241833, -0.224049, 0.260697, -0.237992, 0.279922, -0.257495);
+ path.cubicTo(0.299147, -0.276999, 0.316276, -0.300129, 0.331310, -0.326886);
+ path.cubicTo(0.338826, -0.341070, 0.349523, -0.367021, 0.363400, -0.404739);
+ path.lineTo(0.379011, -0.404739);
+ path.moveTo(-0.535993, 0.275629);
+
+ painter->translate(w / 2, h / 2);
+ double scale = qMin(w, h) * 8 / 10.0;
+ painter->scale(scale, scale);
+
+ painter->setRenderHint(QPainter::Antialiasing);
+
+ painter->save();
+ painter->translate(.1, .1);
+ painter->fillPath(path, QColor(0, 0, 0, 63));
+ painter->restore();
+
+ painter->setBrush(color);
+ painter->setPen(QPen(Qt::black, 0.02, Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin));
+ painter->drawPath(path);
+}
+
+static void usage()
+{
+ qWarning() << "Usage: mainwindow [-SizeHint<color> <width>x<height>] ...";
+ exit(1);
+}
+
+enum ParseCommandLineArgumentsResult {
+ CommandLineArgumentsOk,
+ CommandLineArgumentsError,
+ HelpRequested
+};
+
+static ParseCommandLineArgumentsResult
+ parseCustomSizeHints(const QStringList &arguments, MainWindow::CustomSizeHintMap *result)
+{
+ result->clear();
+ const auto argumentCount = arguments.size();
+ for (int i = 1; i < argumentCount; ++i) {
+ const QString &arg = arguments.at(i);
+ if (arg.startsWith(QLatin1String("-SizeHint"))) {
+ const QString name = arg.mid(9);
+ if (name.isEmpty())
+ return CommandLineArgumentsError;
+ if (++i == argumentCount)
+ return CommandLineArgumentsError;
+ const QStringView sizeStr{ arguments.at(i) };
+ const auto idx = sizeStr.indexOf(QLatin1Char('x'));
+ if (idx == -1)
+ return CommandLineArgumentsError;
+ bool ok;
+ const int w = sizeStr.left(idx).toInt(&ok);
+ if (!ok)
+ return CommandLineArgumentsError;
+ const int h = sizeStr.mid(idx + 1).toInt(&ok);
+ if (!ok)
+ return CommandLineArgumentsError;
+ result->insert(name, QSize(w, h));
+ } else if (arg == QLatin1String("-h") || arg == QLatin1String("--help")) {
+ return HelpRequested;
+ } else {
+ return CommandLineArgumentsError;
+ }
+ }
+
+ return CommandLineArgumentsOk;
+}
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ MainWindow::CustomSizeHintMap customSizeHints;
+ switch (parseCustomSizeHints(QCoreApplication::arguments(), &customSizeHints)) {
+ case CommandLineArgumentsOk:
+ break;
+ case CommandLineArgumentsError:
+ usage();
+ return -1;
+ case HelpRequested:
+ usage();
+ return 0;
+ }
+ MainWindow mainWin(customSizeHints);
+ mainWin.resize(800, 600);
+ mainWin.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.cpp b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.cpp
new file mode 100644
index 0000000000..2f989ab97c
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.cpp
@@ -0,0 +1,444 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+#include "colorswatch.h"
+#include "toolbar.h"
+
+#include <QActionGroup>
+#include <QLayout>
+#include <QMenu>
+#include <QMenuBar>
+#include <QStatusBar>
+#include <QTextEdit>
+#include <QFile>
+#include <QDataStream>
+#include <QFileDialog>
+#include <QDialogButtonBox>
+#include <QMessageBox>
+#include <QApplication>
+#include <QPainter>
+#include <QMouseEvent>
+#include <QLineEdit>
+#include <QComboBox>
+#include <QLabel>
+#include <QPushButton>
+#include <QTextEdit>
+#include <QDebug>
+
+static const char message[] =
+ "<p><b>Qt Main Window Example</b></p>"
+
+ "<p>This is a demonstration of the QMainWindow, QToolBar and "
+ "QDockWidget classes.</p>"
+
+ "<p>The tool bar and dock widgets can be dragged around and rearranged "
+ "using the mouse or via the menu.</p>"
+
+ "<p>Each dock widget contains a colored frame and a context "
+ "(right-click) menu.</p>"
+
+#ifdef Q_OS_MAC
+ "<p>On OS X, the \"Black\" dock widget has been created as a "
+ "<em>Drawer</em>, which is a special kind of QDockWidget.</p>"
+#endif
+ ;
+
+Q_DECLARE_METATYPE(QDockWidget::DockWidgetFeatures)
+
+MainWindow::MainWindow(const CustomSizeHintMap &customSizeHints,
+ QWidget *parent, Qt::WindowFlags flags)
+ : QMainWindow(parent, flags)
+{
+ Q_UNUSED(message);
+ setObjectName("MainWindow");
+ setWindowTitle("Qt Main Window Example");
+
+ QTextEdit *center = new QTextEdit(this);
+ center->setReadOnly(true);
+ center->setMinimumSize(400, 205);
+ setCentralWidget(center);
+
+ setupToolBar();
+ setupMenuBar();
+ setupDockWidgets(customSizeHints);
+
+ statusBar()->showMessage(tr("Status Bar"));
+}
+
+void MainWindow::actionTriggered(QAction *action)
+{
+ qDebug("action '%s' triggered", action->text().toLocal8Bit().data());
+}
+
+void MainWindow::setupToolBar()
+{
+#ifdef Q_OS_MACOS
+ setUnifiedTitleAndToolBarOnMac(true);
+#endif
+
+ for (int i = 0; i < 3; ++i) {
+ ToolBar *tb = new ToolBar(QString::fromLatin1("Tool Bar %1").arg(i + 1), this);
+ toolBars.append(tb);
+ addToolBar(tb);
+ }
+}
+
+void MainWindow::setupMenuBar()
+{
+ QMenu *menu = menuBar()->addMenu(tr("&File"));
+
+ menu->addAction(tr("Save layout..."), this, &MainWindow::saveLayout);
+ menu->addAction(tr("Load layout..."), this, &MainWindow::loadLayout);
+ menu->addAction(tr("Switch layout direction"),this, &MainWindow::switchLayoutDirection);
+
+ menu->addSeparator();
+ menu->addAction(tr("&Quit"), qApp, &QCoreApplication::quit);
+
+ mainWindowMenu = menuBar()->addMenu(tr("Main window"));
+
+ QAction *action = mainWindowMenu->addAction(tr("Animated docks"));
+ action->setCheckable(true);
+ action->setChecked(dockOptions() & AnimatedDocks);
+ connect(action, &QAction::toggled, this, &MainWindow::setDockOptions);
+
+ action = mainWindowMenu->addAction(tr("Allow nested docks"));
+ action->setCheckable(true);
+ action->setChecked(dockOptions() & AllowNestedDocks);
+ connect(action, &QAction::toggled, this, &MainWindow::setDockOptions);
+
+ action = mainWindowMenu->addAction(tr("Allow tabbed docks"));
+ action->setCheckable(true);
+ action->setChecked(dockOptions() & AllowTabbedDocks);
+ connect(action, &QAction::toggled, this, &MainWindow::setDockOptions);
+
+ action = mainWindowMenu->addAction(tr("Force tabbed docks"));
+ action->setCheckable(true);
+ action->setChecked(dockOptions() & ForceTabbedDocks);
+ connect(action, &QAction::toggled, this, &MainWindow::setDockOptions);
+
+ action = mainWindowMenu->addAction(tr("Vertical tabs"));
+ action->setCheckable(true);
+ action->setChecked(dockOptions() & VerticalTabs);
+ connect(action, &QAction::toggled, this, &MainWindow::setDockOptions);
+
+ action = mainWindowMenu->addAction(tr("Grouped dragging"));
+ action->setCheckable(true);
+ action->setChecked(dockOptions() & GroupedDragging);
+ connect(action, &QAction::toggled, this, &MainWindow::setDockOptions);
+
+ QMenu *toolBarMenu = menuBar()->addMenu(tr("Tool bars"));
+ for (int i = 0; i < toolBars.count(); ++i)
+ toolBarMenu->addMenu(toolBars.at(i)->toolbarMenu());
+
+#ifdef Q_OS_MACOS
+ toolBarMenu->addSeparator();
+
+ action = toolBarMenu->addAction(tr("Unified"));
+ action->setCheckable(true);
+ action->setChecked(unifiedTitleAndToolBarOnMac());
+ connect(action, &QAction::toggled, this, &QMainWindow::setUnifiedTitleAndToolBarOnMac);
+#endif
+
+ dockWidgetMenu = menuBar()->addMenu(tr("&Dock Widgets"));
+
+ QMenu *aboutMenu = menuBar()->addMenu(tr("About"));
+ QAction *aboutAct = aboutMenu->addAction(tr("&About"), this, &MainWindow::about);
+ aboutAct->setStatusTip(tr("Show the application's About box"));
+
+ QAction *aboutQtAct = aboutMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
+ aboutQtAct->setStatusTip(tr("Show the Qt library's About box"));
+}
+
+void MainWindow::setDockOptions()
+{
+ DockOptions opts;
+ QList<QAction*> actions = mainWindowMenu->actions();
+
+ if (actions.at(0)->isChecked())
+ opts |= AnimatedDocks;
+ if (actions.at(1)->isChecked())
+ opts |= AllowNestedDocks;
+ if (actions.at(2)->isChecked())
+ opts |= AllowTabbedDocks;
+ if (actions.at(3)->isChecked())
+ opts |= ForceTabbedDocks;
+ if (actions.at(4)->isChecked())
+ opts |= VerticalTabs;
+ if (actions.at(5)->isChecked())
+ opts |= GroupedDragging;
+
+ QMainWindow::setDockOptions(opts);
+}
+
+void MainWindow::saveLayout()
+{
+ QString fileName
+ = QFileDialog::getSaveFileName(this, tr("Save layout"));
+ if (fileName.isEmpty())
+ return;
+ QFile file(fileName);
+ if (!file.open(QFile::WriteOnly)) {
+ QString msg = tr("Failed to open %1\n%2")
+ .arg(QDir::toNativeSeparators(fileName), file.errorString());
+ QMessageBox::warning(this, tr("Error"), msg);
+ return;
+ }
+
+ QByteArray geo_data = saveGeometry();
+ QByteArray layout_data = saveState();
+
+ bool ok = file.putChar((uchar)geo_data.size());
+ if (ok)
+ ok = file.write(geo_data) == geo_data.size();
+ if (ok)
+ ok = file.write(layout_data) == layout_data.size();
+
+ if (!ok) {
+ QString msg = tr("Error writing to %1\n%2")
+ .arg(QDir::toNativeSeparators(fileName), file.errorString());
+ QMessageBox::warning(this, tr("Error"), msg);
+ return;
+ }
+}
+
+void MainWindow::loadLayout()
+{
+ QString fileName
+ = QFileDialog::getOpenFileName(this, tr("Load layout"));
+ if (fileName.isEmpty())
+ return;
+ QFile file(fileName);
+ if (!file.open(QFile::ReadOnly)) {
+ QString msg = tr("Failed to open %1\n%2")
+ .arg(QDir::toNativeSeparators(fileName), file.errorString());
+ QMessageBox::warning(this, tr("Error"), msg);
+ return;
+ }
+
+ uchar geo_size;
+ QByteArray geo_data;
+ QByteArray layout_data;
+
+ bool ok = file.getChar((char*)&geo_size);
+ if (ok) {
+ geo_data = file.read(geo_size);
+ ok = geo_data.size() == geo_size;
+ }
+ if (ok) {
+ layout_data = file.readAll();
+ ok = layout_data.size() > 0;
+ }
+
+ if (ok)
+ ok = restoreGeometry(geo_data);
+ if (ok)
+ ok = restoreState(layout_data);
+
+ if (!ok) {
+ QString msg = tr("Error reading %1").arg(QDir::toNativeSeparators(fileName));
+ QMessageBox::warning(this, tr("Error"), msg);
+ return;
+ }
+}
+
+static QAction *addCornerAction(const QString &text, QMainWindow *mw, QMenu *menu, QActionGroup *group,
+ Qt::Corner c, Qt::DockWidgetArea a)
+{
+ QAction *result = menu->addAction(text, mw, [=]() { mw->setCorner(c, a); });
+ result->setCheckable(true);
+ group->addAction(result);
+ return result;
+}
+
+void MainWindow::setupDockWidgets(const CustomSizeHintMap &customSizeHints)
+{
+ qRegisterMetaType<QDockWidget::DockWidgetFeatures>();
+
+ QMenu *cornerMenu = dockWidgetMenu->addMenu(tr("Top left corner"));
+ QActionGroup *group = new QActionGroup(this);
+ group->setExclusive(true);
+ QAction *cornerAction = addCornerAction(tr("Top dock area"), this, cornerMenu, group, Qt::TopLeftCorner, Qt::TopDockWidgetArea);
+ cornerAction->setChecked(true);
+ addCornerAction(tr("Left dock area"), this, cornerMenu, group, Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
+
+ cornerMenu = dockWidgetMenu->addMenu(tr("Top right corner"));
+ group = new QActionGroup(this);
+ group->setExclusive(true);
+ cornerAction = addCornerAction(tr("Top dock area"), this, cornerMenu, group, Qt::TopRightCorner, Qt::TopDockWidgetArea);
+ cornerAction->setChecked(true);
+ addCornerAction(tr("Right dock area"), this, cornerMenu, group, Qt::TopRightCorner, Qt::RightDockWidgetArea);
+
+ cornerMenu = dockWidgetMenu->addMenu(tr("Bottom left corner"));
+ group = new QActionGroup(this);
+ group->setExclusive(true);
+ cornerAction = addCornerAction(tr("Bottom dock area"), this, cornerMenu, group, Qt::BottomLeftCorner, Qt::BottomDockWidgetArea);
+ cornerAction->setChecked(true);
+ addCornerAction(tr("Left dock area"), this, cornerMenu, group, Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
+
+ cornerMenu = dockWidgetMenu->addMenu(tr("Bottom right corner"));
+ group = new QActionGroup(this);
+ group->setExclusive(true);
+ cornerAction = addCornerAction(tr("Bottom dock area"), this, cornerMenu, group, Qt::BottomRightCorner, Qt::BottomDockWidgetArea);
+ cornerAction->setChecked(true);
+ addCornerAction(tr("Right dock area"), this, cornerMenu, group, Qt::BottomRightCorner, Qt::RightDockWidgetArea);
+
+ dockWidgetMenu->addSeparator();
+
+ static const struct Set {
+ const char * name;
+ uint flags;
+ Qt::DockWidgetArea area;
+ } sets [] = {
+#ifndef Q_OS_MAC
+ { "Black", 0, Qt::LeftDockWidgetArea },
+#else
+ { "Black", Qt::Drawer, Qt::LeftDockWidgetArea },
+#endif
+ { "White", 0, Qt::RightDockWidgetArea },
+ { "Red", 0, Qt::TopDockWidgetArea },
+ { "Green", 0, Qt::TopDockWidgetArea },
+ { "Blue", 0, Qt::BottomDockWidgetArea },
+ { "Yellow", 0, Qt::BottomDockWidgetArea }
+ };
+ const int setCount = sizeof(sets) / sizeof(Set);
+
+ const QIcon qtIcon(QPixmap(":/res/qt.png"));
+ for (int i = 0; i < setCount; ++i) {
+ ColorSwatch *swatch = new ColorSwatch(tr(sets[i].name), this, Qt::WindowFlags(sets[i].flags));
+ if (i % 2)
+ swatch->setWindowIcon(qtIcon);
+ if (qstrcmp(sets[i].name, "Blue") == 0) {
+ BlueTitleBar *titlebar = new BlueTitleBar(swatch);
+ swatch->setTitleBarWidget(titlebar);
+ connect(swatch, &QDockWidget::topLevelChanged, titlebar, &BlueTitleBar::updateMask);
+ connect(swatch, &QDockWidget::featuresChanged, titlebar, &BlueTitleBar::updateMask, Qt::QueuedConnection);
+ }
+
+ QString name = QString::fromLatin1(sets[i].name);
+ if (customSizeHints.contains(name))
+ swatch->setCustomSizeHint(customSizeHints.value(name));
+
+ addDockWidget(sets[i].area, swatch);
+ dockWidgetMenu->addMenu(swatch->colorSwatchMenu());
+ }
+
+ destroyDockWidgetMenu = new QMenu(tr("Destroy dock widget"), this);
+ destroyDockWidgetMenu->setEnabled(false);
+ connect(destroyDockWidgetMenu, &QMenu::triggered, this, &MainWindow::destroyDockWidget);
+
+ dockWidgetMenu->addSeparator();
+ dockWidgetMenu->addAction(tr("Add dock widget..."), this, &MainWindow::createDockWidget);
+ dockWidgetMenu->addMenu(destroyDockWidgetMenu);
+}
+
+void MainWindow::switchLayoutDirection()
+{
+ if (layoutDirection() == Qt::LeftToRight)
+ QApplication::setLayoutDirection(Qt::RightToLeft);
+ else
+ QApplication::setLayoutDirection(Qt::LeftToRight);
+}
+
+class CreateDockWidgetDialog : public QDialog
+{
+public:
+ explicit CreateDockWidgetDialog(QWidget *parent = nullptr);
+
+ QString enteredObjectName() const { return m_objectName->text(); }
+ Qt::DockWidgetArea location() const;
+
+private:
+ QLineEdit *m_objectName;
+ QComboBox *m_location;
+};
+
+CreateDockWidgetDialog::CreateDockWidgetDialog(QWidget *parent)
+ : QDialog(parent)
+ , m_objectName(new QLineEdit(this))
+ , m_location(new QComboBox(this))
+{
+ setWindowTitle(tr("Add Dock Widget"));
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ QGridLayout *layout = new QGridLayout(this);
+
+ layout->addWidget(new QLabel(tr("Object name:")), 0, 0);
+ layout->addWidget(m_objectName, 0, 1);
+
+ layout->addWidget(new QLabel(tr("Location:")), 1, 0);
+ m_location->setEditable(false);
+ m_location->addItem(tr("Top"));
+ m_location->addItem(tr("Left"));
+ m_location->addItem(tr("Right"));
+ m_location->addItem(tr("Bottom"));
+ m_location->addItem(tr("Restore"));
+ layout->addWidget(m_location, 1, 1);
+
+ QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
+ connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+ connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ layout->addWidget(buttonBox, 2, 0, 1, 2);
+}
+
+Qt::DockWidgetArea CreateDockWidgetDialog::location() const
+{
+ switch (m_location->currentIndex()) {
+ case 0: return Qt::TopDockWidgetArea;
+ case 1: return Qt::LeftDockWidgetArea;
+ case 2: return Qt::RightDockWidgetArea;
+ case 3: return Qt::BottomDockWidgetArea;
+ default:
+ break;
+ }
+ return Qt::NoDockWidgetArea;
+}
+
+void MainWindow::createDockWidget()
+{
+ CreateDockWidgetDialog dialog(this);
+ if (dialog.exec() == QDialog::Rejected)
+ return;
+
+ QDockWidget *dw = new QDockWidget;
+ const QString name = dialog.enteredObjectName();
+ dw->setObjectName(name);
+ dw->setWindowTitle(name);
+ dw->setWidget(new QTextEdit);
+
+ Qt::DockWidgetArea area = dialog.location();
+ switch (area) {
+ case Qt::LeftDockWidgetArea:
+ case Qt::RightDockWidgetArea:
+ case Qt::TopDockWidgetArea:
+ case Qt::BottomDockWidgetArea:
+ addDockWidget(area, dw);
+ break;
+ default:
+ if (!restoreDockWidget(dw)) {
+ QMessageBox::warning(this, QString(), tr("Failed to restore dock widget"));
+ delete dw;
+ return;
+ }
+ break;
+ }
+
+ extraDockWidgets.append(dw);
+ destroyDockWidgetMenu->setEnabled(true);
+ destroyDockWidgetMenu->addAction(new QAction(name, this));
+}
+
+void MainWindow::destroyDockWidget(QAction *action)
+{
+ auto index = destroyDockWidgetMenu->actions().indexOf(action);
+ delete extraDockWidgets.takeAt(index);
+ destroyDockWidgetMenu->removeAction(action);
+ action->deleteLater();
+
+ if (destroyDockWidgetMenu->isEmpty())
+ destroyDockWidgetMenu->setEnabled(false);
+}
+
+void MainWindow::about()
+{
+ QMessageBox::about(this, tr("About MainWindows"), message);
+}
diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.h b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.h
new file mode 100644
index 0000000000..ec522d1a51
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QMap>
+#include <QString>
+#include <QSize>
+
+class ToolBar;
+QT_FORWARD_DECLARE_CLASS(QMenu)
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ typedef QMap<QString, QSize> CustomSizeHintMap;
+
+ explicit MainWindow(const CustomSizeHintMap &customSizeHints,
+ QWidget *parent = nullptr,
+ Qt::WindowFlags flags = { });
+
+public slots:
+ void actionTriggered(QAction *action);
+ void saveLayout();
+ void loadLayout();
+ void switchLayoutDirection();
+ void setDockOptions();
+
+ void createDockWidget();
+ void destroyDockWidget(QAction *action);
+
+ void about();
+
+private:
+ void setupToolBar();
+ void setupMenuBar();
+ void setupDockWidgets(const CustomSizeHintMap &customSizeHints);
+
+ QList<ToolBar*> toolBars;
+ QMenu *dockWidgetMenu;
+ QMenu *mainWindowMenu;
+ QList<QDockWidget *> extraDockWidgets;
+ QMenu *destroyDockWidgetMenu;
+};
+
+#endif // MAINWINDOW_H
diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.pro b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.pro
new file mode 100644
index 0000000000..49f4d30720
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.pro
@@ -0,0 +1,18 @@
+TEMPLATE = app
+QT += widgets
+requires(qtConfig(combobox))
+
+HEADERS += colorswatch.h mainwindow.h toolbar.h
+SOURCES += colorswatch.cpp mainwindow.cpp toolbar.cpp main.cpp
+build_all:!build_pass {
+ CONFIG -= build_all
+ CONFIG += release
+}
+
+#! [qrc]
+RESOURCES += mainwindow.qrc
+#! [qrc]
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/mainwindows/mainwindow
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.qrc b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.qrc
new file mode 100644
index 0000000000..47ff22a3e4
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mainwindow/mainwindow.qrc
@@ -0,0 +1,8 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/res">
+ <file>qt.png</file>
+ <file>titlebarLeft.png</file>
+ <file>titlebarCenter.png</file>
+ <file>titlebarRight.png</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/qt.png b/tests/manual/examples/widgets/mainwindows/mainwindow/qt.png
new file mode 100644
index 0000000000..4f68e162de
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mainwindow/qt.png
Binary files differ
diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarCenter.png b/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarCenter.png
new file mode 100644
index 0000000000..5cc141355c
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarCenter.png
Binary files differ
diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarLeft.png b/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarLeft.png
new file mode 100644
index 0000000000..315166202b
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarLeft.png
Binary files differ
diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarRight.png b/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarRight.png
new file mode 100644
index 0000000000..a4505268ec
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mainwindow/titlebarRight.png
Binary files differ
diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/toolbar.cpp b/tests/manual/examples/widgets/mainwindows/mainwindow/toolbar.cpp
new file mode 100644
index 0000000000..d73c12f50d
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mainwindow/toolbar.cpp
@@ -0,0 +1,308 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "toolbar.h"
+
+#include <QRandomGenerator>
+
+#include <QActionGroup>
+#include <QMainWindow>
+#include <QMenu>
+#include <QPainter>
+#include <QPainterPath>
+#include <QSpinBox>
+#include <QLabel>
+#include <QToolTip>
+
+#include <stdlib.h>
+
+static QPixmap genIcon(const QSize &iconSize, const QString &, const QColor &color, qreal pixelRatio)
+{
+ int w = qRound(iconSize.width() * pixelRatio);
+ int h = qRound(iconSize.height() * pixelRatio);
+
+ QImage image(w, h, QImage::Format_ARGB32_Premultiplied);
+ image.fill(0);
+
+ QPainter p(&image);
+
+ extern void render_qt_text(QPainter *, int, int, const QColor &);
+ render_qt_text(&p, w, h, color);
+
+ QPixmap pm = QPixmap::fromImage(image, Qt::DiffuseDither | Qt::DiffuseAlphaDither);
+ pm.setDevicePixelRatio(pixelRatio);
+ return pm;
+}
+
+static QPixmap genIcon(const QSize &iconSize, int number, const QColor &color, qreal pixelRatio)
+{ return genIcon(iconSize, QString::number(number), color, pixelRatio); }
+
+ToolBar::ToolBar(const QString &title, QMainWindow *mainWindow)
+ : QToolBar(mainWindow)
+ , mainWindow(mainWindow)
+ , spinbox(nullptr)
+ , spinboxAction(nullptr)
+{
+ setWindowTitle(title);
+ setObjectName(title);
+
+ setIconSize(QSize(32, 32));
+
+ qreal dpr = devicePixelRatio();
+ menu = new QMenu("One", this);
+ menu->setIcon(genIcon(iconSize(), 1, Qt::black, dpr));
+ menu->addAction(genIcon(iconSize(), "A", Qt::blue, dpr), "A");
+ menu->addAction(genIcon(iconSize(), "B", Qt::blue, dpr), "B");
+ menu->addAction(genIcon(iconSize(), "C", Qt::blue, dpr), "C");
+ addAction(menu->menuAction());
+
+ QAction *two = addAction(genIcon(iconSize(), 2, Qt::white, dpr), "Two");
+ QFont boldFont;
+ boldFont.setBold(true);
+ two->setFont(boldFont);
+
+ addAction(genIcon(iconSize(), 3, Qt::red, dpr), "Three");
+ addAction(genIcon(iconSize(), 4, Qt::green, dpr), "Four");
+ addAction(genIcon(iconSize(), 5, Qt::blue, dpr), "Five");
+ addAction(genIcon(iconSize(), 6, Qt::yellow, dpr), "Six");
+ orderAction = new QAction(this);
+ orderAction->setText(tr("Order Items in Tool Bar"));
+ connect(orderAction, &QAction::triggered, this, &ToolBar::order);
+
+ randomizeAction = new QAction(this);
+ randomizeAction->setText(tr("Randomize Items in Tool Bar"));
+ connect(randomizeAction, &QAction::triggered, this, &ToolBar::randomize);
+
+ addSpinBoxAction = new QAction(this);
+ addSpinBoxAction->setText(tr("Add Spin Box"));
+ connect(addSpinBoxAction, &QAction::triggered, this, &ToolBar::addSpinBox);
+
+ removeSpinBoxAction = new QAction(this);
+ removeSpinBoxAction->setText(tr("Remove Spin Box"));
+ removeSpinBoxAction->setEnabled(false);
+ connect(removeSpinBoxAction, &QAction::triggered, this, &ToolBar::removeSpinBox);
+
+ movableAction = new QAction(tr("Movable"), this);
+ movableAction->setCheckable(true);
+ connect(movableAction, &QAction::triggered, this, &ToolBar::changeMovable);
+
+ allowedAreasActions = new QActionGroup(this);
+ allowedAreasActions->setExclusive(false);
+
+ allowLeftAction = new QAction(tr("Allow on Left"), this);
+ allowLeftAction->setCheckable(true);
+ connect(allowLeftAction, &QAction::triggered, this, &ToolBar::allowLeft);
+
+ allowRightAction = new QAction(tr("Allow on Right"), this);
+ allowRightAction->setCheckable(true);
+ connect(allowRightAction, &QAction::triggered, this, &ToolBar::allowRight);
+
+ allowTopAction = new QAction(tr("Allow on Top"), this);
+ allowTopAction->setCheckable(true);
+ connect(allowTopAction, &QAction::triggered, this, &ToolBar::allowTop);
+
+ allowBottomAction = new QAction(tr("Allow on Bottom"), this);
+ allowBottomAction->setCheckable(true);
+ connect(allowBottomAction, &QAction::triggered, this, &ToolBar::allowBottom);
+
+ allowedAreasActions->addAction(allowLeftAction);
+ allowedAreasActions->addAction(allowRightAction);
+ allowedAreasActions->addAction(allowTopAction);
+ allowedAreasActions->addAction(allowBottomAction);
+
+ areaActions = new QActionGroup(this);
+ areaActions->setExclusive(true);
+
+ leftAction = new QAction(tr("Place on Left") , this);
+ leftAction->setCheckable(true);
+ connect(leftAction, &QAction::triggered, this, &ToolBar::placeLeft);
+
+ rightAction = new QAction(tr("Place on Right") , this);
+ rightAction->setCheckable(true);
+ connect(rightAction, &QAction::triggered, this, &ToolBar::placeRight);
+
+ topAction = new QAction(tr("Place on Top") , this);
+ topAction->setCheckable(true);
+ connect(topAction, &QAction::triggered, this, &ToolBar::placeTop);
+
+ bottomAction = new QAction(tr("Place on Bottom") , this);
+ bottomAction->setCheckable(true);
+ connect(bottomAction, &QAction::triggered, this, &ToolBar::placeBottom);
+
+ areaActions->addAction(leftAction);
+ areaActions->addAction(rightAction);
+ areaActions->addAction(topAction);
+ areaActions->addAction(bottomAction);
+
+ connect(movableAction, &QAction::triggered, areaActions, &QActionGroup::setEnabled);
+
+ connect(movableAction, &QAction::triggered, allowedAreasActions, &QActionGroup::setEnabled);
+
+ menu = new QMenu(title, this);
+ menu->addAction(toggleViewAction());
+ menu->addSeparator();
+ menu->addAction(orderAction);
+ menu->addAction(randomizeAction);
+ menu->addSeparator();
+ menu->addAction(addSpinBoxAction);
+ menu->addAction(removeSpinBoxAction);
+ menu->addSeparator();
+ menu->addAction(movableAction);
+ menu->addSeparator();
+ menu->addActions(allowedAreasActions->actions());
+ menu->addSeparator();
+ menu->addActions(areaActions->actions());
+ menu->addSeparator();
+ menu->addAction(tr("Insert break"), this, &ToolBar::insertToolBarBreak);
+
+ connect(menu, &QMenu::aboutToShow, this, &ToolBar::updateMenu);
+
+ randomize();
+}
+
+void ToolBar::updateMenu()
+{
+ const Qt::ToolBarArea area = mainWindow->toolBarArea(this);
+ const Qt::ToolBarAreas areas = allowedAreas();
+
+ movableAction->setChecked(isMovable());
+
+ allowLeftAction->setChecked(isAreaAllowed(Qt::LeftToolBarArea));
+ allowRightAction->setChecked(isAreaAllowed(Qt::RightToolBarArea));
+ allowTopAction->setChecked(isAreaAllowed(Qt::TopToolBarArea));
+ allowBottomAction->setChecked(isAreaAllowed(Qt::BottomToolBarArea));
+
+ if (allowedAreasActions->isEnabled()) {
+ allowLeftAction->setEnabled(area != Qt::LeftToolBarArea);
+ allowRightAction->setEnabled(area != Qt::RightToolBarArea);
+ allowTopAction->setEnabled(area != Qt::TopToolBarArea);
+ allowBottomAction->setEnabled(area != Qt::BottomToolBarArea);
+ }
+
+ leftAction->setChecked(area == Qt::LeftToolBarArea);
+ rightAction->setChecked(area == Qt::RightToolBarArea);
+ topAction->setChecked(area == Qt::TopToolBarArea);
+ bottomAction->setChecked(area == Qt::BottomToolBarArea);
+
+ if (areaActions->isEnabled()) {
+ leftAction->setEnabled(areas & Qt::LeftToolBarArea);
+ rightAction->setEnabled(areas & Qt::RightToolBarArea);
+ topAction->setEnabled(areas & Qt::TopToolBarArea);
+ bottomAction->setEnabled(areas & Qt::BottomToolBarArea);
+ }
+}
+
+void ToolBar::order()
+{
+ QList<QAction *> ordered;
+ QList<QAction *> actions1 = actions();
+ const QList<QAction *> childActions = findChildren<QAction *>();
+ for (QAction *action : childActions) {
+ if (!actions1.contains(action))
+ continue;
+ actions1.removeAll(action);
+ ordered.append(action);
+ }
+
+ clear();
+ addActions(ordered);
+
+ orderAction->setEnabled(false);
+}
+
+void ToolBar::randomize()
+{
+ QList<QAction *> randomized;
+ QList<QAction *> actions = this->actions();
+ while (!actions.isEmpty()) {
+ QAction *action = actions.takeAt(QRandomGenerator::global()->bounded(actions.size()));
+ randomized.append(action);
+ }
+ clear();
+ addActions(randomized);
+
+ orderAction->setEnabled(true);
+}
+
+void ToolBar::addSpinBox()
+{
+ if (!spinbox)
+ spinbox = new QSpinBox(this);
+ if (!spinboxAction)
+ spinboxAction = addWidget(spinbox);
+ else
+ addAction(spinboxAction);
+
+ addSpinBoxAction->setEnabled(false);
+ removeSpinBoxAction->setEnabled(true);
+}
+
+void ToolBar::removeSpinBox()
+{
+ if (spinboxAction)
+ removeAction(spinboxAction);
+
+ addSpinBoxAction->setEnabled(true);
+ removeSpinBoxAction->setEnabled(false);
+}
+
+void ToolBar::allow(Qt::ToolBarArea area, bool a)
+{
+ Qt::ToolBarAreas areas = allowedAreas();
+ areas = a ? areas | area : areas & ~area;
+ setAllowedAreas(areas);
+
+ if (areaActions->isEnabled()) {
+ leftAction->setEnabled(areas & Qt::LeftToolBarArea);
+ rightAction->setEnabled(areas & Qt::RightToolBarArea);
+ topAction->setEnabled(areas & Qt::TopToolBarArea);
+ bottomAction->setEnabled(areas & Qt::BottomToolBarArea);
+ }
+}
+
+void ToolBar::place(Qt::ToolBarArea area, bool p)
+{
+ if (!p)
+ return;
+
+ mainWindow->addToolBar(area, this);
+
+ if (allowedAreasActions->isEnabled()) {
+ allowLeftAction->setEnabled(area != Qt::LeftToolBarArea);
+ allowRightAction->setEnabled(area != Qt::RightToolBarArea);
+ allowTopAction->setEnabled(area != Qt::TopToolBarArea);
+ allowBottomAction->setEnabled(area != Qt::BottomToolBarArea);
+ }
+}
+
+void ToolBar::changeMovable(bool movable)
+{ setMovable(movable); }
+
+void ToolBar::allowLeft(bool a)
+{ allow(Qt::LeftToolBarArea, a); }
+
+void ToolBar::allowRight(bool a)
+{ allow(Qt::RightToolBarArea, a); }
+
+void ToolBar::allowTop(bool a)
+{ allow(Qt::TopToolBarArea, a); }
+
+void ToolBar::allowBottom(bool a)
+{ allow(Qt::BottomToolBarArea, a); }
+
+void ToolBar::placeLeft(bool p)
+{ place(Qt::LeftToolBarArea, p); }
+
+void ToolBar::placeRight(bool p)
+{ place(Qt::RightToolBarArea, p); }
+
+void ToolBar::placeTop(bool p)
+{ place(Qt::TopToolBarArea, p); }
+
+void ToolBar::placeBottom(bool p)
+{ place(Qt::BottomToolBarArea, p); }
+
+void ToolBar::insertToolBarBreak()
+{
+ mainWindow->insertToolBarBreak(this);
+}
diff --git a/tests/manual/examples/widgets/mainwindows/mainwindow/toolbar.h b/tests/manual/examples/widgets/mainwindows/mainwindow/toolbar.h
new file mode 100644
index 0000000000..0030adbe23
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mainwindow/toolbar.h
@@ -0,0 +1,74 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef TOOLBAR_H
+#define TOOLBAR_H
+
+#include <QToolBar>
+
+QT_FORWARD_DECLARE_CLASS(QAction)
+QT_FORWARD_DECLARE_CLASS(QActionGroup)
+QT_FORWARD_DECLARE_CLASS(QMenu)
+QT_FORWARD_DECLARE_CLASS(QSpinBox)
+
+class ToolBar : public QToolBar
+{
+ Q_OBJECT
+
+public:
+ explicit ToolBar(const QString &title, QMainWindow *mainWindow);
+
+ QMenu *toolbarMenu() const { return menu; }
+
+private slots:
+ void order();
+ void randomize();
+ void addSpinBox();
+ void removeSpinBox();
+
+ void changeMovable(bool movable);
+
+ void allowLeft(bool a);
+ void allowRight(bool a);
+ void allowTop(bool a);
+ void allowBottom(bool a);
+
+ void placeLeft(bool p);
+ void placeRight(bool p);
+ void placeTop(bool p);
+ void placeBottom(bool p);
+
+ void updateMenu();
+ void insertToolBarBreak();
+
+private:
+ void allow(Qt::ToolBarArea area, bool allow);
+ void place(Qt::ToolBarArea area, bool place);
+
+ QMainWindow *mainWindow;
+
+ QSpinBox *spinbox;
+ QAction *spinboxAction;
+
+ QMenu *menu;
+ QAction *orderAction;
+ QAction *randomizeAction;
+ QAction *addSpinBoxAction;
+ QAction *removeSpinBoxAction;
+
+ QAction *movableAction;
+
+ QActionGroup *allowedAreasActions;
+ QAction *allowLeftAction;
+ QAction *allowRightAction;
+ QAction *allowTopAction;
+ QAction *allowBottomAction;
+
+ QActionGroup *areaActions;
+ QAction *leftAction;
+ QAction *rightAction;
+ QAction *topAction;
+ QAction *bottomAction;
+};
+
+#endif // TOOLBAR_H
diff --git a/tests/manual/examples/widgets/mainwindows/mdi/CMakeLists.txt b/tests/manual/examples/widgets/mainwindows/mdi/CMakeLists.txt
new file mode 100644
index 0000000000..2f544b9510
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mdi/CMakeLists.txt
@@ -0,0 +1,55 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(mdi LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/mainwindows/mdi")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(mdi
+ main.cpp
+ mainwindow.cpp mainwindow.h
+ mdichild.cpp mdichild.h
+)
+
+set_target_properties(mdi PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(mdi PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(mdi_resource_files
+ "images/copy.png"
+ "images/cut.png"
+ "images/new.png"
+ "images/open.png"
+ "images/paste.png"
+ "images/save.png"
+)
+
+qt_add_resources(mdi "mdi"
+ PREFIX
+ "/"
+ FILES
+ ${mdi_resource_files}
+)
+
+install(TARGETS mdi
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/mainwindows/mdi/images/copy.png b/tests/manual/examples/widgets/mainwindows/mdi/images/copy.png
new file mode 100644
index 0000000000..2aeb28288f
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mdi/images/copy.png
Binary files differ
diff --git a/tests/manual/examples/widgets/mainwindows/mdi/images/cut.png b/tests/manual/examples/widgets/mainwindows/mdi/images/cut.png
new file mode 100644
index 0000000000..54638e9386
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mdi/images/cut.png
Binary files differ
diff --git a/tests/manual/examples/widgets/mainwindows/mdi/images/new.png b/tests/manual/examples/widgets/mainwindows/mdi/images/new.png
new file mode 100644
index 0000000000..12131b0100
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mdi/images/new.png
Binary files differ
diff --git a/tests/manual/examples/widgets/mainwindows/mdi/images/open.png b/tests/manual/examples/widgets/mainwindows/mdi/images/open.png
new file mode 100644
index 0000000000..45fa2883a7
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mdi/images/open.png
Binary files differ
diff --git a/tests/manual/examples/widgets/mainwindows/mdi/images/paste.png b/tests/manual/examples/widgets/mainwindows/mdi/images/paste.png
new file mode 100644
index 0000000000..c14425cad1
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mdi/images/paste.png
Binary files differ
diff --git a/tests/manual/examples/widgets/mainwindows/mdi/images/save.png b/tests/manual/examples/widgets/mainwindows/mdi/images/save.png
new file mode 100644
index 0000000000..e65a29d5f1
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mdi/images/save.png
Binary files differ
diff --git a/tests/manual/examples/widgets/mainwindows/mdi/main.cpp b/tests/manual/examples/widgets/mainwindows/mdi/main.cpp
new file mode 100644
index 0000000000..ee70050544
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mdi/main.cpp
@@ -0,0 +1,29 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ QCoreApplication::setApplicationName("MDI Example");
+ QCoreApplication::setOrganizationName("QtProject");
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+ QCommandLineParser parser;
+ parser.setApplicationDescription("Qt MDI Example");
+ parser.addHelpOption();
+ parser.addVersionOption();
+ parser.addPositionalArgument("file", "The file to open.");
+ parser.process(app);
+
+ MainWindow mainWin;
+ const QStringList posArgs = parser.positionalArguments();
+ for (const QString &fileName : posArgs)
+ mainWin.openFile(fileName);
+ mainWin.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/mainwindows/mdi/mainwindow.cpp b/tests/manual/examples/widgets/mainwindows/mdi/mainwindow.cpp
new file mode 100644
index 0000000000..c0f9091002
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mdi/mainwindow.cpp
@@ -0,0 +1,476 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+
+#include "mainwindow.h"
+#include "mdichild.h"
+
+MainWindow::MainWindow()
+ : mdiArea(new QMdiArea)
+{
+ mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ setCentralWidget(mdiArea);
+ connect(mdiArea, &QMdiArea::subWindowActivated,
+ this, &MainWindow::updateMenus);
+
+ createActions();
+ createStatusBar();
+ updateMenus();
+
+ readSettings();
+
+ setWindowTitle(tr("MDI"));
+ setUnifiedTitleAndToolBarOnMac(true);
+}
+
+void MainWindow::closeEvent(QCloseEvent *event)
+{
+ mdiArea->closeAllSubWindows();
+ if (mdiArea->currentSubWindow()) {
+ event->ignore();
+ } else {
+ writeSettings();
+ event->accept();
+ }
+}
+
+void MainWindow::newFile()
+{
+ MdiChild *child = createMdiChild();
+ child->newFile();
+ child->show();
+}
+
+void MainWindow::open()
+{
+ const QString fileName = QFileDialog::getOpenFileName(this);
+ if (!fileName.isEmpty())
+ openFile(fileName);
+}
+
+bool MainWindow::openFile(const QString &fileName)
+{
+ if (QMdiSubWindow *existing = findMdiChild(fileName)) {
+ mdiArea->setActiveSubWindow(existing);
+ return true;
+ }
+ const bool succeeded = loadFile(fileName);
+ if (succeeded)
+ statusBar()->showMessage(tr("File loaded"), 2000);
+ return succeeded;
+}
+
+bool MainWindow::loadFile(const QString &fileName)
+{
+ MdiChild *child = createMdiChild();
+ const bool succeeded = child->loadFile(fileName);
+ if (succeeded)
+ child->show();
+ else
+ child->close();
+ MainWindow::prependToRecentFiles(fileName);
+ return succeeded;
+}
+
+void MainWindow::newSubWindow()
+{
+ MdiChild *child = createMdiChild(static_cast<MdiChild *>(mdiArea->activeSubWindow()->widget()));
+ child->show();
+}
+
+static inline QString recentFilesKey() { return QStringLiteral("recentFileList"); }
+static inline QString fileKey() { return QStringLiteral("file"); }
+
+static QStringList readRecentFiles(QSettings &settings)
+{
+ QStringList result;
+ const int count = settings.beginReadArray(recentFilesKey());
+ for (int i = 0; i < count; ++i) {
+ settings.setArrayIndex(i);
+ result.append(settings.value(fileKey()).toString());
+ }
+ settings.endArray();
+ return result;
+}
+
+static void writeRecentFiles(const QStringList &files, QSettings &settings)
+{
+ const int count = files.size();
+ settings.beginWriteArray(recentFilesKey());
+ for (int i = 0; i < count; ++i) {
+ settings.setArrayIndex(i);
+ settings.setValue(fileKey(), files.at(i));
+ }
+ settings.endArray();
+}
+
+bool MainWindow::hasRecentFiles()
+{
+ QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
+ const int count = settings.beginReadArray(recentFilesKey());
+ settings.endArray();
+ return count > 0;
+}
+
+void MainWindow::prependToRecentFiles(const QString &fileName)
+{
+ QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
+
+ const QStringList oldRecentFiles = readRecentFiles(settings);
+ QStringList recentFiles = oldRecentFiles;
+ recentFiles.removeAll(fileName);
+ recentFiles.prepend(fileName);
+ if (oldRecentFiles != recentFiles)
+ writeRecentFiles(recentFiles, settings);
+
+ setRecentFilesVisible(!recentFiles.isEmpty());
+}
+
+void MainWindow::setRecentFilesVisible(bool visible)
+{
+ recentFileSubMenuAct->setVisible(visible);
+ recentFileSeparator->setVisible(visible);
+}
+
+void MainWindow::updateRecentFileActions()
+{
+ QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
+
+ const QStringList recentFiles = readRecentFiles(settings);
+ const int count = qMin(int(MaxRecentFiles), recentFiles.size());
+ int i = 0;
+ for ( ; i < count; ++i) {
+ const QString fileName = QFileInfo(recentFiles.at(i)).fileName();
+ recentFileActs[i]->setText(tr("&%1 %2").arg(i + 1).arg(fileName));
+ recentFileActs[i]->setData(recentFiles.at(i));
+ recentFileActs[i]->setVisible(true);
+ }
+ for ( ; i < MaxRecentFiles; ++i)
+ recentFileActs[i]->setVisible(false);
+}
+
+void MainWindow::openRecentFile()
+{
+ if (const QAction *action = qobject_cast<const QAction *>(sender()))
+ openFile(action->data().toString());
+}
+
+void MainWindow::save()
+{
+ if (activeMdiChild() && activeMdiChild()->save())
+ statusBar()->showMessage(tr("File saved"), 2000);
+}
+
+void MainWindow::saveAs()
+{
+ MdiChild *child = activeMdiChild();
+ if (child && child->saveAs()) {
+ statusBar()->showMessage(tr("File saved"), 2000);
+ MainWindow::prependToRecentFiles(child->currentFile());
+ }
+}
+
+#ifndef QT_NO_CLIPBOARD
+void MainWindow::cut()
+{
+ if (activeMdiChild())
+ activeMdiChild()->cut();
+}
+
+void MainWindow::copy()
+{
+ if (activeMdiChild())
+ activeMdiChild()->copy();
+}
+
+void MainWindow::paste()
+{
+ if (activeMdiChild())
+ activeMdiChild()->paste();
+}
+#endif
+
+void MainWindow::about()
+{
+ QMessageBox::about(this, tr("About MDI"),
+ tr("The <b>MDI</b> example demonstrates how to write multiple "
+ "document interface applications using Qt."));
+}
+
+void MainWindow::updateMenus()
+{
+ bool hasMdiChild = (activeMdiChild() != nullptr);
+ saveAct->setEnabled(hasMdiChild);
+ saveAsAct->setEnabled(hasMdiChild);
+#ifndef QT_NO_CLIPBOARD
+ pasteAct->setEnabled(hasMdiChild);
+#endif
+ newWindowAct->setEnabled(hasMdiChild);
+ closeAct->setEnabled(hasMdiChild);
+ closeAllAct->setEnabled(hasMdiChild);
+ tileAct->setEnabled(hasMdiChild);
+ cascadeAct->setEnabled(hasMdiChild);
+ nextAct->setEnabled(hasMdiChild);
+ previousAct->setEnabled(hasMdiChild);
+ windowMenuSeparatorAct->setVisible(hasMdiChild);
+
+#ifndef QT_NO_CLIPBOARD
+ bool hasSelection = (activeMdiChild() &&
+ activeMdiChild()->textCursor().hasSelection());
+ cutAct->setEnabled(hasSelection);
+ copyAct->setEnabled(hasSelection);
+#endif
+}
+
+void MainWindow::updateWindowMenu()
+{
+ windowMenu->clear();
+ windowMenu->addAction(newWindowAct);
+ windowMenu->addAction(closeAct);
+ windowMenu->addAction(closeAllAct);
+ windowMenu->addSeparator();
+ windowMenu->addAction(tileAct);
+ windowMenu->addAction(cascadeAct);
+ windowMenu->addSeparator();
+ windowMenu->addAction(nextAct);
+ windowMenu->addAction(previousAct);
+ windowMenu->addAction(windowMenuSeparatorAct);
+
+ QList<QMdiSubWindow *> windows = mdiArea->subWindowList();
+ windowMenuSeparatorAct->setVisible(!windows.isEmpty());
+
+ for (int i = 0; i < windows.size(); ++i) {
+ QMdiSubWindow *mdiSubWindow = windows.at(i);
+ MdiChild *child = qobject_cast<MdiChild *>(mdiSubWindow->widget());
+
+ QString text;
+ if (i < 9) {
+ text = tr("&%1 %2").arg(i + 1)
+ .arg(child->userFriendlyCurrentFile());
+ } else {
+ text = tr("%1 %2").arg(i + 1)
+ .arg(child->userFriendlyCurrentFile());
+ }
+ QAction *action = windowMenu->addAction(text, mdiSubWindow, [this, mdiSubWindow]() {
+ mdiArea->setActiveSubWindow(mdiSubWindow);
+ });
+ action->setCheckable(true);
+ action ->setChecked(child == activeMdiChild());
+ }
+}
+
+MdiChild *MainWindow::createMdiChild(MdiChild *other)
+{
+ MdiChild *child = new MdiChild(other);
+ mdiArea->addSubWindow(child);
+
+#ifndef QT_NO_CLIPBOARD
+ connect(child, &QTextEdit::copyAvailable, cutAct, &QAction::setEnabled);
+ connect(child, &QTextEdit::copyAvailable, copyAct, &QAction::setEnabled);
+#endif
+
+ return child;
+}
+
+void MainWindow::createActions()
+{
+ QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
+ QToolBar *fileToolBar = addToolBar(tr("File"));
+
+ const QIcon newIcon = QIcon::fromTheme("document-new", QIcon(":/images/new.png"));
+ newAct = new QAction(newIcon, tr("&New"), this);
+ newAct->setShortcuts(QKeySequence::New);
+ newAct->setStatusTip(tr("Create a new file"));
+ connect(newAct, &QAction::triggered, this, &MainWindow::newFile);
+ fileMenu->addAction(newAct);
+ fileToolBar->addAction(newAct);
+
+//! [qaction setup]
+ const QIcon openIcon = QIcon::fromTheme("document-open", QIcon(":/images/open.png"));
+ QAction *openAct = new QAction(openIcon, tr("&Open..."), this);
+ openAct->setShortcuts(QKeySequence::Open);
+ openAct->setStatusTip(tr("Open an existing file"));
+ connect(openAct, &QAction::triggered, this, &MainWindow::open);
+ fileMenu->addAction(openAct);
+ fileToolBar->addAction(openAct);
+//! [qaction setup]
+
+ const QIcon saveIcon = QIcon::fromTheme("document-save", QIcon(":/images/save.png"));
+ saveAct = new QAction(saveIcon, tr("&Save"), this);
+ saveAct->setShortcuts(QKeySequence::Save);
+ saveAct->setStatusTip(tr("Save the document to disk"));
+ connect(saveAct, &QAction::triggered, this, &MainWindow::save);
+ fileToolBar->addAction(saveAct);
+
+ const QIcon saveAsIcon = QIcon::fromTheme("document-save-as");
+ saveAsAct = new QAction(saveAsIcon, tr("Save &As..."), this);
+ saveAsAct->setShortcuts(QKeySequence::SaveAs);
+ saveAsAct->setStatusTip(tr("Save the document under a new name"));
+ connect(saveAsAct, &QAction::triggered, this, &MainWindow::saveAs);
+ fileMenu->addAction(saveAsAct);
+
+ fileMenu->addSeparator();
+
+ QMenu *recentMenu = fileMenu->addMenu(tr("Recent..."));
+ connect(recentMenu, &QMenu::aboutToShow, this, &MainWindow::updateRecentFileActions);
+ recentFileSubMenuAct = recentMenu->menuAction();
+
+ for (int i = 0; i < MaxRecentFiles; ++i) {
+ recentFileActs[i] = recentMenu->addAction(QString(), this, &MainWindow::openRecentFile);
+ recentFileActs[i]->setVisible(false);
+ }
+
+ recentFileSeparator = fileMenu->addSeparator();
+
+ setRecentFilesVisible(MainWindow::hasRecentFiles());
+
+ fileMenu->addAction(tr("Switch layout direction"), this, &MainWindow::switchLayoutDirection);
+
+ fileMenu->addSeparator();
+
+//! [0]
+ const QIcon exitIcon = QIcon::fromTheme("application-exit");
+ QAction *exitAct = fileMenu->addAction(exitIcon, tr("E&xit"), qApp, &QApplication::quit);
+ exitAct->setShortcuts(QKeySequence::Quit);
+ exitAct->setStatusTip(tr("Exit the application"));
+ fileMenu->addAction(exitAct);
+//! [0]
+
+#ifndef QT_NO_CLIPBOARD
+ QMenu *editMenu = menuBar()->addMenu(tr("&Edit"));
+ QToolBar *editToolBar = addToolBar(tr("Edit"));
+
+ const QIcon cutIcon = QIcon::fromTheme("edit-cut", QIcon(":/images/cut.png"));
+ cutAct = new QAction(cutIcon, tr("Cu&t"), this);
+ cutAct->setShortcuts(QKeySequence::Cut);
+ cutAct->setStatusTip(tr("Cut the current selection's contents to the "
+ "clipboard"));
+ connect(cutAct, &QAction::triggered, this, &MainWindow::cut);
+ editMenu->addAction(cutAct);
+ editToolBar->addAction(cutAct);
+
+ const QIcon copyIcon = QIcon::fromTheme("edit-copy", QIcon(":/images/copy.png"));
+ copyAct = new QAction(copyIcon, tr("&Copy"), this);
+ copyAct->setShortcuts(QKeySequence::Copy);
+ copyAct->setStatusTip(tr("Copy the current selection's contents to the "
+ "clipboard"));
+ connect(copyAct, &QAction::triggered, this, &MainWindow::copy);
+ editMenu->addAction(copyAct);
+ editToolBar->addAction(copyAct);
+
+ const QIcon pasteIcon = QIcon::fromTheme("edit-paste", QIcon(":/images/paste.png"));
+ pasteAct = new QAction(pasteIcon, tr("&Paste"), this);
+ pasteAct->setShortcuts(QKeySequence::Paste);
+ pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current "
+ "selection"));
+ connect(pasteAct, &QAction::triggered, this, &MainWindow::paste);
+ editMenu->addAction(pasteAct);
+ editToolBar->addAction(pasteAct);
+#endif
+
+ windowMenu = menuBar()->addMenu(tr("&Window"));
+ connect(windowMenu, &QMenu::aboutToShow, this, &MainWindow::updateWindowMenu);
+
+ newWindowAct = new QAction(tr("&New view"), this);
+ newWindowAct->setStatusTip(tr("Make another window viewing the same document"));
+ connect(newWindowAct, &QAction::triggered,
+ this, &MainWindow::newSubWindow);
+
+ closeAct = new QAction(tr("Cl&ose"), this);
+ closeAct->setStatusTip(tr("Close the active window"));
+ connect(closeAct, &QAction::triggered,
+ mdiArea, &QMdiArea::closeActiveSubWindow);
+
+ closeAllAct = new QAction(tr("Close &All"), this);
+ closeAllAct->setStatusTip(tr("Close all the windows"));
+ connect(closeAllAct, &QAction::triggered, mdiArea, &QMdiArea::closeAllSubWindows);
+
+ tileAct = new QAction(tr("&Tile"), this);
+ tileAct->setStatusTip(tr("Tile the windows"));
+ connect(tileAct, &QAction::triggered, mdiArea, &QMdiArea::tileSubWindows);
+
+ cascadeAct = new QAction(tr("&Cascade"), this);
+ cascadeAct->setStatusTip(tr("Cascade the windows"));
+ connect(cascadeAct, &QAction::triggered, mdiArea, &QMdiArea::cascadeSubWindows);
+
+ nextAct = new QAction(tr("Ne&xt"), this);
+ nextAct->setShortcuts(QKeySequence::NextChild);
+ nextAct->setStatusTip(tr("Move the focus to the next window"));
+ connect(nextAct, &QAction::triggered, mdiArea, &QMdiArea::activateNextSubWindow);
+
+ previousAct = new QAction(tr("Pre&vious"), this);
+ previousAct->setShortcuts(QKeySequence::PreviousChild);
+ previousAct->setStatusTip(tr("Move the focus to the previous "
+ "window"));
+ connect(previousAct, &QAction::triggered, mdiArea, &QMdiArea::activatePreviousSubWindow);
+
+ windowMenuSeparatorAct = new QAction(this);
+ windowMenuSeparatorAct->setSeparator(true);
+
+ updateWindowMenu();
+
+ menuBar()->addSeparator();
+
+ QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
+
+ QAction *aboutAct = helpMenu->addAction(tr("&About"), this, &MainWindow::about);
+ aboutAct->setStatusTip(tr("Show the application's About box"));
+
+ QAction *aboutQtAct = helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
+ aboutQtAct->setStatusTip(tr("Show the Qt library's About box"));
+}
+
+void MainWindow::createStatusBar()
+{
+ statusBar()->showMessage(tr("Ready"));
+}
+
+void MainWindow::readSettings()
+{
+ QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
+ const QByteArray geometry = settings.value("geometry", QByteArray()).toByteArray();
+ if (geometry.isEmpty()) {
+ const QRect availableGeometry = screen()->availableGeometry();
+ resize(availableGeometry.width() / 3, availableGeometry.height() / 2);
+ move((availableGeometry.width() - width()) / 2,
+ (availableGeometry.height() - height()) / 2);
+ } else {
+ restoreGeometry(geometry);
+ }
+}
+
+void MainWindow::writeSettings()
+{
+ QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
+ settings.setValue("geometry", saveGeometry());
+}
+
+MdiChild *MainWindow::activeMdiChild() const
+{
+ if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow())
+ return qobject_cast<MdiChild *>(activeSubWindow->widget());
+ return nullptr;
+}
+
+QMdiSubWindow *MainWindow::findMdiChild(const QString &fileName) const
+{
+ QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath();
+
+ const QList<QMdiSubWindow *> subWindows = mdiArea->subWindowList();
+ for (QMdiSubWindow *window : subWindows) {
+ MdiChild *mdiChild = qobject_cast<MdiChild *>(window->widget());
+ if (mdiChild->currentFile() == canonicalFilePath)
+ return window;
+ }
+ return nullptr;
+}
+
+void MainWindow::switchLayoutDirection()
+{
+ if (layoutDirection() == Qt::LeftToRight)
+ QGuiApplication::setLayoutDirection(Qt::RightToLeft);
+ else
+ QGuiApplication::setLayoutDirection(Qt::LeftToRight);
+}
diff --git a/tests/manual/examples/widgets/mainwindows/mdi/mainwindow.h b/tests/manual/examples/widgets/mainwindows/mdi/mainwindow.h
new file mode 100644
index 0000000000..c8b720e216
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mdi/mainwindow.h
@@ -0,0 +1,87 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+
+class MdiChild;
+QT_BEGIN_NAMESPACE
+class QAction;
+class QMenu;
+class QMdiArea;
+class QMdiSubWindow;
+class QTextDocument;
+QT_END_NAMESPACE
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow();
+
+ bool openFile(const QString &fileName);
+
+protected:
+ void closeEvent(QCloseEvent *event) override;
+
+private slots:
+ void newFile();
+ void open();
+ void save();
+ void saveAs();
+ void updateRecentFileActions();
+ void openRecentFile();
+#ifndef QT_NO_CLIPBOARD
+ void cut();
+ void copy();
+ void paste();
+#endif
+ void about();
+ void updateMenus();
+ void updateWindowMenu();
+ MdiChild *createMdiChild(MdiChild *other = nullptr);
+ void switchLayoutDirection();
+ void newSubWindow();
+
+private:
+ enum { MaxRecentFiles = 5 };
+
+ void createActions();
+ void createStatusBar();
+ void readSettings();
+ void writeSettings();
+ bool loadFile(const QString &fileName);
+ static bool hasRecentFiles();
+ void prependToRecentFiles(const QString &fileName);
+ void setRecentFilesVisible(bool visible);
+ MdiChild *activeMdiChild() const;
+ QMdiSubWindow *findMdiChild(const QString &fileName) const;
+
+ QMdiArea *mdiArea;
+
+ QMenu *windowMenu;
+ QAction *newAct;
+ QAction *saveAct;
+ QAction *saveAsAct;
+ QAction *recentFileActs[MaxRecentFiles];
+ QAction *recentFileSeparator;
+ QAction *recentFileSubMenuAct;
+#ifndef QT_NO_CLIPBOARD
+ QAction *cutAct;
+ QAction *copyAct;
+ QAction *pasteAct;
+#endif
+ QAction *newWindowAct;
+ QAction *closeAct;
+ QAction *closeAllAct;
+ QAction *tileAct;
+ QAction *cascadeAct;
+ QAction *nextAct;
+ QAction *previousAct;
+ QAction *windowMenuSeparatorAct;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/mainwindows/mdi/mdi.pro b/tests/manual/examples/widgets/mainwindows/mdi/mdi.pro
new file mode 100644
index 0000000000..f2c236f7e2
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mdi/mdi.pro
@@ -0,0 +1,13 @@
+QT += widgets
+requires(qtConfig(filedialog))
+
+HEADERS = mainwindow.h \
+ mdichild.h
+SOURCES = main.cpp \
+ mainwindow.cpp \
+ mdichild.cpp
+RESOURCES = mdi.qrc
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/mainwindows/mdi
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/mainwindows/mdi/mdi.qrc b/tests/manual/examples/widgets/mainwindows/mdi/mdi.qrc
new file mode 100644
index 0000000000..0a776fab4d
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mdi/mdi.qrc
@@ -0,0 +1,10 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>images/copy.png</file>
+ <file>images/cut.png</file>
+ <file>images/new.png</file>
+ <file>images/open.png</file>
+ <file>images/paste.png</file>
+ <file>images/save.png</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/mainwindows/mdi/mdichild.cpp b/tests/manual/examples/widgets/mainwindows/mdi/mdichild.cpp
new file mode 100644
index 0000000000..b922826ae3
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mdi/mdichild.cpp
@@ -0,0 +1,158 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+
+#include "mdichild.h"
+
+MdiChild::MdiChild(MdiChild *other)
+{
+ setAttribute(Qt::WA_DeleteOnClose);
+ if (other) {
+ auto *doc = other->document();
+ setDocument(doc);
+ setWindowModified(other->isWindowModified());
+ connect(doc, &QTextDocument::contentsChanged,
+ this, &MdiChild::documentWasModified);
+ curFile = other->curFile;
+ setWindowTitle(userFriendlyCurrentFile() + "[*]");
+ }
+}
+
+void MdiChild::newFile()
+{
+ static int sequenceNumber = 1;
+
+ isUntitled = true;
+ curFile = tr("document%1.txt").arg(sequenceNumber++);
+ setWindowTitle(curFile + "[*]");
+
+ connect(document(), &QTextDocument::contentsChanged,
+ this, &MdiChild::documentWasModified);
+}
+
+bool MdiChild::loadFile(const QString &fileName)
+{
+ QFile file(fileName);
+ if (!file.open(QFile::ReadOnly | QFile::Text)) {
+ QMessageBox::warning(this, tr("MDI"),
+ tr("Cannot read file %1:\n%2.")
+ .arg(fileName)
+ .arg(file.errorString()));
+ return false;
+ }
+
+ QTextStream in(&file);
+ QGuiApplication::setOverrideCursor(Qt::WaitCursor);
+ setPlainText(in.readAll());
+ QGuiApplication::restoreOverrideCursor();
+
+ setCurrentFile(fileName);
+
+ connect(document(), &QTextDocument::contentsChanged,
+ this, &MdiChild::documentWasModified);
+
+ return true;
+}
+
+bool MdiChild::save()
+{
+ if (isUntitled) {
+ return saveAs();
+ } else {
+ return saveFile(curFile);
+ }
+}
+
+bool MdiChild::saveAs()
+{
+ QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"),
+ curFile);
+ if (fileName.isEmpty())
+ return false;
+
+ return saveFile(fileName);
+}
+
+bool MdiChild::saveFile(const QString &fileName)
+{
+ QString errorMessage;
+
+ QGuiApplication::setOverrideCursor(Qt::WaitCursor);
+ QSaveFile file(fileName);
+ if (file.open(QFile::WriteOnly | QFile::Text)) {
+ QTextStream out(&file);
+ out << toPlainText();
+ if (!file.commit()) {
+ errorMessage = tr("Cannot write file %1:\n%2.")
+ .arg(QDir::toNativeSeparators(fileName), file.errorString());
+ }
+ } else {
+ errorMessage = tr("Cannot open file %1 for writing:\n%2.")
+ .arg(QDir::toNativeSeparators(fileName), file.errorString());
+ }
+ QGuiApplication::restoreOverrideCursor();
+
+ if (!errorMessage.isEmpty()) {
+ QMessageBox::warning(this, tr("MDI"), errorMessage);
+ return false;
+ }
+
+ setCurrentFile(fileName);
+ return true;
+}
+
+QString MdiChild::userFriendlyCurrentFile()
+{
+ return strippedName(curFile);
+}
+
+void MdiChild::closeEvent(QCloseEvent *event)
+{
+ if (maybeSave()) {
+ event->accept();
+ } else {
+ event->ignore();
+ }
+}
+
+void MdiChild::documentWasModified()
+{
+ setWindowModified(document()->isModified());
+}
+
+bool MdiChild::maybeSave()
+{
+ if (!document()->isModified())
+ return true;
+ const QMessageBox::StandardButton ret
+ = QMessageBox::warning(this, tr("MDI"),
+ tr("'%1' has been modified.\n"
+ "Do you want to save your changes?")
+ .arg(userFriendlyCurrentFile()),
+ QMessageBox::Save | QMessageBox::Discard
+ | QMessageBox::Cancel);
+ switch (ret) {
+ case QMessageBox::Save:
+ return save();
+ case QMessageBox::Cancel:
+ return false;
+ default:
+ break;
+ }
+ return true;
+}
+
+void MdiChild::setCurrentFile(const QString &fileName)
+{
+ curFile = QFileInfo(fileName).canonicalFilePath();
+ isUntitled = false;
+ document()->setModified(false);
+ setWindowModified(false);
+ setWindowTitle(userFriendlyCurrentFile() + "[*]");
+}
+
+QString MdiChild::strippedName(const QString &fullFileName)
+{
+ return QFileInfo(fullFileName).fileName();
+}
diff --git a/tests/manual/examples/widgets/mainwindows/mdi/mdichild.h b/tests/manual/examples/widgets/mainwindows/mdi/mdichild.h
new file mode 100644
index 0000000000..813a845594
--- /dev/null
+++ b/tests/manual/examples/widgets/mainwindows/mdi/mdichild.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MDICHILD_H
+#define MDICHILD_H
+
+#include <QTextEdit>
+
+class MdiChild : public QTextEdit
+{
+ Q_OBJECT
+
+public:
+ MdiChild(MdiChild *other = nullptr);
+
+ void newFile();
+ bool loadFile(const QString &fileName);
+ bool save();
+ bool saveAs();
+ bool saveFile(const QString &fileName);
+ QString userFriendlyCurrentFile();
+ QString currentFile() { return curFile; }
+
+protected:
+ void closeEvent(QCloseEvent *event) override;
+
+private slots:
+ void documentWasModified();
+
+private:
+ bool maybeSave();
+ void setCurrentFile(const QString &fileName);
+ QString strippedName(const QString &fullFileName);
+
+ QString curFile;
+ bool isUntitled = true;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/painting/fontsampler/CMakeLists.txt b/tests/manual/examples/widgets/painting/fontsampler/CMakeLists.txt
new file mode 100644
index 0000000000..576bd122a0
--- /dev/null
+++ b/tests/manual/examples/widgets/painting/fontsampler/CMakeLists.txt
@@ -0,0 +1,45 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(fontsampler LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/painting/fontsampler")
+
+find_package(Qt6
+ REQUIRED COMPONENTS Core Gui Widgets
+ OPTIONAL_COMPONENTS PrintSupport
+)
+
+qt_standard_project_setup()
+
+qt_add_executable(fontsampler
+ main.cpp
+ mainwindow.cpp mainwindow.h
+ mainwindowbase.ui
+)
+
+set_target_properties(fontsampler PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(fontsampler PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+if(TARGET Qt6::PrintSupport)
+ target_link_libraries(fontsampler PRIVATE Qt6::PrintSupport)
+endif()
+
+install(TARGETS fontsampler
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/painting/fontsampler/fontsampler.pro b/tests/manual/examples/widgets/painting/fontsampler/fontsampler.pro
new file mode 100644
index 0000000000..5d3461f4b8
--- /dev/null
+++ b/tests/manual/examples/widgets/painting/fontsampler/fontsampler.pro
@@ -0,0 +1,12 @@
+QT += widgets
+requires(qtConfig(combobox))
+qtHaveModule(printsupport): QT += printsupport
+
+FORMS = mainwindowbase.ui
+HEADERS = mainwindow.h
+SOURCES = main.cpp \
+ mainwindow.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/painting/fontsampler
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/painting/fontsampler/main.cpp b/tests/manual/examples/widgets/painting/fontsampler/main.cpp
new file mode 100644
index 0000000000..7d7cf3e573
--- /dev/null
+++ b/tests/manual/examples/widgets/painting/fontsampler/main.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ MainWindow window;
+ window.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/painting/fontsampler/mainwindow.cpp b/tests/manual/examples/widgets/painting/fontsampler/mainwindow.cpp
new file mode 100644
index 0000000000..e2bd768f79
--- /dev/null
+++ b/tests/manual/examples/widgets/painting/fontsampler/mainwindow.cpp
@@ -0,0 +1,314 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#if defined(QT_PRINTSUPPORT_LIB)
+#include <QtPrintSupport/qtprintsupportglobal.h>
+#if QT_CONFIG(printdialog)
+#include <QPrinter>
+#include <QPrintDialog>
+#if QT_CONFIG(printpreviewdialog)
+#include <QPrintPreviewDialog>
+#endif
+#endif
+#endif
+
+#include "mainwindow.h"
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent)
+{
+ setupUi(this);
+
+ sampleSizes << 32 << 24 << 16 << 14 << 12 << 8 << 4 << 2 << 1;
+ markedCount = 0;
+ setupFontTree();
+
+ connect(quitAction, &QAction::triggered,
+ qApp, &QApplication::quit);
+ connect(fontTree, &QTreeWidget::currentItemChanged,
+ this, &MainWindow::showFont);
+ connect(fontTree, &QTreeWidget::itemChanged,
+ this, &MainWindow::updateStyles);
+
+ fontTree->topLevelItem(0)->setSelected(true);
+ showFont(fontTree->topLevelItem(0));
+}
+
+void MainWindow::setupFontTree()
+{
+ fontTree->setColumnCount(1);
+ fontTree->setHeaderLabels({ tr("Font") });
+
+ const QStringList fontFamilies = QFontDatabase::families();
+ for (const QString &family : fontFamilies) {
+ const QStringList styles = QFontDatabase::styles(family);
+ if (styles.isEmpty())
+ continue;
+
+ QTreeWidgetItem *familyItem = new QTreeWidgetItem(fontTree);
+ familyItem->setText(0, family);
+ familyItem->setCheckState(0, Qt::Unchecked);
+ familyItem->setFlags(familyItem->flags() | Qt::ItemIsAutoTristate);
+
+ for (const QString &style : styles) {
+ QTreeWidgetItem *styleItem = new QTreeWidgetItem(familyItem);
+ styleItem->setText(0, style);
+ styleItem->setCheckState(0, Qt::Unchecked);
+ styleItem->setData(0, Qt::UserRole, QVariant(QFontDatabase::weight(family, style)));
+ styleItem->setData(0, Qt::UserRole + 1, QVariant(QFontDatabase::italic(family, style)));
+ }
+ }
+}
+
+void MainWindow::on_clearAction_triggered()
+{
+ const QList<QTreeWidgetItem *> items = fontTree->selectedItems();
+ for (QTreeWidgetItem *item : items)
+ item->setSelected(false);
+ fontTree->currentItem()->setSelected(true);
+}
+
+void MainWindow::on_markAction_triggered()
+{
+ markUnmarkFonts(Qt::Checked);
+}
+
+void MainWindow::on_unmarkAction_triggered()
+{
+ markUnmarkFonts(Qt::Unchecked);
+}
+
+void MainWindow::markUnmarkFonts(Qt::CheckState state)
+{
+ const QList<QTreeWidgetItem *> items = fontTree->selectedItems();
+ for (QTreeWidgetItem *item : items) {
+ if (item->checkState(0) != state)
+ item->setCheckState(0, state);
+ }
+}
+
+void MainWindow::showFont(QTreeWidgetItem *item)
+{
+ if (!item)
+ return;
+
+ QString family;
+ QString style;
+ int weight;
+ bool italic;
+
+ if (item->parent()) {
+ family = item->parent()->text(0);
+ style = item->text(0);
+ weight = item->data(0, Qt::UserRole).toInt();
+ italic = item->data(0, Qt::UserRole + 1).toBool();
+ } else {
+ family = item->text(0);
+ style = item->child(0)->text(0);
+ weight = item->child(0)->data(0, Qt::UserRole).toInt();
+ italic = item->child(0)->data(0, Qt::UserRole + 1).toBool();
+ }
+
+ QString oldText = textEdit->toPlainText().trimmed();
+ bool modified = textEdit->document()->isModified();
+ textEdit->clear();
+ QFont font(family, 32, weight, italic);
+ font.setStyleName(style);
+ textEdit->document()->setDefaultFont(font);
+
+ QTextCursor cursor = textEdit->textCursor();
+ QTextBlockFormat blockFormat;
+ blockFormat.setAlignment(Qt::AlignCenter);
+ cursor.insertBlock(blockFormat);
+
+ if (modified)
+ cursor.insertText(QString(oldText));
+ else
+ cursor.insertText(QString("%1 %2").arg(family).arg(style));
+
+ textEdit->document()->setModified(modified);
+}
+
+void MainWindow::updateStyles(QTreeWidgetItem *item, int column)
+{
+ if (!item || column != 0)
+ return;
+
+ Qt::CheckState state = item->checkState(0);
+ QTreeWidgetItem *parent = item->parent();
+
+ if (parent) {
+ // Only count style items.
+ if (state == Qt::Checked)
+ ++markedCount;
+ else
+ --markedCount;
+ }
+
+ printAction->setEnabled(markedCount > 0);
+ printPreviewAction->setEnabled(markedCount > 0);
+}
+
+QMap<QString, StyleItems> MainWindow::currentPageMap()
+{
+ QMap<QString, StyleItems> pageMap;
+
+ for (int row = 0; row < fontTree->topLevelItemCount(); ++row) {
+ QTreeWidgetItem *familyItem = fontTree->topLevelItem(row);
+ QString family;
+
+ if (familyItem->checkState(0) == Qt::Checked) {
+ family = familyItem->text(0);
+ pageMap[family] = StyleItems();
+ }
+
+ for (int childRow = 0; childRow < familyItem->childCount(); ++childRow) {
+ QTreeWidgetItem *styleItem = familyItem->child(childRow);
+ if (styleItem->checkState(0) == Qt::Checked)
+ pageMap[family].append(styleItem);
+ }
+ }
+
+ return pageMap;
+}
+
+void MainWindow::on_printAction_triggered()
+{
+#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog)
+ pageMap = currentPageMap();
+
+ if (pageMap.count() == 0)
+ return;
+
+ QPrinter printer(QPrinter::HighResolution);
+ QPrintDialog dialog(&printer, this);
+ if (dialog.exec() != QDialog::Accepted)
+ return;
+
+ int from = printer.fromPage();
+ int to = printer.toPage();
+ if (from <= 0 && to <= 0)
+ printer.setFromTo(1, pageMap.keys().count());
+
+ printDocument(&printer);
+#endif
+}
+
+void MainWindow::printDocument(QPrinter *printer)
+{
+#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog)
+ printer->setFromTo(1, pageMap.count());
+
+ QProgressDialog progress(tr("Preparing font samples..."), tr("&Cancel"),
+ 0, pageMap.count(), this);
+ progress.setWindowModality(Qt::ApplicationModal);
+ progress.setWindowTitle(tr("Font Sampler"));
+ progress.setMinimum(printer->fromPage() - 1);
+ progress.setMaximum(printer->toPage());
+
+ QPainter painter;
+ painter.begin(printer);
+ bool firstPage = true;
+
+ for (int page = printer->fromPage(); page <= printer->toPage(); ++page) {
+
+ if (!firstPage)
+ printer->newPage();
+
+ qApp->processEvents();
+ if (progress.wasCanceled())
+ break;
+
+ printPage(page - 1, &painter, printer);
+ progress.setValue(page);
+ firstPage = false;
+ }
+
+ painter.end();
+#endif
+}
+
+void MainWindow::on_printPreviewAction_triggered()
+{
+#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printpreviewdialog)
+ pageMap = currentPageMap();
+
+ if (pageMap.count() == 0)
+ return;
+
+ QPrinter printer(QPrinter::HighResolution);
+ QPrintPreviewDialog preview(&printer, this);
+ connect(&preview, &QPrintPreviewDialog::paintRequested,
+ this, &MainWindow::printDocument);
+ preview.exec();
+#endif
+}
+
+void MainWindow::printPage(int index, QPainter *painter, QPrinter *printer)
+{
+#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog)
+ const QString family = std::next(pageMap.begin(), index).key();
+ const StyleItems items = pageMap.value(family);
+
+ // Find the dimensions of the text on each page.
+ qreal width = 0.0;
+ qreal height = 0.0;
+ for (const QTreeWidgetItem *item : items) {
+ QString style = item->text(0);
+ int weight = item->data(0, Qt::UserRole).toInt();
+ bool italic = item->data(0, Qt::UserRole + 1).toBool();
+
+ // Calculate the maximum width and total height of the text.
+ for (int size : std::as_const(sampleSizes)) {
+ QFont font(family, size, weight, italic);
+ font.setStyleName(style);
+ font = QFont(font, painter->device());
+ QFontMetricsF fontMetrics(font);
+ QRectF rect = fontMetrics.boundingRect(
+ QString("%1 %2").arg(family).arg(style));
+ width = qMax(rect.width(), width);
+ height += rect.height();
+ }
+ }
+
+ qreal xScale = printer->pageRect(QPrinter::DevicePixel).width() / width;
+ qreal yScale = printer->pageRect(QPrinter::DevicePixel).height() / height;
+ qreal scale = qMin(xScale, yScale);
+
+ qreal remainingHeight = printer->pageRect(QPrinter::DevicePixel).height()/scale - height;
+ qreal spaceHeight = (remainingHeight / 4.0) / (items.count() + 1);
+ qreal interLineHeight = (remainingHeight / 4.0) / (sampleSizes.count() * items.count());
+
+ painter->save();
+ painter->translate(printer->pageRect(QPrinter::DevicePixel).width() / 2.0, printer->pageRect(QPrinter::DevicePixel).height() / 2.0);
+ painter->scale(scale, scale);
+ painter->setBrush(QBrush(Qt::black));
+
+ qreal x = -width / 2.0;
+ qreal y = -height / 2.0 - remainingHeight / 4.0 + spaceHeight;
+
+ for (const QTreeWidgetItem *item : items) {
+ QString style = item->text(0);
+ int weight = item->data(0, Qt::UserRole).toInt();
+ bool italic = item->data(0, Qt::UserRole + 1).toBool();
+
+ // Draw each line of text.
+ for (int size : std::as_const(sampleSizes)) {
+ QFont font(family, size, weight, italic);
+ font.setStyleName(style);
+ font = QFont(font, painter->device());
+ QFontMetricsF fontMetrics(font);
+ QRectF rect = fontMetrics.boundingRect(QString("%1 %2").arg(
+ font.family()).arg(style));
+ y += rect.height();
+ painter->setFont(font);
+ painter->drawText(QPointF(x, y), QString("%1 %2").arg(family).arg(style));
+ y += interLineHeight;
+ }
+ y += spaceHeight;
+ }
+
+ painter->restore();
+#endif
+}
diff --git a/tests/manual/examples/widgets/painting/fontsampler/mainwindow.h b/tests/manual/examples/widgets/painting/fontsampler/mainwindow.h
new file mode 100644
index 0000000000..131fb06312
--- /dev/null
+++ b/tests/manual/examples/widgets/painting/fontsampler/mainwindow.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include "ui_mainwindowbase.h"
+
+#if defined(QT_PRINTSUPPORT_LIB)
+#include <QtPrintSupport/qtprintsupportglobal.h>
+#endif
+
+#include <QList>
+#include <QMap>
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+class QPrinter;
+class QTextEdit;
+class QTreeWidget;
+class QTreeWidgetItem;
+QT_END_NAMESPACE
+
+typedef QList<QTreeWidgetItem *> StyleItems;
+
+class MainWindow : public QMainWindow, private Ui::MainWindowBase
+{
+ Q_OBJECT
+
+public:
+ MainWindow(QWidget *parent = nullptr);
+
+public slots:
+ void on_clearAction_triggered();
+ void on_markAction_triggered();
+ void on_unmarkAction_triggered();
+ void on_printAction_triggered();
+ void on_printPreviewAction_triggered();
+ void printDocument(QPrinter *printer);
+ void printPage(int index, QPainter *painter, QPrinter *printer);
+ void showFont(QTreeWidgetItem *item);
+ void updateStyles(QTreeWidgetItem *item, int column);
+
+private:
+ QMap<QString, StyleItems> currentPageMap();
+ void markUnmarkFonts(Qt::CheckState state);
+ void setupFontTree();
+
+ QList<int> sampleSizes;
+ QMap<QString, StyleItems> pageMap;
+ int markedCount;
+};
+
+#endif // MAINWINDOW_H
diff --git a/tests/manual/examples/widgets/painting/fontsampler/mainwindowbase.ui b/tests/manual/examples/widgets/painting/fontsampler/mainwindowbase.ui
new file mode 100644
index 0000000000..1a95ebd6d1
--- /dev/null
+++ b/tests/manual/examples/widgets/painting/fontsampler/mainwindowbase.ui
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindowBase</class>
+ <widget class="QMainWindow" name="MainWindowBase">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>345</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Font Sampler</string>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QTextEdit" name="textEdit"/>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>18</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menu_Selection">
+ <property name="title">
+ <string>&amp;Selection</string>
+ </property>
+ <addaction name="markAction"/>
+ <addaction name="unmarkAction"/>
+ <addaction name="clearAction"/>
+ </widget>
+ <widget class="QMenu" name="menu_File">
+ <property name="title">
+ <string>&amp;File</string>
+ </property>
+ <addaction name="printPreviewAction"/>
+ <addaction name="printAction"/>
+ <addaction name="quitAction"/>
+ </widget>
+ <addaction name="menu_File"/>
+ <addaction name="menu_Selection"/>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ <widget class="QDockWidget" name="dockWidget">
+ <property name="features">
+ <set>QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable</set>
+ </property>
+ <property name="windowTitle">
+ <string>Available Fonts</string>
+ </property>
+ <attribute name="dockWidgetArea">
+ <number>1</number>
+ </attribute>
+ <widget class="QWidget" name="dockWidgetContents">
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QTreeWidget" name="fontTree">
+ <property name="selectionMode">
+ <enum>QAbstractItemView::ExtendedSelection</enum>
+ </property>
+ <column>
+ <property name="text">
+ <string notr="true">1</string>
+ </property>
+ </column>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <action name="printAction">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Print...</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+P</string>
+ </property>
+ </action>
+ <action name="quitAction">
+ <property name="text">
+ <string>E&amp;xit</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Q</string>
+ </property>
+ </action>
+ <action name="markAction">
+ <property name="text">
+ <string>&amp;Mark</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+M</string>
+ </property>
+ </action>
+ <action name="unmarkAction">
+ <property name="text">
+ <string>&amp;Unmark</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+U</string>
+ </property>
+ </action>
+ <action name="clearAction">
+ <property name="text">
+ <string>&amp;Clear</string>
+ </property>
+ </action>
+ <action name="printPreviewAction">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Print Preview...</string>
+ </property>
+ </action>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tests/manual/examples/widgets/richtext/calendar/CMakeLists.txt b/tests/manual/examples/widgets/richtext/calendar/CMakeLists.txt
new file mode 100644
index 0000000000..7b8e10060b
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/calendar/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(calendar LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/richtext/calendar")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(calendar
+ main.cpp
+ mainwindow.cpp mainwindow.h
+)
+
+set_target_properties(calendar PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(calendar PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS calendar
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/richtext/calendar/calendar.pro b/tests/manual/examples/widgets/richtext/calendar/calendar.pro
new file mode 100644
index 0000000000..199c1dbb8d
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/calendar/calendar.pro
@@ -0,0 +1,10 @@
+QT += widgets
+requires(qtConfig(combobox))
+
+HEADERS = mainwindow.h
+SOURCES = main.cpp \
+ mainwindow.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/richtext/calendar
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/richtext/calendar/main.cpp b/tests/manual/examples/widgets/richtext/calendar/main.cpp
new file mode 100644
index 0000000000..5641ae527a
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/calendar/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ MainWindow window;
+ window.resize(640, 256);
+ window.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/richtext/calendar/mainwindow.cpp b/tests/manual/examples/widgets/richtext/calendar/mainwindow.cpp
new file mode 100644
index 0000000000..0b44c96d46
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/calendar/mainwindow.cpp
@@ -0,0 +1,179 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+
+#include <QtWidgets>
+
+//! [0]
+MainWindow::MainWindow()
+{
+ selectedDate = QDate::currentDate();
+ fontSize = 10;
+
+ QWidget *centralWidget = new QWidget;
+//! [0]
+
+//! [1]
+ QLabel *dateLabel = new QLabel(tr("Date:"));
+ QComboBox *monthCombo = new QComboBox;
+
+ for (int month = 1; month <= 12; ++month)
+ monthCombo->addItem(QLocale::system().monthName(month));
+
+ QDateTimeEdit *yearEdit = new QDateTimeEdit;
+ yearEdit->setDisplayFormat("yyyy");
+ yearEdit->setDateRange(QDate(1753, 1, 1), QDate(8000, 1, 1));
+//! [1]
+
+ monthCombo->setCurrentIndex(selectedDate.month() - 1);
+ yearEdit->setDate(selectedDate);
+
+//! [2]
+ QLabel *fontSizeLabel = new QLabel(tr("Font size:"));
+ QSpinBox *fontSizeSpinBox = new QSpinBox;
+ fontSizeSpinBox->setRange(1, 64);
+
+ editor = new QTextBrowser;
+ insertCalendar();
+//! [2]
+
+//! [3]
+ connect(monthCombo, &QComboBox::activated,
+ this, &MainWindow::setMonth);
+ connect(yearEdit, &QDateTimeEdit::dateChanged,
+ this, &MainWindow::setYear);
+ connect(fontSizeSpinBox, &QSpinBox::valueChanged,
+ this, &MainWindow::setFontSize);
+//! [3]
+
+ fontSizeSpinBox->setValue(10);
+
+//! [4]
+ QHBoxLayout *controlsLayout = new QHBoxLayout;
+ controlsLayout->addWidget(dateLabel);
+ controlsLayout->addWidget(monthCombo);
+ controlsLayout->addWidget(yearEdit);
+ controlsLayout->addSpacing(24);
+ controlsLayout->addWidget(fontSizeLabel);
+ controlsLayout->addWidget(fontSizeSpinBox);
+ controlsLayout->addStretch(1);
+
+ QVBoxLayout *centralLayout = new QVBoxLayout;
+ centralLayout->addLayout(controlsLayout);
+ centralLayout->addWidget(editor, 1);
+ centralWidget->setLayout(centralLayout);
+
+ setCentralWidget(centralWidget);
+//! [4]
+}
+
+//! [5]
+void MainWindow::insertCalendar()
+{
+ editor->clear();
+ QTextCursor cursor = editor->textCursor();
+ cursor.beginEditBlock();
+
+ QDate date(selectedDate.year(), selectedDate.month(), 1);
+//! [5]
+
+//! [6]
+ QTextTableFormat tableFormat;
+ tableFormat.setAlignment(Qt::AlignHCenter);
+ tableFormat.setBackground(QColor("#e0e0e0"));
+ tableFormat.setCellPadding(2);
+ tableFormat.setCellSpacing(4);
+//! [6] //! [7]
+ QList<QTextLength> constraints;
+ constraints << QTextLength(QTextLength::PercentageLength, 14)
+ << QTextLength(QTextLength::PercentageLength, 14)
+ << QTextLength(QTextLength::PercentageLength, 14)
+ << QTextLength(QTextLength::PercentageLength, 14)
+ << QTextLength(QTextLength::PercentageLength, 14)
+ << QTextLength(QTextLength::PercentageLength, 14)
+ << QTextLength(QTextLength::PercentageLength, 14);
+ tableFormat.setColumnWidthConstraints(constraints);
+//! [7]
+
+//! [8]
+ QTextTable *table = cursor.insertTable(1, 7, tableFormat);
+//! [8]
+
+//! [9]
+ QTextFrame *frame = cursor.currentFrame();
+ QTextFrameFormat frameFormat = frame->frameFormat();
+ frameFormat.setBorder(1);
+ frame->setFrameFormat(frameFormat);
+//! [9]
+
+//! [10]
+ QTextCharFormat format = cursor.charFormat();
+ format.setFontPointSize(fontSize);
+
+ QTextCharFormat boldFormat = format;
+ boldFormat.setFontWeight(QFont::Bold);
+
+ QTextCharFormat highlightedFormat = boldFormat;
+ highlightedFormat.setBackground(Qt::yellow);
+//! [10]
+
+//! [11]
+ for (int weekDay = 1; weekDay <= 7; ++weekDay) {
+ QTextTableCell cell = table->cellAt(0, weekDay-1);
+//! [11] //! [12]
+ QTextCursor cellCursor = cell.firstCursorPosition();
+ cellCursor.insertText(QLocale::system().dayName(weekDay), boldFormat);
+ }
+//! [12]
+
+//! [13]
+ table->insertRows(table->rows(), 1);
+//! [13]
+
+ while (date.month() == selectedDate.month()) {
+ int weekDay = date.dayOfWeek();
+ QTextTableCell cell = table->cellAt(table->rows()-1, weekDay-1);
+ QTextCursor cellCursor = cell.firstCursorPosition();
+
+ if (date == QDate::currentDate())
+ cellCursor.insertText(QString("%1").arg(date.day()), highlightedFormat);
+ else
+ cellCursor.insertText(QString("%1").arg(date.day()), format);
+
+ date = date.addDays(1);
+ if (weekDay == 7 && date.month() == selectedDate.month())
+ table->insertRows(table->rows(), 1);
+ }
+
+ cursor.endEditBlock();
+//! [14]
+ setWindowTitle(tr("Calendar for %1 %2"
+ ).arg(QLocale::system().monthName(selectedDate.month())
+ ).arg(selectedDate.year()));
+}
+//! [14]
+
+//! [15]
+void MainWindow::setFontSize(int size)
+{
+ fontSize = size;
+ insertCalendar();
+}
+//! [15]
+
+//! [16]
+void MainWindow::setMonth(int month)
+{
+ selectedDate = QDate(selectedDate.year(), month + 1, selectedDate.day());
+ insertCalendar();
+}
+//! [16]
+
+//! [17]
+void MainWindow::setYear(QDate date)
+{
+ selectedDate = QDate(date.year(), selectedDate.month(), selectedDate.day());
+ insertCalendar();
+}
+//! [17]
diff --git a/tests/manual/examples/widgets/richtext/calendar/mainwindow.h b/tests/manual/examples/widgets/richtext/calendar/mainwindow.h
new file mode 100644
index 0000000000..76632820d6
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/calendar/mainwindow.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QDate>
+#include <QMainWindow>
+
+QT_BEGIN_NAMESPACE
+class QTextBrowser;
+QT_END_NAMESPACE
+
+//! [0]
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow();
+
+public slots:
+ void setFontSize(int size);
+ void setMonth(int month);
+ void setYear(QDate date);
+
+private:
+ void insertCalendar();
+
+ int fontSize;
+ QDate selectedDate;
+ QTextBrowser *editor;
+};
+//! [0]
+
+#endif // MAINWINDOW_H
diff --git a/tests/manual/examples/widgets/richtext/textedit/CMakeLists.txt b/tests/manual/examples/widgets/richtext/textedit/CMakeLists.txt
new file mode 100644
index 0000000000..6e8df9134d
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/CMakeLists.txt
@@ -0,0 +1,105 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(textedit LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/richtext/textedit")
+
+find_package(Qt6
+ REQUIRED COMPONENTS Core Gui Widgets
+ OPTIONAL_COMPONENTS PrintSupport
+)
+
+qt_standard_project_setup()
+
+qt_add_executable(textedit
+ main.cpp
+ textedit.cpp textedit.h
+)
+
+set_target_properties(textedit PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(textedit PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+if (TARGET Qt6::PrintSupport)
+ target_link_libraries(textedit PRIVATE Qt6::PrintSupport)
+endif()
+
+# Resources:
+set(textedit_resource_files
+ "example.html"
+ "images/logo32.png"
+ "images/mac/checkbox-checked.png"
+ "images/mac/checkbox.png"
+ "images/mac/editcopy.png"
+ "images/mac/editcut.png"
+ "images/mac/editpaste.png"
+ "images/mac/editredo.png"
+ "images/mac/editundo.png"
+ "images/mac/exportpdf.png"
+ "images/mac/filenew.png"
+ "images/mac/fileopen.png"
+ "images/mac/fileprint.png"
+ "images/mac/filesave.png"
+ "images/mac/format-indent-less.png"
+ "images/mac/format-indent-more.png"
+ "images/mac/textbold.png"
+ "images/mac/textcenter.png"
+ "images/mac/textitalic.png"
+ "images/mac/textjustify.png"
+ "images/mac/textleft.png"
+ "images/mac/textright.png"
+ "images/mac/textunder.png"
+ "images/mac/textundercolor.png"
+ "images/mac/zoomin.png"
+ "images/mac/zoomout.png"
+ "images/win/checkbox-checked.png"
+ "images/win/checkbox.png"
+ "images/win/editcopy.png"
+ "images/win/editcut.png"
+ "images/win/editpaste.png"
+ "images/win/editredo.png"
+ "images/win/editundo.png"
+ "images/win/exportpdf.png"
+ "images/win/filenew.png"
+ "images/win/fileopen.png"
+ "images/win/fileprint.png"
+ "images/win/filesave.png"
+ "images/win/format-indent-less.png"
+ "images/win/format-indent-more.png"
+ "images/win/textbold.png"
+ "images/win/textcenter.png"
+ "images/win/textitalic.png"
+ "images/win/textjustify.png"
+ "images/win/textleft.png"
+ "images/win/textright.png"
+ "images/win/textunder.png"
+ "images/win/textundercolor.png"
+ "images/win/zoomin.png"
+ "images/win/zoomout.png"
+)
+
+qt_add_resources(textedit "textedit"
+ PREFIX
+ "/"
+ FILES
+ ${textedit_resource_files}
+)
+
+install(TARGETS textedit
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/richtext/textedit/example.html b/tests/manual/examples/widgets/richtext/textedit/example.html
new file mode 100644
index 0000000000..038f71acfe
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/example.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><title>QTextEdit Example</title><style type="text/css">
+p, li { white-space: pre-wrap; }
+hr { height: 1px; border-width: 0; }
+</style></head><body style=" font-family:'Helvetica'; font-size:9pt; font-weight:400; font-style:normal;">
+<h1 align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600;">QTextEdit</span></h1>
+<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">The QTextEdit widget is an advanced editor that supports formatted rich text. It can be used to display HTML and other rich document formats. Internally, QTextEdit uses the QTextDocument class to describe both the high-level structure of each document and the low-level formatting of paragraphs.</span></p>
+<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;">If you are viewing this document in the <span style=" font-style:italic;">textedit</span> example, you can edit this document to explore Qt's rich text editing features. We have included some comments in each of the following sections to encourage you to experiment. </p>
+<h2 style=" margin-top:16px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:18pt; font-weight:600;"><span style=" font-size:16pt;">Font and Paragraph Styles</span></h2>
+<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">QTextEdit supports </span><span style=" font-size:11pt; font-weight:600;">bold</span><span style=" font-size:11pt;">, </span><span style=" font-size:11pt; font-style:italic;">italic</span><span style=" font-size:11pt;">, and </span><span style=" font-size:11pt; text-decoration: underline;">underlined</span><span style=" font-size:11pt;"> font styles, and can display </span><span style=" font-size:11pt; font-weight:600; color:#00007f;">multicolored</span><span style=" font-size:11pt;"> </span><span style=" font-size:11pt; font-weight:600; color:#aa0000;">text</span><span style=" font-size:11pt;">. Font families such as </span><span style=" font-family:'Times New Roman'; font-size:11pt; font-weight:600;">Times New Roman</span><span style=" font-size:11pt;"> and </span><span style=" font-family:'Courier'; font-size:11pt; font-weight:600;">Courier</span><span style=" font-size:11pt;"> can also be used directly. </span><span style=" font-size:11pt; font-style:italic;">If you place the cursor in a region of styled text, the controls in the tool bars will change to reflect the current style.</span></p>
+<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;">Paragraphs can be formatted so that the text is left-aligned, right-aligned, centered, or fully justified.</p>
+<p align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-style:italic;">Try changing the alignment of some text and resize the editor to see how the text layout changes.</span> </p>
+<h2 align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16pt; font-weight:600;">Lists</span></h2>
+<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:16pt; font-weight:600;"><span style=" font-size:11pt; font-weight:400;">Different kinds of lists can be included in rich text documents. Standard bullet lists can be nested, using different symbols for each level of the list: </span></p>
+<ul style="-qt-list-indent: 1;"><li style=" font-size:11pt;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Disc symbols are typically used for top-level list items. </li></ul>
+<ul type=circle style="-qt-list-indent: 2;"><li style=" font-size:11pt;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Circle symbols can be used to distinguish between items in lower-level lists.</li></ul>
+<ul type=square style="-qt-list-indent: 3;"><li style=" font-size:11pt;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Square symbols provide a reasonable alternative to discs and circles. </li></ul>
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;">Ordered lists can be created that can be used for tables of contents. Different characters can be used to enumerate items, and we can use both Roman and Arabic numerals in the same list structure: </p>
+<ol style="-qt-list-indent: 1;"><li style=" font-size:11pt;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Introduction</li>
+<li style=" font-size:11pt;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt Tools </li></ol>
+<ol type=a style="-qt-list-indent: 2;"><li style=" font-size:11pt;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt Assistant</li>
+<li style=" font-size:11pt;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt Widgets Designer</li>
+<ol type=A style="-qt-list-indent: 3;"><li style=" font-size:11pt;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Form Editor</li>
+<li style=" font-size:11pt;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Component Architecture</li></ol>
+<li style=" font-size:11pt;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt Linguist</li></ol>
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"></p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;">The list will automatically be renumbered if you add or remove items. <span style=" font-style:italic;">Try adding new sections to the above list or removing existing item to see the numbers change.</span> </p>
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"></p>
+<h2 style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-size:16pt; font-weight:600;">Images</span></h2>
+<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:16pt; font-weight:600;"><span style=" font-size:11pt; font-weight:400;">Inline images are treated like ordinary ranges of characters in the text editor, so they flow with the surrounding text. Images can also be selected in the same way as text, making it easy to cut, copy, and paste them. </span></p>
+<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><img src=":/images/logo32.png" /><span style=" font-style:italic;"> Try to select this image by clicking and dragging over it with the mouse, or use the text cursor to select it by holding down Shift and using the arrow keys. You can then cut or copy it, and paste it into different parts of this document.</span></p>
+<h2 align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16pt; font-weight:600;">Horizontal Rule</span></h2>
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Here is an example:</p>
+<hr width="50%" style=" background-color:green;"/>
+<h2 align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-size:16pt; font-weight:600;">Tables</span></h2>
+<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:16pt; font-weight:600;"><span style=" font-size:11pt; font-weight:400;">QTextEdit can arrange and format tables, supporting features such as row and column spans, text formatting within cells, and size constraints for columns. </span></p>
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"></p>
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"></p>
+<table border="1" align="center" width="90%" cellspacing="0" cellpadding="4">
+<tr>
+<td>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p></td>
+<td>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Development Tools </span></p></td>
+<td>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Programming Techniques </span></p></td>
+<td>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Graphical User Interfaces </span></p></td></tr>
+<tr>
+<td>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">9:00 - 11:00 </span></p></td>
+<td colspan="3">
+<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Introduction to <span style=" font-style:italic;">Qt </span></p></td></tr>
+<tr>
+<td>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">11:00 - 13:00 </span></p></td>
+<td>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using <span style=" font-style:italic;">qmake</span> </p></td>
+<td>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Object-oriented Programming </p></td>
+<td>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Layouts in <span style=" font-style:italic;">Qt</span> </p></td></tr>
+<tr>
+<td>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">13:00 - 15:00 </span></p></td>
+<td>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">Qt Widgets Designer</span> Tutorial </p></td>
+<td rowspan="2">
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Extreme Programming </p></td>
+<td>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Writing Custom Styles </p></td></tr>
+<tr>
+<td>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">15:00 - 17:00 </span></p></td>
+<td>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">Qt Linguist</span> and Internationalization </p></td>
+<td></td></tr></table>
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"></p>
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt; font-style:italic;">Try adding text to the cells in the table and experiment with the alignment of the paragraphs.</p>
+<h2 style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-size:16pt; font-weight:600;">Hyperlinks</span></h2>
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">QTextEdit is designed to support hyperlinks between documents, and this feature is used extensively in </span><span style=" font-size:11pt; font-style:italic;">Qt Assistant</span><span style=" font-size:11pt;">. Hyperlinks are automatically created when an HTML file is imported into an editor. Since the rich text framework supports hyperlinks natively, they can also be created programmatically.</span></p>
+<h2 style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-size:16pt; font-weight:600;">Undo and Redo</span></h2>
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;">Full support for undo and redo operations is built into QTextEdit and the underlying rich text framework. Operations on a document can be packaged together to make editing a more comfortable experience for the user.</p>
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-style:italic;">Try making changes to this document and press Ctrl+Z to undo them. You can always recover the original contents of the document.</span> </p></body></html>
diff --git a/tests/manual/examples/widgets/richtext/textedit/example.md b/tests/manual/examples/widgets/richtext/textedit/example.md
new file mode 100644
index 0000000000..8a5eed72f6
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/example.md
@@ -0,0 +1,104 @@
+# QTextEdit
+
+The QTextEdit widget is an advanced editor that supports formatted rich text.
+It can be used to display HTML and other rich document formats. Internally,
+QTextEdit uses the QTextDocument class to describe both the high-level
+structure of each document and the low-level formatting of paragraphs.
+
+If you are viewing this document in the textedit example, you can edit this
+document to explore Qt's rich text editing features. We have included some
+comments in each of the following sections to encourage you to experiment.
+
+## Font and Paragraph Styles
+
+QTextEdit supports **bold**, *italic*, _underline_ &amp; ~~strikethrough~~ font
+styles, and can display
+<span style="font-size:10pt; font-weight:600; color:#00007f;"> multicolored</span>
+text. Font families such as
+<span style="font-family:Times New Roman">Times New Roman</span> and
+<span style="font-family:Courier">Courier</span>
+can also be used directly. *If you place the cursor in a region of styled text,
+the controls in the tool bars will change to reflect the current style.*
+
+Paragraphs can be formatted so that the text is left-aligned, right-aligned,
+centered, or fully justified.
+
+*Try changing the alignment of some text and resize the editor to see how the
+text layout changes.*
+
+## Lists
+
+Different kinds of lists can be included in rich text documents. Standard
+bullet lists can be nested, using different symbols for each level of the list:
+
+- Disc symbols are typically used for top-level list items.
+ * Circle symbols can be used to distinguish between items in lower-level
+ lists.
+ + Square symbols provide a reasonable alternative to discs and circles.
+
+Ordered lists can be created that can be used for tables of contents. Different
+characters can be used to enumerate items, and we can use both Roman and Arabic
+numerals in the same list structure:
+
+1. Introduction
+2. Qt Tools
+ 1) Qt Assistant
+ 2) Qt Widgets Designer
+ 1. Form Editor
+ 2. Component Architecture
+ 3) Qt Linguist
+
+The list will automatically be renumbered if you add or remove items. *Try
+adding new sections to the above list or removing existing item to see the
+numbers change.*
+
+Task lists can be used to track progress on projects:
+
+- [ ] This is not yet done
+- This is just a bullet point
+- [x] This is done
+
+## Images
+
+Inline images are treated like ordinary ranges of characters in the text
+editor, so they flow with the surrounding text. Images can also be selected in
+the same way as text, making it easy to cut, copy, and paste them.
+
+![logo](images/logo32.png "logo") *Try to select this image by clicking and
+dragging over it with the mouse, or use the text cursor to select it by holding
+down Shift and using the arrow keys. You can then cut or copy it, and paste it
+into different parts of this document.*
+
+## Tables
+
+QTextEdit can arrange and format tables, supporting features such as row and
+column spans, text formatting within cells, and size constraints for columns.
+
+| | Development Tools | Programming Techniques | Graphical User Interfaces |
+| ------------: | ----------------- | ---------------------- | ------------------------- |
+| 9:00 - 11:00 | Introduction to Qt |||
+| 11:00 - 13:00 | Using qmake | Object-oriented Programming | Layouts in Qt |
+| 13:00 - 15:00 | Qt Widgets Designer Tutorial | Extreme Programming | Writing Custom Styles |
+| 15:00 - 17:00 | Qt Linguist and Internationalization | &nbsp; | &nbsp; |
+
+*Try adding text to the cells in the table and experiment with the alignment of
+the paragraphs.*
+
+## Hyperlinks
+
+QTextEdit is designed to support hyperlinks between documents, and this feature
+is used extensively in
+[Qt Assistant](http://doc.qt.io/qt-5/qtassistant-index.html). Hyperlinks are
+automatically created when an HTML file is imported into an editor. Since the
+rich text framework supports hyperlinks natively, they can also be created
+programmatically.
+
+## Undo and Redo
+
+Full support for undo and redo operations is built into QTextEdit and the
+underlying rich text framework. Operations on a document can be packaged
+together to make editing a more comfortable experience for the user.
+
+*Try making changes to this document and press `Ctrl+Z` to undo them. You can
+always recover the original contents of the document.*
+
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/logo32.png b/tests/manual/examples/widgets/richtext/textedit/images/logo32.png
new file mode 100644
index 0000000000..5f91e9873b
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/logo32.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/checkbox-checked.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/checkbox-checked.png
new file mode 100644
index 0000000000..a072d7fb5c
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/checkbox-checked.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/checkbox.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/checkbox.png
new file mode 100644
index 0000000000..4064909712
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/checkbox.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/editcopy.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/editcopy.png
new file mode 100644
index 0000000000..f551364464
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/editcopy.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/editcut.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/editcut.png
new file mode 100644
index 0000000000..a784fd5709
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/editcut.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/editpaste.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/editpaste.png
new file mode 100644
index 0000000000..64c0b2d6ab
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/editpaste.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/editredo.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/editredo.png
new file mode 100644
index 0000000000..8875bf246c
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/editredo.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/editundo.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/editundo.png
new file mode 100644
index 0000000000..a3bd5e0bf2
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/editundo.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/exportpdf.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/exportpdf.png
new file mode 100644
index 0000000000..9e199407af
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/exportpdf.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/filenew.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/filenew.png
new file mode 100644
index 0000000000..d3882c7b3f
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/filenew.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/fileopen.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/fileopen.png
new file mode 100644
index 0000000000..fc06c5ec63
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/fileopen.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/fileprint.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/fileprint.png
new file mode 100644
index 0000000000..10ca56c82a
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/fileprint.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/filesave.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/filesave.png
new file mode 100644
index 0000000000..e65a29d5f1
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/filesave.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/format-indent-less.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/format-indent-less.png
new file mode 100644
index 0000000000..e38074e78b
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/format-indent-less.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/format-indent-more.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/format-indent-more.png
new file mode 100644
index 0000000000..1bdeabd354
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/format-indent-more.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/textbold.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/textbold.png
new file mode 100644
index 0000000000..38400bd1f6
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/textbold.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/textcenter.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/textcenter.png
new file mode 100644
index 0000000000..2ef5b2ee6f
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/textcenter.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/textitalic.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/textitalic.png
new file mode 100644
index 0000000000..0170ee26a6
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/textitalic.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/textjustify.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/textjustify.png
new file mode 100644
index 0000000000..39cd6c1a9d
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/textjustify.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/textleft.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/textleft.png
new file mode 100644
index 0000000000..83a66d5535
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/textleft.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/textright.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/textright.png
new file mode 100644
index 0000000000..e7c04645cf
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/textright.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/textunder.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/textunder.png
new file mode 100644
index 0000000000..968bac5e90
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/textunder.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/textundercolor.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/textundercolor.png
new file mode 100644
index 0000000000..30e24e61c3
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/textundercolor.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/zoomin.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/zoomin.png
new file mode 100644
index 0000000000..d46f5aff0d
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/zoomin.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/mac/zoomout.png b/tests/manual/examples/widgets/richtext/textedit/images/mac/zoomout.png
new file mode 100644
index 0000000000..46326566d1
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/mac/zoomout.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/checkbox-checked.png b/tests/manual/examples/widgets/richtext/textedit/images/win/checkbox-checked.png
new file mode 100644
index 0000000000..a072d7fb5c
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/checkbox-checked.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/checkbox.png b/tests/manual/examples/widgets/richtext/textedit/images/win/checkbox.png
new file mode 100644
index 0000000000..4064909712
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/checkbox.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/editcopy.png b/tests/manual/examples/widgets/richtext/textedit/images/win/editcopy.png
new file mode 100644
index 0000000000..1121b47d8b
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/editcopy.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/editcut.png b/tests/manual/examples/widgets/richtext/textedit/images/win/editcut.png
new file mode 100644
index 0000000000..38e55f7420
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/editcut.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/editpaste.png b/tests/manual/examples/widgets/richtext/textedit/images/win/editpaste.png
new file mode 100644
index 0000000000..ffab15aaf8
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/editpaste.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/editredo.png b/tests/manual/examples/widgets/richtext/textedit/images/win/editredo.png
new file mode 100644
index 0000000000..9d679fe6fc
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/editredo.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/editundo.png b/tests/manual/examples/widgets/richtext/textedit/images/win/editundo.png
new file mode 100644
index 0000000000..eee23d24a3
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/editundo.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/exportpdf.png b/tests/manual/examples/widgets/richtext/textedit/images/win/exportpdf.png
new file mode 100644
index 0000000000..ebb44e6b88
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/exportpdf.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/filenew.png b/tests/manual/examples/widgets/richtext/textedit/images/win/filenew.png
new file mode 100644
index 0000000000..af5d122141
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/filenew.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/fileopen.png b/tests/manual/examples/widgets/richtext/textedit/images/win/fileopen.png
new file mode 100644
index 0000000000..fc6f17e977
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/fileopen.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/fileprint.png b/tests/manual/examples/widgets/richtext/textedit/images/win/fileprint.png
new file mode 100644
index 0000000000..ba7c02dc18
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/fileprint.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/filesave.png b/tests/manual/examples/widgets/richtext/textedit/images/win/filesave.png
new file mode 100644
index 0000000000..8feec99bee
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/filesave.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/format-indent-less.png b/tests/manual/examples/widgets/richtext/textedit/images/win/format-indent-less.png
new file mode 100644
index 0000000000..e38074e78b
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/format-indent-less.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/format-indent-more.png b/tests/manual/examples/widgets/richtext/textedit/images/win/format-indent-more.png
new file mode 100644
index 0000000000..1bdeabd354
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/format-indent-more.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/textbold.png b/tests/manual/examples/widgets/richtext/textedit/images/win/textbold.png
new file mode 100644
index 0000000000..9cbc7138b9
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/textbold.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/textcenter.png b/tests/manual/examples/widgets/richtext/textedit/images/win/textcenter.png
new file mode 100644
index 0000000000..11efb4b852
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/textcenter.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/textitalic.png b/tests/manual/examples/widgets/richtext/textedit/images/win/textitalic.png
new file mode 100644
index 0000000000..b30ce14c14
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/textitalic.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/textjustify.png b/tests/manual/examples/widgets/richtext/textedit/images/win/textjustify.png
new file mode 100644
index 0000000000..9de0c88085
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/textjustify.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/textleft.png b/tests/manual/examples/widgets/richtext/textedit/images/win/textleft.png
new file mode 100644
index 0000000000..16f80bc325
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/textleft.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/textright.png b/tests/manual/examples/widgets/richtext/textedit/images/win/textright.png
new file mode 100644
index 0000000000..16872df62a
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/textright.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/textunder.png b/tests/manual/examples/widgets/richtext/textedit/images/win/textunder.png
new file mode 100644
index 0000000000..c72eff53fb
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/textunder.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/textundercolor.png b/tests/manual/examples/widgets/richtext/textedit/images/win/textundercolor.png
new file mode 100644
index 0000000000..30e24e61c3
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/textundercolor.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/zoomin.png b/tests/manual/examples/widgets/richtext/textedit/images/win/zoomin.png
new file mode 100644
index 0000000000..2e586fc7bf
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/zoomin.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/images/win/zoomout.png b/tests/manual/examples/widgets/richtext/textedit/images/win/zoomout.png
new file mode 100644
index 0000000000..a736d39343
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/images/win/zoomout.png
Binary files differ
diff --git a/tests/manual/examples/widgets/richtext/textedit/main.cpp b/tests/manual/examples/widgets/richtext/textedit/main.cpp
new file mode 100644
index 0000000000..4d46dcd51c
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/main.cpp
@@ -0,0 +1,36 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "textedit.h"
+
+#include <QApplication>
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+#include <QScreen>
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ QCoreApplication::setOrganizationName("QtProject");
+ QCoreApplication::setApplicationName("Rich Text");
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+ QCommandLineParser parser;
+ parser.setApplicationDescription(QCoreApplication::applicationName());
+ parser.addHelpOption();
+ parser.addVersionOption();
+ parser.addPositionalArgument("file", "The file to open.");
+ parser.process(a);
+
+ TextEdit mw;
+
+ const QRect availableGeometry = mw.screen()->availableGeometry();
+ mw.resize(availableGeometry.width() / 2, (availableGeometry.height() * 2) / 3);
+ mw.move((availableGeometry.width() - mw.width()) / 2,
+ (availableGeometry.height() - mw.height()) / 2);
+
+ if (!mw.load(parser.positionalArguments().value(0, QLatin1String(":/example.html"))))
+ mw.fileNew();
+
+ mw.show();
+ return a.exec();
+}
diff --git a/tests/manual/examples/widgets/richtext/textedit/textedit.cpp b/tests/manual/examples/widgets/richtext/textedit/textedit.cpp
new file mode 100644
index 0000000000..5f9e8d87b7
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/textedit.cpp
@@ -0,0 +1,904 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "textedit.h"
+
+#include <QActionGroup>
+#include <QApplication>
+#include <QClipboard>
+#include <QColorDialog>
+#include <QComboBox>
+#include <QFontComboBox>
+#include <QFile>
+#include <QFileDialog>
+#include <QFileInfo>
+#include <QFontDatabase>
+#include <QMenu>
+#include <QMenuBar>
+#include <QTextEdit>
+#include <QStatusBar>
+#include <QToolBar>
+#include <QTextCursor>
+#include <QTextDocumentWriter>
+#include <QTextList>
+#include <QTimer>
+#include <QtDebug>
+#include <QCloseEvent>
+#include <QMessageBox>
+#include <QMimeData>
+#include <QMimeDatabase>
+#include <QStringDecoder>
+#if defined(QT_PRINTSUPPORT_LIB)
+#include <QtPrintSupport/qtprintsupportglobal.h>
+#if QT_CONFIG(printer)
+#if QT_CONFIG(printdialog)
+#include <QPrintDialog>
+#endif
+#include <QPrinter>
+#if QT_CONFIG(printpreviewdialog)
+#include <QPrintPreviewDialog>
+#endif
+#endif
+#endif
+
+#ifdef Q_OS_MAC
+const QString rsrcPath = ":/images/mac";
+#else
+const QString rsrcPath = ":/images/win";
+#endif
+
+TextEdit::TextEdit(QWidget *parent)
+ : QMainWindow(parent)
+{
+#ifdef Q_OS_MACOS
+ setUnifiedTitleAndToolBarOnMac(true);
+#endif
+ setWindowTitle(QCoreApplication::applicationName());
+
+ textEdit = new QTextEdit(this);
+ connect(textEdit, &QTextEdit::currentCharFormatChanged,
+ this, &TextEdit::currentCharFormatChanged);
+ connect(textEdit, &QTextEdit::cursorPositionChanged,
+ this, &TextEdit::cursorPositionChanged);
+ setCentralWidget(textEdit);
+
+ setToolButtonStyle(Qt::ToolButtonFollowStyle);
+ setupFileActions();
+ setupEditActions();
+ setupTextActions();
+
+ {
+ QMenu *helpMenu = menuBar()->addMenu(tr("Help"));
+ helpMenu->addAction(tr("About"), this, &TextEdit::about);
+ helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
+ }
+
+ QFont textFont("Helvetica");
+ textFont.setStyleHint(QFont::SansSerif);
+ textEdit->setFont(textFont);
+ fontChanged(textEdit->font());
+ colorChanged(textEdit->textColor());
+ alignmentChanged(textEdit->alignment());
+
+ auto *document = textEdit->document();
+ connect(document, &QTextDocument::modificationChanged,
+ actionSave, &QAction::setEnabled);
+ connect(document, &QTextDocument::modificationChanged,
+ this, &QWidget::setWindowModified);
+ connect(document, &QTextDocument::undoAvailable,
+ actionUndo, &QAction::setEnabled);
+ connect(document, &QTextDocument::redoAvailable,
+ actionRedo, &QAction::setEnabled);
+
+ setWindowModified(document->isModified());
+ actionSave->setEnabled(document->isModified());
+ actionUndo->setEnabled(document->isUndoAvailable());
+ actionRedo->setEnabled(document->isRedoAvailable());
+
+#ifndef QT_NO_CLIPBOARD
+ actionCut->setEnabled(false);
+ connect(textEdit, &QTextEdit::copyAvailable, actionCut, &QAction::setEnabled);
+ actionCopy->setEnabled(false);
+ connect(textEdit, &QTextEdit::copyAvailable, actionCopy, &QAction::setEnabled);
+
+ connect(QGuiApplication::clipboard(), &QClipboard::dataChanged,
+ this, &TextEdit::clipboardDataChanged);
+#endif
+
+ textEdit->setFocus();
+ setCurrentFileName(QString());
+
+#ifdef Q_OS_MACOS
+ // Use dark text on light background on macOS, also in dark mode.
+ QPalette pal = textEdit->palette();
+ pal.setColor(QPalette::Base, QColor(Qt::white));
+ pal.setColor(QPalette::Text, QColor(Qt::black));
+ textEdit->setPalette(pal);
+#endif
+}
+
+//! [closeevent]
+void TextEdit::closeEvent(QCloseEvent *e)
+{
+ if (closeAccepted) {
+ e->accept();
+ return;
+ }
+
+ e->ignore();
+ maybeSave(SaveContinuation::Close);
+}
+//! [closeevent]
+
+void TextEdit::setupFileActions()
+{
+ QToolBar *tb = addToolBar(tr("File Actions"));
+ QMenu *menu = menuBar()->addMenu(tr("&File"));
+
+ const QIcon newIcon = QIcon::fromTheme("document-new", QIcon(rsrcPath + "/filenew.png"));
+ QAction *a = menu->addAction(newIcon, tr("&New"), this, &TextEdit::fileNew);
+ tb->addAction(a);
+ a->setPriority(QAction::LowPriority);
+ a->setShortcut(QKeySequence::New);
+
+ const QIcon openIcon = QIcon::fromTheme("document-open", QIcon(rsrcPath + "/fileopen.png"));
+ a = menu->addAction(openIcon, tr("&Open..."), this, &TextEdit::fileOpen);
+ a->setShortcut(QKeySequence::Open);
+ tb->addAction(a);
+
+ menu->addSeparator();
+
+ const QIcon saveIcon = QIcon::fromTheme("document-save", QIcon(rsrcPath + "/filesave.png"));
+ actionSave = menu->addAction(saveIcon, tr("&Save"), this,
+ [this]() { fileSave(SaveContinuation::None); });
+ actionSave->setShortcut(QKeySequence::Save);
+ actionSave->setEnabled(false);
+ tb->addAction(actionSave);
+
+ a = menu->addAction(tr("Save &As..."), this, [this]() { fileSaveAs(SaveContinuation::None); });
+ a->setPriority(QAction::LowPriority);
+ menu->addSeparator();
+
+#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printer)
+ const QIcon printIcon = QIcon::fromTheme("document-print", QIcon(rsrcPath + "/fileprint.png"));
+ a = menu->addAction(printIcon, tr("&Print..."), this, &TextEdit::filePrint);
+ a->setPriority(QAction::LowPriority);
+ a->setShortcut(QKeySequence::Print);
+ tb->addAction(a);
+
+ const QIcon filePrintIcon = QIcon::fromTheme("fileprint", QIcon(rsrcPath + "/fileprint.png"));
+ menu->addAction(filePrintIcon, tr("Print Preview..."), this, &TextEdit::filePrintPreview);
+
+ const QIcon exportPdfIcon = QIcon::fromTheme("exportpdf", QIcon(rsrcPath + "/exportpdf.png"));
+ a = menu->addAction(exportPdfIcon, tr("&Export PDF..."), this, &TextEdit::filePrintPdf);
+ a->setPriority(QAction::LowPriority);
+ a->setShortcut(Qt::CTRL | Qt::Key_D);
+ tb->addAction(a);
+
+ menu->addSeparator();
+#endif
+
+ a = menu->addAction(tr("&Quit"), qApp, &QCoreApplication::quit);
+ a->setShortcut(Qt::CTRL | Qt::Key_Q);
+}
+
+void TextEdit::setupEditActions()
+{
+ QToolBar *tb = addToolBar(tr("Edit Actions"));
+ QMenu *menu = menuBar()->addMenu(tr("&Edit"));
+
+ const QIcon undoIcon = QIcon::fromTheme("edit-undo", QIcon(rsrcPath + "/editundo.png"));
+ actionUndo = menu->addAction(undoIcon, tr("&Undo"), textEdit, &QTextEdit::undo);
+ actionUndo->setShortcut(QKeySequence::Undo);
+ tb->addAction(actionUndo);
+
+ const QIcon redoIcon = QIcon::fromTheme("edit-redo", QIcon(rsrcPath + "/editredo.png"));
+ actionRedo = menu->addAction(redoIcon, tr("&Redo"), textEdit, &QTextEdit::redo);
+ actionRedo->setPriority(QAction::LowPriority);
+ actionRedo->setShortcut(QKeySequence::Redo);
+ tb->addAction(actionRedo);
+ menu->addSeparator();
+
+#ifndef QT_NO_CLIPBOARD
+ const QIcon cutIcon = QIcon::fromTheme("edit-cut", QIcon(rsrcPath + "/editcut.png"));
+ actionCut = menu->addAction(cutIcon, tr("Cu&t"), textEdit, &QTextEdit::cut);
+ actionCut->setPriority(QAction::LowPriority);
+ actionCut->setShortcut(QKeySequence::Cut);
+ tb->addAction(actionCut);
+
+ const QIcon copyIcon = QIcon::fromTheme("edit-copy", QIcon(rsrcPath + "/editcopy.png"));
+ actionCopy = menu->addAction(copyIcon, tr("&Copy"), textEdit, &QTextEdit::copy);
+ actionCopy->setPriority(QAction::LowPriority);
+ actionCopy->setShortcut(QKeySequence::Copy);
+ tb->addAction(actionCopy);
+
+ const QIcon pasteIcon = QIcon::fromTheme("edit-paste", QIcon(rsrcPath + "/editpaste.png"));
+ actionPaste = menu->addAction(pasteIcon, tr("&Paste"), textEdit, &QTextEdit::paste);
+ actionPaste->setPriority(QAction::LowPriority);
+ actionPaste->setShortcut(QKeySequence::Paste);
+ tb->addAction(actionPaste);
+ if (const QMimeData *md = QGuiApplication::clipboard()->mimeData())
+ actionPaste->setEnabled(md->hasText());
+#endif
+}
+
+void TextEdit::setupTextActions()
+{
+ QToolBar *tb = addToolBar(tr("Format Actions"));
+ QMenu *menu = menuBar()->addMenu(tr("F&ormat"));
+
+ const QIcon boldIcon = QIcon::fromTheme("format-text-bold", QIcon(rsrcPath + "/textbold.png"));
+ actionTextBold = menu->addAction(boldIcon, tr("&Bold"), this, &TextEdit::textBold);
+ actionTextBold->setShortcut(Qt::CTRL | Qt::Key_B);
+ actionTextBold->setPriority(QAction::LowPriority);
+ QFont bold;
+ bold.setBold(true);
+ actionTextBold->setFont(bold);
+ tb->addAction(actionTextBold);
+ actionTextBold->setCheckable(true);
+
+ const QIcon italicIcon = QIcon::fromTheme("format-text-italic", QIcon(rsrcPath + "/textitalic.png"));
+ actionTextItalic = menu->addAction(italicIcon, tr("&Italic"), this, &TextEdit::textItalic);
+ actionTextItalic->setPriority(QAction::LowPriority);
+ actionTextItalic->setShortcut(Qt::CTRL | Qt::Key_I);
+ QFont italic;
+ italic.setItalic(true);
+ actionTextItalic->setFont(italic);
+ tb->addAction(actionTextItalic);
+ actionTextItalic->setCheckable(true);
+
+ const QIcon underlineIcon = QIcon::fromTheme("format-text-underline", QIcon(rsrcPath + "/textunder.png"));
+ actionTextUnderline = menu->addAction(underlineIcon, tr("&Underline"), this, &TextEdit::textUnderline);
+ actionTextUnderline->setShortcut(Qt::CTRL | Qt::Key_U);
+ actionTextUnderline->setPriority(QAction::LowPriority);
+ QFont underline;
+ underline.setUnderline(true);
+ actionTextUnderline->setFont(underline);
+ tb->addAction(actionTextUnderline);
+ actionTextUnderline->setCheckable(true);
+
+ menu->addSeparator();
+
+ const QIcon leftIcon = QIcon::fromTheme("format-justify-left", QIcon(rsrcPath + "/textleft.png"));
+ actionAlignLeft = new QAction(leftIcon, tr("&Left"), this);
+ actionAlignLeft->setShortcut(Qt::CTRL | Qt::Key_L);
+ actionAlignLeft->setCheckable(true);
+ actionAlignLeft->setPriority(QAction::LowPriority);
+ const QIcon centerIcon = QIcon::fromTheme("format-justify-center", QIcon(rsrcPath + "/textcenter.png"));
+ actionAlignCenter = new QAction(centerIcon, tr("C&enter"), this);
+ actionAlignCenter->setShortcut(Qt::CTRL | Qt::Key_E);
+ actionAlignCenter->setCheckable(true);
+ actionAlignCenter->setPriority(QAction::LowPriority);
+ const QIcon rightIcon = QIcon::fromTheme("format-justify-right", QIcon(rsrcPath + "/textright.png"));
+ actionAlignRight = new QAction(rightIcon, tr("&Right"), this);
+ actionAlignRight->setShortcut(Qt::CTRL | Qt::Key_R);
+ actionAlignRight->setCheckable(true);
+ actionAlignRight->setPriority(QAction::LowPriority);
+ const QIcon fillIcon = QIcon::fromTheme("format-justify-fill", QIcon(rsrcPath + "/textjustify.png"));
+ actionAlignJustify = new QAction(fillIcon, tr("&Justify"), this);
+ actionAlignJustify->setShortcut(Qt::CTRL | Qt::Key_J);
+ actionAlignJustify->setCheckable(true);
+ actionAlignJustify->setPriority(QAction::LowPriority);
+ const QIcon indentMoreIcon = QIcon::fromTheme("format-indent-more", QIcon(rsrcPath + "/format-indent-more.png"));
+ actionIndentMore = menu->addAction(indentMoreIcon, tr("&Indent"), this, &TextEdit::indent);
+ actionIndentMore->setShortcut(Qt::CTRL | Qt::Key_BracketRight);
+ actionIndentMore->setPriority(QAction::LowPriority);
+ const QIcon indentLessIcon = QIcon::fromTheme("format-indent-less", QIcon(rsrcPath + "/format-indent-less.png"));
+ actionIndentLess = menu->addAction(indentLessIcon, tr("&Unindent"), this, &TextEdit::unindent);
+ actionIndentLess->setShortcut(Qt::CTRL | Qt::Key_BracketLeft);
+ actionIndentLess->setPriority(QAction::LowPriority);
+
+ // Make sure the alignLeft is always left of the alignRight
+ QActionGroup *alignGroup = new QActionGroup(this);
+ connect(alignGroup, &QActionGroup::triggered, this, &TextEdit::textAlign);
+
+ if (QGuiApplication::isLeftToRight()) {
+ alignGroup->addAction(actionAlignLeft);
+ alignGroup->addAction(actionAlignCenter);
+ alignGroup->addAction(actionAlignRight);
+ } else {
+ alignGroup->addAction(actionAlignRight);
+ alignGroup->addAction(actionAlignCenter);
+ alignGroup->addAction(actionAlignLeft);
+ }
+ alignGroup->addAction(actionAlignJustify);
+
+ tb->addActions(alignGroup->actions());
+ menu->addActions(alignGroup->actions());
+ tb->addAction(actionIndentMore);
+ tb->addAction(actionIndentLess);
+ menu->addAction(actionIndentMore);
+ menu->addAction(actionIndentLess);
+
+ menu->addSeparator();
+
+ QPixmap pix(16, 16);
+ pix.fill(Qt::black);
+ actionTextColor = menu->addAction(pix, tr("&Color..."), this, &TextEdit::textColor);
+ tb->addAction(actionTextColor);
+
+ const QIcon underlineColorIcon(rsrcPath + "/textundercolor.png");
+ actionUnderlineColor = menu->addAction(underlineColorIcon, tr("Underline color..."), this, &TextEdit::underlineColor);
+ tb->addAction(actionUnderlineColor);
+
+ menu->addSeparator();
+
+ const QIcon checkboxIcon = QIcon::fromTheme("status-checkbox-checked", QIcon(rsrcPath + "/checkbox-checked.png"));
+ actionToggleCheckState = menu->addAction(checkboxIcon, tr("Chec&ked"), this, &TextEdit::setChecked);
+ actionToggleCheckState->setShortcut(Qt::CTRL | Qt::Key_K);
+ actionToggleCheckState->setCheckable(true);
+ actionToggleCheckState->setPriority(QAction::LowPriority);
+ tb->addAction(actionToggleCheckState);
+
+ tb = addToolBar(tr("Format Actions"));
+ tb->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
+ addToolBarBreak(Qt::TopToolBarArea);
+ addToolBar(tb);
+
+ comboStyle = new QComboBox(tb);
+ tb->addWidget(comboStyle);
+ comboStyle->addItems({"Standard",
+ "Bullet List (Disc)",
+ "Bullet List (Circle)",
+ "Bullet List (Square)",
+ "Task List (Unchecked)",
+ "Task List (Checked)",
+ "Ordered List (Decimal)",
+ "Ordered List (Alpha lower)",
+ "Ordered List (Alpha upper)",
+ "Ordered List (Roman lower)",
+ "Ordered List (Roman upper)",
+ "Heading 1",
+ "Heading 2",
+ "Heading 3",
+ "Heading 4",
+ "Heading 5",
+ "Heading 6"}),
+
+ connect(comboStyle, &QComboBox::activated, this, &TextEdit::textStyle);
+
+ comboFont = new QFontComboBox(tb);
+ tb->addWidget(comboFont);
+ connect(comboFont, &QComboBox::textActivated, this, &TextEdit::textFamily);
+
+ comboSize = new QComboBox(tb);
+ comboSize->setObjectName("comboSize");
+ tb->addWidget(comboSize);
+ comboSize->setEditable(true);
+
+ const QList<int> standardSizes = QFontDatabase::standardSizes();
+ for (int size : standardSizes)
+ comboSize->addItem(QString::number(size));
+ comboSize->setCurrentIndex(standardSizes.indexOf(QApplication::font().pointSize()));
+
+ connect(comboSize, &QComboBox::textActivated, this, &TextEdit::textSize);
+}
+
+bool TextEdit::load(const QString &f)
+{
+ if (!QFile::exists(f))
+ return false;
+ QFile file(f);
+ if (!file.open(QFile::ReadOnly))
+ return false;
+
+ QByteArray data = file.readAll();
+ QMimeDatabase db;
+ const QString &mimeTypeName = db.mimeTypeForFileNameAndData(f, data).name();
+ if (mimeTypeName == u"text/html") {
+ auto encoding = QStringDecoder::encodingForHtml(data);
+ QString str = QStringDecoder(encoding ? *encoding : QStringDecoder::Utf8)(data);
+ QUrl fileUrl = f.startsWith(u':') ? QUrl(f) : QUrl::fromLocalFile(f);
+ textEdit->document()->setBaseUrl(fileUrl.adjusted(QUrl::RemoveFilename));
+ textEdit->setHtml(str);
+#if QT_CONFIG(textmarkdownreader)
+ } else if (mimeTypeName == u"text/markdown") {
+ textEdit->setMarkdown(QString::fromUtf8(data));
+#endif
+ } else {
+ textEdit->setPlainText(QString::fromUtf8(data));
+ }
+
+ setCurrentFileName(f);
+ return true;
+}
+
+void TextEdit::maybeSave(SaveContinuation continuation)
+{
+ if (!textEdit->document()->isModified()) {
+ // Execute continuation as soon as control has returned to the event loop so that existing
+ // dialogs do not get in the way of closing the window.
+ QTimer::singleShot(0, [this, continuation]() { fileSaveComplete(continuation); });
+ return;
+ }
+
+ QMessageBox *msgBox =
+ new QMessageBox(QMessageBox::Icon::Warning, QCoreApplication::applicationName(),
+ tr("The document has been modified.\n"
+ "Do you want to save your changes?"),
+ QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, this);
+
+ connect(msgBox, &QMessageBox::finished, [=](int result) {
+ if (result == QMessageBox::Save) {
+ fileSave(continuation);
+ return;
+ }
+ fileSaveComplete(result == QMessageBox::Discard ? continuation : SaveContinuation::None);
+ });
+
+ msgBox->setAttribute(Qt::WA_DeleteOnClose);
+ msgBox->open();
+}
+
+void TextEdit::setCurrentFileName(const QString &fileName)
+{
+ this->fileName = fileName;
+ textEdit->document()->setModified(false);
+
+ QString shownName;
+ if (fileName.isEmpty())
+ shownName = "untitled.txt";
+ else
+ shownName = QFileInfo(fileName).fileName();
+
+ setWindowTitle(tr("%1[*] - %2").arg(shownName, QCoreApplication::applicationName()));
+ setWindowModified(false);
+}
+
+void TextEdit::fileNew()
+{
+ maybeSave(SaveContinuation::Clear);
+}
+
+void TextEdit::fileOpen()
+{
+ QFileDialog *fileDialog = new QFileDialog(this, tr("Open File..."));
+ fileDialog->setAcceptMode(QFileDialog::AcceptOpen);
+ fileDialog->setFileMode(QFileDialog::ExistingFile);
+ fileDialog->setMimeTypeFilters({
+#if QT_CONFIG(texthtmlparser)
+ "text/html",
+#endif
+#if QT_CONFIG(textmarkdownreader)
+
+ "text/markdown",
+#endif
+ "text/plain"});
+
+ connect(fileDialog, &QFileDialog::fileSelected, [=](const QString &file) {
+ statusBar()->showMessage(
+ load(file) ? tr(R"(Opened "%1")").arg(QDir::toNativeSeparators(file))
+ : tr(R"(Could not open "%1")").arg(QDir::toNativeSeparators(file)));
+ });
+ fileDialog->setAttribute(Qt::WA_DeleteOnClose);
+ fileDialog->open();
+}
+
+void TextEdit::fileSave(SaveContinuation continuation)
+{
+ if (fileName.isEmpty() || fileName.startsWith(u":/"))
+ return fileSaveAs(continuation);
+
+ QTextDocumentWriter writer(fileName);
+ bool success = writer.write(textEdit->document());
+ if (success) {
+ textEdit->document()->setModified(false);
+ statusBar()->showMessage(tr("Wrote \"%1\"").arg(QDir::toNativeSeparators(fileName)));
+ } else {
+ statusBar()->showMessage(tr("Could not write to file \"%1\"")
+ .arg(QDir::toNativeSeparators(fileName)));
+ }
+ fileSaveComplete(success ? continuation : SaveContinuation::None);
+}
+
+void TextEdit::fileSaveAs(SaveContinuation continuation)
+{
+ QFileDialog *fileDialog = new QFileDialog(this, tr("Save as..."));
+ fileDialog->setAcceptMode(QFileDialog::AcceptSave);
+ QStringList mimeTypes{"text/plain",
+#if QT_CONFIG(textodfwriter)
+ "application/vnd.oasis.opendocument.text",
+#endif
+#if QT_CONFIG(textmarkdownwriter)
+ "text/markdown",
+#endif
+ "text/html"};
+ fileDialog->setMimeTypeFilters(mimeTypes);
+#if QT_CONFIG(textodfwriter)
+ fileDialog->setDefaultSuffix("odt");
+#endif
+ connect(fileDialog, &QFileDialog::finished, [this, continuation, fileDialog](int result) {
+ if (result != QDialog::Accepted)
+ return;
+ setCurrentFileName(fileDialog->selectedFiles().constFirst());
+ fileSave(continuation);
+ });
+ fileDialog->setAttribute(Qt::WA_DeleteOnClose);
+ fileDialog->open();
+}
+
+void TextEdit::fileSaveComplete(SaveContinuation continuation)
+{
+ switch (continuation) {
+ case SaveContinuation::Clear:
+ textEdit->clear();
+ setCurrentFileName({});
+ return;
+ case SaveContinuation::Close:
+ closeAccepted = true;
+ close();
+ return;
+ case SaveContinuation::None:
+ // NOOP as promised
+ return;
+ }
+}
+
+void TextEdit::filePrint()
+{
+#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog)
+ auto printer = std::make_shared<QPrinter>(QPrinter::HighResolution);
+ QPrintDialog *dlg = new QPrintDialog(printer.get(), this);
+ if (textEdit->textCursor().hasSelection())
+ dlg->setOption(QAbstractPrintDialog::PrintSelection);
+ dlg->setWindowTitle(tr("Print Document"));
+ dlg->setAttribute(Qt::WA_DeleteOnClose);
+ connect(dlg, qOverload<QPrinter *>(&QPrintDialog::accepted),
+ [this](QPrinter *printer) { textEdit->print(printer); });
+ connect(dlg, &QPrintDialog::finished, [printer]() mutable { printer.reset(); });
+ dlg->open();
+#endif
+}
+
+void TextEdit::filePrintPreview()
+{
+#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printpreviewdialog)
+ auto printer = std::make_shared<QPrinter>(QPrinter::HighResolution);
+ QPrintPreviewDialog *preview = new QPrintPreviewDialog(printer.get(), this);
+ preview->setAttribute(Qt::WA_DeleteOnClose);
+ connect(preview, &QPrintPreviewDialog::paintRequested, textEdit, &QTextEdit::print);
+ connect(preview, &QPrintPreviewDialog::finished, [printer]() mutable { printer.reset(); });
+ preview->open();
+#endif
+}
+
+void TextEdit::filePrintPdf()
+{
+#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printer)
+ QFileDialog *fileDialog = new QFileDialog(this, tr("Export PDF"));
+ fileDialog->setAcceptMode(QFileDialog::AcceptSave);
+ fileDialog->setMimeTypeFilters(QStringList("application/pdf"));
+ fileDialog->setDefaultSuffix("pdf");
+ fileDialog->setAttribute(Qt::WA_DeleteOnClose);
+ connect(fileDialog, &QFileDialog::fileSelected, [this](const QString &file) {
+ QPrinter printer(QPrinter::HighResolution);
+ printer.setOutputFormat(QPrinter::PdfFormat);
+ printer.setOutputFileName(file);
+ textEdit->document()->print(&printer);
+ statusBar()->showMessage(tr("Exported \"%1\"").arg(QDir::toNativeSeparators(file)));
+ });
+
+ fileDialog->open();
+#endif
+}
+
+void TextEdit::textBold()
+{
+ QTextCharFormat fmt;
+ fmt.setFontWeight(actionTextBold->isChecked() ? QFont::Bold : QFont::Normal);
+ mergeFormatOnWordOrSelection(fmt);
+}
+
+void TextEdit::textUnderline()
+{
+ QTextCharFormat fmt;
+ fmt.setFontUnderline(actionTextUnderline->isChecked());
+ mergeFormatOnWordOrSelection(fmt);
+}
+
+void TextEdit::textItalic()
+{
+ QTextCharFormat fmt;
+ fmt.setFontItalic(actionTextItalic->isChecked());
+ mergeFormatOnWordOrSelection(fmt);
+}
+
+void TextEdit::textFamily(const QString &f)
+{
+ QTextCharFormat fmt;
+ fmt.setFontFamilies({f});
+ mergeFormatOnWordOrSelection(fmt);
+}
+
+void TextEdit::textSize(const QString &p)
+{
+ qreal pointSize = p.toFloat();
+ if (pointSize > 0) {
+ QTextCharFormat fmt;
+ fmt.setFontPointSize(pointSize);
+ mergeFormatOnWordOrSelection(fmt);
+ }
+}
+
+void TextEdit::textStyle(int styleIndex)
+{
+ QTextCursor cursor = textEdit->textCursor();
+ QTextListFormat::Style style = QTextListFormat::ListStyleUndefined;
+ QTextBlockFormat::MarkerType marker = QTextBlockFormat::MarkerType::NoMarker;
+
+ switch (styleIndex) {
+ case 1:
+ style = QTextListFormat::ListDisc;
+ break;
+ case 2:
+ style = QTextListFormat::ListCircle;
+ break;
+ case 3:
+ style = QTextListFormat::ListSquare;
+ break;
+ case 4:
+ if (cursor.currentList())
+ style = cursor.currentList()->format().style();
+ else
+ style = QTextListFormat::ListDisc;
+ marker = QTextBlockFormat::MarkerType::Unchecked;
+ break;
+ case 5:
+ if (cursor.currentList())
+ style = cursor.currentList()->format().style();
+ else
+ style = QTextListFormat::ListDisc;
+ marker = QTextBlockFormat::MarkerType::Checked;
+ break;
+ case 6:
+ style = QTextListFormat::ListDecimal;
+ break;
+ case 7:
+ style = QTextListFormat::ListLowerAlpha;
+ break;
+ case 8:
+ style = QTextListFormat::ListUpperAlpha;
+ break;
+ case 9:
+ style = QTextListFormat::ListLowerRoman;
+ break;
+ case 10:
+ style = QTextListFormat::ListUpperRoman;
+ break;
+ default:
+ break;
+ }
+
+ cursor.beginEditBlock();
+
+ QTextBlockFormat blockFmt = cursor.blockFormat();
+
+ if (style == QTextListFormat::ListStyleUndefined) {
+ blockFmt.setObjectIndex(-1);
+ int headingLevel = styleIndex >= 11 ? styleIndex - 11 + 1 : 0; // H1 to H6, or Standard
+ blockFmt.setHeadingLevel(headingLevel);
+ cursor.setBlockFormat(blockFmt);
+
+ int sizeAdjustment = headingLevel ? 4 - headingLevel : 0; // H1 to H6: +3 to -2
+ QTextCharFormat fmt;
+ fmt.setFontWeight(headingLevel ? QFont::Bold : QFont::Normal);
+ fmt.setProperty(QTextFormat::FontSizeAdjustment, sizeAdjustment);
+ cursor.select(QTextCursor::LineUnderCursor);
+ cursor.mergeCharFormat(fmt);
+ textEdit->mergeCurrentCharFormat(fmt);
+ } else {
+ blockFmt.setMarker(marker);
+ cursor.setBlockFormat(blockFmt);
+ QTextListFormat listFmt;
+ if (cursor.currentList()) {
+ listFmt = cursor.currentList()->format();
+ } else {
+ listFmt.setIndent(blockFmt.indent() + 1);
+ blockFmt.setIndent(0);
+ cursor.setBlockFormat(blockFmt);
+ }
+ listFmt.setStyle(style);
+ cursor.createList(listFmt);
+ }
+
+ cursor.endEditBlock();
+}
+
+void TextEdit::textColor()
+{
+ QColorDialog *dlg = new QColorDialog(this);
+ dlg->setCurrentColor(textEdit->textColor());
+ connect(dlg, &QColorDialog::colorSelected, [this](const QColor &color) {
+ if (!color.isValid())
+ return;
+ QTextCharFormat fmt;
+ fmt.setForeground(color);
+ mergeFormatOnWordOrSelection(fmt);
+ colorChanged(color);
+ });
+ dlg->setAttribute(Qt::WA_DeleteOnClose);
+ dlg->open();
+}
+
+void TextEdit::underlineColor()
+{
+ QColorDialog *dlg = new QColorDialog(this);
+ dlg->setCurrentColor(textEdit->textColor());
+ connect(dlg, &QColorDialog::colorSelected, [this](const QColor &color) {
+ if (!color.isValid())
+ return;
+ QTextCharFormat fmt;
+ fmt.setUnderlineColor(color);
+ mergeFormatOnWordOrSelection(fmt);
+ colorChanged(color);
+ });
+ dlg->setAttribute(Qt::WA_DeleteOnClose);
+ dlg->open();
+}
+
+void TextEdit::textAlign(QAction *a)
+{
+ if (a == actionAlignLeft)
+ textEdit->setAlignment(Qt::AlignLeft | Qt::AlignAbsolute);
+ else if (a == actionAlignCenter)
+ textEdit->setAlignment(Qt::AlignHCenter);
+ else if (a == actionAlignRight)
+ textEdit->setAlignment(Qt::AlignRight | Qt::AlignAbsolute);
+ else if (a == actionAlignJustify)
+ textEdit->setAlignment(Qt::AlignJustify);
+}
+
+void TextEdit::setChecked(bool checked)
+{
+ textStyle(checked ? 5 : 4);
+}
+
+void TextEdit::indent()
+{
+ modifyIndentation(1);
+}
+
+void TextEdit::unindent()
+{
+ modifyIndentation(-1);
+}
+
+void TextEdit::modifyIndentation(int amount)
+{
+ QTextCursor cursor = textEdit->textCursor();
+ cursor.beginEditBlock();
+ if (cursor.currentList()) {
+ QTextListFormat listFmt = cursor.currentList()->format();
+ // See whether the line above is the list we want to move this item into,
+ // or whether we need a new list.
+ QTextCursor above(cursor);
+ above.movePosition(QTextCursor::Up);
+ if (above.currentList() && listFmt.indent() + amount == above.currentList()->format().indent()) {
+ above.currentList()->add(cursor.block());
+ } else {
+ listFmt.setIndent(listFmt.indent() + amount);
+ cursor.createList(listFmt);
+ }
+ } else {
+ QTextBlockFormat blockFmt = cursor.blockFormat();
+ blockFmt.setIndent(blockFmt.indent() + amount);
+ cursor.setBlockFormat(blockFmt);
+ }
+ cursor.endEditBlock();
+}
+
+void TextEdit::currentCharFormatChanged(const QTextCharFormat &format)
+{
+ fontChanged(format.font());
+ colorChanged(format.foreground().color());
+}
+
+void TextEdit::cursorPositionChanged()
+{
+ alignmentChanged(textEdit->alignment());
+ QTextList *list = textEdit->textCursor().currentList();
+ if (list) {
+ switch (list->format().style()) {
+ case QTextListFormat::ListDisc:
+ comboStyle->setCurrentIndex(1);
+ break;
+ case QTextListFormat::ListCircle:
+ comboStyle->setCurrentIndex(2);
+ break;
+ case QTextListFormat::ListSquare:
+ comboStyle->setCurrentIndex(3);
+ break;
+ case QTextListFormat::ListDecimal:
+ comboStyle->setCurrentIndex(6);
+ break;
+ case QTextListFormat::ListLowerAlpha:
+ comboStyle->setCurrentIndex(7);
+ break;
+ case QTextListFormat::ListUpperAlpha:
+ comboStyle->setCurrentIndex(8);
+ break;
+ case QTextListFormat::ListLowerRoman:
+ comboStyle->setCurrentIndex(9);
+ break;
+ case QTextListFormat::ListUpperRoman:
+ comboStyle->setCurrentIndex(10);
+ break;
+ default:
+ comboStyle->setCurrentIndex(-1);
+ break;
+ }
+ switch (textEdit->textCursor().block().blockFormat().marker()) {
+ case QTextBlockFormat::MarkerType::NoMarker:
+ actionToggleCheckState->setChecked(false);
+ break;
+ case QTextBlockFormat::MarkerType::Unchecked:
+ comboStyle->setCurrentIndex(4);
+ actionToggleCheckState->setChecked(false);
+ break;
+ case QTextBlockFormat::MarkerType::Checked:
+ comboStyle->setCurrentIndex(5);
+ actionToggleCheckState->setChecked(true);
+ break;
+ }
+ } else {
+ int headingLevel = textEdit->textCursor().blockFormat().headingLevel();
+ comboStyle->setCurrentIndex(headingLevel ? headingLevel + 10 : 0);
+ }
+}
+
+void TextEdit::clipboardDataChanged()
+{
+#ifndef QT_NO_CLIPBOARD
+ if (const QMimeData *md = QGuiApplication::clipboard()->mimeData())
+ actionPaste->setEnabled(md->hasText());
+#endif
+}
+
+void TextEdit::about()
+{
+ QMessageBox *msgBox =
+ new QMessageBox(QMessageBox::Icon::Information, tr("About"),
+ tr("This example demonstrates Qt's rich text editing facilities in "
+ "action, providing an example document for you to experiment with."),
+ QMessageBox::NoButton, this);
+ msgBox->setAttribute(Qt::WA_DeleteOnClose);
+ msgBox->open();
+}
+
+void TextEdit::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
+{
+ QTextCursor cursor = textEdit->textCursor();
+ if (!cursor.hasSelection())
+ cursor.select(QTextCursor::WordUnderCursor);
+ cursor.mergeCharFormat(format);
+ textEdit->mergeCurrentCharFormat(format);
+}
+
+void TextEdit::fontChanged(const QFont &f)
+{
+ comboFont->setCurrentIndex(comboFont->findText(QFontInfo(f).family()));
+ comboSize->setCurrentIndex(comboSize->findText(QString::number(f.pointSize())));
+ actionTextBold->setChecked(f.bold());
+ actionTextItalic->setChecked(f.italic());
+ actionTextUnderline->setChecked(f.underline());
+}
+
+void TextEdit::colorChanged(const QColor &c)
+{
+ QPixmap pix(16, 16);
+ pix.fill(c);
+ actionTextColor->setIcon(pix);
+}
+
+void TextEdit::alignmentChanged(Qt::Alignment a)
+{
+ if (a.testFlag(Qt::AlignLeft))
+ actionAlignLeft->setChecked(true);
+ else if (a.testFlag(Qt::AlignHCenter))
+ actionAlignCenter->setChecked(true);
+ else if (a.testFlag(Qt::AlignRight))
+ actionAlignRight->setChecked(true);
+ else if (a.testFlag(Qt::AlignJustify))
+ actionAlignJustify->setChecked(true);
+}
+
diff --git a/tests/manual/examples/widgets/richtext/textedit/textedit.h b/tests/manual/examples/widgets/richtext/textedit/textedit.h
new file mode 100644
index 0000000000..f0f4abb827
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/textedit.h
@@ -0,0 +1,110 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef TEXTEDIT_H
+#define TEXTEDIT_H
+
+#include <QMainWindow>
+
+QT_BEGIN_NAMESPACE
+class QAction;
+class QComboBox;
+class QFontComboBox;
+class QTextEdit;
+class QTextCharFormat;
+class QMenu;
+class QPrinter;
+QT_END_NAMESPACE
+
+class TextEdit : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ TextEdit(QWidget *parent = nullptr);
+
+ bool load(const QString &f);
+
+public slots:
+ void fileNew();
+
+protected:
+ void closeEvent(QCloseEvent *e) override;
+
+private:
+ enum class SaveContinuation { None, Close, Clear };
+
+private slots:
+ void fileOpen();
+ void filePrint();
+ void filePrintPreview();
+ void filePrintPdf();
+
+ void textBold();
+ void textUnderline();
+ void textItalic();
+ void textFamily(const QString &f);
+ void textSize(const QString &p);
+ void textStyle(int styleIndex);
+ void textColor();
+ void underlineColor();
+ void textAlign(QAction *a);
+ void setChecked(bool checked);
+ void indent();
+ void unindent();
+
+ void currentCharFormatChanged(const QTextCharFormat &format);
+ void cursorPositionChanged();
+
+ void clipboardDataChanged();
+ void about();
+
+private:
+ void setupFileActions();
+ void setupEditActions();
+ void setupTextActions();
+ void maybeSave(SaveContinuation saveContinuation);
+ void setCurrentFileName(const QString &fileName);
+ void modifyIndentation(int amount);
+
+ void mergeFormatOnWordOrSelection(const QTextCharFormat &format);
+ void fontChanged(const QFont &f);
+ void colorChanged(const QColor &c);
+ void alignmentChanged(Qt::Alignment a);
+
+ void fileSave(SaveContinuation continuation);
+ void fileSaveAs(SaveContinuation continuation);
+ void fileSaveComplete(SaveContinuation continuation);
+
+ QAction *actionSave;
+ QAction *actionTextBold;
+ QAction *actionTextUnderline;
+ QAction *actionTextItalic;
+ QAction *actionTextColor;
+ QAction *actionUnderlineColor;
+ QAction *actionAlignLeft;
+ QAction *actionAlignCenter;
+ QAction *actionAlignRight;
+ QAction *actionAlignJustify;
+ QAction *actionIndentLess;
+ QAction *actionIndentMore;
+ QAction *actionToggleCheckState;
+ QAction *actionUndo;
+ QAction *actionRedo;
+#ifndef QT_NO_CLIPBOARD
+ QAction *actionCut;
+ QAction *actionCopy;
+ QAction *actionPaste;
+#endif
+
+ QComboBox *comboStyle;
+ QFontComboBox *comboFont;
+ QComboBox *comboSize;
+
+ QString fileName;
+ QTextEdit *textEdit;
+
+ bool closeAccepted = false;
+};
+
+#endif // TEXTEDIT_H
diff --git a/tests/manual/examples/widgets/richtext/textedit/textedit.pro b/tests/manual/examples/widgets/richtext/textedit/textedit.pro
new file mode 100644
index 0000000000..b7a2155b0b
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/textedit.pro
@@ -0,0 +1,22 @@
+QT += widgets
+requires(qtConfig(filedialog))
+qtHaveModule(printsupport): QT += printsupport
+
+TEMPLATE = app
+TARGET = textedit
+
+HEADERS = textedit.h
+SOURCES = textedit.cpp \
+ main.cpp
+
+RESOURCES += textedit.qrc
+build_all:!build_pass {
+ CONFIG -= build_all
+ CONFIG += release
+}
+
+EXAMPLE_FILES = textedit.qdoc
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/richtext/textedit
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/richtext/textedit/textedit.qdoc b/tests/manual/examples/widgets/richtext/textedit/textedit.qdoc
new file mode 100644
index 0000000000..ce574353b4
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/textedit.qdoc
@@ -0,0 +1,20 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*! \page textedit-example.html
+
+ \ingroup examples
+ \title Text Edit Example
+
+ \brief This example displays a text editor with the user interface written
+ in pure C++.
+
+ A similar example which uses \QD to produce the user
+ interface is in the \l {Qt Widgets Designer Manual}.
+
+
+ See \c{$QTDIR/examples/textedit} for the source code.
+
+*/
+
+
diff --git a/tests/manual/examples/widgets/richtext/textedit/textedit.qrc b/tests/manual/examples/widgets/richtext/textedit/textedit.qrc
new file mode 100644
index 0000000000..a30d50fdbf
--- /dev/null
+++ b/tests/manual/examples/widgets/richtext/textedit/textedit.qrc
@@ -0,0 +1,54 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/">
+ <file>images/logo32.png</file>
+ <file>images/mac/checkbox.png</file>
+ <file>images/mac/checkbox-checked.png</file>
+ <file>images/mac/editcopy.png</file>
+ <file>images/mac/editcut.png</file>
+ <file>images/mac/editpaste.png</file>
+ <file>images/mac/editredo.png</file>
+ <file>images/mac/editundo.png</file>
+ <file>images/mac/exportpdf.png</file>
+ <file>images/mac/filenew.png</file>
+ <file>images/mac/fileopen.png</file>
+ <file>images/mac/fileprint.png</file>
+ <file>images/mac/filesave.png</file>
+ <file>images/mac/format-indent-less.png</file>
+ <file>images/mac/format-indent-more.png</file>
+ <file>images/mac/textbold.png</file>
+ <file>images/mac/textcenter.png</file>
+ <file>images/mac/textitalic.png</file>
+ <file>images/mac/textjustify.png</file>
+ <file>images/mac/textleft.png</file>
+ <file>images/mac/textright.png</file>
+ <file>images/mac/textunder.png</file>
+ <file>images/mac/textundercolor.png</file>
+ <file>images/mac/zoomin.png</file>
+ <file>images/mac/zoomout.png</file>
+ <file>images/win/checkbox.png</file>
+ <file>images/win/checkbox-checked.png</file>
+ <file>images/win/editcopy.png</file>
+ <file>images/win/editcut.png</file>
+ <file>images/win/editpaste.png</file>
+ <file>images/win/editredo.png</file>
+ <file>images/win/editundo.png</file>
+ <file>images/win/exportpdf.png</file>
+ <file>images/win/filenew.png</file>
+ <file>images/win/fileopen.png</file>
+ <file>images/win/fileprint.png</file>
+ <file>images/win/filesave.png</file>
+ <file>images/win/format-indent-less.png</file>
+ <file>images/win/format-indent-more.png</file>
+ <file>images/win/textbold.png</file>
+ <file>images/win/textcenter.png</file>
+ <file>images/win/textitalic.png</file>
+ <file>images/win/textjustify.png</file>
+ <file>images/win/textleft.png</file>
+ <file>images/win/textright.png</file>
+ <file>images/win/textunder.png</file>
+ <file>images/win/textundercolor.png</file>
+ <file>images/win/zoomin.png</file>
+ <file>images/win/zoomout.png</file>
+ <file>example.html</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/scroller/CMakeLists.txt b/tests/manual/examples/widgets/scroller/CMakeLists.txt
new file mode 100644
index 0000000000..cd920bd361
--- /dev/null
+++ b/tests/manual/examples/widgets/scroller/CMakeLists.txt
@@ -0,0 +1,3 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+qt_internal_add_example(graphicsview)
diff --git a/tests/manual/examples/widgets/scroller/graphicsview/CMakeLists.txt b/tests/manual/examples/widgets/scroller/graphicsview/CMakeLists.txt
new file mode 100644
index 0000000000..3c50b6d99f
--- /dev/null
+++ b/tests/manual/examples/widgets/scroller/graphicsview/CMakeLists.txt
@@ -0,0 +1,36 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(graphicsview LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/scroller/graphicsview")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(graphicsview
+ main.cpp
+)
+
+set_target_properties(graphicsview PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(graphicsview PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS graphicsview
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/scroller/graphicsview/graphicsview.pro b/tests/manual/examples/widgets/scroller/graphicsview/graphicsview.pro
new file mode 100644
index 0000000000..39bdb2bc2c
--- /dev/null
+++ b/tests/manual/examples/widgets/scroller/graphicsview/graphicsview.pro
@@ -0,0 +1,8 @@
+QT += widgets
+
+TEMPLATE = app
+SOURCES = main.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/scroller/graphicsview
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/scroller/graphicsview/main.cpp b/tests/manual/examples/widgets/scroller/graphicsview/main.cpp
new file mode 100644
index 0000000000..b8df2b1b2e
--- /dev/null
+++ b/tests/manual/examples/widgets/scroller/graphicsview/main.cpp
@@ -0,0 +1,255 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtCore>
+#include <QtWidgets>
+
+#define NUM_ITEMS 100
+#define NUM_LISTS 10
+
+/*!
+ \class RectObject
+ Note that it needs to be a QGraphicsObject or else the gestures will not work correctly.
+*/
+class RectObject : public QGraphicsObject
+{
+ Q_OBJECT
+
+public:
+
+ RectObject(const QString &text, qreal x, qreal y, qreal width, qreal height, QBrush brush, QGraphicsItem *parent = nullptr)
+ : QGraphicsObject(parent)
+ , m_text(text)
+ , m_rect(x, y, width, height)
+ , m_pen(brush.color().lighter(), 3.0)
+ , m_brush(brush)
+ {
+ setFlag(QGraphicsItem::ItemClipsToShape, true);
+ }
+
+ QRectF boundingRect() const override
+ {
+ // here we only want the size of the children and not the size of the children of the children...
+ qreal halfpw = m_pen.widthF() / 2;
+ QRectF rect = m_rect;
+ if (halfpw > 0.0)
+ rect.adjust(-halfpw, -halfpw, halfpw, halfpw);
+
+ return rect;
+ }
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
+ {
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+ painter->setPen(m_pen);
+ painter->setBrush(m_brush);
+ painter->drawRect(m_rect);
+
+ painter->setPen(Qt::black);
+ QFont f;
+ f.setPixelSize(m_rect.height());
+ painter->setFont(f);
+ painter->drawText(m_rect, Qt::AlignCenter, m_text);
+ }
+
+ QString m_text;
+ QRectF m_rect;
+ QPen m_pen;
+ QBrush m_brush;
+};
+
+class ViewObject : public QGraphicsObject
+{
+ Q_OBJECT
+public:
+ ViewObject(QGraphicsObject *parent)
+ : QGraphicsObject(parent)
+ { }
+
+ QRectF boundingRect() const override
+ {
+ QRectF rect;
+ const auto items = childItems();
+ for (const QGraphicsItem *item : items)
+ rect |= item->boundingRect().translated(item->pos());
+ return rect;
+ }
+
+ void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override
+ { }
+};
+
+class ListObject : public QGraphicsObject
+{
+ Q_OBJECT
+
+public:
+ ListObject(const QSizeF &size, bool useTouch)
+ {
+ m_size = size;
+ setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
+ // grab gesture via Touch or Mouse events
+ QScroller::grabGesture(this, useTouch ? QScroller::TouchGesture : QScroller::LeftMouseButtonGesture);
+
+ // this needs to be QGraphicsOBJECT - otherwise gesture recognition
+ // will not work for the parent of the viewport (in this case the
+ // list)
+ m_viewport = new ViewObject(this);
+
+ }
+
+ QGraphicsObject *viewport() const
+ {
+ return m_viewport;
+ }
+
+ bool event(QEvent *e) override
+ {
+ switch (e->type()) {
+// ![2]
+ case QEvent::ScrollPrepare: {
+ QScrollPrepareEvent *se = static_cast<QScrollPrepareEvent *>(e);
+ se->setViewportSize(m_size);
+ QRectF br = m_viewport->boundingRect();
+ se->setContentPosRange(QRectF(0, 0,
+ qMax(qreal(0), br.width() - m_size.width()),
+ qMax(qreal(0), br.height() - m_size.height())));
+ se->setContentPos(-m_viewport->pos());
+ se->accept();
+ return true;
+ }
+// ![1]
+// ![2]
+ case QEvent::Scroll: {
+ QScrollEvent *se = static_cast<QScrollEvent *>(e);
+ m_viewport->setPos(-se->contentPos() - se->overshootDistance());
+ return true;
+ }
+// ![2]
+ default:
+ break;
+ }
+ return QGraphicsObject::event(e);
+ }
+
+ bool sceneEvent(QEvent *e) override
+ {
+ switch (e->type()) {
+ case QEvent::TouchBegin: {
+ // We need to return true for the TouchBegin here in the
+ // top-most graphics object - otherwise gestures in our parent
+ // objects will NOT work at all (the accept() flag is already
+ // set due to our setAcceptTouchEvents(true) call in the c'tor
+ return true;
+
+ }
+ case QEvent::GraphicsSceneMousePress: {
+ // We need to return true for the MousePress here in the
+ // top-most graphics object - otherwise gestures in our parent
+ // objects will NOT work at all (the accept() flag is already
+ // set to true by Qt)
+ return true;
+
+ }
+ default:
+ break;
+ }
+ return QGraphicsObject::sceneEvent(e);
+ }
+
+ QRectF boundingRect() const override
+ {
+ return QRectF(0, 0, m_size.width() + 3, m_size.height());
+ }
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
+ {
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+ painter->setPen(QPen(QColor(100, 100, 100), 3.0));
+ painter->drawRect(QRectF(1.5, 1.5, m_size.width() - 3, m_size.height() - 3));
+ }
+
+ QSizeF m_size;
+ ViewObject *m_viewport;
+};
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow(bool useTouch)
+ {
+ m_scene = new QGraphicsScene();
+
+ // -- make the main list
+ ListObject *mainList = new ListObject(QSizeF(780, 400), useTouch);
+ mainList->setObjectName(QLatin1String("MainList"));
+ m_scene->addItem(mainList);
+// ![3]
+ for (int i=0; i<NUM_LISTS; i++) {
+ ListObject *childList = new ListObject(QSizeF(mainList->m_size.width()/3, mainList->m_size.height()), useTouch);
+ childList->setObjectName(QString("ChildList %1").arg(i));
+ fillList(childList);
+ childList->setParentItem(mainList->viewport());
+ childList->setPos(i*mainList->m_size.width()/3, 0);
+ }
+ mainList->viewport()->setPos(0, 0);
+
+
+ /*
+ list1->setTransformOriginPoint(200, 200);
+ list1->setRotation(135);
+ list1->setPos(20 + 200 * .41, 20 + 200 * .41);
+ */
+// ![3]
+
+ m_view = new QGraphicsView(m_scene);
+ setCentralWidget(m_view);
+ setWindowTitle(tr("Gesture example"));
+ m_scene->setSceneRect(0, 0, m_view->viewport()->width(), m_view->viewport()->height());
+ }
+
+ /**
+ * Fills the list object \a list with RectObjects.
+ */
+ void fillList(ListObject *list)
+ {
+ qreal h = list->m_size.height() / 10;
+ for (int i=0; i<NUM_ITEMS; i++) {
+ QColor color = QColor(255*i/NUM_ITEMS, 255*(NUM_ITEMS-i)/NUM_ITEMS, 127*(i%2)+64*(i/2%2));
+ QString text = QLatin1String("Item #") + QString::number(i);
+ QGraphicsItem *rect = new RectObject(text, 0, 0, list->m_size.width() - 6, h - 3, QBrush(color), list->viewport());
+ rect->setPos(3, h*i+3);
+ }
+ list->viewport()->setPos(0, 0);
+ }
+
+
+protected:
+ void resizeEvent(QResizeEvent *e) override
+ {
+ // resize the scene according to our own size to prevent scrolling
+ m_scene->setSceneRect(0, 0, m_view->viewport()->width(), m_view->viewport()->height());
+ QMainWindow::resizeEvent(e);
+ }
+
+ QGraphicsScene *m_scene;
+ QGraphicsView *m_view;
+};
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ bool touch = (a.arguments().contains(QLatin1String("--touch")));
+ MainWindow mw(touch);
+ mw.show();
+#ifdef Q_OS_MAC
+ mw.raise();
+#endif
+ return a.exec();
+}
+
+#include "main.moc"
diff --git a/tests/manual/examples/widgets/scroller/scroller.pro b/tests/manual/examples/widgets/scroller/scroller.pro
new file mode 100644
index 0000000000..bac3f26554
--- /dev/null
+++ b/tests/manual/examples/widgets/scroller/scroller.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += graphicsview
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/CMakeLists.txt b/tests/manual/examples/widgets/tools/plugandpaint/CMakeLists.txt
new file mode 100644
index 0000000000..16b261f2f1
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(plugandpaint LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/tools/plugandpaint")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+add_subdirectory(plugins)
+add_subdirectory(app)
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/app/CMakeLists.txt b/tests/manual/examples/widgets/tools/plugandpaint/app/CMakeLists.txt
new file mode 100644
index 0000000000..b4b6277e2a
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/app/CMakeLists.txt
@@ -0,0 +1,36 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+qt_add_executable(plugandpaint
+ interfaces.h
+ main.cpp
+ mainwindow.cpp mainwindow.h
+ paintarea.cpp paintarea.h
+ plugindialog.cpp plugindialog.h
+)
+
+set_target_properties(plugandpaint PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(plugandpaint PRIVATE
+ Qt6::Widgets
+ pnp_basictools
+)
+
+if(QT6_IS_SHARED_LIBS_BUILD)
+ # Build the shared plugin too when building this example target.
+ add_dependencies(plugandpaint pnp_extrafilters)
+else()
+ # Link the extrafilters plugin if Qt is built statically.
+ target_link_libraries(plugandpaint PRIVATE
+ pnp_extrafilters
+ )
+endif()
+
+install(TARGETS plugandpaint
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/app/app.pro b/tests/manual/examples/widgets/tools/plugandpaint/app/app.pro
new file mode 100644
index 0000000000..e5ff02ecf2
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/app/app.pro
@@ -0,0 +1,37 @@
+#! [0]
+TARGET = plugandpaint
+DESTDIR = ..
+
+QT += widgets
+
+HEADERS = interfaces.h \
+ mainwindow.h \
+ paintarea.h \
+ plugindialog.h
+SOURCES = main.cpp \
+ mainwindow.cpp \
+ paintarea.cpp \
+ plugindialog.cpp
+
+LIBS = -L../plugins
+
+macx-xcode {
+ LIBS += -lpnp_basictools$($${QMAKE_XCODE_LIBRARY_SUFFIX_SETTING})
+} else {
+ android {
+ LIBS += -lpnp_basictools_$${QT_ARCH}
+ } else {
+ LIBS += -lpnp_basictools
+ }
+ if(!debug_and_release|build_pass):CONFIG(debug, debug|release) {
+ mac:LIBS = $$member(LIBS, 0) $$member(LIBS, 1)_debug
+ win32:LIBS = $$member(LIBS, 0) $$member(LIBS, 1)d
+ }
+}
+#! [0]
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tools/plugandpaint
+INSTALLS += target
+
+CONFIG += install_ok # Do not cargo-cult this!
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/app/interfaces.h b/tests/manual/examples/widgets/tools/plugandpaint/app/interfaces.h
new file mode 100644
index 0000000000..9cd0e34fda
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/app/interfaces.h
@@ -0,0 +1,76 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef INTERFACES_H
+#define INTERFACES_H
+
+#include <QtPlugin>
+
+QT_BEGIN_NAMESPACE
+class QImage;
+class QPainter;
+class QWidget;
+class QPainterPath;
+class QPoint;
+class QRect;
+class QString;
+QT_END_NAMESPACE
+
+//! [0]
+class BrushInterface
+{
+public:
+ virtual ~BrushInterface() = default;
+
+ virtual QStringList brushes() const = 0;
+ virtual QRect mousePress(const QString &brush, QPainter &painter,
+ const QPoint &pos) = 0;
+ virtual QRect mouseMove(const QString &brush, QPainter &painter,
+ const QPoint &oldPos, const QPoint &newPos) = 0;
+ virtual QRect mouseRelease(const QString &brush, QPainter &painter,
+ const QPoint &pos) = 0;
+};
+//! [0]
+
+//! [1]
+class ShapeInterface
+{
+public:
+ virtual ~ShapeInterface() = default;
+
+ virtual QStringList shapes() const = 0;
+ virtual QPainterPath generateShape(const QString &shape,
+ QWidget *parent) = 0;
+};
+//! [1]
+
+//! [2]
+class FilterInterface
+{
+public:
+ virtual ~FilterInterface() = default;
+
+ virtual QStringList filters() const = 0;
+ virtual QImage filterImage(const QString &filter, const QImage &image,
+ QWidget *parent) = 0;
+};
+//! [2]
+
+QT_BEGIN_NAMESPACE
+//! [3] //! [4]
+#define BrushInterface_iid "org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface/1.0"
+
+Q_DECLARE_INTERFACE(BrushInterface, BrushInterface_iid)
+//! [3]
+
+#define ShapeInterface_iid "org.qt-project.Qt.Examples.PlugAndPaint.ShapeInterface/1.0"
+
+Q_DECLARE_INTERFACE(ShapeInterface, ShapeInterface_iid)
+//! [5]
+#define FilterInterface_iid "org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface/1.0"
+
+Q_DECLARE_INTERFACE(FilterInterface, FilterInterface_iid)
+//! [4] //! [5]
+QT_END_NAMESPACE
+
+#endif
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/app/main.cpp b/tests/manual/examples/widgets/tools/plugandpaint/app/main.cpp
new file mode 100644
index 0000000000..3fc647dcca
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/app/main.cpp
@@ -0,0 +1,19 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [0]
+#include "mainwindow.h"
+
+#include <QApplication>
+#include <QtPlugin>
+
+Q_IMPORT_PLUGIN(BasicToolsPlugin)
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ MainWindow window;
+ window.show();
+ return app.exec();
+}
+//! [0]
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/app/mainwindow.cpp b/tests/manual/examples/widgets/tools/plugandpaint/app/mainwindow.cpp
new file mode 100644
index 0000000000..8e58ae4ba8
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/app/mainwindow.cpp
@@ -0,0 +1,282 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+
+#include "mainwindow.h"
+#include "interfaces.h"
+#include "paintarea.h"
+#include "plugindialog.h"
+
+#include <QAction>
+#include <QActionGroup>
+#include <QApplication>
+#include <QColorDialog>
+#include <QFileDialog>
+#include <QInputDialog>
+#include <QMenu>
+#include <QMenuBar>
+#include <QMessageBox>
+#include <QPluginLoader>
+#include <QScrollArea>
+#include <QTimer>
+
+MainWindow::MainWindow() : paintArea(new PaintArea)
+ , scrollArea(new QScrollArea)
+{
+ scrollArea->setBackgroundRole(QPalette::Dark);
+ scrollArea->setWidget(paintArea);
+ setCentralWidget(scrollArea);
+
+ createActions();
+ createMenus();
+ loadPlugins();
+
+ setWindowTitle(tr("Plug & Paint"));
+
+ if (!brushActionGroup->actions().isEmpty())
+ brushActionGroup->actions().first()->trigger();
+
+ QTimer::singleShot(500, this, &MainWindow::aboutPlugins);
+}
+
+void MainWindow::open()
+{
+ const QString fileName = QFileDialog::getOpenFileName(this,
+ tr("Open File"),
+ QDir::currentPath());
+ if (!fileName.isEmpty()) {
+ if (!paintArea->openImage(fileName)) {
+ QMessageBox::information(this, tr("Plug & Paint"),
+ tr("Cannot load %1.").arg(fileName));
+ return;
+ }
+ paintArea->adjustSize();
+ }
+}
+
+bool MainWindow::saveAs()
+{
+ const QString initialPath = QDir::currentPath() + "/untitled.png";
+
+ const QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"),
+ initialPath);
+ if (fileName.isEmpty())
+ return false;
+
+ return paintArea->saveImage(fileName, "png");
+}
+
+void MainWindow::brushColor()
+{
+ const QColor newColor = QColorDialog::getColor(paintArea->brushColor());
+ if (newColor.isValid())
+ paintArea->setBrushColor(newColor);
+}
+
+void MainWindow::brushWidth()
+{
+ bool ok;
+ const int newWidth = QInputDialog::getInt(this, tr("Plug & Paint"),
+ tr("Select brush width:"),
+ paintArea->brushWidth(),
+ 1, 50, 1, &ok);
+ if (ok)
+ paintArea->setBrushWidth(newWidth);
+}
+
+//! [0]
+void MainWindow::changeBrush()
+{
+ auto action = qobject_cast<QAction *>(sender());
+ if (!action)
+ return;
+ auto iBrush = qobject_cast<BrushInterface *>(action->parent());
+ if (!iBrush)
+ return;
+ const QString brush = action->text();
+
+ paintArea->setBrush(iBrush, brush);
+}
+//! [0]
+
+//! [1]
+void MainWindow::insertShape()
+{
+ auto action = qobject_cast<QAction *>(sender());
+ if (!action)
+ return;
+ auto iShape = qobject_cast<ShapeInterface *>(action->parent());
+ if (!iShape)
+ return;
+
+ const QPainterPath path = iShape->generateShape(action->text(), this);
+ if (!path.isEmpty())
+ paintArea->insertShape(path);
+}
+//! [1]
+
+//! [2]
+void MainWindow::applyFilter()
+{
+ auto action = qobject_cast<QAction *>(sender());
+ if (!action)
+ return;
+ auto iFilter = qobject_cast<FilterInterface *>(action->parent());
+ if (!iFilter)
+ return;
+
+ const QImage image = iFilter->filterImage(action->text(), paintArea->image(),
+ this);
+ paintArea->setImage(image);
+}
+//! [2]
+
+void MainWindow::about()
+{
+ QMessageBox::about(this, tr("About Plug & Paint"),
+ tr("The <b>Plug & Paint</b> example demonstrates how to write Qt "
+ "applications that can be extended through plugins."));
+}
+
+//! [3]
+void MainWindow::aboutPlugins()
+{
+ PluginDialog dialog(pluginsDir.path(), pluginFileNames, this);
+ dialog.exec();
+}
+//! [3]
+
+void MainWindow::createActions()
+{
+ openAct = new QAction(tr("&Open..."), this);
+ openAct->setShortcuts(QKeySequence::Open);
+ connect(openAct, &QAction::triggered, this, &MainWindow::open);
+
+ saveAsAct = new QAction(tr("&Save As..."), this);
+ saveAsAct->setShortcuts(QKeySequence::SaveAs);
+ connect(saveAsAct, &QAction::triggered, this, &MainWindow::saveAs);
+
+ exitAct = new QAction(tr("E&xit"), this);
+ exitAct->setShortcuts(QKeySequence::Quit);
+ connect(exitAct, &QAction::triggered, this, &MainWindow::close);
+
+ brushColorAct = new QAction(tr("&Brush Color..."), this);
+ connect(brushColorAct, &QAction::triggered, this, &MainWindow::brushColor);
+
+ brushWidthAct = new QAction(tr("&Brush Width..."), this);
+ connect(brushWidthAct, &QAction::triggered, this, &MainWindow::brushWidth);
+
+ brushActionGroup = new QActionGroup(this);
+
+ aboutAct = new QAction(tr("&About"), this);
+ connect(aboutAct, &QAction::triggered, this, &MainWindow::about);
+
+ aboutQtAct = new QAction(tr("About &Qt"), this);
+ connect(aboutQtAct, &QAction::triggered, qApp, &QApplication::aboutQt);
+
+ aboutPluginsAct = new QAction(tr("About &Plugins"), this);
+ connect(aboutPluginsAct, &QAction::triggered, this, &MainWindow::aboutPlugins);
+}
+
+void MainWindow::createMenus()
+{
+ fileMenu = menuBar()->addMenu(tr("&File"));
+ fileMenu->addAction(openAct);
+ fileMenu->addAction(saveAsAct);
+ fileMenu->addSeparator();
+ fileMenu->addAction(exitAct);
+
+ brushMenu = menuBar()->addMenu(tr("&Brush"));
+ brushMenu->addAction(brushColorAct);
+ brushMenu->addAction(brushWidthAct);
+ brushMenu->addSeparator();
+
+ shapesMenu = menuBar()->addMenu(tr("&Shapes"));
+
+ filterMenu = menuBar()->addMenu(tr("&Filter"));
+
+ menuBar()->addSeparator();
+
+ helpMenu = menuBar()->addMenu(tr("&Help"));
+ helpMenu->addAction(aboutAct);
+ helpMenu->addAction(aboutQtAct);
+ helpMenu->addAction(aboutPluginsAct);
+}
+
+//! [4]
+void MainWindow::loadPlugins()
+{
+ const auto staticInstances = QPluginLoader::staticInstances();
+ for (QObject *plugin : staticInstances)
+ populateMenus(plugin);
+//! [4] //! [5]
+
+ pluginsDir = QDir(QCoreApplication::applicationDirPath());
+
+#if defined(Q_OS_WIN)
+ if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
+ pluginsDir.cdUp();
+#elif defined(Q_OS_MAC)
+ if (pluginsDir.dirName() == "MacOS") {
+ pluginsDir.cdUp();
+ pluginsDir.cdUp();
+ pluginsDir.cdUp();
+ }
+#endif
+ pluginsDir.cd("plugins");
+//! [5]
+
+//! [6]
+ const auto entryList = pluginsDir.entryList(QDir::Files);
+ for (const QString &fileName : entryList) {
+ QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
+ QObject *plugin = loader.instance();
+ if (plugin) {
+ populateMenus(plugin);
+ pluginFileNames += fileName;
+//! [6] //! [7]
+ }
+//! [7] //! [8]
+ }
+//! [8]
+
+//! [9]
+ brushMenu->setEnabled(!brushActionGroup->actions().isEmpty());
+ shapesMenu->setEnabled(!shapesMenu->actions().isEmpty());
+ filterMenu->setEnabled(!filterMenu->actions().isEmpty());
+}
+//! [9]
+
+//! [10]
+void MainWindow::populateMenus(QObject *plugin)
+{
+ auto iBrush = qobject_cast<BrushInterface *>(plugin);
+ if (iBrush)
+ addToMenu(plugin, iBrush->brushes(), brushMenu, &MainWindow::changeBrush,
+ brushActionGroup);
+
+ auto iShape = qobject_cast<ShapeInterface *>(plugin);
+ if (iShape)
+ addToMenu(plugin, iShape->shapes(), shapesMenu, &MainWindow::insertShape);
+
+ auto iFilter = qobject_cast<FilterInterface *>(plugin);
+ if (iFilter)
+ addToMenu(plugin, iFilter->filters(), filterMenu, &MainWindow::applyFilter);
+}
+//! [10]
+
+void MainWindow::addToMenu(QObject *plugin, const QStringList &texts,
+ QMenu *menu, Member member,
+ QActionGroup *actionGroup)
+{
+ for (const QString &text : texts) {
+ auto action = new QAction(text, plugin);
+ connect(action, &QAction::triggered, this, member);
+ menu->addAction(action);
+
+ if (actionGroup) {
+ action->setCheckable(true);
+ actionGroup->addAction(action);
+ }
+ }
+}
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/app/mainwindow.h b/tests/manual/examples/widgets/tools/plugandpaint/app/mainwindow.h
new file mode 100644
index 0000000000..bc09471ba5
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/app/mainwindow.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QDir>
+#include <QMainWindow>
+#include <QStringList>
+
+QT_BEGIN_NAMESPACE
+class QAction;
+class QActionGroup;
+class QMenu;
+class QScrollArea;
+QT_END_NAMESPACE
+class PaintArea;
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow();
+
+private slots:
+ void open();
+ bool saveAs();
+ void brushColor();
+ void brushWidth();
+ void changeBrush();
+ void insertShape();
+ void applyFilter();
+ void about();
+ void aboutPlugins();
+
+private:
+ typedef void (MainWindow::*Member)();
+
+ void createActions();
+ void createMenus();
+ void loadPlugins();
+ void populateMenus(QObject *plugin);
+ void addToMenu(QObject *plugin, const QStringList &texts, QMenu *menu,
+ Member member, QActionGroup *actionGroup = nullptr);
+
+ PaintArea *paintArea = nullptr;
+ QScrollArea *scrollArea = nullptr;
+ QDir pluginsDir;
+ QStringList pluginFileNames;
+
+ QMenu *fileMenu = nullptr;
+ QMenu *brushMenu = nullptr;
+ QMenu *shapesMenu = nullptr;
+ QMenu *filterMenu = nullptr;
+ QMenu *helpMenu = nullptr;
+ QActionGroup *brushActionGroup = nullptr;
+ QAction *openAct = nullptr;
+ QAction *saveAsAct = nullptr;
+ QAction *exitAct = nullptr;
+ QAction *brushWidthAct = nullptr;
+ QAction *brushColorAct = nullptr;
+ QAction *aboutAct = nullptr;
+ QAction *aboutQtAct = nullptr;
+ QAction *aboutPluginsAct = nullptr;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/app/paintarea.cpp b/tests/manual/examples/widgets/tools/plugandpaint/app/paintarea.cpp
new file mode 100644
index 0000000000..3596f7979c
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/app/paintarea.cpp
@@ -0,0 +1,152 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+
+#include "paintarea.h"
+#include "interfaces.h"
+
+#include <QMouseEvent>
+#include <QPainter>
+
+PaintArea::PaintArea(QWidget *parent) : QWidget(parent)
+{
+ setAttribute(Qt::WA_StaticContents);
+ setAttribute(Qt::WA_OpaquePaintEvent);
+
+ theImage.fill(qRgb(255, 255, 255));
+}
+
+bool PaintArea::openImage(const QString &fileName)
+{
+ QImage image;
+ if (!image.load(fileName))
+ return false;
+
+ setImage(image);
+ return true;
+}
+
+bool PaintArea::saveImage(const QString &fileName, const char *fileFormat)
+{
+ return theImage.save(fileName, fileFormat);
+}
+
+void PaintArea::setImage(const QImage &image)
+{
+ theImage = image.convertToFormat(QImage::Format_RGB32);
+ update();
+ updateGeometry();
+}
+
+void PaintArea::insertShape(const QPainterPath &path)
+{
+ pendingPath = path;
+#ifndef QT_NO_CURSOR
+ setCursor(Qt::CrossCursor);
+#endif
+}
+
+void PaintArea::setBrushColor(const QColor &color)
+{
+ this->color = color;
+}
+
+void PaintArea::setBrushWidth(int width)
+{
+ thickness = width;
+}
+
+//! [0]
+void PaintArea::setBrush(BrushInterface *brushInterface, const QString &brush)
+{
+ this->brushInterface = brushInterface;
+ this->brush = brush;
+}
+//! [0]
+
+QSize PaintArea::sizeHint() const
+{
+ return theImage.size();
+}
+
+void PaintArea::paintEvent(QPaintEvent * /* event */)
+{
+ QPainter painter(this);
+ painter.drawImage(QPoint(0, 0), theImage);
+}
+
+void PaintArea::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton) {
+ if (!pendingPath.isEmpty()) {
+ QPainter painter(&theImage);
+ setupPainter(painter);
+
+ const QRectF boundingRect = pendingPath.boundingRect();
+ QLinearGradient gradient(boundingRect.topRight(),
+ boundingRect.bottomLeft());
+ gradient.setColorAt(0.0, QColor(color.red(), color.green(),
+ color.blue(), 63));
+ gradient.setColorAt(1.0, QColor(color.red(), color.green(),
+ color.blue(), 191));
+ painter.setBrush(gradient);
+ painter.translate(event->position().toPoint() - boundingRect.center());
+ painter.drawPath(pendingPath);
+
+ pendingPath = QPainterPath();
+#ifndef QT_NO_CURSOR
+ unsetCursor();
+#endif
+ update();
+ } else {
+ if (brushInterface) {
+ QPainter painter(&theImage);
+ setupPainter(painter);
+ const QRect rect = brushInterface->mousePress(brush, painter,
+ event->position().toPoint());
+ update(rect);
+ }
+
+ lastPos = event->position().toPoint();
+ }
+ }
+}
+
+//! [1]
+void PaintArea::mouseMoveEvent(QMouseEvent *event)
+{
+ if ((event->buttons() & Qt::LeftButton) && lastPos != QPoint(-1, -1)) {
+ if (brushInterface) {
+ QPainter painter(&theImage);
+ setupPainter(painter);
+ const QRect rect = brushInterface->mouseMove(brush, painter, lastPos,
+ event->position().toPoint());
+ update(rect);
+ }
+
+ lastPos = event->position().toPoint();
+ }
+}
+//! [1]
+
+void PaintArea::mouseReleaseEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton && lastPos != QPoint(-1, -1)) {
+ if (brushInterface) {
+ QPainter painter(&theImage);
+ setupPainter(painter);
+ QRect rect = brushInterface->mouseRelease(brush, painter,
+ event->position().toPoint());
+ update(rect);
+ }
+
+ lastPos = QPoint(-1, -1);
+ }
+}
+
+void PaintArea::setupPainter(QPainter &painter)
+{
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ painter.setPen(QPen(color, thickness, Qt::SolidLine, Qt::RoundCap,
+ Qt::RoundJoin));
+}
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/app/paintarea.h b/tests/manual/examples/widgets/tools/plugandpaint/app/paintarea.h
new file mode 100644
index 0000000000..f24db0ba89
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/app/paintarea.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PAINTAREA_H
+#define PAINTAREA_H
+
+#include <QColor>
+#include <QImage>
+#include <QPainterPath>
+#include <QWidget>
+
+class BrushInterface;
+
+class PaintArea : public QWidget
+{
+ Q_OBJECT
+
+public:
+ PaintArea(QWidget *parent = nullptr);
+
+ bool openImage(const QString &fileName);
+ bool saveImage(const QString &fileName, const char *fileFormat);
+ void setImage(const QImage &image);
+ void insertShape(const QPainterPath &path);
+ void setBrushColor(const QColor &color);
+ void setBrushWidth(int width);
+ void setBrush(BrushInterface *brushInterface, const QString &brush);
+
+ QImage image() const { return theImage; }
+ QColor brushColor() const { return color; }
+ int brushWidth() const { return thickness; }
+ QSize sizeHint() const override;
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+
+private:
+ void setupPainter(QPainter &painter);
+
+ QImage theImage = {500, 400, QImage::Format_RGB32};
+ QColor color = Qt::blue;
+ int thickness = 3;
+
+ BrushInterface *brushInterface = nullptr;
+ QString brush;
+ QPoint lastPos = {-1, -1};
+
+ QPainterPath pendingPath;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/app/plugindialog.cpp b/tests/manual/examples/widgets/tools/plugandpaint/app/plugindialog.cpp
new file mode 100644
index 0000000000..2ff5c4b1e3
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/app/plugindialog.cpp
@@ -0,0 +1,118 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+
+#include "plugindialog.h"
+#include "interfaces.h"
+
+#include <QDir>
+#include <QGridLayout>
+#include <QHeaderView>
+#include <QLabel>
+#include <QPluginLoader>
+#include <QPushButton>
+#include <QStringList>
+#include <QTreeWidget>
+#include <QTreeWidgetItem>
+
+PluginDialog::PluginDialog(const QString &path, const QStringList &fileNames,
+ QWidget *parent) :
+ QDialog(parent),
+ label(new QLabel),
+ treeWidget(new QTreeWidget),
+ okButton(new QPushButton(tr("OK")))
+{
+ treeWidget->setAlternatingRowColors(false);
+ treeWidget->setSelectionMode(QAbstractItemView::NoSelection);
+ treeWidget->setColumnCount(1);
+ treeWidget->header()->hide();
+
+ okButton->setDefault(true);
+
+ connect(okButton, &QAbstractButton::clicked, this, &QWidget::close);
+
+ QGridLayout *mainLayout = new QGridLayout;
+ mainLayout->setColumnStretch(0, 1);
+ mainLayout->setColumnStretch(2, 1);
+ mainLayout->addWidget(label, 0, 0, 1, 3);
+ mainLayout->addWidget(treeWidget, 1, 0, 1, 3);
+ mainLayout->addWidget(okButton, 2, 1);
+ setLayout(mainLayout);
+
+ interfaceIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirOpenIcon),
+ QIcon::Normal, QIcon::On);
+ interfaceIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirClosedIcon),
+ QIcon::Normal, QIcon::Off);
+ featureIcon.addPixmap(style()->standardPixmap(QStyle::SP_FileIcon));
+
+ setWindowTitle(tr("Plugin Information"));
+ findPlugins(path, fileNames);
+}
+
+//! [0]
+void PluginDialog::findPlugins(const QString &path,
+ const QStringList &fileNames)
+{
+ label->setText(tr("Plug & Paint found the following plugins\n"
+ "(looked in %1):")
+ .arg(QDir::toNativeSeparators(path)));
+
+ const QDir dir(path);
+
+ const auto staticInstances = QPluginLoader::staticInstances();
+ for (QObject *plugin : staticInstances)
+ populateTreeWidget(plugin, tr("%1 (Static Plugin)")
+ .arg(plugin->metaObject()->className()));
+
+ for (const QString &fileName : fileNames) {
+ QPluginLoader loader(dir.absoluteFilePath(fileName));
+ QObject *plugin = loader.instance();
+ if (plugin)
+ populateTreeWidget(plugin, fileName);
+ }
+}
+//! [0]
+
+//! [1]
+void PluginDialog::populateTreeWidget(QObject *plugin, const QString &text)
+{
+ auto pluginItem = new QTreeWidgetItem(treeWidget);
+ pluginItem->setText(0, text);
+ pluginItem->setExpanded(true);
+
+ QFont boldFont = pluginItem->font(0);
+ boldFont.setBold(true);
+ pluginItem->setFont(0, boldFont);
+
+ if (plugin) {
+ auto iBrush = qobject_cast<BrushInterface *>(plugin);
+ if (iBrush)
+ addItems(pluginItem, "BrushInterface", iBrush->brushes());
+
+ auto iShape = qobject_cast<ShapeInterface *>(plugin);
+ if (iShape)
+ addItems(pluginItem, "ShapeInterface", iShape->shapes());
+
+ auto iFilter = qobject_cast<FilterInterface *>(plugin);
+ if (iFilter)
+ addItems(pluginItem, "FilterInterface", iFilter->filters());
+ }
+}
+//! [1]
+
+void PluginDialog::addItems(QTreeWidgetItem *pluginItem,
+ const char *interfaceName,
+ const QStringList &features)
+{
+ auto interfaceItem = new QTreeWidgetItem(pluginItem);
+ interfaceItem->setText(0, interfaceName);
+ interfaceItem->setIcon(0, interfaceIcon);
+
+ for (QString feature : features) {
+ if (feature.endsWith("..."))
+ feature.chop(3);
+ auto featureItem = new QTreeWidgetItem(interfaceItem);
+ featureItem->setText(0, feature);
+ featureItem->setIcon(0, featureIcon);
+ }
+}
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/app/plugindialog.h b/tests/manual/examples/widgets/tools/plugandpaint/app/plugindialog.h
new file mode 100644
index 0000000000..32b8aa6fe0
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/app/plugindialog.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PLUGINDIALOG_H
+#define PLUGINDIALOG_H
+
+#include <QDialog>
+#include <QIcon>
+
+QT_BEGIN_NAMESPACE
+class QLabel;
+class QPushButton;
+class QTreeWidget;
+class QTreeWidgetItem;
+QT_END_NAMESPACE
+
+class PluginDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ PluginDialog(const QString &path, const QStringList &fileNames,
+ QWidget *parent = nullptr);
+
+private:
+ void findPlugins(const QString &path, const QStringList &fileNames);
+ void populateTreeWidget(QObject *plugin, const QString &text);
+ void addItems(QTreeWidgetItem *pluginItem, const char *interfaceName,
+ const QStringList &features);
+
+ QLabel *label = nullptr;
+ QTreeWidget *treeWidget = nullptr;
+ QPushButton *okButton = nullptr;
+ QIcon interfaceIcon;
+ QIcon featureIcon;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/plugandpaint.pro b/tests/manual/examples/widgets/tools/plugandpaint/plugandpaint.pro
new file mode 100644
index 0000000000..58c4dbbb6e
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/plugandpaint.pro
@@ -0,0 +1,7 @@
+QT_FOR_CONFIG += widgets
+requires(qtConfig(inputdialog))
+
+TEMPLATE = subdirs
+SUBDIRS = plugins app
+
+app.depends = plugins
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/plugins/CMakeLists.txt b/tests/manual/examples/widgets/tools/plugandpaint/plugins/CMakeLists.txt
new file mode 100644
index 0000000000..c468cdc191
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/plugins/CMakeLists.txt
@@ -0,0 +1,4 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+add_subdirectory(basictools)
+add_subdirectory(extrafilters)
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/CMakeLists.txt b/tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/CMakeLists.txt
new file mode 100644
index 0000000000..8b6436173b
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+qt_add_plugin(pnp_basictools
+ STATIC
+ CLASS_NAME BasicToolsPlugin
+ basictoolsplugin.cpp basictoolsplugin.h
+)
+
+target_include_directories(pnp_basictools PRIVATE
+ ../../app
+)
+
+target_link_libraries(pnp_basictools PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictools.json b/tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictools.json
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictools.json
@@ -0,0 +1 @@
+{}
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictools.pro b/tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictools.pro
new file mode 100644
index 0000000000..f5ba95252c
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictools.pro
@@ -0,0 +1,17 @@
+#! [0]
+TEMPLATE = lib
+CONFIG += plugin static
+QT += widgets
+INCLUDEPATH += ../../app
+HEADERS = basictoolsplugin.h
+SOURCES = basictoolsplugin.cpp
+TARGET = $$qtLibraryTarget(pnp_basictools)
+DESTDIR = ../../plugins
+#! [0]
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tools/plugandpaint/plugins
+INSTALLS += target
+
+CONFIG += install_ok # Do not cargo-cult this!
+uikit: CONFIG += debug_and_release
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictoolsplugin.cpp b/tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictoolsplugin.cpp
new file mode 100644
index 0000000000..6a350f38a4
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictoolsplugin.cpp
@@ -0,0 +1,150 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "basictoolsplugin.h"
+
+#include <QInputDialog>
+#include <QPainter>
+#include <QRandomGenerator>
+#include <QtMath>
+
+//! [0]
+QStringList BasicToolsPlugin::brushes() const
+{
+ return {tr("Pencil"), tr("Air Brush"), tr("Random Letters")};
+}
+//! [0]
+
+//! [1]
+QRect BasicToolsPlugin::mousePress(const QString &brush, QPainter &painter,
+ const QPoint &pos)
+{
+ return mouseMove(brush, painter, pos, pos);
+}
+//! [1]
+
+//! [2]
+QRect BasicToolsPlugin::mouseMove(const QString &brush, QPainter &painter,
+ const QPoint &oldPos, const QPoint &newPos)
+{
+ painter.save();
+
+ int rad = painter.pen().width() / 2;
+ QRect boundingRect = QRect(oldPos, newPos).normalized()
+ .adjusted(-rad, -rad, +rad, +rad);
+ QColor color = painter.pen().color();
+ int thickness = painter.pen().width();
+ QColor transparentColor(color.red(), color.green(), color.blue(), 0);
+//! [2] //! [3]
+
+ if (brush == tr("Pencil")) {
+ painter.drawLine(oldPos, newPos);
+ } else if (brush == tr("Air Brush")) {
+ int numSteps = 2 + (newPos - oldPos).manhattanLength() / 2;
+
+ painter.setBrush(QBrush(color, Qt::Dense6Pattern));
+ painter.setPen(Qt::NoPen);
+
+ for (int i = 0; i < numSteps; ++i) {
+ int x = oldPos.x() + i * (newPos.x() - oldPos.x()) / (numSteps - 1);
+ int y = oldPos.y() + i * (newPos.y() - oldPos.y()) / (numSteps - 1);
+
+ painter.drawEllipse(x - (thickness / 2), y - (thickness / 2),
+ thickness, thickness);
+ }
+ } else if (brush == tr("Random Letters")) {
+ QChar ch(QRandomGenerator::global()->bounded('A', 'Z' + 1));
+
+ QFont biggerFont = painter.font();
+ biggerFont.setBold(true);
+ biggerFont.setPointSize(biggerFont.pointSize() + thickness);
+ painter.setFont(biggerFont);
+
+ painter.drawText(newPos, QString(ch));
+
+ QFontMetrics metrics(painter.font());
+ boundingRect = metrics.boundingRect(ch);
+ boundingRect.translate(newPos);
+ boundingRect.adjust(-10, -10, +10, +10);
+ }
+ painter.restore();
+ return boundingRect;
+}
+//! [3]
+
+//! [4]
+QRect BasicToolsPlugin::mouseRelease(const QString & /* brush */,
+ QPainter & /* painter */,
+ const QPoint & /* pos */)
+{
+ return QRect(0, 0, 0, 0);
+}
+//! [4]
+
+//! [5]
+QStringList BasicToolsPlugin::shapes() const
+{
+ return {tr("Circle"), tr("Star"), tr("Text...")};
+}
+//! [5]
+
+//! [6]
+QPainterPath BasicToolsPlugin::generateShape(const QString &shape,
+ QWidget *parent)
+{
+ QPainterPath path;
+
+ if (shape == tr("Circle")) {
+ path.addEllipse(0, 0, 50, 50);
+ } else if (shape == tr("Star")) {
+ path.moveTo(90, 50);
+ for (int i = 1; i < 5; ++i) {
+ path.lineTo(50 + 40 * std::cos(0.8 * i * M_PI),
+ 50 + 40 * std::sin(0.8 * i * M_PI));
+ }
+ path.closeSubpath();
+ } else if (shape == tr("Text...")) {
+ QString text = QInputDialog::getText(parent, tr("Text Shape"),
+ tr("Enter text:"),
+ QLineEdit::Normal, tr("Qt"));
+ if (!text.isEmpty()) {
+ QFont timesFont("Times", 50);
+ timesFont.setStyleStrategy(QFont::ForceOutline);
+ path.addText(0, 0, timesFont, text);
+ }
+ }
+
+ return path;
+}
+//! [6]
+
+//! [7]
+QStringList BasicToolsPlugin::filters() const
+{
+ return {tr("Invert Pixels"), tr("Swap RGB"), tr("Grayscale")};
+}
+//! [7]
+
+//! [8]
+QImage BasicToolsPlugin::filterImage(const QString &filter, const QImage &image,
+ QWidget * /* parent */)
+{
+ QImage result = image.convertToFormat(QImage::Format_RGB32);
+
+ if (filter == tr("Invert Pixels")) {
+ result.invertPixels();
+ } else if (filter == tr("Swap RGB")) {
+ result = result.rgbSwapped();
+ } else if (filter == tr("Grayscale")) {
+ for (int y = 0; y < result.height(); ++y) {
+ for (int x = 0; x < result.width(); ++x) {
+ QRgb pixel = result.pixel(x, y);
+ int gray = qGray(pixel);
+ int alpha = qAlpha(pixel);
+ result.setPixel(x, y, qRgba(gray, gray, gray, alpha));
+ }
+ }
+ }
+ return result;
+}
+//! [8]
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictoolsplugin.h b/tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictoolsplugin.h
new file mode 100644
index 0000000000..9fb3295bf5
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/plugins/basictools/basictoolsplugin.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef BASICTOOLSPLUGIN_H
+#define BASICTOOLSPLUGIN_H
+
+//! [0]
+#include <interfaces.h>
+
+#include <QImage>
+#include <QObject>
+#include <QPainterPath>
+#include <QRect>
+#include <QStringList>
+#include <QtPlugin>
+
+//! [1]
+class BasicToolsPlugin : public QObject,
+ public BrushInterface,
+ public ShapeInterface,
+ public FilterInterface
+{
+ Q_OBJECT
+//! [4]
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface" FILE "basictools.json")
+//! [4]
+ Q_INTERFACES(BrushInterface ShapeInterface FilterInterface)
+//! [0]
+
+//! [2]
+public:
+//! [1]
+ // BrushInterface
+ QStringList brushes() const override;
+ QRect mousePress(const QString &brush, QPainter &painter,
+ const QPoint &pos) override;
+ QRect mouseMove(const QString &brush, QPainter &painter,
+ const QPoint &oldPos, const QPoint &newPos) override;
+ QRect mouseRelease(const QString &brush, QPainter &painter,
+ const QPoint &pos) override;
+
+ // ShapeInterface
+ QStringList shapes() const override;
+ QPainterPath generateShape(const QString &shape, QWidget *parent) override;
+
+ // FilterInterface
+ QStringList filters() const override;
+ QImage filterImage(const QString &filter, const QImage &image,
+ QWidget *parent) override;
+//! [3]
+};
+//! [2] //! [3]
+
+#endif
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/CMakeLists.txt b/tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/CMakeLists.txt
new file mode 100644
index 0000000000..ead67decd5
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/CMakeLists.txt
@@ -0,0 +1,26 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+qt_add_plugin(pnp_extrafilters
+ CLASS_NAME ExtraFiltersPlugin
+ extrafiltersplugin.cpp extrafiltersplugin.h
+)
+
+set_target_properties(pnp_extrafilters PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/app"
+)
+
+target_include_directories(pnp_extrafilters PRIVATE
+ ../../app
+)
+
+target_link_libraries(pnp_extrafilters PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS pnp_extrafilters
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}/plugins"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}/plugins"
+)
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafilters.json b/tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafilters.json
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafilters.json
@@ -0,0 +1 @@
+{}
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafilters.pro b/tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafilters.pro
new file mode 100644
index 0000000000..e137b04823
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafilters.pro
@@ -0,0 +1,17 @@
+#! [0]
+TEMPLATE = lib
+CONFIG += plugin
+QT += widgets
+INCLUDEPATH += ../../app
+HEADERS = extrafiltersplugin.h
+SOURCES = extrafiltersplugin.cpp
+TARGET = $$qtLibraryTarget(pnp_extrafilters)
+DESTDIR = ../../plugins
+
+#! [0]
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tools/plugandpaint/plugins
+INSTALLS += target
+
+CONFIG += install_ok # Do not cargo-cult this!
+uikit: CONFIG += debug_and_release
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafiltersplugin.cpp b/tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafiltersplugin.cpp
new file mode 100644
index 0000000000..5356efc328
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafiltersplugin.cpp
@@ -0,0 +1,82 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "extrafiltersplugin.h"
+
+#include <QInputDialog>
+
+QStringList ExtraFiltersPlugin::filters() const
+{
+ return {tr("Flip Horizontally"), tr("Flip Vertically"),
+ tr("Smudge..."), tr("Threshold...")};
+}
+
+QImage ExtraFiltersPlugin::filterImage(const QString &filter,
+ const QImage &image, QWidget *parent)
+{
+ QImage original = image.convertToFormat(QImage::Format_RGB32);
+ QImage result = original;
+
+ if (filter == tr("Flip Horizontally")) {
+ for (int y = 0; y < original.height(); ++y) {
+ for (int x = 0; x < original.width(); ++x) {
+ QRgb pixel = original.pixel(original.width() - x - 1, y);
+ result.setPixel(x, y, pixel);
+ }
+ }
+ } else if (filter == tr("Flip Vertically")) {
+ for (int y = 0; y < original.height(); ++y) {
+ for (int x = 0; x < original.width(); ++x) {
+ QRgb pixel = original.pixel(x, original.height() - y - 1);
+ result.setPixel(x, y, pixel);
+ }
+ }
+ } else if (filter == tr("Smudge...")) {
+ bool ok;
+ int numIters = QInputDialog::getInt(parent, tr("Smudge Filter"),
+ tr("Enter number of iterations:"),
+ 5, 1, 20, 1, &ok);
+ if (ok) {
+ for (int i = 0; i < numIters; ++i) {
+ for (int y = 1; y < original.height() - 1; ++y) {
+ for (int x = 1; x < original.width() - 1; ++x) {
+ QRgb p1 = original.pixel(x, y);
+ QRgb p2 = original.pixel(x, y + 1);
+ QRgb p3 = original.pixel(x, y - 1);
+ QRgb p4 = original.pixel(x + 1, y);
+ QRgb p5 = original.pixel(x - 1, y);
+
+ int red = (qRed(p1) + qRed(p2) + qRed(p3) + qRed(p4)
+ + qRed(p5)) / 5;
+ int green = (qGreen(p1) + qGreen(p2) + qGreen(p3)
+ + qGreen(p4) + qGreen(p5)) / 5;
+ int blue = (qBlue(p1) + qBlue(p2) + qBlue(p3)
+ + qBlue(p4) + qBlue(p5)) / 5;
+ int alpha = (qAlpha(p1) + qAlpha(p2) + qAlpha(p3)
+ + qAlpha(p4) + qAlpha(p5)) / 5;
+
+ result.setPixel(x, y, qRgba(red, green, blue, alpha));
+ }
+ }
+ }
+ }
+ } else if (filter == tr("Threshold...")) {
+ bool ok;
+ int threshold = QInputDialog::getInt(parent, tr("Threshold Filter"),
+ tr("Enter threshold:"),
+ 10, 1, 256, 1, &ok);
+ if (ok) {
+ int factor = 256 / threshold;
+ for (int y = 0; y < original.height(); ++y) {
+ for (int x = 0; x < original.width(); ++x) {
+ QRgb pixel = original.pixel(x, y);
+ result.setPixel(x, y, qRgba(qRed(pixel) / factor * factor,
+ qGreen(pixel) / factor * factor,
+ qBlue(pixel) / factor * factor,
+ qAlpha(pixel)));
+ }
+ }
+ }
+ }
+ return result;
+}
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafiltersplugin.h b/tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafiltersplugin.h
new file mode 100644
index 0000000000..0fc50d2fd0
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/plugins/extrafilters/extrafiltersplugin.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef EXTRAFILTERSPLUGIN_H
+#define EXTRAFILTERSPLUGIN_H
+
+//! [0]
+#include <interfaces.h>
+
+#include <QObject>
+#include <QtPlugin>
+#include <QStringList>
+#include <QImage>
+
+class ExtraFiltersPlugin : public QObject, public FilterInterface
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface" FILE "extrafilters.json")
+ Q_INTERFACES(FilterInterface)
+
+public:
+ QStringList filters() const override;
+ QImage filterImage(const QString &filter, const QImage &image,
+ QWidget *parent) override;
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/tools/plugandpaint/plugins/plugins.pro b/tests/manual/examples/widgets/tools/plugandpaint/plugins/plugins.pro
new file mode 100644
index 0000000000..e15220c621
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/plugins/plugins.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+SUBDIRS = basictools \
+ extrafilters
diff --git a/tests/manual/examples/widgets/tools/settingseditor/CMakeLists.txt b/tests/manual/examples/widgets/tools/settingseditor/CMakeLists.txt
new file mode 100644
index 0000000000..3b934a9ae9
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/settingseditor/CMakeLists.txt
@@ -0,0 +1,20 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(settingseditor LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+qt_internal_add_manual_test(settingseditor
+ SOURCES
+ locationdialog.cpp locationdialog.h
+ main.cpp
+ mainwindow.cpp mainwindow.h
+ settingstree.cpp settingstree.h
+ variantdelegate.cpp variantdelegate.h
+ LIBRARIES
+ Qt::Widgets
+)
diff --git a/tests/manual/examples/widgets/tools/settingseditor/inifiles/licensepage.ini b/tests/manual/examples/widgets/tools/settingseditor/inifiles/licensepage.ini
new file mode 100644
index 0000000000..608d1b7885
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/settingseditor/inifiles/licensepage.ini
@@ -0,0 +1,46 @@
+[Field%201]
+Bottom=89
+Flags=MULTILINE|VSCROLL|READONLY
+Left=4
+Right=296
+State=No license agreement file found. Please contact support.
+Top=14
+Type=Text
+
+[Field%202]
+Bottom=8
+Left=4
+Right=294
+Text=Press Page Down to see the rest of the agreement.
+Top=0
+Type=Label
+
+[Field%203]
+Bottom=111
+Left=4
+Right=297
+Text=If you accept the terms of the agreement, select the first option below. You must accept the agreement to install this software. Click Next to continue.
+Top=92
+Type=Label
+
+[Field%204]
+Bottom=129
+Flags=GROUP|NOTIFY
+Left=4
+Right=299
+Text=I &accept the terms in the License Agreement
+Top=120
+Type=RadioButton
+
+[Field%205]
+Bottom=140
+Flags=NOTIFY
+Left=4
+Right=300
+State=1
+Text=I &do not accept the terms in the License Agreement
+Top=129
+Type=RadioButton
+
+[Settings]
+NumFields=5
diff --git a/tests/manual/examples/widgets/tools/settingseditor/inifiles/qsa.ini b/tests/manual/examples/widgets/tools/settingseditor/inifiles/qsa.ini
new file mode 100644
index 0000000000..56a2964ee5
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/settingseditor/inifiles/qsa.ini
@@ -0,0 +1,26 @@
+[Field%201]
+Bottom=65
+Left=0
+Right=299
+Text=QSA Build Options
+Top=9
+Type=Groupbox
+
+[Field%202]
+Bottom=37
+Left=20
+Right=284
+Text=Don't compile QSA Workbench into QSA.
+Top=27
+Type=Checkbox
+
+[Field%203]
+Bottom=56
+Left=20
+Right=247
+Text=Don't compile QSA Workbench nor QSA Editor into QSA.
+Top=45
+Type=Checkbox
+
+[Settings]
+NumFields=3
diff --git a/tests/manual/examples/widgets/tools/settingseditor/locationdialog.cpp b/tests/manual/examples/widgets/tools/settingseditor/locationdialog.cpp
new file mode 100644
index 0000000000..1c41d45009
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/settingseditor/locationdialog.cpp
@@ -0,0 +1,192 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "locationdialog.h"
+
+#include <QBoxLayout>
+#include <QComboBox>
+#include <QDialogButtonBox>
+#include <QDir>
+#include <QPushButton>
+#include <QGroupBox>
+#include <QHeaderView>
+#include <QLabel>
+#include <QLineEdit>
+#include <QTableWidget>
+#include <QTableWidgetItem>
+
+LocationDialog::LocationDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ formatComboBox = new QComboBox;
+ formatComboBox->addItem(tr("Native"));
+ formatComboBox->addItem(tr("INI"));
+
+ scopeComboBox = new QComboBox;
+ scopeComboBox->addItem(tr("User"));
+ scopeComboBox->addItem(tr("System"));
+
+ organizationComboBox = new QComboBox;
+ organizationComboBox->addItem(tr("QtProject"));
+ organizationComboBox->setEditable(true);
+
+ applicationComboBox = new QComboBox;
+ applicationComboBox->addItem(tr("Any"));
+ applicationComboBox->addItem(tr("Qt Creator"));
+ applicationComboBox->addItem(tr("Assistant"));
+ applicationComboBox->addItem(tr("Designer"));
+ applicationComboBox->addItem(tr("Linguist"));
+ applicationComboBox->setEditable(true);
+ applicationComboBox->setCurrentIndex(1);
+
+ formatLabel = new QLabel(tr("&Format:"));
+ formatLabel->setBuddy(formatComboBox);
+
+ scopeLabel = new QLabel(tr("&Scope:"));
+ scopeLabel->setBuddy(scopeComboBox);
+
+ organizationLabel = new QLabel(tr("&Organization:"));
+ organizationLabel->setBuddy(organizationComboBox);
+
+ applicationLabel = new QLabel(tr("&Application:"));
+ applicationLabel->setBuddy(applicationComboBox);
+
+ locationsGroupBox = new QGroupBox(tr("Setting Locations"));
+
+ const QStringList labels{tr("Location"), tr("Access")};
+
+ locationsTable = new QTableWidget;
+ locationsTable->setSelectionMode(QAbstractItemView::SingleSelection);
+ locationsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+ locationsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ locationsTable->setColumnCount(2);
+ locationsTable->setHorizontalHeaderLabels(labels);
+ locationsTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
+ locationsTable->horizontalHeader()->resizeSection(1, 180);
+ connect(locationsTable, &QTableWidget::itemActivated, this, &LocationDialog::itemActivated);
+
+ buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+
+ connect(formatComboBox, &QComboBox::activated,
+ this, &LocationDialog::updateLocationsTable);
+ connect(scopeComboBox, &QComboBox::activated,
+ this, &LocationDialog::updateLocationsTable);
+ connect(organizationComboBox->lineEdit(),
+ &QLineEdit::editingFinished,
+ this, &LocationDialog::updateLocationsTable);
+ connect(applicationComboBox->lineEdit(),
+ &QLineEdit::editingFinished,
+ this, &LocationDialog::updateLocationsTable);
+ connect(applicationComboBox, &QComboBox::activated,
+ this, &LocationDialog::updateLocationsTable);
+ connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+
+ QVBoxLayout *locationsLayout = new QVBoxLayout(locationsGroupBox);
+ locationsLayout->addWidget(locationsTable);
+
+ QGridLayout *mainLayout = new QGridLayout(this);
+ mainLayout->addWidget(formatLabel, 0, 0);
+ mainLayout->addWidget(formatComboBox, 0, 1);
+ mainLayout->addWidget(scopeLabel, 1, 0);
+ mainLayout->addWidget(scopeComboBox, 1, 1);
+ mainLayout->addWidget(organizationLabel, 2, 0);
+ mainLayout->addWidget(organizationComboBox, 2, 1);
+ mainLayout->addWidget(applicationLabel, 3, 0);
+ mainLayout->addWidget(applicationComboBox, 3, 1);
+ mainLayout->addWidget(locationsGroupBox, 4, 0, 1, 2);
+ mainLayout->addWidget(buttonBox, 5, 0, 1, 2);
+
+ updateLocationsTable();
+
+ setWindowTitle(tr("Open Application Settings"));
+ resize(650, 400);
+}
+
+QSettings::Format LocationDialog::format() const
+{
+ if (formatComboBox->currentIndex() == 0)
+ return QSettings::NativeFormat;
+ else
+ return QSettings::IniFormat;
+}
+
+QSettings::Scope LocationDialog::scope() const
+{
+ if (scopeComboBox->currentIndex() == 0)
+ return QSettings::UserScope;
+ else
+ return QSettings::SystemScope;
+}
+
+QString LocationDialog::organization() const
+{
+ return organizationComboBox->currentText();
+}
+
+QString LocationDialog::application() const
+{
+ if (applicationComboBox->currentText() == tr("Any"))
+ return QString();
+ else
+ return applicationComboBox->currentText();
+}
+
+void LocationDialog::itemActivated(QTableWidgetItem *)
+{
+ buttonBox->button(QDialogButtonBox::Ok)->animateClick();
+}
+
+void LocationDialog::updateLocationsTable()
+{
+ locationsTable->setUpdatesEnabled(false);
+ locationsTable->setRowCount(0);
+
+ for (int i = 0; i < 2; ++i) {
+ if (i == 0 && scope() == QSettings::SystemScope)
+ continue;
+
+ QSettings::Scope actualScope = (i == 0) ? QSettings::UserScope
+ : QSettings::SystemScope;
+ for (int j = 0; j < 2; ++j) {
+ if (j == 0 && application().isEmpty())
+ continue;
+
+ QString actualApplication;
+ if (j == 0)
+ actualApplication = application();
+ QSettings settings(format(), actualScope, organization(),
+ actualApplication);
+
+ int row = locationsTable->rowCount();
+ locationsTable->setRowCount(row + 1);
+
+ QTableWidgetItem *item0 = new QTableWidgetItem(QDir::toNativeSeparators(settings.fileName()));
+
+ QTableWidgetItem *item1 = new QTableWidgetItem;
+ bool disable = (settings.childKeys().isEmpty()
+ && settings.childGroups().isEmpty());
+
+ if (row == 0) {
+ if (settings.isWritable()) {
+ item1->setText(tr("Read-write"));
+ disable = false;
+ } else {
+ item1->setText(tr("Read-only"));
+ }
+ buttonBox->button(QDialogButtonBox::Ok)->setDisabled(disable);
+ } else {
+ item1->setText(tr("Read-only fallback"));
+ }
+
+ if (disable) {
+ item0->setFlags(item0->flags() & ~Qt::ItemIsEnabled);
+ item1->setFlags(item1->flags() & ~Qt::ItemIsEnabled);
+ }
+
+ locationsTable->setItem(row, 0, item0);
+ locationsTable->setItem(row, 1, item1);
+ }
+ }
+ locationsTable->setUpdatesEnabled(true);
+}
diff --git a/tests/manual/examples/widgets/tools/settingseditor/locationdialog.h b/tests/manual/examples/widgets/tools/settingseditor/locationdialog.h
new file mode 100644
index 0000000000..4bcef76ce7
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/settingseditor/locationdialog.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef LOCATIONDIALOG_H
+#define LOCATIONDIALOG_H
+
+#include <QDialog>
+#include <QSettings>
+
+QT_BEGIN_NAMESPACE
+class QComboBox;
+class QDialogButtonBox;
+class QGroupBox;
+class QLabel;
+class QTableWidget;
+class QTableWidgetItem;
+QT_END_NAMESPACE
+
+class LocationDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ LocationDialog(QWidget *parent = nullptr);
+
+ QSettings::Format format() const;
+ QSettings::Scope scope() const;
+ QString organization() const;
+ QString application() const;
+
+private slots:
+ void updateLocationsTable();
+ void itemActivated(QTableWidgetItem *);
+
+private:
+ QLabel *formatLabel;
+ QLabel *scopeLabel;
+ QLabel *organizationLabel;
+ QLabel *applicationLabel;
+ QComboBox *formatComboBox;
+ QComboBox *scopeComboBox;
+ QComboBox *organizationComboBox;
+ QComboBox *applicationComboBox;
+ QGroupBox *locationsGroupBox;
+ QTableWidget *locationsTable;
+ QDialogButtonBox *buttonBox;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/tools/settingseditor/main.cpp b/tests/manual/examples/widgets/tools/settingseditor/main.cpp
new file mode 100644
index 0000000000..f49701be5c
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/settingseditor/main.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ QCoreApplication::setApplicationName("Settings Editor");
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+
+ MainWindow mainWin;
+ mainWin.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/tools/settingseditor/mainwindow.cpp b/tests/manual/examples/widgets/tools/settingseditor/mainwindow.cpp
new file mode 100644
index 0000000000..be9f19e8cc
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/settingseditor/mainwindow.cpp
@@ -0,0 +1,175 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "locationdialog.h"
+#include "mainwindow.h"
+#include "settingstree.h"
+
+#include <QAction>
+#include <QApplication>
+#include <QFileDialog>
+#include <QInputDialog>
+#include <QLineEdit>
+#include <QMenuBar>
+#include <QMessageBox>
+#include <QScreen>
+#include <QStandardPaths>
+#include <QStatusBar>
+
+MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
+ , settingsTree(new SettingsTree)
+{
+ setCentralWidget(settingsTree);
+
+ createActions();
+
+ autoRefreshAct->setChecked(true);
+ fallbacksAct->setChecked(true);
+
+ setWindowTitle(QCoreApplication::applicationName());
+ const QRect availableGeometry = screen()->availableGeometry();
+ adjustSize();
+ move((availableGeometry.width() - width()) / 2, (availableGeometry.height() - height()) / 2);
+}
+
+void MainWindow::openSettings()
+{
+ if (!locationDialog)
+ locationDialog = new LocationDialog(this);
+
+ if (locationDialog->exec() != QDialog::Accepted)
+ return;
+
+ SettingsPtr settings(new QSettings(locationDialog->format(),
+ locationDialog->scope(),
+ locationDialog->organization(),
+ locationDialog->application()));
+
+ setSettingsObject(settings);
+ fallbacksAct->setEnabled(true);
+}
+
+void MainWindow::openIniFile()
+{
+ const QString directory = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
+ const QString fileName =
+ QFileDialog::getOpenFileName(this, tr("Open INI File"),
+ directory, tr("INI Files (*.ini *.conf)"));
+ if (fileName.isEmpty())
+ return;
+
+ SettingsPtr settings(new QSettings(fileName, QSettings::IniFormat));
+
+ setSettingsObject(settings);
+ fallbacksAct->setEnabled(false);
+}
+
+void MainWindow::openPropertyList()
+{
+ const QString directory = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
+ const QString fileName =
+ QFileDialog::getOpenFileName(this, tr("Open Property List"),
+ directory, tr("Property List Files (*.plist)"));
+ if (fileName.isEmpty())
+ return;
+
+ SettingsPtr settings(new QSettings(fileName, QSettings::NativeFormat));
+ setSettingsObject(settings);
+ fallbacksAct->setEnabled(false);
+}
+
+void MainWindow::openRegistryPath()
+{
+ const QString path =
+ QInputDialog::getText(this, tr("Open Registry Path"),
+ tr("Enter the path in the Windows registry:"),
+ QLineEdit::Normal, "HKEY_CURRENT_USER\\");
+ if (path.isEmpty())
+ return;
+
+ SettingsPtr settings(new QSettings(path, QSettings::NativeFormat));
+
+ setSettingsObject(settings);
+ fallbacksAct->setEnabled(false);
+}
+
+void MainWindow::about()
+{
+ QMessageBox::about(this, tr("About Settings Editor"),
+ tr("The <b>Settings Editor</b> example shows how to access "
+ "application settings using Qt."));
+}
+
+void MainWindow::createActions()
+{
+ QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
+
+ QAction *openSettingsAct = fileMenu->addAction(tr("&Open Application Settings..."), this, &MainWindow::openSettings);
+ openSettingsAct->setShortcuts(QKeySequence::Open);
+
+ QAction *openIniFileAct = fileMenu->addAction(tr("Open I&NI File..."), this, &MainWindow::openIniFile);
+ openIniFileAct->setShortcut(tr("Ctrl+N"));
+
+#ifdef Q_OS_MACOS
+ QAction *openPropertyListAct = fileMenu->addAction(tr("Open Apple &Property List..."), this, &MainWindow::openPropertyList);
+ openPropertyListAct->setShortcut(tr("Ctrl+P"));
+#endif // Q_OS_MACOS
+
+#ifdef Q_OS_WIN
+ QAction *openRegistryPathAct = fileMenu->addAction(tr("Open Windows &Registry Path..."), this, &MainWindow::openRegistryPath);
+ openRegistryPathAct->setShortcut(tr("Ctrl+G"));
+#endif // Q_OS_WIN
+
+ fileMenu->addSeparator();
+
+ refreshAct = fileMenu->addAction(tr("&Refresh"), settingsTree, &SettingsTree::refresh);
+ refreshAct->setShortcut(tr("Ctrl+R"));
+ refreshAct->setEnabled(false);
+
+ fileMenu->addSeparator();
+
+ QAction *exitAct = fileMenu->addAction(tr("E&xit"), this, &QWidget::close);
+ exitAct->setShortcuts(QKeySequence::Quit);
+
+ QMenu *optionsMenu = menuBar()->addMenu(tr("&Options"));
+
+ autoRefreshAct = optionsMenu->addAction(tr("&Auto-Refresh"));
+ autoRefreshAct->setShortcut(tr("Ctrl+A"));
+ autoRefreshAct->setCheckable(true);
+ autoRefreshAct->setEnabled(false);
+ connect(autoRefreshAct, &QAction::triggered,
+ settingsTree, &SettingsTree::setAutoRefresh);
+ connect(autoRefreshAct, &QAction::triggered,
+ refreshAct, &QAction::setDisabled);
+
+ fallbacksAct = optionsMenu->addAction(tr("&Fallbacks"));
+ fallbacksAct->setShortcut(tr("Ctrl+F"));
+ fallbacksAct->setCheckable(true);
+ fallbacksAct->setEnabled(false);
+ connect(fallbacksAct, &QAction::triggered,
+ settingsTree, &SettingsTree::setFallbacksEnabled);
+
+ QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
+ helpMenu->addAction(tr("&About"), this, &MainWindow::about);
+ helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
+}
+
+void MainWindow::setSettingsObject(const SettingsPtr &settings)
+{
+ settings->setFallbacksEnabled(fallbacksAct->isChecked());
+ settingsTree->setSettingsObject(settings);
+
+ refreshAct->setEnabled(true);
+ autoRefreshAct->setEnabled(true);
+
+ QString niceName = QDir::cleanPath(settings->fileName());
+ int pos = niceName.lastIndexOf(QLatin1Char('/'));
+ if (pos != -1)
+ niceName.remove(0, pos + 1);
+
+ if (!settings->isWritable())
+ niceName = tr("%1 (read only)").arg(niceName);
+
+ setWindowTitle(tr("%1 - %2").arg(niceName, QCoreApplication::applicationName()));
+ statusBar()->showMessage(tr("Opened \"%1\"").arg(QDir::toNativeSeparators(settings->fileName())));
+}
diff --git a/tests/manual/examples/widgets/tools/settingseditor/mainwindow.h b/tests/manual/examples/widgets/tools/settingseditor/mainwindow.h
new file mode 100644
index 0000000000..84bdaef966
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/settingseditor/mainwindow.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+class QAction;
+class QSettings;
+QT_END_NAMESPACE
+class LocationDialog;
+class SettingsTree;
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ typedef QSharedPointer<QSettings> SettingsPtr;
+
+ MainWindow(QWidget *parent = nullptr);
+
+private slots:
+ void openSettings();
+ void openIniFile();
+ void openPropertyList();
+ void openRegistryPath();
+ void about();
+
+private:
+ void createActions();
+ void setSettingsObject(const SettingsPtr &settings);
+
+ SettingsTree *settingsTree = nullptr;
+ LocationDialog *locationDialog = nullptr;
+ QAction *refreshAct = nullptr;
+ QAction *autoRefreshAct = nullptr;
+ QAction *fallbacksAct = nullptr;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/tools/settingseditor/settingseditor.pro b/tests/manual/examples/widgets/tools/settingseditor/settingseditor.pro
new file mode 100644
index 0000000000..4880b7e582
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/settingseditor/settingseditor.pro
@@ -0,0 +1,18 @@
+QT += widgets
+requires(qtConfig(tablewidget))
+
+HEADERS = locationdialog.h \
+ mainwindow.h \
+ settingstree.h \
+ variantdelegate.h
+SOURCES = locationdialog.cpp \
+ main.cpp \
+ mainwindow.cpp \
+ settingstree.cpp \
+ variantdelegate.cpp
+
+EXAMPLE_FILES = inifiles
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tools/settingseditor
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/tools/settingseditor/settingstree.cpp b/tests/manual/examples/widgets/tools/settingseditor/settingstree.cpp
new file mode 100644
index 0000000000..5de2a8cff1
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/settingseditor/settingstree.cpp
@@ -0,0 +1,231 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "settingstree.h"
+#include "variantdelegate.h"
+
+#include <QApplication>
+#include <QHeaderView>
+#include <QScreen>
+#include <QSettings>
+
+SettingsTree::SettingsTree(QWidget *parent)
+ : QTreeWidget(parent),
+ m_typeChecker(new TypeChecker)
+{
+ setItemDelegate(new VariantDelegate(m_typeChecker, this));
+
+ setHeaderLabels({tr("Setting"), tr("Type"), tr("Value")});
+ header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
+ header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
+ header()->setSectionResizeMode(2, QHeaderView::Stretch);
+
+ refreshTimer.setInterval(2000);
+
+ groupIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirClosedIcon),
+ QIcon::Normal, QIcon::Off);
+ groupIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirOpenIcon),
+ QIcon::Normal, QIcon::On);
+ keyIcon.addPixmap(style()->standardPixmap(QStyle::SP_FileIcon));
+
+ connect(&refreshTimer, &QTimer::timeout, this, &SettingsTree::maybeRefresh);
+}
+
+SettingsTree::~SettingsTree() = default;
+
+void SettingsTree::setSettingsObject(const SettingsPtr &newSettings)
+{
+ settings = newSettings;
+ clear();
+
+ if (settings.isNull()) {
+ refreshTimer.stop();
+ } else {
+ refresh();
+ if (autoRefresh)
+ refreshTimer.start();
+ }
+}
+
+QSize SettingsTree::sizeHint() const
+{
+ const QRect availableGeometry = screen()->availableGeometry();
+ return QSize(availableGeometry.width() * 2 / 3, availableGeometry.height() * 2 / 3);
+}
+
+void SettingsTree::setAutoRefresh(bool autoRefresh)
+{
+ this->autoRefresh = autoRefresh;
+ if (!settings.isNull()) {
+ if (autoRefresh) {
+ maybeRefresh();
+ refreshTimer.start();
+ } else {
+ refreshTimer.stop();
+ }
+ }
+}
+
+void SettingsTree::setFallbacksEnabled(bool enabled)
+{
+ if (!settings.isNull()) {
+ settings->setFallbacksEnabled(enabled);
+ refresh();
+ }
+}
+
+void SettingsTree::maybeRefresh()
+{
+ if (state() != EditingState)
+ refresh();
+}
+
+void SettingsTree::refresh()
+{
+ if (settings.isNull())
+ return;
+
+ disconnect(this, &QTreeWidget::itemChanged,
+ this, &SettingsTree::updateSetting);
+
+ settings->sync();
+ updateChildItems(nullptr);
+
+ connect(this, &QTreeWidget::itemChanged,
+ this, &SettingsTree::updateSetting);
+}
+
+bool SettingsTree::event(QEvent *event)
+{
+ if (event->type() == QEvent::WindowActivate) {
+ if (isActiveWindow() && autoRefresh)
+ maybeRefresh();
+ }
+ return QTreeWidget::event(event);
+}
+
+void SettingsTree::updateSetting(QTreeWidgetItem *item)
+{
+ QString key = item->text(0);
+ QTreeWidgetItem *ancestor = item->parent();
+ while (ancestor) {
+ key.prepend(ancestor->text(0) + QLatin1Char('/'));
+ ancestor = ancestor->parent();
+ }
+
+ settings->setValue(key, item->data(2, Qt::UserRole));
+ if (autoRefresh)
+ refresh();
+}
+
+void SettingsTree::updateChildItems(QTreeWidgetItem *parent)
+{
+ int dividerIndex = 0;
+
+ const QStringList childGroups = settings->childGroups();
+ for (const QString &group : childGroups) {
+ QTreeWidgetItem *child;
+ int childIndex = findChild(parent, group, dividerIndex);
+ if (childIndex != -1) {
+ child = childAt(parent, childIndex);
+ child->setText(1, QString());
+ child->setText(2, QString());
+ child->setData(2, Qt::UserRole, QVariant());
+ moveItemForward(parent, childIndex, dividerIndex);
+ } else {
+ child = createItem(group, parent, dividerIndex);
+ }
+ child->setIcon(0, groupIcon);
+ ++dividerIndex;
+
+ settings->beginGroup(group);
+ updateChildItems(child);
+ settings->endGroup();
+ }
+
+ const QStringList childKeys = settings->childKeys();
+ for (const QString &key : childKeys) {
+ QTreeWidgetItem *child;
+ int childIndex = findChild(parent, key, 0);
+
+ if (childIndex == -1 || childIndex >= dividerIndex) {
+ if (childIndex != -1) {
+ child = childAt(parent, childIndex);
+ for (int i = 0; i < child->childCount(); ++i)
+ delete childAt(child, i);
+ moveItemForward(parent, childIndex, dividerIndex);
+ } else {
+ child = createItem(key, parent, dividerIndex);
+ }
+ child->setIcon(0, keyIcon);
+ ++dividerIndex;
+ } else {
+ child = childAt(parent, childIndex);
+ }
+
+ QVariant value = settings->value(key);
+ if (value.userType() == QMetaType::UnknownType) {
+ child->setText(1, "Invalid");
+ } else {
+ if (value.typeId() == QMetaType::QString) {
+ const QString stringValue = value.toString();
+ if (m_typeChecker->boolExp.match(stringValue).hasMatch()) {
+ value.setValue(stringValue.compare("true", Qt::CaseInsensitive) == 0);
+ } else if (m_typeChecker->signedIntegerExp.match(stringValue).hasMatch())
+ value.setValue(stringValue.toInt());
+ }
+
+ child->setText(1, value.typeName());
+ }
+ child->setText(2, VariantDelegate::displayText(value));
+ child->setData(2, Qt::UserRole, value);
+ }
+
+ while (dividerIndex < childCount(parent))
+ delete childAt(parent, dividerIndex);
+}
+
+QTreeWidgetItem *SettingsTree::createItem(const QString &text,
+ QTreeWidgetItem *parent, int index)
+{
+ QTreeWidgetItem *after = nullptr;
+ if (index != 0)
+ after = childAt(parent, index - 1);
+
+ QTreeWidgetItem *item;
+ if (parent)
+ item = new QTreeWidgetItem(parent, after);
+ else
+ item = new QTreeWidgetItem(this, after);
+
+ item->setText(0, text);
+ item->setFlags(item->flags() | Qt::ItemIsEditable);
+ return item;
+}
+
+QTreeWidgetItem *SettingsTree::childAt(QTreeWidgetItem *parent, int index) const
+{
+ return (parent ? parent->child(index) : topLevelItem(index));
+}
+
+int SettingsTree::childCount(QTreeWidgetItem *parent) const
+{
+ return (parent ? parent->childCount() : topLevelItemCount());
+}
+
+int SettingsTree::findChild(QTreeWidgetItem *parent, const QString &text,
+ int startIndex) const
+{
+ for (int i = startIndex; i < childCount(parent); ++i) {
+ if (childAt(parent, i)->text(0) == text)
+ return i;
+ }
+ return -1;
+}
+
+void SettingsTree::moveItemForward(QTreeWidgetItem *parent, int oldIndex,
+ int newIndex)
+{
+ for (int i = 0; i < oldIndex - newIndex; ++i)
+ delete childAt(parent, newIndex);
+}
diff --git a/tests/manual/examples/widgets/tools/settingseditor/settingstree.h b/tests/manual/examples/widgets/tools/settingseditor/settingstree.h
new file mode 100644
index 0000000000..8dfa52113f
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/settingseditor/settingstree.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef SETTINGSTREE_H
+#define SETTINGSTREE_H
+
+#include <QIcon>
+#include <QTimer>
+#include <QTreeWidget>
+#include <QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+class QSettings;
+QT_END_NAMESPACE
+
+struct TypeChecker;
+
+class SettingsTree : public QTreeWidget
+{
+ Q_OBJECT
+
+public:
+ using SettingsPtr = QSharedPointer<QSettings>;
+ using TypeCheckerPtr = QSharedPointer<TypeChecker>;
+
+ SettingsTree(QWidget *parent = nullptr);
+ ~SettingsTree();
+
+ void setSettingsObject(const SettingsPtr &settings);
+ QSize sizeHint() const override;
+
+public slots:
+ void setAutoRefresh(bool autoRefresh);
+ void setFallbacksEnabled(bool enabled);
+ void maybeRefresh();
+ void refresh();
+
+protected:
+ bool event(QEvent *event) override;
+
+private slots:
+ void updateSetting(QTreeWidgetItem *item);
+
+private:
+ void updateChildItems(QTreeWidgetItem *parent);
+ QTreeWidgetItem *createItem(const QString &text, QTreeWidgetItem *parent,
+ int index);
+ QTreeWidgetItem *childAt(QTreeWidgetItem *parent, int index) const;
+ int childCount(QTreeWidgetItem *parent) const;
+ int findChild(QTreeWidgetItem *parent, const QString &text, int startIndex) const;
+ void moveItemForward(QTreeWidgetItem *parent, int oldIndex, int newIndex);
+
+ SettingsPtr settings;
+ TypeCheckerPtr m_typeChecker;
+ QTimer refreshTimer;
+ QIcon groupIcon;
+ QIcon keyIcon;
+ bool autoRefresh = false;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/tools/settingseditor/variantdelegate.cpp b/tests/manual/examples/widgets/tools/settingseditor/variantdelegate.cpp
new file mode 100644
index 0000000000..ed51a1645b
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/settingseditor/variantdelegate.cpp
@@ -0,0 +1,377 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "variantdelegate.h"
+
+#include <QCheckBox>
+#include <QDateTime>
+#include <QLineEdit>
+#include <QSpinBox>
+#include <QRegularExpressionValidator>
+#include <QTextStream>
+
+#include <algorithm>
+
+static bool isPrintableChar(char c)
+{
+ return uchar(c) >= 32 && uchar(c) < 128;
+}
+
+static bool isPrintable(const QByteArray &ba)
+{
+ return std::all_of(ba.cbegin(), ba.cend(), isPrintableChar);
+}
+
+static QString byteArrayToString(const QByteArray &ba)
+{
+ if (isPrintable(ba))
+ return QString::fromLatin1(ba);
+ QString result;
+ for (char c : ba) {
+ if (isPrintableChar(c)) {
+ if (c == '\\')
+ result += QLatin1Char(c);
+ result += QLatin1Char(c);
+ } else {
+ const uint uc = uchar(c);
+ result += "\\x";
+ if (uc < 16)
+ result += '0';
+ result += QString::number(uc, 16);
+ }
+ }
+ return result;
+}
+
+TypeChecker::TypeChecker()
+{
+ boolExp.setPattern("^(true)|(false)$");
+ boolExp.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
+ Q_ASSERT(boolExp.isValid());
+
+ byteArrayExp.setPattern(R"RX(^[\x00-\xff]*$)RX");
+ charExp.setPattern("^.$");
+ Q_ASSERT(charExp.isValid());
+ colorExp.setPattern(R"RX(^\(([0-9]*),([0-9]*),([0-9]*),([0-9]*)\)$)RX");
+ Q_ASSERT(colorExp.isValid());
+ doubleExp.setPattern("");
+ pointExp.setPattern(R"RX(^\((-?[0-9]*),(-?[0-9]*)\)$)RX");
+ Q_ASSERT(pointExp.isValid());
+ rectExp.setPattern(R"RX(^\((-?[0-9]*),(-?[0-9]*),(-?[0-9]*),(-?[0-9]*)\)$)RX");
+ Q_ASSERT(rectExp.isValid());
+ signedIntegerExp.setPattern("^-?[0-9]*$");
+ Q_ASSERT(signedIntegerExp.isValid());
+ sizeExp = pointExp;
+ unsignedIntegerExp.setPattern("^[0-9]+$");
+ Q_ASSERT(unsignedIntegerExp.isValid());
+
+ const QString datePattern = "([0-9]{,4})-([0-9]{,2})-([0-9]{,2})";
+ dateExp.setPattern('^' + datePattern + '$');
+ Q_ASSERT(dateExp.isValid());
+ const QString timePattern = "([0-9]{,2}):([0-9]{,2}):([0-9]{,2})";
+ timeExp.setPattern('^' + timePattern + '$');
+ Q_ASSERT(timeExp.isValid());
+ dateTimeExp.setPattern('^' + datePattern + 'T' + timePattern + '$');
+ Q_ASSERT(dateTimeExp.isValid());
+}
+
+VariantDelegate::VariantDelegate(const QSharedPointer<TypeChecker> &typeChecker,
+ QObject *parent)
+ : QStyledItemDelegate(parent),
+ m_typeChecker(typeChecker)
+{
+}
+
+void VariantDelegate::paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ if (index.column() == 2) {
+ QVariant value = index.model()->data(index, Qt::UserRole);
+ if (!isSupportedType(value.userType())) {
+ QStyleOptionViewItem myOption = option;
+ myOption.state &= ~QStyle::State_Enabled;
+ QStyledItemDelegate::paint(painter, myOption, index);
+ return;
+ }
+ }
+
+ QStyledItemDelegate::paint(painter, option, index);
+}
+
+QWidget *VariantDelegate::createEditor(QWidget *parent,
+ const QStyleOptionViewItem & /* option */,
+ const QModelIndex &index) const
+{
+ if (index.column() != 2)
+ return nullptr;
+
+ QVariant originalValue = index.model()->data(index, Qt::UserRole);
+ if (!isSupportedType(originalValue.userType()))
+ return nullptr;
+
+ switch (originalValue.userType()) {
+ case QMetaType::Bool:
+ return new QCheckBox(parent);
+ break;
+ case QMetaType::Int:
+ case QMetaType::LongLong: {
+ auto spinBox = new QSpinBox(parent);
+ spinBox->setRange(-32767, 32767);
+ return spinBox;
+ }
+ case QMetaType::UInt:
+ case QMetaType::ULongLong: {
+ auto spinBox = new QSpinBox(parent);
+ spinBox->setRange(0, 63335);
+ return spinBox;
+ }
+ default:
+ break;
+ }
+
+ QLineEdit *lineEdit = new QLineEdit(parent);
+ lineEdit->setFrame(false);
+
+ QRegularExpression regExp;
+
+ switch (originalValue.userType()) {
+ case QMetaType::Bool:
+ regExp = m_typeChecker->boolExp;
+ break;
+ case QMetaType::QByteArray:
+ regExp = m_typeChecker->byteArrayExp;
+ break;
+ case QMetaType::QChar:
+ regExp = m_typeChecker->charExp;
+ break;
+ case QMetaType::QColor:
+ regExp = m_typeChecker->colorExp;
+ break;
+ case QMetaType::QDate:
+ regExp = m_typeChecker->dateExp;
+ break;
+ case QMetaType::QDateTime:
+ regExp = m_typeChecker->dateTimeExp;
+ break;
+ case QMetaType::Double:
+ regExp = m_typeChecker->doubleExp;
+ break;
+ case QMetaType::Int:
+ case QMetaType::LongLong:
+ regExp = m_typeChecker->signedIntegerExp;
+ break;
+ case QMetaType::QPoint:
+ regExp = m_typeChecker->pointExp;
+ break;
+ case QMetaType::QRect:
+ regExp = m_typeChecker->rectExp;
+ break;
+ case QMetaType::QSize:
+ regExp = m_typeChecker->sizeExp;
+ break;
+ case QMetaType::QTime:
+ regExp = m_typeChecker->timeExp;
+ break;
+ case QMetaType::UInt:
+ case QMetaType::ULongLong:
+ regExp = m_typeChecker->unsignedIntegerExp;
+ break;
+ default:
+ break;
+ }
+
+ if (regExp.isValid()) {
+ QValidator *validator = new QRegularExpressionValidator(regExp, lineEdit);
+ lineEdit->setValidator(validator);
+ }
+
+ return lineEdit;
+}
+
+void VariantDelegate::setEditorData(QWidget *editor,
+ const QModelIndex &index) const
+{
+ QVariant value = index.model()->data(index, Qt::UserRole);
+ if (auto spinBox = qobject_cast<QSpinBox *>(editor)) {
+ const auto userType = value.userType();
+ if (userType == QMetaType::UInt || userType == QMetaType::ULongLong)
+ spinBox->setValue(value.toUInt());
+ else
+ spinBox->setValue(value.toInt());
+ } else if (auto checkBox = qobject_cast<QCheckBox *>(editor)) {
+ checkBox->setChecked(value.toBool());
+ } else if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor)) {
+ if (value.userType() == QMetaType::QByteArray
+ && !isPrintable(value.toByteArray())) {
+ lineEdit->setReadOnly(true);
+ }
+ lineEdit->setText(displayText(value));
+ }
+}
+
+void VariantDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
+ const QModelIndex &index) const
+{
+ const QVariant originalValue = index.model()->data(index, Qt::UserRole);
+ QVariant value;
+
+ if (auto spinBox = qobject_cast<QSpinBox *>(editor)) {
+ value.setValue(spinBox->value());
+ } else if (auto checkBox = qobject_cast<QCheckBox *>(editor)) {
+ value.setValue(checkBox->isChecked());
+ } else if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor)) {
+ if (!lineEdit->isModified())
+ return;
+
+ QString text = lineEdit->text();
+ const QValidator *validator = lineEdit->validator();
+ if (validator) {
+ int pos;
+ if (validator->validate(text, pos) != QValidator::Acceptable)
+ return;
+ }
+
+ QRegularExpressionMatch match;
+
+ switch (originalValue.userType()) {
+ case QMetaType::QChar:
+ value = text.at(0);
+ break;
+ case QMetaType::QColor:
+ match = m_typeChecker->colorExp.match(text);
+ value = QColor(qMin(match.captured(1).toInt(), 255),
+ qMin(match.captured(2).toInt(), 255),
+ qMin(match.captured(3).toInt(), 255),
+ qMin(match.captured(4).toInt(), 255));
+ break;
+ case QMetaType::QDate:
+ {
+ QDate date = QDate::fromString(text, Qt::ISODate);
+ if (!date.isValid())
+ return;
+ value = date;
+ }
+ break;
+ case QMetaType::QDateTime:
+ {
+ QDateTime dateTime = QDateTime::fromString(text, Qt::ISODate);
+ if (!dateTime.isValid())
+ return;
+ value = dateTime;
+ }
+ break;
+ case QMetaType::QPoint:
+ match = m_typeChecker->pointExp.match(text);
+ value = QPoint(match.captured(1).toInt(), match.captured(2).toInt());
+ break;
+ case QMetaType::QRect:
+ match = m_typeChecker->rectExp.match(text);
+ value = QRect(match.captured(1).toInt(), match.captured(2).toInt(),
+ match.captured(3).toInt(), match.captured(4).toInt());
+ break;
+ case QMetaType::QSize:
+ match = m_typeChecker->sizeExp.match(text);
+ value = QSize(match.captured(1).toInt(), match.captured(2).toInt());
+ break;
+ case QMetaType::QStringList:
+ value = text.split(',');
+ break;
+ case QMetaType::QTime:
+ {
+ QTime time = QTime::fromString(text, Qt::ISODate);
+ if (!time.isValid())
+ return;
+ value = time;
+ }
+ break;
+ default:
+ value = text;
+ value.convert(originalValue.metaType());
+ }
+ }
+
+ model->setData(index, displayText(value), Qt::DisplayRole);
+ model->setData(index, value, Qt::UserRole);
+}
+
+bool VariantDelegate::isSupportedType(int type)
+{
+ switch (type) {
+ case QMetaType::Bool:
+ case QMetaType::QByteArray:
+ case QMetaType::QChar:
+ case QMetaType::QColor:
+ case QMetaType::QDate:
+ case QMetaType::QDateTime:
+ case QMetaType::Double:
+ case QMetaType::Int:
+ case QMetaType::LongLong:
+ case QMetaType::QPoint:
+ case QMetaType::QRect:
+ case QMetaType::QSize:
+ case QMetaType::QString:
+ case QMetaType::QStringList:
+ case QMetaType::QTime:
+ case QMetaType::UInt:
+ case QMetaType::ULongLong:
+ return true;
+ default:
+ return false;
+ }
+}
+
+QString VariantDelegate::displayText(const QVariant &value)
+{
+ switch (value.userType()) {
+ case QMetaType::Bool:
+ return value.toBool() ? "✓" : "☐";
+ case QMetaType::QByteArray:
+ return byteArrayToString(value.toByteArray());
+ case QMetaType::QChar:
+ case QMetaType::Double:
+ case QMetaType::Int:
+ case QMetaType::LongLong:
+ case QMetaType::QString:
+ case QMetaType::UInt:
+ case QMetaType::ULongLong:
+ return value.toString();
+ case QMetaType::QColor:
+ {
+ QColor color = qvariant_cast<QColor>(value);
+ return QString("(%1,%2,%3,%4)")
+ .arg(color.red()).arg(color.green())
+ .arg(color.blue()).arg(color.alpha());
+ }
+ case QMetaType::QDate:
+ return value.toDate().toString(Qt::ISODate);
+ case QMetaType::QDateTime:
+ return value.toDateTime().toString(Qt::ISODate);
+ case QMetaType::UnknownType:
+ return "<Invalid>";
+ case QMetaType::QPoint:
+ {
+ QPoint point = value.toPoint();
+ return QString("(%1,%2)").arg(point.x()).arg(point.y());
+ }
+ case QMetaType::QRect:
+ {
+ QRect rect = value.toRect();
+ return QString("(%1,%2,%3,%4)")
+ .arg(rect.x()).arg(rect.y())
+ .arg(rect.width()).arg(rect.height());
+ }
+ case QMetaType::QSize:
+ {
+ QSize size = value.toSize();
+ return QString("(%1,%2)").arg(size.width()).arg(size.height());
+ }
+ case QMetaType::QStringList:
+ return value.toStringList().join(',');
+ case QMetaType::QTime:
+ return value.toTime().toString(Qt::ISODate);
+ default:
+ break;
+ }
+ return QString("<%1>").arg(value.typeName());
+}
diff --git a/tests/manual/examples/widgets/tools/settingseditor/variantdelegate.h b/tests/manual/examples/widgets/tools/settingseditor/variantdelegate.h
new file mode 100644
index 0000000000..dc06d51bbc
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/settingseditor/variantdelegate.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef VARIANTDELEGATE_H
+#define VARIANTDELEGATE_H
+
+#include <QStyledItemDelegate>
+#include <QRegularExpression>
+#include <QSharedPointer>
+
+struct TypeChecker
+{
+ TypeChecker();
+
+ QRegularExpression boolExp;
+ QRegularExpression byteArrayExp;
+ QRegularExpression charExp;
+ QRegularExpression colorExp;
+ QRegularExpression dateExp;
+ QRegularExpression dateTimeExp;
+ QRegularExpression doubleExp;
+ QRegularExpression pointExp;
+ QRegularExpression rectExp;
+ QRegularExpression signedIntegerExp;
+ QRegularExpression sizeExp;
+ QRegularExpression timeExp;
+ QRegularExpression unsignedIntegerExp;
+};
+
+class VariantDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+
+public:
+ explicit VariantDelegate(const QSharedPointer<TypeChecker> &typeChecker,
+ QObject *parent = nullptr);
+
+ void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+ QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+ void setEditorData(QWidget *editor, const QModelIndex &index) const override;
+ void setModelData(QWidget *editor, QAbstractItemModel *model,
+ const QModelIndex &index) const override;
+
+ static bool isSupportedType(int type);
+ static QString displayText(const QVariant &value);
+
+private:
+ QSharedPointer<TypeChecker> m_typeChecker;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/touch/dials/CMakeLists.txt b/tests/manual/examples/widgets/touch/dials/CMakeLists.txt
new file mode 100644
index 0000000000..fd4b7859a9
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/dials/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(dials LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/touch/dials")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(dials
+ dials.ui
+ main.cpp
+)
+
+set_target_properties(dials PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(dials PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS dials
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/touch/dials/dials.pro b/tests/manual/examples/widgets/touch/dials/dials.pro
new file mode 100644
index 0000000000..0e823551cc
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/dials/dials.pro
@@ -0,0 +1,8 @@
+QT += widgets
+
+SOURCES += main.cpp
+FORMS += dials.ui
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/touch/dials
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/touch/dials/dials.ui b/tests/manual/examples/widgets/touch/dials/dials.ui
new file mode 100644
index 0000000000..8ca7ae9475
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/dials/dials.ui
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Dials</class>
+ <widget class="QWidget" name="Dials">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QDial" name="dial_1">
+ <property name="notchesVisible">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QDial" name="dial_2">
+ <property name="notchesVisible">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QDial" name="dial_3">
+ <property name="notchesVisible">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QDial" name="dial_4">
+ <property name="notchesVisible">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QDial" name="dial_5">
+ <property name="notchesVisible">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDial" name="dial_6">
+ <property name="notchesVisible">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QDial" name="dial_7">
+ <property name="notchesVisible">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QDial" name="dial_8">
+ <property name="notchesVisible">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tests/manual/examples/widgets/touch/dials/doc/images/touch-dials-example.png b/tests/manual/examples/widgets/touch/dials/doc/images/touch-dials-example.png
new file mode 100644
index 0000000000..60e1776fc3
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/dials/doc/images/touch-dials-example.png
Binary files differ
diff --git a/tests/manual/examples/widgets/touch/dials/doc/src/touch-dials.qdoc b/tests/manual/examples/widgets/touch/dials/doc/src/touch-dials.qdoc
new file mode 100644
index 0000000000..448430ed79
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/dials/doc/src/touch-dials.qdoc
@@ -0,0 +1,14 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example touch/dials
+ \title Touch Dials Example
+ \ingroup touchinputexamples
+ \brief Shows how to apply touch to a set of standard Qt widgets.
+
+ The Touch Dials example shows how to apply touch to a set of
+ standard Qt widgets.
+
+ \image touch-dials-example.png
+*/
diff --git a/tests/manual/examples/widgets/touch/dials/main.cpp b/tests/manual/examples/widgets/touch/dials/main.cpp
new file mode 100644
index 0000000000..63fdb7db3b
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/dials/main.cpp
@@ -0,0 +1,21 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+#include <QWidget>
+#include <QDial>
+
+#include "ui_dials.h"
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ QWidget window;
+ Ui::Dials dialsUi;
+ dialsUi.setupUi(&window);
+ const QList<QAbstractSlider *> sliders = window.findChildren<QAbstractSlider *>();
+ for (QAbstractSlider *slider : sliders)
+ slider->setAttribute(Qt::WA_AcceptTouchEvents);
+ window.showMaximized();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/touch/fingerpaint/CMakeLists.txt b/tests/manual/examples/widgets/touch/fingerpaint/CMakeLists.txt
new file mode 100644
index 0000000000..c06a91b53b
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/fingerpaint/CMakeLists.txt
@@ -0,0 +1,45 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(fingerpaint LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/touch/fingerpaint")
+
+find_package(Qt6
+ REQUIRED COMPONENTS Core Gui Widgets
+ OPTIONAL_COMPONENTS PrintSupport
+)
+
+qt_standard_project_setup()
+
+qt_add_executable(fingerpaint
+ main.cpp
+ mainwindow.cpp mainwindow.h
+ scribblearea.cpp scribblearea.h
+)
+
+set_target_properties(fingerpaint PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(fingerpaint PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+if (TARGET Qt6::PrintSupport)
+ target_link_libraries(fingerpaint PRIVATE Qt6::PrintSupport)
+endif()
+
+install(TARGETS fingerpaint
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/touch/fingerpaint/doc/src/fingerpaint.qdoc b/tests/manual/examples/widgets/touch/fingerpaint/doc/src/fingerpaint.qdoc
new file mode 100644
index 0000000000..0e3319004c
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/fingerpaint/doc/src/fingerpaint.qdoc
@@ -0,0 +1,18 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example touch/fingerpaint
+ \title Finger Paint Example
+ \ingroup touchinputexamples
+ \brief Shows the use of a touchscreen to make a simple painting application.
+
+ The Finger Paint example shows the use of a touchscreen with a custom widget
+ to create a simple painting application.
+
+ \image touch-fingerpaint-example.png
+
+ This example was specifically designed to work with a touchscreen, using
+ QTouchEvent instead of QMouseEvent to handle user input over the custom
+ widget. As a result, it is not possible to draw with the mouse cursor.
+*/
diff --git a/tests/manual/examples/widgets/touch/fingerpaint/fingerpaint.pro b/tests/manual/examples/widgets/touch/fingerpaint/fingerpaint.pro
new file mode 100644
index 0000000000..6370da6607
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/fingerpaint/fingerpaint.pro
@@ -0,0 +1,13 @@
+QT += widgets
+requires(qtConfig(filedialog))
+qtHaveModule(printsupport): QT += printsupport
+
+HEADERS = mainwindow.h \
+ scribblearea.h
+SOURCES = main.cpp \
+ mainwindow.cpp \
+ scribblearea.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/touch/fingerpaint
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/touch/fingerpaint/main.cpp b/tests/manual/examples/widgets/touch/fingerpaint/main.cpp
new file mode 100644
index 0000000000..d283c7b85c
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/fingerpaint/main.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ MainWindow window;
+ window.showMaximized();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/touch/fingerpaint/mainwindow.cpp b/tests/manual/examples/widgets/touch/fingerpaint/mainwindow.cpp
new file mode 100644
index 0000000000..b9109fceeb
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/fingerpaint/mainwindow.cpp
@@ -0,0 +1,180 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+
+#include "mainwindow.h"
+#include "scribblearea.h"
+
+//! [0]
+MainWindow::MainWindow()
+{
+ scribbleArea = new ScribbleArea;
+ setCentralWidget(scribbleArea);
+
+ createActions();
+ createMenus();
+
+ setWindowTitle(tr("Finger Paint"));
+ resize(500, 500);
+}
+//! [0]
+
+//! [1]
+void MainWindow::closeEvent(QCloseEvent *event)
+//! [1] //! [2]
+{
+ if (maybeSave()) {
+ event->accept();
+ } else {
+ event->ignore();
+ }
+}
+//! [2]
+
+//! [3]
+void MainWindow::open()
+//! [3] //! [4]
+{
+ if (maybeSave()) {
+ QString fileName = QFileDialog::getOpenFileName(this,
+ tr("Open File"), QDir::currentPath());
+ if (!fileName.isEmpty())
+ scribbleArea->openImage(fileName);
+ }
+}
+//! [4]
+
+//! [5]
+void MainWindow::save()
+//! [5] //! [6]
+{
+ QAction *action = qobject_cast<QAction *>(sender());
+ QByteArray fileFormat = action->data().toByteArray();
+ saveFile(fileFormat);
+}
+//! [6]
+
+//! [11]
+void MainWindow::about()
+//! [11] //! [12]
+{
+ QMessageBox::about(this, tr("About Scribble"),
+ tr("<p>The <b>Scribble</b> example shows how to use QMainWindow as the "
+ "base widget for an application, and how to reimplement some of "
+ "QWidget's event handlers to receive the events generated for "
+ "the application's widgets:</p><p> We reimplement the mouse event "
+ "handlers to facilitate drawing, the paint event handler to "
+ "update the application and the resize event handler to optimize "
+ "the application's appearance. In addition we reimplement the "
+ "close event handler to intercept the close events before "
+ "terminating the application.</p><p> The example also demonstrates "
+ "how to use QPainter to draw an image in real time, as well as "
+ "to repaint widgets.</p>"));
+}
+//! [12]
+
+//! [13]
+void MainWindow::createActions()
+//! [13] //! [14]
+{
+ openAct = new QAction(tr("&Open..."), this);
+ openAct->setShortcut(tr("Ctrl+O"));
+ connect(openAct, &QAction::triggered, this, &MainWindow::open);
+
+ const QList<QByteArray> imageFormats = QImageWriter::supportedImageFormats();
+ for (const QByteArray &format : imageFormats) {
+ QString text = tr("%1...").arg(QString(format).toUpper());
+
+ QAction *action = new QAction(text, this);
+ action->setData(format);
+ connect(action, &QAction::triggered, this, &MainWindow::save);
+ saveAsActs.append(action);
+ }
+
+ printAct = new QAction(tr("&Print..."), this);
+ connect(printAct, &QAction::triggered, scribbleArea, &ScribbleArea::print);
+
+ exitAct = new QAction(tr("E&xit"), this);
+ exitAct->setShortcut(tr("Ctrl+Q"));
+ connect(exitAct, &QAction::triggered, this, &QWidget::close);
+
+ clearScreenAct = new QAction(tr("&Clear Screen"), this);
+ clearScreenAct->setShortcut(tr("Ctrl+L"));
+ connect(clearScreenAct, &QAction::triggered,
+ scribbleArea, &ScribbleArea::clearImage);
+
+ aboutAct = new QAction(tr("&About"), this);
+ connect(aboutAct, &QAction::triggered, this, &MainWindow::about);
+
+ aboutQtAct = new QAction(tr("About &Qt"), this);
+ connect(aboutQtAct, &QAction::triggered, qApp, &QApplication::aboutQt);
+}
+//! [14]
+
+//! [15]
+void MainWindow::createMenus()
+//! [15] //! [16]
+{
+ saveAsMenu = new QMenu(tr("&Save As"), this);
+ saveAsMenu->addActions(saveAsActs);
+
+ fileMenu = new QMenu(tr("&File"), this);
+ fileMenu->addAction(openAct);
+ fileMenu->addMenu(saveAsMenu);
+ fileMenu->addAction(printAct);
+ fileMenu->addSeparator();
+ fileMenu->addAction(exitAct);
+
+ optionMenu = new QMenu(tr("&Options"), this);
+ optionMenu->addAction(clearScreenAct);
+
+ helpMenu = new QMenu(tr("&Help"), this);
+ helpMenu->addAction(aboutAct);
+ helpMenu->addAction(aboutQtAct);
+
+ menuBar()->addMenu(fileMenu);
+ menuBar()->addMenu(optionMenu);
+ menuBar()->addMenu(helpMenu);
+}
+//! [16]
+
+//! [17]
+bool MainWindow::maybeSave()
+//! [17] //! [18]
+{
+ if (scribbleArea->isModified()) {
+ QMessageBox::StandardButton ret;
+ ret = QMessageBox::warning(this, tr("Scribble"),
+ tr("The image has been modified.\n"
+ "Do you want to save your changes?"),
+ QMessageBox::Save | QMessageBox::Discard
+ | QMessageBox::Cancel);
+ if (ret == QMessageBox::Save) {
+ return saveFile("png");
+ } else if (ret == QMessageBox::Cancel) {
+ return false;
+ }
+ }
+ return true;
+}
+//! [18]
+
+//! [19]
+bool MainWindow::saveFile(const QByteArray &fileFormat)
+//! [19] //! [20]
+{
+ QString initialPath = QDir::currentPath() + "/untitled." + fileFormat;
+
+ QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"),
+ initialPath,
+ tr("%1 Files (*.%2);;All Files (*)")
+ .arg(QString::fromLatin1(fileFormat.toUpper()))
+ .arg(QString::fromLatin1(fileFormat)));
+ if (fileName.isEmpty()) {
+ return false;
+ } else {
+ return scribbleArea->saveImage(fileName, fileFormat.constData());
+ }
+}
+//! [20]
diff --git a/tests/manual/examples/widgets/touch/fingerpaint/mainwindow.h b/tests/manual/examples/widgets/touch/fingerpaint/mainwindow.h
new file mode 100644
index 0000000000..a4aac1e36b
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/fingerpaint/mainwindow.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QList>
+#include <QMainWindow>
+
+class ScribbleArea;
+
+//! [0]
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow();
+
+protected:
+ void closeEvent(QCloseEvent *event) override;
+
+private slots:
+ void open();
+ void save();
+ void about();
+
+private:
+ void createActions();
+ void createMenus();
+ bool maybeSave();
+ bool saveFile(const QByteArray &fileFormat);
+
+ ScribbleArea *scribbleArea;
+
+ QMenu *saveAsMenu;
+ QMenu *fileMenu;
+ QMenu *optionMenu;
+ QMenu *helpMenu;
+
+ QAction *openAct;
+ QList<QAction *> saveAsActs;
+ QAction *exitAct;
+ QAction *printAct;
+ QAction *clearScreenAct;
+ QAction *aboutAct;
+ QAction *aboutQtAct;
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/touch/fingerpaint/scribblearea.cpp b/tests/manual/examples/widgets/touch/fingerpaint/scribblearea.cpp
new file mode 100644
index 0000000000..fb5e62906b
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/fingerpaint/scribblearea.cpp
@@ -0,0 +1,190 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#if defined(QT_PRINTSUPPORT_LIB)
+#include <QtPrintSupport/qtprintsupportglobal.h>
+#if QT_CONFIG(printdialog)
+#include <QPrinter>
+#include <QPrintDialog>
+#endif
+#endif
+
+#include "scribblearea.h"
+
+static const qreal MinimumDiameter = 3.0;
+static const qreal MaximumDiameter = 50.0;
+
+//! [0]
+ScribbleArea::ScribbleArea(QWidget *parent)
+ : QWidget(parent)
+{
+ setAttribute(Qt::WA_AcceptTouchEvents);
+ setAttribute(Qt::WA_StaticContents);
+ modified = false;
+
+ myPenColors
+ << QColor("green")
+ << QColor("purple")
+ << QColor("red")
+ << QColor("blue")
+ << QColor("yellow")
+
+ << QColor("pink")
+ << QColor("orange")
+ << QColor("brown")
+ << QColor("grey")
+ << QColor("black");
+}
+//! [0]
+
+//! [1]
+bool ScribbleArea::openImage(const QString &fileName)
+//! [1] //! [2]
+{
+ QImage loadedImage;
+ if (!loadedImage.load(fileName))
+ return false;
+
+ QSize newSize = loadedImage.size().expandedTo(size());
+ resizeImage(&loadedImage, newSize);
+ image = loadedImage;
+ modified = false;
+ update();
+ return true;
+}
+//! [2]
+
+//! [3]
+bool ScribbleArea::saveImage(const QString &fileName, const char *fileFormat)
+//! [3] //! [4]
+{
+ QImage visibleImage = image;
+ resizeImage(&visibleImage, size());
+
+ if (visibleImage.save(fileName, fileFormat)) {
+ modified = false;
+ return true;
+ } else {
+ return false;
+ }
+}
+//! [4]
+
+//! [9]
+void ScribbleArea::clearImage()
+//! [9] //! [10]
+{
+ image.fill(qRgb(255, 255, 255));
+ modified = true;
+ update();
+}
+//! [10]
+
+//! [12] //! [13]
+void ScribbleArea::paintEvent(QPaintEvent *event)
+//! [13] //! [14]
+{
+ QPainter painter(this);
+ const QRect rect = event->rect();
+ painter.drawImage(rect.topLeft(), image, rect);
+}
+//! [14]
+
+//! [15]
+void ScribbleArea::resizeEvent(QResizeEvent *event)
+//! [15] //! [16]
+{
+ if (width() > image.width() || height() > image.height()) {
+ int newWidth = qMax(width() + 128, image.width());
+ int newHeight = qMax(height() + 128, image.height());
+ resizeImage(&image, QSize(newWidth, newHeight));
+ update();
+ }
+ QWidget::resizeEvent(event);
+}
+//! [16]
+
+//! [19]
+void ScribbleArea::resizeImage(QImage *image, const QSize &newSize)
+//! [19] //! [20]
+{
+ if (image->size() == newSize)
+ return;
+
+ QImage newImage(newSize, QImage::Format_RGB32);
+ newImage.fill(qRgb(255, 255, 255));
+ QPainter painter(&newImage);
+ painter.drawImage(QPoint(0, 0), *image);
+ *image = newImage;
+}
+//! [20]
+
+//! [21]
+void ScribbleArea::print()
+{
+#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog)
+ QPrinter printer(QPrinter::HighResolution);
+
+ QPrintDialog printDialog(&printer, this);
+//! [21] //! [22]
+ if (printDialog.exec() == QDialog::Accepted) {
+ QPainter painter(&printer);
+ QRect rect = painter.viewport();
+ QSize size = image.size();
+ size.scale(rect.size(), Qt::KeepAspectRatio);
+ painter.setViewport(rect.x(), rect.y(), size.width(), size.height());
+ painter.setWindow(image.rect());
+ painter.drawImage(0, 0, image);
+ }
+#endif // QT_CONFIG(printdialog)
+}
+//! [22]
+
+bool ScribbleArea::event(QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ {
+ const QTouchEvent *touch = static_cast<QTouchEvent *>(event);
+ const auto touchPoints = static_cast<QTouchEvent *>(event)->points();
+ for (const QTouchEvent::TouchPoint &touchPoint : touchPoints) {
+ switch (touchPoint.state()) {
+ case QEventPoint::Stationary:
+ case QEventPoint::Released:
+ // don't do anything if this touch point hasn't moved or has been released
+ continue;
+ default:
+ {
+ QSizeF diams = touchPoint.ellipseDiameters();
+ if (diams.isEmpty()) {
+ qreal diameter = MaximumDiameter;
+ if (touch->pointingDevice()->capabilities().testFlag(QPointingDevice::Capability::Pressure))
+ diameter = MinimumDiameter + (MaximumDiameter - MinimumDiameter) * touchPoint.pressure();
+ diams = QSizeF(diameter, diameter);
+ }
+
+ QPainter painter(&image);
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(myPenColors.at(touchPoint.id() % myPenColors.count()));
+ painter.drawEllipse(touchPoint.position(), diams.width() / 2, diams.height() / 2);
+ painter.end();
+
+ modified = true;
+ const int rad = 2;
+ QRectF rect(QPointF(), diams);
+ rect.moveCenter(touchPoint.position());
+ update(rect.toRect().adjusted(-rad,-rad, +rad, +rad));
+ }
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ return QWidget::event(event);
+ }
+ return true;
+}
diff --git a/tests/manual/examples/widgets/touch/fingerpaint/scribblearea.h b/tests/manual/examples/widgets/touch/fingerpaint/scribblearea.h
new file mode 100644
index 0000000000..624f07074a
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/fingerpaint/scribblearea.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef SCRIBBLEAREA_H
+#define SCRIBBLEAREA_H
+
+#include <QColor>
+#include <QImage>
+#include <QPoint>
+#include <QWidget>
+
+//! [0]
+class ScribbleArea : public QWidget
+{
+ Q_OBJECT
+
+public:
+ ScribbleArea(QWidget *parent = nullptr);
+
+ bool openImage(const QString &fileName);
+ bool saveImage(const QString &fileName, const char *fileFormat);
+
+ bool isModified() const { return modified; }
+
+public slots:
+ void clearImage();
+ void print();
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+ void resizeEvent(QResizeEvent *event) override;
+ bool event(QEvent *event) override;
+
+private:
+ void resizeImage(QImage *image, const QSize &newSize);
+
+ bool modified;
+ QList<QColor> myPenColors;
+ QImage image;
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/touch/pinchzoom/CMakeLists.txt b/tests/manual/examples/widgets/touch/pinchzoom/CMakeLists.txt
new file mode 100644
index 0000000000..8820d8d44b
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/pinchzoom/CMakeLists.txt
@@ -0,0 +1,50 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(pinchzoom LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/touch/pinchzoom")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(pinchzoom
+ graphicsview.cpp graphicsview.h
+ main.cpp
+ mouse.cpp mouse.h
+)
+
+set_target_properties(pinchzoom PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(pinchzoom PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(mice_resource_files
+ "images/cheese.jpg"
+)
+
+qt_add_resources(pinchzoom "mice"
+ PREFIX
+ "/"
+ FILES
+ ${mice_resource_files}
+)
+
+install(TARGETS pinchzoom
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/touch/pinchzoom/doc/images/pinch-zoom-example.png b/tests/manual/examples/widgets/touch/pinchzoom/doc/images/pinch-zoom-example.png
new file mode 100644
index 0000000000..7db51fbf55
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/pinchzoom/doc/images/pinch-zoom-example.png
Binary files differ
diff --git a/tests/manual/examples/widgets/touch/pinchzoom/doc/src/pinchzoom.qdoc b/tests/manual/examples/widgets/touch/pinchzoom/doc/src/pinchzoom.qdoc
new file mode 100644
index 0000000000..aef5c59407
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/pinchzoom/doc/src/pinchzoom.qdoc
@@ -0,0 +1,14 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example touch/pinchzoom
+ \title Pinch Zoom Example
+ \ingroup touchinputexamples
+ \brief Shows how to recognize a gesture.
+
+ The Pinch Zoom example shows how to use low-level touch information
+ to recognize a gesture.
+
+ \image touch-pinchzoom-example.png
+*/
diff --git a/tests/manual/examples/widgets/touch/pinchzoom/graphicsview.cpp b/tests/manual/examples/widgets/touch/pinchzoom/graphicsview.cpp
new file mode 100644
index 0000000000..61042e7e02
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/pinchzoom/graphicsview.cpp
@@ -0,0 +1,48 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "graphicsview.h"
+
+#include <QScrollBar>
+#include <QTouchEvent>
+
+GraphicsView::GraphicsView(QGraphicsScene *scene, QWidget *parent)
+ : QGraphicsView(scene, parent)
+{
+ viewport()->setAttribute(Qt::WA_AcceptTouchEvents);
+ setDragMode(ScrollHandDrag);
+}
+
+bool GraphicsView::viewportEvent(QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ {
+ QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
+ const auto touchPoints = touchEvent->points();
+ if (touchPoints.count() == 2) {
+ // determine scale factor
+ const QEventPoint &touchPoint0 = touchPoints.first();
+ const QEventPoint &touchPoint1 = touchPoints.last();
+ qreal currentScaleFactor =
+ QLineF(touchPoint0.position(), touchPoint1.position()).length()
+ / QLineF(touchPoint0.pressPosition(), touchPoint1.pressPosition()).length();
+ if (touchEvent->touchPointStates() & QEventPoint::Released) {
+ // if one of the fingers is released, remember the current scale
+ // factor so that adding another finger later will continue zooming
+ // by adding new scale factor to the existing remembered value.
+ totalScaleFactor *= currentScaleFactor;
+ currentScaleFactor = 1;
+ }
+ setTransform(QTransform::fromScale(totalScaleFactor * currentScaleFactor,
+ totalScaleFactor * currentScaleFactor));
+ }
+ return true;
+ }
+ default:
+ break;
+ }
+ return QGraphicsView::viewportEvent(event);
+}
diff --git a/tests/manual/examples/widgets/touch/pinchzoom/graphicsview.h b/tests/manual/examples/widgets/touch/pinchzoom/graphicsview.h
new file mode 100644
index 0000000000..e6a300f418
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/pinchzoom/graphicsview.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#pragma once
+#include <QGraphicsView>
+
+class GraphicsView : public QGraphicsView
+{
+ Q_OBJECT
+
+public:
+ GraphicsView(QGraphicsScene *scene = nullptr, QWidget *parent = nullptr);
+
+ bool viewportEvent(QEvent *event) override;
+
+private:
+ qreal totalScaleFactor = 1;
+};
diff --git a/tests/manual/examples/widgets/touch/pinchzoom/images/cheese.jpg b/tests/manual/examples/widgets/touch/pinchzoom/images/cheese.jpg
new file mode 100644
index 0000000000..dea5795fd0
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/pinchzoom/images/cheese.jpg
Binary files differ
diff --git a/tests/manual/examples/widgets/touch/pinchzoom/main.cpp b/tests/manual/examples/widgets/touch/pinchzoom/main.cpp
new file mode 100644
index 0000000000..85b5cad1f8
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/pinchzoom/main.cpp
@@ -0,0 +1,47 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "graphicsview.h"
+#include "mouse.h"
+
+#include <QApplication>
+#include <cmath>
+
+static constexpr int MouseCount = 7;
+
+//! [0]
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+//! [0]
+
+//! [1]
+ QGraphicsScene scene;
+ scene.setSceneRect(-300, -300, 600, 600);
+//! [1] //! [2]
+ scene.setItemIndexMethod(QGraphicsScene::NoIndex);
+//! [2]
+
+//! [3]
+ for (int i = 0; i < MouseCount; ++i) {
+ Mouse *mouse = new Mouse;
+ mouse->setPos(::sin((i * 6.28) / MouseCount) * 200,
+ ::cos((i * 6.28) / MouseCount) * 200);
+ scene.addItem(mouse);
+ }
+//! [3]
+
+//! [4]
+ GraphicsView view(&scene);
+ view.setRenderHint(QPainter::Antialiasing);
+ view.setBackgroundBrush(QPixmap(":/images/cheese.jpg"));
+//! [4] //! [5]
+ view.setCacheMode(QGraphicsView::CacheBackground);
+ view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
+//! [5] //! [6]
+ view.setWindowTitle(QT_TRANSLATE_NOOP(QGraphicsView, "Colliding Mice"));
+ view.showMaximized();
+
+ return app.exec();
+}
+//! [6]
diff --git a/tests/manual/examples/widgets/touch/pinchzoom/mice.qrc b/tests/manual/examples/widgets/touch/pinchzoom/mice.qrc
new file mode 100644
index 0000000000..accdb4d0a6
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/pinchzoom/mice.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/" >
+ <file>images/cheese.jpg</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/touch/pinchzoom/mouse.cpp b/tests/manual/examples/widgets/touch/pinchzoom/mouse.cpp
new file mode 100644
index 0000000000..ae0d497833
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/pinchzoom/mouse.cpp
@@ -0,0 +1,158 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mouse.h"
+
+#include <QGraphicsScene>
+#include <QPainter>
+#include <QRandomGenerator>
+#include <QStyleOption>
+#include <qmath.h>
+
+constexpr qreal Pi = M_PI;
+constexpr qreal TwoPi = 2 * M_PI;
+
+static qreal normalizeAngle(qreal angle)
+{
+ while (angle < 0)
+ angle += TwoPi;
+ while (angle > TwoPi)
+ angle -= TwoPi;
+ return angle;
+}
+
+//! [0]
+Mouse::Mouse() : color(QRandomGenerator::global()->bounded(256),
+ QRandomGenerator::global()->bounded(256),
+ QRandomGenerator::global()->bounded(256))
+{
+ setTransform(QTransform().rotate(QRandomGenerator::global()->bounded(360 * 16)), true);
+ startTimer(1000 / 33);
+}
+//! [0]
+
+//! [1]
+QRectF Mouse::boundingRect() const
+{
+ qreal adjust = 0.5;
+ return QRectF(-18 - adjust, -22 - adjust,
+ 36 + adjust, 60 + adjust);
+}
+//! [1]
+
+//! [2]
+QPainterPath Mouse::shape() const
+{
+ QPainterPath path;
+ path.addRect(-10, -20, 20, 40);
+ return path;
+}
+//! [2]
+
+//! [3]
+void Mouse::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
+{
+ // Body
+ painter->setBrush(color);
+ painter->drawEllipse(-10, -20, 20, 40);
+
+ // Eyes
+ painter->setBrush(Qt::white);
+ painter->drawEllipse(-10, -17, 8, 8);
+ painter->drawEllipse(2, -17, 8, 8);
+
+ // Nose
+ painter->setBrush(Qt::black);
+ painter->drawEllipse(QRectF(-2, -22, 4, 4));
+
+ // Pupils
+ painter->drawEllipse(QRectF(-8.0 + mouseEyeDirection, -17, 4, 4));
+ painter->drawEllipse(QRectF(4.0 + mouseEyeDirection, -17, 4, 4));
+
+ // Ears
+ painter->setBrush(scene()->collidingItems(this).isEmpty() ? Qt::darkYellow : Qt::red);
+ painter->drawEllipse(-17, -12, 16, 16);
+ painter->drawEllipse(1, -12, 16, 16);
+
+ // Tail
+ QPainterPath path(QPointF(0, 20));
+ path.cubicTo(-5, 22, -5, 22, 0, 25);
+ path.cubicTo(5, 27, 5, 32, 0, 30);
+ path.cubicTo(-5, 32, -5, 42, 0, 35);
+ painter->setBrush(Qt::NoBrush);
+ painter->drawPath(path);
+}
+//! [3]
+
+//! [4]
+void Mouse::timerEvent(QTimerEvent *)
+{
+//! [4]
+ // Don't move too far away
+//! [5]
+ QLineF lineToCenter(QPointF(0, 0), mapFromScene(0, 0));
+ if (lineToCenter.length() > 150) {
+ qreal angleToCenter = std::atan2(lineToCenter.dy(), lineToCenter.dx());
+ angleToCenter = normalizeAngle((Pi - angleToCenter) + Pi / 2);
+
+ if (angleToCenter < Pi && angleToCenter > Pi / 4) {
+ // Rotate left
+ angle += (angle < -Pi / 2) ? 0.25 : -0.25;
+ } else if (angleToCenter >= Pi && angleToCenter < (Pi + Pi / 2 + Pi / 4)) {
+ // Rotate right
+ angle += (angle < Pi / 2) ? 0.25 : -0.25;
+ }
+ } else if (::sin(angle) < 0) {
+ angle += 0.25;
+ } else if (::sin(angle) > 0) {
+ angle -= 0.25;
+//! [5] //! [6]
+ }
+//! [6]
+
+ // Try not to crash with any other mice
+//! [7]
+ QList<QGraphicsItem *> dangerMice = scene()->items(QPolygonF()
+ << mapToScene(0, 0)
+ << mapToScene(-30, -50)
+ << mapToScene(30, -50));
+ for (QGraphicsItem *item : dangerMice) {
+ if (item == this)
+ continue;
+
+ QLineF lineToMouse(QPointF(0, 0), mapFromItem(item, 0, 0));
+ qreal angleToMouse = std::atan2(lineToMouse.dy(), lineToMouse.dx());
+ angleToMouse = normalizeAngle((Pi - angleToMouse) + Pi / 2);
+
+ if (angleToMouse >= 0 && angleToMouse < Pi / 2) {
+ // Rotate right
+ angle += 0.5;
+ } else if (angleToMouse <= TwoPi && angleToMouse > (TwoPi - Pi / 2)) {
+ // Rotate left
+ angle -= 0.5;
+//! [7] //! [8]
+ }
+//! [8] //! [9]
+ }
+//! [9]
+
+ // Add some random movement
+//! [10]
+ if (dangerMice.size() > 1 && QRandomGenerator::global()->bounded(10) == 0) {
+ if (QRandomGenerator::global()->bounded(1))
+ angle += QRandomGenerator::global()->bounded(1 / 500.0);
+ else
+ angle -= QRandomGenerator::global()->bounded(1 / 500.0);
+ }
+//! [10]
+
+//! [11]
+ speed += (-50 + QRandomGenerator::global()->bounded(100)) / 100.0;
+
+ qreal dx = ::sin(angle) * 10;
+ mouseEyeDirection = (qAbs(dx / 5) < 1) ? 0 : dx / 5;
+
+ setTransform(QTransform().rotate(dx), true);
+ setPos(mapToParent(0, -(3 + sin(speed) * 3)));
+}
+//! [11]
diff --git a/tests/manual/examples/widgets/touch/pinchzoom/mouse.h b/tests/manual/examples/widgets/touch/pinchzoom/mouse.h
new file mode 100644
index 0000000000..6b3ef98c22
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/pinchzoom/mouse.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MOUSE_H
+#define MOUSE_H
+
+#include <QGraphicsObject>
+
+//! [0]
+class Mouse : public QGraphicsObject
+{
+ Q_OBJECT
+
+public:
+ Mouse();
+
+ QRectF boundingRect() const override;
+ QPainterPath shape() const override;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget) override;
+
+protected:
+ void timerEvent(QTimerEvent *event) override;
+
+private:
+ qreal angle = 0;
+ qreal speed = 0;
+ qreal mouseEyeDirection = 0;
+ QColor color;
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/touch/pinchzoom/pinchzoom.pro b/tests/manual/examples/widgets/touch/pinchzoom/pinchzoom.pro
new file mode 100644
index 0000000000..ebbc7ddf1f
--- /dev/null
+++ b/tests/manual/examples/widgets/touch/pinchzoom/pinchzoom.pro
@@ -0,0 +1,16 @@
+QT += widgets
+
+HEADERS += \
+ mouse.h \
+ graphicsview.h
+SOURCES += \
+ main.cpp \
+ mouse.cpp \
+ graphicsview.cpp
+
+RESOURCES += \
+ mice.qrc
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/touch/pinchzoom
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/CMakeLists.txt
new file mode 100644
index 0000000000..066a323d61
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/CMakeLists.txt
@@ -0,0 +1,10 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+qt_internal_add_example(part1)
+qt_internal_add_example(part2)
+qt_internal_add_example(part3)
+qt_internal_add_example(part4)
+qt_internal_add_example(part5)
+qt_internal_add_example(part6)
+qt_internal_add_example(part7)
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/README b/tests/manual/examples/widgets/tutorials/addressbook/README
new file mode 100644
index 0000000000..07897b9683
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/README
@@ -0,0 +1,40 @@
+The Address Book Tutorial shows how to put together a simple yet
+fully-functioning GUI application. The tutorial chapters can be found in the
+Qt documentation, which can be viewed using Qt Assistant or a Web browser.
+
+The tutorial is also available online at
+
+http://qt-project.org/doc/qt-5.0/qtwidgets/tutorials-addressbook.html
+
+All programs corresponding to the chapters in the tutorial should
+automatically be built when Qt is compiled, or will be provided as
+pre-built executables if you have obtained a binary package of Qt.
+
+If you have only compiled the Qt libraries, use the following instructions
+to build the tutorial.
+
+On Linux/Unix:
+
+Typing 'make' in this directory builds all the programs (part1/part1,
+part2/part2, part3/part3 and so on). Typing 'make' in each subdirectory
+builds just that tutorial program.
+
+On Windows:
+
+Create a single Visual Studio project for the tutorial directory in
+the usual way. You can do this by typing the following at the command
+line:
+
+qmake -tp vc
+
+You should now be able to open the project file in Visual Studio and
+build all of the tutorial programs at the same time.
+
+On Mac OS X:
+
+Create an Xcode project with the .pro file in the tutorial directory.
+You can do this by typing the following at the command line:
+
+qmake -spec macx-xcode
+
+Then open the generated Xcode project in Xcode and build it.
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-labeled-layout.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-labeled-layout.png
new file mode 100644
index 0000000000..b19cb360a1
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-labeled-layout.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-labeled-screenshot.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-labeled-screenshot.png
new file mode 100644
index 0000000000..f9b91eebe6
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-labeled-screenshot.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-screenshot.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-screenshot.png
new file mode 100644
index 0000000000..454b0959e6
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-screenshot.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-contact.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-contact.png
new file mode 100644
index 0000000000..6f2b947b21
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-contact.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-flowchart.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-flowchart.png
new file mode 100644
index 0000000000..ca9af3720d
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-flowchart.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-successful.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-successful.png
new file mode 100644
index 0000000000..99a2154007
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-successful.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-labeled-layout.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-labeled-layout.png
new file mode 100644
index 0000000000..1e000c8f31
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-labeled-layout.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-signals-and-slots.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-signals-and-slots.png
new file mode 100644
index 0000000000..e49f8dc262
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-signals-and-slots.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-stretch-effects.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-stretch-effects.png
new file mode 100644
index 0000000000..d9f7f31227
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-stretch-effects.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-labeled-layout.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-labeled-layout.png
new file mode 100644
index 0000000000..1981ba8cb6
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-labeled-layout.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-linkedlist.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-linkedlist.png
new file mode 100644
index 0000000000..e7f4725dce
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-linkedlist.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-screenshot.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-screenshot.png
new file mode 100644
index 0000000000..75159b4045
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-screenshot.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part4-remove.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part4-remove.png
new file mode 100644
index 0000000000..8eb259ef02
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part4-remove.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-finddialog.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-finddialog.png
new file mode 100644
index 0000000000..743d92ef6f
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-finddialog.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-notfound.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-notfound.png
new file mode 100644
index 0000000000..2d35766ab5
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-notfound.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-screenshot.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-screenshot.png
new file mode 100644
index 0000000000..3abe2775c2
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-screenshot.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-signals-and-slots.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-signals-and-slots.png
new file mode 100644
index 0000000000..1771e7bbbf
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-signals-and-slots.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-load.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-load.png
new file mode 100644
index 0000000000..a027a1decb
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-load.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-save.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-save.png
new file mode 100644
index 0000000000..757feeb9ac
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-save.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-screenshot.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-screenshot.png
new file mode 100644
index 0000000000..7bb2f749bf
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-screenshot.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part7-screenshot.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part7-screenshot.png
new file mode 100644
index 0000000000..3e7b3ca522
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part7-screenshot.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-screenshot.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-screenshot.png
new file mode 100644
index 0000000000..3fba6e849e
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-screenshot.png
Binary files differ
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial.qdoc b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial.qdoc
new file mode 100644
index 0000000000..2f7884bee8
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial.qdoc
@@ -0,0 +1,948 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page tutorials-addressbook.html
+
+ \title Address Book Tutorial
+ \ingroup examples-layout
+ \brief An introduction to GUI programming, showing how to put together a
+ simple yet fully-functioning application.
+
+ This tutorial is an introduction to GUI programming with the Qt
+ cross-platform framework.
+
+ \image addressbook-tutorial-screenshot.png
+
+ \omit
+ It doesn't cover everything; the emphasis is on teaching the programming
+ philosophy of GUI programming, and Qt's features are introduced as needed.
+ Some commonly used features are never used in this tutorial.
+ \endomit
+
+ In this tutorial, you will learn about some of the basic
+ components of Qt, including:
+
+ \list
+ \li Widgets and layout managers
+ \li Container classes
+ \li Signals and slots
+ \li Input and output devices
+ \endlist
+
+ Tutorial contents:
+
+ \list 1
+ \li \l{tutorials/addressbook/part1}{Designing the User Interface}
+ \li \l{tutorials/addressbook/part2}{Adding Addresses}
+ \li \l{tutorials/addressbook/part3}{Navigating between Entries}
+ \li \l{tutorials/addressbook/part4}{Editing and Removing Addresses}
+ \li \l{tutorials/addressbook/part5}{Adding a Find Function}
+ \li \l{tutorials/addressbook/part6}{Loading and Saving}
+ \li \l{tutorials/addressbook/part7}{Additional Features}
+ \endlist
+
+ The tutorial source code is located in \c{tutorials/addressbook}.
+
+ Although this little application does not look much like a
+ fully-fledged modern GUI application, it uses many of the basic
+ elements that are used in more complex applications. After you
+ have worked through this tutorial, we recommend reading the
+ \l{mainwindows/application}{Application} example, which presents a
+ small GUI application, with menus, toolbars, a status bar, and so
+ on.
+*/
+
+/*!
+ \example tutorials/addressbook/part1
+ \title Part 1 - Designing the User Interface
+ \brief Describes how to code the user interface of the Address Book Example.
+ This first part covers the design of the basic graphical user
+ interface (GUI) for our address book application.
+
+ The first step in creating a GUI program is to design the user
+ interface. Here the our goal is to set up the labels and input
+ fields to implement a basic address book. The figure below is a
+ screenshot of the expected output.
+
+ \image addressbook-tutorial-part1-screenshot.png
+
+ We require two QLabel objects, \c nameLabel and \c addressLabel, as well
+ as two input fields, a QLineEdit object, \c nameLine, and a QTextEdit
+ object, \c addressText, to enable the user to enter a contact's name and
+ address. The widgets used and their positions are shown in the figure
+ below.
+
+ \image addressbook-tutorial-part1-labeled-screenshot.png
+
+ There are three files used to implement this address book:
+
+ \list
+ \li \c{addressbook.h} - the definition file for the \c AddressBook
+ class,
+ \li \c{addressbook.cpp} - the implementation file for the
+ \c AddressBook class, and
+ \li \c{main.cpp} - the file containing a \c main() function, with
+ an instance of \c AddressBook.
+ \endlist
+
+ \section1 Qt Programming - Subclassing
+
+ When writing Qt programs, we usually subclass Qt objects to add
+ functionality. This is one of the essential concepts behind creating
+ custom widgets or collections of standard widgets. Subclassing to
+ extend or change the behavior of a widget has the following advantages:
+
+ \list
+ \li We can write implementations of virtual or pure virtual functions to
+ obtain exactly what we need, falling back on the base class's implementation
+ when necessary.
+ \li It allows us to encapsulate parts of the user interface within a class,
+ so that the other parts of the application don't need to know about the
+ individual widgets in the user interface.
+ \li The subclass can be used to create multiple custom widgets in the same
+ application or library, and the code for the subclass can be reused in other
+ projects.
+ \endlist
+
+ Since Qt does not provide a specific address book widget, we subclass a
+ standard Qt widget class and add features to it. The \c AddressBook class
+ we create in this tutorial can be reused in situations where a basic address
+ book widget is needed.
+
+ \section1 Defining the AddressBook Class
+
+ The \c{tutorials/addressbook/part1/addressbook.h} file is
+ used to define the \c AddressBook class.
+
+ We start by defining \c AddressBook as a QWidget subclass and declaring
+ a constructor. We also use the Q_OBJECT macro to indicate that the class
+ uses internationalization and Qt's signals and slots features, even
+ if we do not use all of these features at this stage.
+
+ \snippet tutorials/addressbook/part1/addressbook.h class definition
+
+ The class holds declarations of \c nameLine and \c addressText,
+ the private instances of QLineEdit and QTextEdit mentioned
+ earlier. The data stored in \c nameLine and \c addressText will
+ be needed for many of the address book functions.
+
+ We don't include declarations of the QLabel objects we will use
+ because we will not need to reference them once they have been
+ created. The way Qt tracks the ownership of objects is explained
+ in the next section.
+
+ The Q_OBJECT macro itself implements some of the more advanced features of Qt.
+ For now, it is useful to think of the Q_OBJECT macro as a shortcut which allows
+ us to use the \l{QObject::}{tr()} and \l{QObject::}{connect()} functions.
+
+ We have now completed the \c addressbook.h file and we move on to
+ implement the corresponding \c addressbook.cpp file.
+
+ \section1 Implementing the AddressBook Class
+
+ The constructor of \c AddressBook accepts a QWidget parameter, \a parent.
+ By convention, we pass this parameter to the base class's constructor.
+ This concept of ownership, where a parent can have one or more children,
+ is useful for grouping widgets in Qt. For example, if you delete a parent,
+ all of its children will be deleted as well.
+
+ \snippet tutorials/addressbook/part1/addressbook.cpp constructor and input fields
+
+ In this constructor, the QLabel objects \c nameLabel and \c
+ addressLabel are instantiated, as well as \c nameLine and \c
+ addressText. The \l{QObject::tr()}{tr()} function returns a
+ translated version of the string, if there is one
+ available. Otherwise it returns the string itself. This function
+ marks its QString parameter as one that should be translated into
+ other languages. It should be used wherever a translatable string
+ appears.
+
+ When programming with Qt, it is useful to know how layouts work.
+ Qt provides three main layout classes: QHBoxLayout, QVBoxLayout
+ and QGridLayout to handle the positioning of widgets.
+
+ \image addressbook-tutorial-part1-labeled-layout.png
+
+ We use a QGridLayout to position our labels and input fields in a
+ structured manner. QGridLayout divides the available space into a grid and
+ places widgets in the cells we specify with row and column numbers. The
+ diagram above shows the layout cells and the position of our widgets, and
+ we specify this arrangement using the following code:
+
+ \snippet tutorials/addressbook/part1/addressbook.cpp layout
+
+ Notice that \c addressLabel is positioned using Qt::AlignTop as an
+ additional argument. This is to make sure it is not vertically centered in
+ cell (1,0). For a basic overview on Qt Layouts, refer to the
+ \l{Layout Management} documentation.
+
+ In order to install the layout object onto the widget, we have to invoke
+ the widget's \l{QWidget::setLayout()}{setLayout()} function:
+
+ \snippet tutorials/addressbook/part1/addressbook.cpp setting the layout
+
+ Lastly, we set the widget's title to "Simple Address Book".
+
+ \section1 Running the Application
+
+ A separate file, \c main.cpp, is used for the \c main() function. Within
+ this function, we instantiate a QApplication object, \c app. QApplication
+ is responsible for various application-wide resources, such as the default
+ font and cursor, and for running an event loop. Hence, there is always one
+ QApplication object in every GUI application using Qt.
+
+ \snippet tutorials/addressbook/part1/main.cpp main function
+
+ We construct a new \c AddressBook widget on the stack and invoke
+ its \l{QWidget::show()}{show()} function to display it.
+ However, the widget will not be shown until the application's event loop
+ is started. We start the event loop by calling the application's
+ \l{QApplication::}{exec()} function; the result returned by this function
+ is used as the return value from the \c main() function. At this point,
+ it becomes apparent why we instantiated \c AddressBook on the stack: It
+ will now go out of scope. Therefore, \c AddressBook and all its child widgets
+ will be deleted, thus preventing memory leaks.
+*/
+
+/*!
+ \example tutorials/addressbook/part2
+ \title Part 2 - Adding Addresses
+ \brief Describes the code for inserting records in the Address Book Example.
+
+ The next step in creating the address book is to implement some
+ user interactions.
+
+ \image addressbook-tutorial-part2-add-contact.png
+
+ We will provide a push button that the user can click to add a new contact.
+ Also, some form of data structure is needed to store these contacts in an
+ organized way.
+
+ \section1 Defining the AddressBook Class
+
+ Now that we have the labels and input fields set up, we add push buttons to
+ complete the process of adding a contact. This means that our
+ \c addressbook.h file now has three QPushButton objects declared and three
+ corresponding public slots.
+
+ \snippet tutorials/addressbook/part2/addressbook.h slots
+
+ A slot is a function that responds to a particular signal. We will discuss
+ this concept in further detail when implementing the \c AddressBook class.
+ However, for an overview of Qt's signals and slots concept, you can refer
+ to the \l{Signals and Slots} document.
+
+ Three QPushButton objects (\c addButton, \c submitButton, and
+ \c cancelButton) are now included in our private variable declarations,
+ along with \c nameLine and \c addressText.
+
+ \snippet tutorials/addressbook/part2/addressbook.h pushbutton declaration
+
+ We need a container to store our address book contacts, so that we can
+ traverse and display them. A QMap object, \c contacts, is used for this
+ purpose as it holds a key-value pair: the contact's name as the \e key,
+ and the contact's address as the \e{value}.
+
+ \snippet tutorials/addressbook/part2/addressbook.h remaining private variables
+
+ We also declare two private QString objects, \c oldName and \c oldAddress.
+ These objects are needed to hold the name and address of the contact that
+ was last displayed, before the user clicked \uicontrol Add. So, when the user clicks
+ \uicontrol Cancel, we can revert to displaying the details of the last contact.
+
+ \section1 Implementing the AddressBook Class
+
+ Within the constructor of \c AddressBook, we set the \c nameLine and
+ \c addressText to read-only, so that we can only display but not edit
+ existing contact details.
+
+ \dots
+ \snippet tutorials/addressbook/part2/addressbook.cpp setting readonly 1
+ \dots
+ \snippet tutorials/addressbook/part2/addressbook.cpp setting readonly 2
+
+ Then, we instantiate our push buttons: \c addButton, \c submitButton, and
+ \c cancelButton.
+
+ \snippet tutorials/addressbook/part2/addressbook.cpp pushbutton declaration
+
+ The \c addButton is displayed by invoking the \l{QPushButton::show()}
+ {show()} function, while the \c submitButton and \c cancelButton are
+ hidden by invoking \l{QPushButton::hide()}{hide()}. These two push
+ buttons will only be displayed when the user clicks \uicontrol Add and this is
+ handled by the \c addContact() function discussed below.
+
+ \snippet tutorials/addressbook/part2/addressbook.cpp connecting signals and slots
+
+ We connect the push buttons' \l{QPushButton::clicked()}{clicked()} signal
+ to their respective slots. The figure below illustrates this.
+
+ \image addressbook-tutorial-part2-signals-and-slots.png
+
+ Next, we arrange our push buttons neatly to the right of our address book
+ widget, using a QVBoxLayout to line them up vertically.
+
+ \snippet tutorials/addressbook/part2/addressbook.cpp vertical layout
+
+ The \l{QBoxLayout::addStretch()}{addStretch()} function is used to ensure
+ the push buttons are not evenly spaced, but arranged closer to the top of
+ the widget. The figure below shows the difference between using
+ \l{QBoxLayout::addStretch()}{addStretch()} and not using it.
+
+ \image addressbook-tutorial-part2-stretch-effects.png
+
+ We then add \c buttonLayout1 to \c mainLayout, using
+ \l{QGridLayout::addLayout()}{addLayout()}. This gives us nested layouts
+ as \c buttonLayout1 is now a child of \c mainLayout.
+
+ \snippet tutorials/addressbook/part2/addressbook.cpp grid layout
+
+ Our layout coordinates now look like this:
+
+ \image addressbook-tutorial-part2-labeled-layout.png
+
+ In the \c addContact() function, we store the last displayed contact
+ details in \c oldName and \c oldAddress. Then we clear these input
+ fields and turn off the read-only mode. The focus is set on \c nameLine
+ and we display \c submitButton and \c cancelButton.
+
+ \snippet tutorials/addressbook/part2/addressbook.cpp addContact
+
+ The \c submitContact() function can be divided into three parts:
+
+ \list 1
+ \li We extract the contact's details from \c nameLine and \c addressText
+ and store them in QString objects. We also validate to make sure that the
+ user did not click \uicontrol Submit with empty input fields; otherwise, a
+ QMessageBox is displayed to remind the user for a name and address.
+
+ \snippet tutorials/addressbook/part2/addressbook.cpp submitContact part1
+
+ \li We then proceed to check if the contact already exists. If it does not
+ exist, we add the contact to \c contacts and we display a QMessageBox to
+ inform the user that the contact has been added.
+
+ \snippet tutorials/addressbook/part2/addressbook.cpp submitContact part2
+
+ If the contact already exists, again, we display a QMessageBox to inform
+ the user about this, preventing the user from adding duplicate contacts.
+ Our \c contacts object is based on key-value pairs of name and address,
+ hence, we want to ensure that \e key is unique.
+
+ \li Once we have handled both cases mentioned above, we restore the push
+ buttons to their normal state with the following code:
+
+ \snippet tutorials/addressbook/part2/addressbook.cpp submitContact part3
+
+ \endlist
+
+ The screenshot below shows the QMessageBox object we use to display
+ information messages to the user.
+
+ \image addressbook-tutorial-part2-add-successful.png
+
+ The \c cancel() function restores the last displayed contact details and
+ enables \c addButton, as well as hides \c submitButton and
+ \c cancelButton.
+
+ \snippet tutorials/addressbook/part2/addressbook.cpp cancel
+
+ The general idea behind adding a contact is to give the user the
+ flexibility to click \uicontrol Submit or \uicontrol Cancel at any time. The flowchart below
+ further explains this concept:
+
+ \image addressbook-tutorial-part2-add-flowchart.png
+*/
+
+/*!
+ \example tutorials/addressbook/part3
+ \title Part 3 - Navigating between Entries
+ \brief Explains the code that enables navigating the contacts.
+
+ The address book is now about half complete. We should add the
+ capability to navigate the contacts, but first we must
+ decide what sort of a data structure we need for containing these
+ contacts.
+
+ In the previous section, we used a QMap of key-value pairs with
+ the contact's name as the \e key, and the contact's address as the
+ \e value. This works well for our case. However, in order to
+ navigate and display each entry, a little bit of enhancement is
+ needed.
+
+ We enhance the QMap by making it replicate a data structure similar to a
+ circularly-linked list, where all elements are connected, including the
+ first element and the last element. The figure below illustrates this data
+ structure.
+
+ \image addressbook-tutorial-part3-linkedlist.png
+
+ \section1 Defining the AddressBook Class
+
+ To add navigation functions to the address book, we must add two
+ more slots to the \c AddressBook class: \c next() and \c
+ previous() to the \c addressbook.h file:
+
+ \snippet tutorials/addressbook/part3/addressbook.h navigation functions
+
+ We also require another two QPushButton objects, so we declare \c nextButton
+ and \c previousButton as private variables:
+
+ \snippet tutorials/addressbook/part3/addressbook.h navigation pushbuttons
+
+ \section1 Implementing the AddressBook Class
+
+ In the \c AddressBook constructor in \c addressbook.cpp, we instantiate
+ \c nextButton and \c previousButton and disable them by default. This is
+ because navigation is only enabled when there is more than one contact
+ in the address book.
+
+ \snippet tutorials/addressbook/part3/addressbook.cpp navigation pushbuttons
+
+ We then connect these push buttons to their respective slots:
+
+ \snippet tutorials/addressbook/part3/addressbook.cpp connecting navigation signals
+
+ The image below is the expected graphical user interface.
+
+ \image addressbook-tutorial-part3-screenshot.png
+
+ We follow basic conventions for \c next() and \c previous() functions by
+ placing the \c nextButton on the right and the \c previousButton on the
+ left. In order to achieve this intuitive layout, we use QHBoxLayout to
+ place the widgets side-by-side:
+
+ \snippet tutorials/addressbook/part3/addressbook.cpp navigation layout
+
+ The QHBoxLayout object, \c buttonLayout2, is then added to \c mainLayout.
+
+ \snippet tutorials/addressbook/part3/addressbook.cpp adding navigation layout
+
+ The figure below shows the coordinates of the widgets in \c mainLayout.
+ \image addressbook-tutorial-part3-labeled-layout.png
+
+ Within our \c addContact() function, we have to disable these buttons so
+ that the user does not attempt to navigate while adding a contact.
+
+ \snippet tutorials/addressbook/part3/addressbook.cpp disabling navigation
+
+ Also, in our \c submitContact() function, we enable the navigation
+ buttons, \c nextButton and \c previousButton, depending on the size
+ of \c contacts. As mentioned earlier, navigation is only enabled when
+ there is more than one contact in the address book. The following lines
+ of code demonstrates how to do this:
+
+ \snippet tutorials/addressbook/part3/addressbook.cpp enabling navigation
+
+ We also include these lines of code in the \c cancel() function.
+
+ Recall that we intend to emulate a circularly-linked list with our QMap
+ object, \c contacts. So, in the \c next() function, we obtain an iterator
+ for \c contacts and then:
+
+ \list
+ \li If the iterator is not at the end of \c contacts, we increment it
+ by one.
+ \li If the iterator is at the end of \c contacts, we move it to the
+ beginning of \c contacts. This gives us the illusion that our QMap is
+ working like a circularly-linked list.
+ \endlist
+
+ \snippet tutorials/addressbook/part3/addressbook.cpp next() function
+
+ Once we have iterated to the correct object in \c contacts, we display
+ its contents on \c nameLine and \c addressText.
+
+ Similarly, for the \c previous() function, we obtain an iterator for
+ \c contacts and then:
+
+ \list
+ \li If the iterator is at the end of \c contacts, we clear the
+ display and return.
+ \li If the iterator is at the beginning of \c contacts, we move it to
+ the end.
+ \li We then decrement the iterator by one.
+ \endlist
+
+ \snippet tutorials/addressbook/part3/addressbook.cpp previous() function
+
+ Again, we display the contents of the current object in \c contacts.
+
+*/
+
+/*!
+ \example tutorials/addressbook/part4
+ \title Part 4 - Editing and Removing Addresses
+ \brief Explains how to add edit and remove functionality.
+
+ Now we look at ways to modify the contents of contacts stored in
+ the address book.
+
+ \image addressbook-tutorial-screenshot.png
+
+ We now have an address book that not only holds contacts in an
+ organized manner, but also allows navigation. It would be
+ convenient to include edit and remove functions so that a
+ contact's details can be changed when needed. However, this
+ requires a little improvement, in the form of enums. We defined
+ two modes: \c{AddingMode} and \c{NavigationMode}, but they were
+ not defined as enum values. Instead, we enabled and disabled the
+ corresponding buttons manually, resulting in multiple lines of
+ repeated code.
+
+ Here we define the \c Mode enum with three different values:
+
+ \list
+ \li \c{NavigationMode},
+ \li \c{AddingMode}, and
+ \li \c{EditingMode}.
+ \endlist
+
+ \section1 Defining the AddressBook Class
+
+ The \c addressbook.h file is updated to contain the \c Mode enum:
+
+ \snippet tutorials/addressbook/part4/addressbook.h Mode enum
+
+ We also add two new slots, \c editContact() and \c removeContact(), to
+ our current list of public slots.
+
+ \snippet tutorials/addressbook/part4/addressbook.h edit and remove slots
+
+ In order to switch between modes, we introduce the \c updateInterface() function
+ to control the enabling and disabling of all QPushButton objects. We also
+ add two new push buttons, \c editButton and \c removeButton, for the edit
+ and remove functions mentioned earlier.
+
+ \snippet tutorials/addressbook/part4/addressbook.h updateInterface() declaration
+ \dots
+ \snippet tutorials/addressbook/part4/addressbook.h buttons declaration
+ \dots
+ \snippet tutorials/addressbook/part4/addressbook.h mode declaration
+
+ Lastly, we declare \c currentMode to keep track of the enum's current mode.
+
+ \section1 Implementing the AddressBook Class
+
+ We now implement the mode-changing features of the address
+ book. The \c editButton and \c removeButton are instantiated and
+ disabled by default. The address book starts with zero contacts
+ in memory.
+
+ \snippet tutorials/addressbook/part4/addressbook.cpp edit and remove buttons
+
+ These buttons are then connected to their respective slots, \c editContact()
+ and \c removeContact(), and we add them to \c buttonLayout1.
+
+ \snippet tutorials/addressbook/part4/addressbook.cpp connecting edit and remove
+ \dots
+ \snippet tutorials/addressbook/part4/addressbook.cpp adding edit and remove to the layout
+
+ The \c editContact() function stores the contact's old details in
+ \c oldName and \c oldAddress, before switching the mode to \c EditingMode.
+ In this mode, the \c submitButton and \c cancelButton are both enabled,
+ hence, the user can change the contact's details and click either button.
+
+ \snippet tutorials/addressbook/part4/addressbook.cpp editContact() function
+
+ The \c submitContact() function has been divided in two with an \c{if-else}
+ statement. We check \c currentMode to see if it's in \c AddingMode. If it is,
+ we proceed with our adding process.
+
+ \snippet tutorials/addressbook/part4/addressbook.cpp submitContact() function beginning
+ \dots
+ \snippet tutorials/addressbook/part4/addressbook.cpp submitContact() function part1
+
+ Otherwise, we check to see if \c currentMode is in \c EditingMode. If it
+ is, we compare \c oldName with \c name. If the name has changed, we remove
+ the old contact from \c contacts and insert the newly updated contact.
+
+ \snippet tutorials/addressbook/part4/addressbook.cpp submitContact() function part2
+
+ If only the address has changed (i.e., \c oldAddress is not the same as \c address),
+ we update the contact's address. Lastly, we set \c currentMode to
+ \c NavigationMode. This is an important step as it re-enables all the
+ disabled push buttons.
+
+ To remove a contact from the address book, we implement the
+ \c removeContact() function. This function checks to see if the contact
+ exists in \c contacts.
+
+ \snippet tutorials/addressbook/part4/addressbook.cpp removeContact() function
+
+ If it does, we display a QMessageBox, to confirm the removal with the
+ user. Once the user has confirmed, we call \c previous() to ensure that the
+ user interface shows another contact, and we remove the contact using \l{QMap}'s
+ \l{QMap::remove()}{remove()} function. As a courtesy, we display a QMessageBox
+ to inform the user. Both the message boxes used in this function are shown below:
+
+ \image addressbook-tutorial-part4-remove.png
+
+ \section2 Updating the User Interface
+
+ We mentioned the \c updateInterface() function earlier as a means to
+ enable and disable the push buttons depending on the current mode.
+ The function updates the current mode according to the \c mode argument
+ passed to it, assigning it to \c currentMode before checking its value.
+
+ Each of the push buttons is then enabled or disabled, depending on the
+ current mode. The code for \c AddingMode and \c EditingMode is shown below:
+
+ \snippet tutorials/addressbook/part4/addressbook.cpp update interface() part 1
+
+ For \c NavigationMode, however, we include conditions within the parameters
+ of the QPushButton::setEnabled() function. This is to ensure that
+ \c editButton and \c removeButton are enabled when there is at least one
+ contact in the address book; \c nextButton and \c previousButton are only
+ enabled when there is more than one contact in the address book.
+
+ \snippet tutorials/addressbook/part4/addressbook.cpp update interface() part 2
+
+ By setting the mode and updating the user interface in the same
+ function, we avoid the possibility of the user interface getting
+ out of sync with the internal state of the application.
+ */
+
+/*!
+ \example tutorials/addressbook/part5
+ \title Part 5 - Adding a Find Function
+ \brief Describes how to add a find function.
+
+ Here we look at ways to locate contacts and addresses in the
+ address book.
+
+ \image addressbook-tutorial-part5-screenshot.png
+
+ As we add contacts to our address book, it becomes tedious to
+ navigate the list with the \e Next and \e Previous buttons. A \e
+ Find function would be more efficient. The screenshot above shows
+ the \e Find button and its position on the panel of buttons.
+
+ When the user clicks on the \e Find button, it is useful to
+ display a dialog that prompts for a contact's name. Qt provides
+ QDialog, which we subclass here to implement a \c FindDialog
+ class.
+
+ \section1 Defining the FindDialog Class
+
+ \image addressbook-tutorial-part5-finddialog.png
+
+ In order to subclass QDialog, we first include the header for QDialog in
+ the \c finddialog.h file. Also, we use forward declaration to declare
+ QLineEdit and QPushButton since we will be using those widgets in our
+ dialog class.
+
+ As in our \c AddressBook class, the \c FindDialog class includes
+ the Q_OBJECT macro and its constructor is defined to accept a parent
+ QWidget, even though the dialog will be opened as a separate window.
+
+ \snippet tutorials/addressbook/part5/finddialog.h FindDialog header
+
+ We define a public function, \c getFindText(), to be used by classes that
+ instantiate \c FindDialog. This function allows these classes to obtain the
+ search string entered by the user. A public slot, \c findClicked(), is also
+ defined to handle the search string when the user clicks the \uicontrol Find
+ button.
+
+ Lastly, we define the private variables, \c findButton, \c lineEdit
+ and \c findText, corresponding to the \uicontrol Find button, the line edit
+ into which the user types the search string, and an internal string
+ used to store the search string for later use.
+
+ \section1 Implementing the FindDialog Class
+
+ Within the constructor of \c FindDialog, we set up the private variables,
+ \c lineEdit, \c findButton and \c findText. We use a QHBoxLayout to
+ position the widgets.
+
+ \snippet tutorials/addressbook/part5/finddialog.cpp constructor
+
+ We set the layout and window title, as well as connect the signals to their
+ respective slots. Notice that \c{findButton}'s \l{QPushButton::clicked()}
+ {clicked()} signal is connected to \c findClicked() and
+ \l{QDialog::accept()}{accept()}. The \l{QDialog::accept()}{accept()} slot
+ provided by QDialog hides the dialog and sets the result code to
+ \l{QDialog::}{Accepted}. We use this function to help \c{AddressBook}'s
+ \c findContact() function know when the \c FindDialog object has been
+ closed. We will explain this logic in further detail when discussing the
+ \c findContact() function.
+
+ \image addressbook-tutorial-part5-signals-and-slots.png
+
+ In \c findClicked(), we validate \c lineEdit to ensure that the user
+ did not click the \uicontrol Find button without entering a contact's name. Then, we set
+ \c findText to the search string, extracted from \c lineEdit. After that,
+ we clear the contents of \c lineEdit and hide the dialog.
+
+ \snippet tutorials/addressbook/part5/finddialog.cpp findClicked() function
+
+ The \c findText variable has a public getter function, \c getFindText(),
+ associated with it. Since we only ever set \c findText directly in both the
+ constructor and in the \c findClicked() function, we do not create a
+ setter function to accompany \c getFindText().
+ Because \c getFindText() is public, classes instantiating and using
+ \c FindDialog can always access the search string that the user has
+ entered and accepted.
+
+ \snippet tutorials/addressbook/part5/finddialog.cpp getFindText() function
+
+ \section1 Defining the AddressBook Class
+
+ To ensure we can use \c FindDialog from within our \c AddressBook class, we
+ include \c finddialog.h in the \c addressbook.h file.
+
+ \snippet tutorials/addressbook/part5/addressbook.h include finddialog's header
+
+ So far, all our address book features have a QPushButton and a
+ corresponding slot. Similarly, for the \uicontrol Find feature we have
+ \c findButton and \c findContact().
+
+ The \c findButton is declared as a private variable and the
+ \c findContact() function is declared as a public slot.
+
+ \snippet tutorials/addressbook/part5/addressbook.h findContact() declaration
+ \dots
+ \snippet tutorials/addressbook/part5/addressbook.h findButton declaration
+
+ Lastly, we declare the private variable, \c dialog, which we will use to
+ refer to an instance of \c FindDialog.
+
+ \snippet tutorials/addressbook/part5/addressbook.h FindDialog declaration
+
+ Once we have instantiated a dialog, we will want to use it more than once;
+ using a private variable allows us to refer to it from more than one place
+ in the class.
+
+ \section1 Implementing the AddressBook Class
+
+ Within the \c AddressBook class's constructor, we instantiate our private
+ objects, \c findButton and \c findDialog:
+
+ \snippet tutorials/addressbook/part5/addressbook.cpp instantiating findButton
+ \dots
+ \snippet tutorials/addressbook/part5/addressbook.cpp instantiating FindDialog
+
+ Next, we connect the \c{findButton}'s
+ \l{QPushButton::clicked()}{clicked()} signal to \c findContact().
+
+ \snippet tutorials/addressbook/part5/addressbook.cpp signals and slots for find
+
+ Now all that is left is the code for our \c findContact() function:
+
+ \snippet tutorials/addressbook/part5/addressbook.cpp findContact() function
+
+ We start out by displaying the \c FindDialog instance, \c dialog. This is
+ when the user enters a contact name to look up. Once the user clicks
+ the dialog's \c findButton, the dialog is hidden and the result code is
+ set to QDialog::Accepted. This ensures that
+ our \c if statement is always true.
+
+ We then proceed to extract the search string, which in this case is
+ \c contactName, using \c{FindDialog}'s \c getFindText() function. If the
+ contact exists in our address book, we display it immediately. Otherwise,
+ we display the QMessageBox shown below to indicate that their search
+ failed.
+
+ \image addressbook-tutorial-part5-notfound.png
+*/
+
+/*!
+ \example tutorials/addressbook/part6
+ \title Part 6 - Loading and Saving
+ \brief Describes how to add save and load functionality.
+
+ This part covers the Qt file handling features we use to write
+ loading and saving routines for the address book.
+
+ \image addressbook-tutorial-part6-screenshot.png
+
+ Although browsing and searching the contact list are useful
+ features, our address book is not complete until we can save
+ existing contacts and load them again at a later time.
+
+ Qt provides a number of classes for \l{Input/Output and Networking}
+ {input and output}, but we have chosen to use two which are simple to use
+ in combination: QFile and QDataStream.
+
+ A QFile object represents a file on disk that can be read from and written
+ to. QFile is a subclass of the more general QIODevice class which
+ represents many different kinds of devices.
+
+ A QDataStream object is used to serialize binary data so that it can be
+ stored in a QIODevice and retrieved again later. Reading from a QIODevice
+ and writing to it is as simple as opening the stream - with the respective
+ device as a parameter - and reading from or writing to it.
+
+
+ \section1 Defining the AddressBook Class
+
+ We declare two public slots, \c saveToFile() and \c loadFromFile(), as well
+ as two QPushButton objects, \c loadButton and \c saveButton.
+
+ \snippet tutorials/addressbook/part6/addressbook.h save and load functions declaration
+ \dots
+ \snippet tutorials/addressbook/part6/addressbook.h save and load buttons declaration
+
+ \section1 Implementing the AddressBook Class
+
+ In our constructor, we instantiate \c loadButton and \c saveButton.
+ Ideally, it would be more user-friendly to set the push buttons' labels
+ to "Load contacts from a file" and "Save contacts to a file". However, due
+ to the size of our other push buttons, we set the labels to \uicontrol{Load...}
+ and \uicontrol{Save...}. Fortunately, Qt provides a simple way to set tooltips with
+ \l{QWidget::setToolTip()}{setToolTip()} and we use it in the following way
+ for our push buttons:
+
+ \snippet tutorials/addressbook/part6/addressbook.cpp tooltip 1
+ \dots
+ \snippet tutorials/addressbook/part6/addressbook.cpp tooltip 2
+
+ Although it is not shown here, just like the other features we implemented,
+ we add the push buttons to the layout panel on the right, \c buttonLayout1,
+ and we connect the push buttons' \l{QPushButton::clicked()}{clicked()}
+ signals to their respective slots.
+
+ For the saving feature, we first obtain \c fileName using
+ QFileDialog::getSaveFileName(). This is a convenience function provided
+ by QFileDialog, which pops up a modal file dialog and allows the user to
+ enter a file name or select any existing \c{.abk} file. The \c{.abk} file
+ is our Address Book extension that we create when we save contacts.
+
+ \snippet tutorials/addressbook/part6/addressbook.cpp saveToFile() function part1
+
+ The file dialog that pops up is displayed in the screenshot below:
+
+ \image addressbook-tutorial-part6-save.png
+
+ If \c fileName is not empty, we create a QFile object, \c file, with
+ \c fileName. QFile works with QDataStream as QFile is a QIODevice.
+
+ Next, we attempt to open the file in \l{QIODeviceBase::}{WriteOnly} mode.
+ If this is unsuccessful, we display a QMessageBox to inform the user.
+
+ \snippet tutorials/addressbook/part6/addressbook.cpp saveToFile() function part2
+
+ Otherwise, we instantiate a QDataStream object, \c out, to write the open
+ file. QDataStream requires that the same version of the stream is used
+ for reading and writing. We ensure that this is the case by setting the
+ version used to the \l{QDataStream::Qt_4_5}{version introduced with Qt 4.5}
+ before serializing the data to \c file.
+
+ \snippet tutorials/addressbook/part6/addressbook.cpp saveToFile() function part3
+
+ For the loading feature, we also obtain \c fileName using
+ QFileDialog::getOpenFileName(). This function, the counterpart to
+ QFileDialog::getSaveFileName(), also pops up the modal file dialog and
+ allows the user to enter a file name or select any existing \c{.abk} file
+ to load it into the address book.
+
+ \snippet tutorials/addressbook/part6/addressbook.cpp loadFromFile() function part1
+
+ On Windows, for example, this function pops up a native file dialog, as
+ shown in the following screenshot.
+
+ \image addressbook-tutorial-part6-load.png
+
+ If \c fileName is not empty, again, we use a QFile object, \c file, and
+ attempt to open it in \l{QIODeviceBase::}{ReadOnly} mode. Similar to our
+ implementation of \c saveToFile(), if this attempt is unsuccessful, we
+ display a QMessageBox to inform the user.
+
+ \snippet tutorials/addressbook/part6/addressbook.cpp loadFromFile() function part2
+
+ Otherwise, we instantiate a QDataStream object, \c in, set its version as
+ above and read the serialized data into the \c contacts data structure.
+ The \c contacts object is emptied before data is read into it to simplify
+ the file reading process. A more advanced method would be to read the
+ contacts into a temporary QMap object, and copy over non-duplicate contacts
+ into \c contacts.
+
+ \snippet tutorials/addressbook/part6/addressbook.cpp loadFromFile() function part3
+
+ To display the contacts that have been read from the file, we must first
+ validate the data obtained to ensure that the file we read from actually
+ contains address book contacts. If it does, we display the first contact;
+ otherwise, we display a QMessageBox to inform the user about the problem.
+ Lastly, we update the interface to enable and disable the push buttons
+ accordingly.
+*/
+
+/*!
+ \example tutorials/addressbook/part7
+ \title Part 7 - Additional Features
+ \brief Describes how to export data in VCard format.
+
+ This part covers some additional features that make the address
+ book more convenient for the frequent user.
+
+ \image addressbook-tutorial-part7-screenshot.png
+
+ Although our address book is useful in isolation, it would be
+ better if we could exchange contact data with other applications.
+ The vCard format is a popular file format that can be used for
+ this purpose. Here we extend our address book client to allow
+ contacts to be exported to vCard \c{.vcf} files.
+
+ \section1 Defining the AddressBook Class
+
+ We add a QPushButton object, \c exportButton, and a corresponding public
+ slot, \c exportAsVCard() to our \c AddressBook class in the
+ \c addressbook.h file.
+
+ \snippet tutorials/addressbook/part7/addressbook.h exportAsVCard() declaration
+ \dots
+ \snippet tutorials/addressbook/part7/addressbook.h exportButton declaration
+
+ \section1 Implementing the AddressBook Class
+
+ Within the \c AddressBook constructor, we connect \c{exportButton}'s
+ \l{QPushButton::clicked()}{clicked()} signal to \c exportAsVCard().
+ We also add this button to our \c buttonLayout1, the layout responsible
+ for our panel of buttons on the right.
+
+ In our \c exportAsVCard() function, we start by extracting the contact's
+ name into \c name. We declare \c firstName, \c lastName and \c nameList.
+ Next, we look for the index of the first white space in \c name. If there
+ is a white space, we split the contact's name into \c firstName and
+ \c lastName. Then, we replace the space with an underscore ("_").
+ Alternately, if there is no white space, we assume that the contact only
+ has a first name.
+
+ \snippet tutorials/addressbook/part7/addressbook.cpp export function part1
+
+ As with the \c saveToFile() function, we open a file dialog to let the user
+ choose a location for the file. Using the file name chosen, we create an
+ instance of QFile to write to.
+
+ We attempt to open the file in \l{QIODeviceBase::}{WriteOnly} mode. If this
+ process fails, we display a QMessageBox to inform the user about the
+ problem and return. Otherwise, we pass the file as a parameter to a
+ QTextStream object, \c out. Like QDataStream, the QTextStream class
+ provides functionality to read and write plain text to files. As a result,
+ the \c{.vcf} file generated can be opened for editing in a text editor.
+
+ \snippet tutorials/addressbook/part7/addressbook.cpp export function part2
+
+ We then write out a vCard file with the \c{BEGIN:VCARD} tag, followed by
+ the \c{VERSION:2.1} tag. The contact's name is written with the \c{N:}
+ tag. For the \c{FN:} tag, which fills in the "File as" property of a vCard,
+ we have to check whether the contact has a last name or not. If the contact
+ does, we use the details in \c nameList to fill it. Otherwise, we write
+ \c firstName only.
+
+ \snippet tutorials/addressbook/part7/addressbook.cpp export function part3
+
+ We proceed to write the contact's address. The semicolons in the address
+ are escaped with "\\", the newlines are replaced with semicolons, and the
+ commas are replaced with spaces. Lastly, we write the \c{ADR;HOME:;}
+ tag, followed by \c address and then the \c{END:VCARD} tag.
+
+ \snippet tutorials/addressbook/part7/addressbook.cpp export function part4
+
+ In the end, a QMessageBox is displayed to inform the user that the vCard
+ has been successfully exported.
+
+ \e{vCard is a trademark of the \l{http://www.imc.org}
+ {Internet Mail Consortium}}.
+*/
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook.pro b/tests/manual/examples/widgets/tutorials/addressbook/addressbook.pro
new file mode 100644
index 0000000000..d31424998e
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook.pro
@@ -0,0 +1,6 @@
+TEMPLATE = subdirs
+SUBDIRS = part1 part2 part3 part4 part5 part6 part7
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part1/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/part1/CMakeLists.txt
new file mode 100644
index 0000000000..12eaccd384
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part1/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(part1 LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/tutorials/addressbook/part1")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(part1
+ addressbook.cpp addressbook.h
+ main.cpp
+)
+
+set_target_properties(part1 PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(part1 PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS part1
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part1/addressbook.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part1/addressbook.cpp
new file mode 100644
index 0000000000..eae818dbbf
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part1/addressbook.cpp
@@ -0,0 +1,30 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "addressbook.h"
+
+//! [constructor and input fields]
+AddressBook::AddressBook(QWidget *parent)
+ : QWidget(parent)
+{
+ QLabel *nameLabel = new QLabel(tr("Name:"));
+ nameLine = new QLineEdit;
+
+ QLabel *addressLabel = new QLabel(tr("Address:"));
+ addressText = new QTextEdit;
+//! [constructor and input fields]
+
+//! [layout]
+ QGridLayout *mainLayout = new QGridLayout;
+ mainLayout->addWidget(nameLabel, 0, 0);
+ mainLayout->addWidget(nameLine, 0, 1);
+ mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop);
+ mainLayout->addWidget(addressText, 1, 1);
+//! [layout]
+
+//![setting the layout]
+ setLayout(mainLayout);
+ setWindowTitle(tr("Simple Address Book"));
+}
+//! [setting the layout]
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part1/addressbook.h b/tests/manual/examples/widgets/tutorials/addressbook/part1/addressbook.h
new file mode 100644
index 0000000000..f2e28b4135
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part1/addressbook.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef ADDRESSBOOK_H
+#define ADDRESSBOOK_H
+
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QLabel;
+class QLineEdit;
+class QTextEdit;
+QT_END_NAMESPACE
+
+//! [class definition]
+class AddressBook : public QWidget
+{
+ Q_OBJECT
+
+public:
+ AddressBook(QWidget *parent = nullptr);
+
+private:
+ QLineEdit *nameLine;
+ QTextEdit *addressText;
+};
+//! [class definition]
+
+#endif
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part1/main.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part1/main.cpp
new file mode 100644
index 0000000000..879fb606a2
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part1/main.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "addressbook.h"
+
+//! [main function]
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ AddressBook addressBook;
+ addressBook.show();
+
+ return app.exec();
+}
+//! [main function]
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part1/part1.pro b/tests/manual/examples/widgets/tutorials/addressbook/part1/part1.pro
new file mode 100644
index 0000000000..35d4a0152e
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part1/part1.pro
@@ -0,0 +1,11 @@
+QT += widgets
+
+SOURCES = addressbook.cpp \
+ main.cpp
+HEADERS = addressbook.h
+
+QMAKE_PROJECT_NAME = ab_part1
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook/part1
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part2/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/part2/CMakeLists.txt
new file mode 100644
index 0000000000..0231e7bce8
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part2/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(part2 LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/tutorials/addressbook/part2")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(part2
+ addressbook.cpp addressbook.h
+ main.cpp
+)
+
+set_target_properties(part2 PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(part2 PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS part2
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part2/addressbook.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part2/addressbook.cpp
new file mode 100644
index 0000000000..085103c791
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part2/addressbook.cpp
@@ -0,0 +1,123 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "addressbook.h"
+
+AddressBook::AddressBook(QWidget *parent)
+ : QWidget(parent)
+{
+ QLabel *nameLabel = new QLabel(tr("Name:"));
+ nameLine = new QLineEdit;
+//! [setting readonly 1]
+ nameLine->setReadOnly(true);
+//! [setting readonly 1]
+ QLabel *addressLabel = new QLabel(tr("Address:"));
+ addressText = new QTextEdit;
+//! [setting readonly 2]
+ addressText->setReadOnly(true);
+//! [setting readonly 2]
+
+//! [pushbutton declaration]
+ addButton = new QPushButton(tr("&Add"));
+ addButton->show();
+ submitButton = new QPushButton(tr("&Submit"));
+ submitButton->hide();
+ cancelButton = new QPushButton(tr("&Cancel"));
+ cancelButton->hide();
+//! [pushbutton declaration]
+//! [connecting signals and slots]
+ connect(addButton, &QPushButton::clicked,
+ this, &AddressBook::addContact);
+ connect(submitButton, &QPushButton::clicked,
+ this, &AddressBook::submitContact);
+ connect(cancelButton, &QPushButton::clicked,
+ this, &AddressBook::cancel);
+//! [connecting signals and slots]
+//! [vertical layout]
+ QVBoxLayout *buttonLayout1 = new QVBoxLayout;
+ buttonLayout1->addWidget(addButton, Qt::AlignTop);
+ buttonLayout1->addWidget(submitButton);
+ buttonLayout1->addWidget(cancelButton);
+ buttonLayout1->addStretch();
+//! [vertical layout]
+//! [grid layout]
+ QGridLayout *mainLayout = new QGridLayout;
+ mainLayout->addWidget(nameLabel, 0, 0);
+ mainLayout->addWidget(nameLine, 0, 1);
+ mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop);
+ mainLayout->addWidget(addressText, 1, 1);
+ mainLayout->addLayout(buttonLayout1, 1, 2);
+//! [grid layout]
+ setLayout(mainLayout);
+ setWindowTitle(tr("Simple Address Book"));
+}
+//! [addContact]
+void AddressBook::addContact()
+{
+ oldName = nameLine->text();
+ oldAddress = addressText->toPlainText();
+
+ nameLine->clear();
+ addressText->clear();
+
+ nameLine->setReadOnly(false);
+ nameLine->setFocus(Qt::OtherFocusReason);
+ addressText->setReadOnly(false);
+
+ addButton->setEnabled(false);
+ submitButton->show();
+ cancelButton->show();
+}
+//! [addContact]
+
+//! [submitContact part1]
+void AddressBook::submitContact()
+{
+ QString name = nameLine->text();
+ QString address = addressText->toPlainText();
+
+ if (name.isEmpty() || address.isEmpty()) {
+ QMessageBox::information(this, tr("Empty Field"),
+ tr("Please enter a name and address."));
+ return;
+ }
+//! [submitContact part1]
+//! [submitContact part2]
+ if (!contacts.contains(name)) {
+ contacts.insert(name, address);
+ QMessageBox::information(this, tr("Add Successful"),
+ tr("\"%1\" has been added to your address book.").arg(name));
+ } else {
+ QMessageBox::information(this, tr("Add Unsuccessful"),
+ tr("Sorry, \"%1\" is already in your address book.").arg(name));
+ return;
+ }
+//! [submitContact part2]
+//! [submitContact part3]
+ if (contacts.isEmpty()) {
+ nameLine->clear();
+ addressText->clear();
+ }
+
+ nameLine->setReadOnly(true);
+ addressText->setReadOnly(true);
+ addButton->setEnabled(true);
+ submitButton->hide();
+ cancelButton->hide();
+}
+//! [submitContact part3]
+//! [cancel]
+void AddressBook::cancel()
+{
+ nameLine->setText(oldName);
+ nameLine->setReadOnly(true);
+
+ addressText->setText(oldAddress);
+ addressText->setReadOnly(true);
+
+ addButton->setEnabled(true);
+ submitButton->hide();
+ cancelButton->hide();
+}
+//! [cancel]
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part2/addressbook.h b/tests/manual/examples/widgets/tutorials/addressbook/part2/addressbook.h
new file mode 100644
index 0000000000..ecc1a71cee
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part2/addressbook.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef ADDRESSBOOK_H
+#define ADDRESSBOOK_H
+
+#include <QWidget>
+#include <QMap>
+
+QT_BEGIN_NAMESPACE
+class QLabel;
+class QLineEdit;
+class QPushButton;
+class QTextEdit;
+QT_END_NAMESPACE
+
+class AddressBook : public QWidget
+{
+ Q_OBJECT
+
+public:
+ AddressBook(QWidget *parent = nullptr);
+
+//! [slots]
+public slots:
+ void addContact();
+ void submitContact();
+ void cancel();
+//! [slots]
+
+//! [pushbutton declaration]
+private:
+ QPushButton *addButton;
+ QPushButton *submitButton;
+ QPushButton *cancelButton;
+ QLineEdit *nameLine;
+ QTextEdit *addressText;
+//! [pushbutton declaration]
+
+//! [remaining private variables]
+ QMap<QString, QString> contacts;
+ QString oldName;
+ QString oldAddress;
+};
+//! [remaining private variables]
+
+#endif
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part2/main.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part2/main.cpp
new file mode 100644
index 0000000000..879fb606a2
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part2/main.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "addressbook.h"
+
+//! [main function]
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ AddressBook addressBook;
+ addressBook.show();
+
+ return app.exec();
+}
+//! [main function]
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part2/part2.pro b/tests/manual/examples/widgets/tutorials/addressbook/part2/part2.pro
new file mode 100644
index 0000000000..643ffcfebd
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part2/part2.pro
@@ -0,0 +1,11 @@
+QT += widgets
+
+SOURCES = addressbook.cpp \
+ main.cpp
+HEADERS = addressbook.h
+
+QMAKE_PROJECT_NAME = ab_part2
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook/part2
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part3/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/part3/CMakeLists.txt
new file mode 100644
index 0000000000..77d8d8a610
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part3/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(part3 LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/tutorials/addressbook/part3")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(part3
+ addressbook.cpp addressbook.h
+ main.cpp
+)
+
+set_target_properties(part3 PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(part3 PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS part3
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part3/addressbook.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part3/addressbook.cpp
new file mode 100644
index 0000000000..1b37e56880
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part3/addressbook.cpp
@@ -0,0 +1,182 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "addressbook.h"
+
+AddressBook::AddressBook(QWidget *parent)
+ : QWidget(parent)
+{
+ QLabel *nameLabel = new QLabel(tr("Name:"));
+ nameLine = new QLineEdit;
+ nameLine->setReadOnly(true);
+
+ QLabel *addressLabel = new QLabel(tr("Address:"));
+ addressText = new QTextEdit;
+ addressText->setReadOnly(true);
+
+ addButton = new QPushButton(tr("&Add"));
+ addButton->show();
+ submitButton = new QPushButton(tr("&Submit"));
+ submitButton->hide();
+ cancelButton = new QPushButton(tr("&Cancel"));
+ cancelButton->hide();
+//! [navigation pushbuttons]
+ nextButton = new QPushButton(tr("&Next"));
+ nextButton->setEnabled(false);
+ previousButton = new QPushButton(tr("&Previous"));
+ previousButton->setEnabled(false);
+//! [navigation pushbuttons]
+ connect(addButton, &QPushButton::clicked,
+ this, &AddressBook::addContact);
+ connect(submitButton, &QPushButton::clicked,
+ this, &AddressBook::submitContact);
+ connect(cancelButton, &QPushButton::clicked,
+ this, &AddressBook::cancel);
+//! [connecting navigation signals]
+ connect(nextButton, &QPushButton::clicked,
+ this, &AddressBook::next);
+ connect(previousButton, &QPushButton::clicked,
+ this, &AddressBook::previous);
+//! [connecting navigation signals]
+
+ QVBoxLayout *buttonLayout1 = new QVBoxLayout;
+ buttonLayout1->addWidget(addButton, Qt::AlignTop);
+ buttonLayout1->addWidget(submitButton);
+ buttonLayout1->addWidget(cancelButton);
+ buttonLayout1->addStretch();
+//! [navigation layout]
+ QHBoxLayout *buttonLayout2 = new QHBoxLayout;
+ buttonLayout2->addWidget(previousButton);
+ buttonLayout2->addWidget(nextButton);
+//! [ navigation layout]
+ QGridLayout *mainLayout = new QGridLayout;
+ mainLayout->addWidget(nameLabel, 0, 0);
+ mainLayout->addWidget(nameLine, 0, 1);
+ mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop);
+ mainLayout->addWidget(addressText, 1, 1);
+ mainLayout->addLayout(buttonLayout1, 1, 2);
+//! [adding navigation layout]
+ mainLayout->addLayout(buttonLayout2, 2, 1);
+//! [adding navigation layout]
+ setLayout(mainLayout);
+ setWindowTitle(tr("Simple Address Book"));
+}
+
+void AddressBook::addContact()
+{
+ oldName = nameLine->text();
+ oldAddress = addressText->toPlainText();
+
+ nameLine->clear();
+ addressText->clear();
+
+ nameLine->setReadOnly(false);
+ nameLine->setFocus(Qt::OtherFocusReason);
+ addressText->setReadOnly(false);
+
+ addButton->setEnabled(false);
+//! [disabling navigation]
+ nextButton->setEnabled(false);
+ previousButton->setEnabled(false);
+//! [disabling navigation]
+ submitButton->show();
+ cancelButton->show();
+}
+
+void AddressBook::submitContact()
+{
+ QString name = nameLine->text();
+ QString address = addressText->toPlainText();
+
+ if (name.isEmpty() || address.isEmpty()) {
+ QMessageBox::information(this, tr("Empty Field"),
+ tr("Please enter a name and address."));
+ return;
+ }
+
+ if (!contacts.contains(name)) {
+ contacts.insert(name, address);
+ QMessageBox::information(this, tr("Add Successful"),
+ tr("\"%1\" has been added to your address book.").arg(name));
+ } else {
+ QMessageBox::information(this, tr("Add Unsuccessful"),
+ tr("Sorry, \"%1\" is already in your address book.").arg(name));
+ }
+
+ if (contacts.isEmpty()) {
+ nameLine->clear();
+ addressText->clear();
+ }
+
+ nameLine->setReadOnly(true);
+ addressText->setReadOnly(true);
+ addButton->setEnabled(true);
+
+//! [enabling navigation]
+ int number = contacts.size();
+ nextButton->setEnabled(number > 1);
+ previousButton->setEnabled(number > 1);
+//! [enabling navigation]
+ submitButton->hide();
+ cancelButton->hide();
+}
+
+void AddressBook::cancel()
+{
+ nameLine->setText(oldName);
+ addressText->setText(oldAddress);
+
+ if (contacts.isEmpty()) {
+ nameLine->clear();
+ addressText->clear();
+ }
+
+ nameLine->setReadOnly(true);
+ addressText->setReadOnly(true);
+ addButton->setEnabled(true);
+
+ int number = contacts.size();
+ nextButton->setEnabled(number > 1);
+ previousButton->setEnabled(number > 1);
+
+ submitButton->hide();
+ cancelButton->hide();
+}
+
+//! [next() function]
+void AddressBook::next()
+{
+ QString name = nameLine->text();
+ QMap<QString, QString>::iterator i = contacts.find(name);
+
+ if (i != contacts.end())
+ i++;
+
+ if (i == contacts.end())
+ i = contacts.begin();
+
+ nameLine->setText(i.key());
+ addressText->setText(i.value());
+}
+//! [next() function]
+//! [previous() function]
+void AddressBook::previous()
+{
+ QString name = nameLine->text();
+ QMap<QString, QString>::iterator i = contacts.find(name);
+
+ if (i == contacts.end()){
+ nameLine->clear();
+ addressText->clear();
+ return;
+ }
+
+ if (i == contacts.begin())
+ i = contacts.end();
+
+ i--;
+ nameLine->setText(i.key());
+ addressText->setText(i.value());
+}
+//! [previous() function]
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part3/addressbook.h b/tests/manual/examples/widgets/tutorials/addressbook/part3/addressbook.h
new file mode 100644
index 0000000000..0e3aea1e05
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part3/addressbook.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef ADDRESSBOOK_H
+#define ADDRESSBOOK_H
+
+#include <QWidget>
+#include <QMap>
+
+QT_BEGIN_NAMESPACE
+class QLabel;
+class QLineEdit;
+class QPushButton;
+class QTextEdit;
+QT_END_NAMESPACE
+
+class AddressBook : public QWidget
+{
+ Q_OBJECT
+
+public:
+ AddressBook(QWidget *parent = nullptr);
+
+public slots:
+ void addContact();
+ void submitContact();
+ void cancel();
+//! [navigation functions]
+ void next();
+ void previous();
+//! [navigation functions]
+
+private:
+ QPushButton *addButton;
+ QPushButton *submitButton;
+ QPushButton *cancelButton;
+//! [navigation pushbuttons]
+ QPushButton *nextButton;
+ QPushButton *previousButton;
+//! [navigation pushbuttons]
+ QLineEdit *nameLine;
+ QTextEdit *addressText;
+
+ QMap<QString, QString> contacts;
+ QString oldName;
+ QString oldAddress;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part3/main.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part3/main.cpp
new file mode 100644
index 0000000000..1f3aac3397
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part3/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "addressbook.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ AddressBook addressBook;
+ addressBook.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part3/part3.pro b/tests/manual/examples/widgets/tutorials/addressbook/part3/part3.pro
new file mode 100644
index 0000000000..3bacdd9501
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part3/part3.pro
@@ -0,0 +1,11 @@
+QT += widgets
+
+SOURCES = addressbook.cpp \
+ main.cpp
+HEADERS = addressbook.h
+
+QMAKE_PROJECT_NAME = ab_part3
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook/part3
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part4/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/part4/CMakeLists.txt
new file mode 100644
index 0000000000..eb136dbf10
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part4/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(part4 LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/tutorials/addressbook/part4")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(part4
+ addressbook.cpp addressbook.h
+ main.cpp
+)
+
+set_target_properties(part4 PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(part4 PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS part4
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part4/addressbook.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part4/addressbook.cpp
new file mode 100644
index 0000000000..a54a888073
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part4/addressbook.cpp
@@ -0,0 +1,258 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "addressbook.h"
+
+AddressBook::AddressBook(QWidget *parent)
+ : QWidget(parent)
+{
+ QLabel *nameLabel = new QLabel(tr("Name:"));
+ nameLine = new QLineEdit;
+ nameLine->setReadOnly(true);
+
+ QLabel *addressLabel = new QLabel(tr("Address:"));
+ addressText = new QTextEdit;
+ addressText->setReadOnly(true);
+
+ addButton = new QPushButton(tr("&Add"));
+//! [edit and remove buttons]
+ editButton = new QPushButton(tr("&Edit"));
+ editButton->setEnabled(false);
+ removeButton = new QPushButton(tr("&Remove"));
+ removeButton->setEnabled(false);
+//! [edit and remove buttons]
+ submitButton = new QPushButton(tr("&Submit"));
+ submitButton->hide();
+ cancelButton = new QPushButton(tr("&Cancel"));
+ cancelButton->hide();
+
+ nextButton = new QPushButton(tr("&Next"));
+ nextButton->setEnabled(false);
+ previousButton = new QPushButton(tr("&Previous"));
+ previousButton->setEnabled(false);
+
+ connect(addButton, &QPushButton::clicked,
+ this, &AddressBook::addContact);
+ connect(submitButton, &QPushButton::clicked,
+ this, &AddressBook::submitContact);
+//! [connecting edit and remove]
+ connect(editButton, &QPushButton::clicked,
+ this, &AddressBook::editContact);
+ connect(removeButton, &QPushButton::clicked,
+ this, &AddressBook::removeContact);
+//! [connecting edit and remove]
+ connect(cancelButton, &QPushButton::clicked,
+ this, &AddressBook::cancel);
+ connect(nextButton, &QPushButton::clicked,
+ this, &AddressBook::next);
+ connect(previousButton, &QPushButton::clicked,
+ this, &AddressBook::previous);
+
+ QVBoxLayout *buttonLayout1 = new QVBoxLayout;
+ buttonLayout1->addWidget(addButton);
+//! [adding edit and remove to the layout]
+ buttonLayout1->addWidget(editButton);
+ buttonLayout1->addWidget(removeButton);
+//! [adding edit and remove to the layout]
+ buttonLayout1->addWidget(submitButton);
+ buttonLayout1->addWidget(cancelButton);
+ buttonLayout1->addStretch();
+
+ QHBoxLayout *buttonLayout2 = new QHBoxLayout;
+ buttonLayout2->addWidget(previousButton);
+ buttonLayout2->addWidget(nextButton);
+
+ QGridLayout *mainLayout = new QGridLayout;
+ mainLayout->addWidget(nameLabel, 0, 0);
+ mainLayout->addWidget(nameLine, 0, 1);
+ mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop);
+ mainLayout->addWidget(addressText, 1, 1);
+ mainLayout->addLayout(buttonLayout1, 1, 2);
+ mainLayout->addLayout(buttonLayout2, 2, 1);
+
+ setLayout(mainLayout);
+ setWindowTitle(tr("Simple Address Book"));
+}
+
+void AddressBook::addContact()
+{
+ oldName = nameLine->text();
+ oldAddress = addressText->toPlainText();
+
+ nameLine->clear();
+ addressText->clear();
+
+ updateInterface(AddingMode);
+}
+//! [editContact() function]
+void AddressBook::editContact()
+{
+ oldName = nameLine->text();
+ oldAddress = addressText->toPlainText();
+
+ updateInterface(EditingMode);
+}
+//! [editContact() function]
+//! [submitContact() function beginning]
+void AddressBook::submitContact()
+{
+//! [submitContact() function beginning]
+ QString name = nameLine->text();
+ QString address = addressText->toPlainText();
+
+ if (name.isEmpty() || address.isEmpty()) {
+ QMessageBox::information(this, tr("Empty Field"),
+ tr("Please enter a name and address."));
+ return;
+ }
+//! [submitContact() function part1]
+ if (currentMode == AddingMode) {
+
+ if (!contacts.contains(name)) {
+ contacts.insert(name, address);
+ QMessageBox::information(this, tr("Add Successful"),
+ tr("\"%1\" has been added to your address book.").arg(name));
+ } else {
+ QMessageBox::information(this, tr("Add Unsuccessful"),
+ tr("Sorry, \"%1\" is already in your address book.").arg(name));
+ }
+//! [submitContact() function part1]
+//! [submitContact() function part2]
+ } else if (currentMode == EditingMode) {
+
+ if (oldName != name) {
+ if (!contacts.contains(name)) {
+ QMessageBox::information(this, tr("Edit Successful"),
+ tr("\"%1\" has been edited in your address book.").arg(oldName));
+ contacts.remove(oldName);
+ contacts.insert(name, address);
+ } else {
+ QMessageBox::information(this, tr("Edit Unsuccessful"),
+ tr("Sorry, \"%1\" is already in your address book.").arg(name));
+ }
+ } else if (oldAddress != address) {
+ QMessageBox::information(this, tr("Edit Successful"),
+ tr("\"%1\" has been edited in your address book.").arg(name));
+ contacts[name] = address;
+ }
+ }
+
+ updateInterface(NavigationMode);
+}
+//! [submitContact() function part2]
+
+void AddressBook::cancel()
+{
+ nameLine->setText(oldName);
+ addressText->setText(oldAddress);
+ updateInterface(NavigationMode);
+}
+//! [removeContact() function]
+void AddressBook::removeContact()
+{
+ QString name = nameLine->text();
+ QString address = addressText->toPlainText();
+
+ if (contacts.contains(name)) {
+
+ int button = QMessageBox::question(this,
+ tr("Confirm Remove"),
+ tr("Are you sure you want to remove \"%1\"?").arg(name),
+ QMessageBox::Yes | QMessageBox::No);
+
+ if (button == QMessageBox::Yes) {
+
+ previous();
+ contacts.remove(name);
+
+ QMessageBox::information(this, tr("Remove Successful"),
+ tr("\"%1\" has been removed from your address book.").arg(name));
+ }
+ }
+
+ updateInterface(NavigationMode);
+}
+//! [removeContact() function]
+void AddressBook::next()
+{
+ QString name = nameLine->text();
+ QMap<QString, QString>::iterator i = contacts.find(name);
+
+ if (i != contacts.end())
+ i++;
+
+ if (i == contacts.end())
+ i = contacts.begin();
+
+ nameLine->setText(i.key());
+ addressText->setText(i.value());
+}
+
+void AddressBook::previous()
+{
+ QString name = nameLine->text();
+ QMap<QString, QString>::iterator i = contacts.find(name);
+
+ if (i == contacts.end()) {
+ nameLine->clear();
+ addressText->clear();
+ return;
+ }
+
+ if (i == contacts.begin())
+ i = contacts.end();
+
+ i--;
+ nameLine->setText(i.key());
+ addressText->setText(i.value());
+}
+//! [update interface() part 1]
+void AddressBook::updateInterface(Mode mode)
+{
+ currentMode = mode;
+
+ switch (currentMode) {
+
+ case AddingMode:
+ case EditingMode:
+
+ nameLine->setReadOnly(false);
+ nameLine->setFocus(Qt::OtherFocusReason);
+ addressText->setReadOnly(false);
+
+ addButton->setEnabled(false);
+ editButton->setEnabled(false);
+ removeButton->setEnabled(false);
+
+ nextButton->setEnabled(false);
+ previousButton->setEnabled(false);
+
+ submitButton->show();
+ cancelButton->show();
+ break;
+//! [update interface() part 1]
+//! [update interface() part 2]
+ case NavigationMode:
+
+ if (contacts.isEmpty()) {
+ nameLine->clear();
+ addressText->clear();
+ }
+
+ nameLine->setReadOnly(true);
+ addressText->setReadOnly(true);
+ addButton->setEnabled(true);
+
+ int number = contacts.size();
+ editButton->setEnabled(number >= 1);
+ removeButton->setEnabled(number >= 1);
+ nextButton->setEnabled(number > 1);
+ previousButton->setEnabled(number >1 );
+
+ submitButton->hide();
+ cancelButton->hide();
+ break;
+ }
+}
+//! [update interface() part 2]
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part4/addressbook.h b/tests/manual/examples/widgets/tutorials/addressbook/part4/addressbook.h
new file mode 100644
index 0000000000..e77bbd3961
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part4/addressbook.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef ADDRESSBOOK_H
+#define ADDRESSBOOK_H
+
+#include <QWidget>
+#include <QMap>
+
+QT_BEGIN_NAMESPACE
+class QPushButton;
+class QLabel;
+class QLineEdit;
+class QTextEdit;
+QT_END_NAMESPACE
+
+class AddressBook : public QWidget
+{
+ Q_OBJECT
+
+public:
+ AddressBook(QWidget *parent = nullptr);
+//! [Mode enum]
+ enum Mode { NavigationMode, AddingMode, EditingMode };
+//! [Mode enum]
+
+public slots:
+ void addContact();
+ void submitContact();
+ void cancel();
+//! [edit and remove slots]
+ void editContact();
+ void removeContact();
+//! [edit and remove slots]
+ void next();
+ void previous();
+
+private:
+//! [updateInterface() declaration]
+ void updateInterface(Mode mode);
+//! [updateInterface() declaration]
+ QPushButton *addButton;
+//! [buttons declaration]
+ QPushButton *editButton;
+ QPushButton *removeButton;
+//! [buttons declaration]
+ QPushButton *submitButton;
+ QPushButton *cancelButton;
+ QPushButton *nextButton;
+ QPushButton *previousButton;
+ QLineEdit *nameLine;
+ QTextEdit *addressText;
+
+ QMap<QString, QString> contacts;
+ QString oldName;
+ QString oldAddress;
+//! [mode declaration]
+ Mode currentMode;
+//! [mode declaration]
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part4/main.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part4/main.cpp
new file mode 100644
index 0000000000..1f3aac3397
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part4/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "addressbook.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ AddressBook addressBook;
+ addressBook.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part4/part4.pro b/tests/manual/examples/widgets/tutorials/addressbook/part4/part4.pro
new file mode 100644
index 0000000000..02cc5b8e07
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part4/part4.pro
@@ -0,0 +1,11 @@
+QT += widgets
+
+SOURCES = addressbook.cpp \
+ main.cpp
+HEADERS = addressbook.h
+
+QMAKE_PROJECT_NAME = ab_part4
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook/part4
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part5/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/part5/CMakeLists.txt
new file mode 100644
index 0000000000..4c6ec27fae
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part5/CMakeLists.txt
@@ -0,0 +1,38 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(part5 LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/tutorials/addressbook/part5")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(part5
+ addressbook.cpp addressbook.h
+ finddialog.cpp finddialog.h
+ main.cpp
+)
+
+set_target_properties(part5 PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(part5 PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS part5
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part5/addressbook.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part5/addressbook.cpp
new file mode 100644
index 0000000000..52aa5a0b28
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part5/addressbook.cpp
@@ -0,0 +1,283 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "addressbook.h"
+
+AddressBook::AddressBook(QWidget *parent)
+ : QWidget(parent)
+{
+ QLabel *nameLabel = new QLabel(tr("Name:"));
+ nameLine = new QLineEdit;
+ nameLine->setReadOnly(true);
+
+ QLabel *addressLabel = new QLabel(tr("Address:"));
+ addressText = new QTextEdit;
+ addressText->setReadOnly(true);
+
+ addButton = new QPushButton(tr("&Add"));
+
+ editButton = new QPushButton(tr("&Edit"));
+ editButton->setEnabled(false);
+ removeButton = new QPushButton(tr("&Remove"));
+ removeButton->setEnabled(false);
+//! [instantiating findButton]
+ findButton = new QPushButton(tr("&Find"));
+ findButton->setEnabled(false);
+//! [instantiating findButton]
+ submitButton = new QPushButton(tr("&Submit"));
+ submitButton->hide();
+ cancelButton = new QPushButton(tr("&Cancel"));
+ cancelButton->hide();
+
+ nextButton = new QPushButton(tr("&Next"));
+ nextButton->setEnabled(false);
+ previousButton = new QPushButton(tr("&Previous"));
+ previousButton->setEnabled(false);
+
+//! [instantiating FindDialog]
+ dialog = new FindDialog(this);
+//! [instantiating FindDialog]
+
+ connect(addButton, &QPushButton::clicked,
+ this, &AddressBook::addContact);
+ connect(submitButton, &QPushButton::clicked,
+ this, &AddressBook::submitContact);
+ connect(editButton, &QPushButton::clicked,
+ this, &AddressBook::editContact);
+ connect(removeButton, &QPushButton::clicked,
+ this, &AddressBook::removeContact);
+ connect(cancelButton, &QPushButton::clicked,
+ this, &AddressBook::cancel);
+//! [signals and slots for find]
+ connect(findButton, &QPushButton::clicked,
+ this, &AddressBook::findContact);
+//! [signals and slots for find]
+ connect(nextButton, &QPushButton::clicked,
+ this, &AddressBook::next);
+ connect(previousButton, &QPushButton::clicked,
+ this, &AddressBook::previous);
+
+ QVBoxLayout *buttonLayout1 = new QVBoxLayout;
+ buttonLayout1->addWidget(addButton);
+ buttonLayout1->addWidget(editButton);
+ buttonLayout1->addWidget(removeButton);
+//! [adding findButton to layout]
+ buttonLayout1->addWidget(findButton);
+//! [adding findButton to layout]
+ buttonLayout1->addWidget(submitButton);
+ buttonLayout1->addWidget(cancelButton);
+ buttonLayout1->addStretch();
+
+ QHBoxLayout *buttonLayout2 = new QHBoxLayout;
+ buttonLayout2->addWidget(previousButton);
+ buttonLayout2->addWidget(nextButton);
+
+ QGridLayout *mainLayout = new QGridLayout;
+ mainLayout->addWidget(nameLabel, 0, 0);
+ mainLayout->addWidget(nameLine, 0, 1);
+ mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop);
+ mainLayout->addWidget(addressText, 1, 1);
+ mainLayout->addLayout(buttonLayout1, 1, 2);
+ mainLayout->addLayout(buttonLayout2, 2, 1);
+
+ setLayout(mainLayout);
+ setWindowTitle(tr("Simple Address Book"));
+}
+
+void AddressBook::addContact()
+{
+ oldName = nameLine->text();
+ oldAddress = addressText->toPlainText();
+
+ nameLine->clear();
+ addressText->clear();
+
+ updateInterface(AddingMode);
+}
+
+void AddressBook::editContact()
+{
+ oldName = nameLine->text();
+ oldAddress = addressText->toPlainText();
+
+ updateInterface(EditingMode);
+}
+
+void AddressBook::submitContact()
+{
+ QString name = nameLine->text();
+ QString address = addressText->toPlainText();
+
+ if (name.isEmpty() || address.isEmpty()) {
+ QMessageBox::information(this, tr("Empty Field"),
+ tr("Please enter a name and address."));
+ return;
+ }
+
+ if (currentMode == AddingMode) {
+
+ if (!contacts.contains(name)) {
+ contacts.insert(name, address);
+ QMessageBox::information(this, tr("Add Successful"),
+ tr("\"%1\" has been added to your address book.").arg(name));
+ } else {
+ QMessageBox::information(this, tr("Add Unsuccessful"),
+ tr("Sorry, \"%1\" is already in your address book.").arg(name));
+ }
+ } else if (currentMode == EditingMode) {
+
+ if (oldName != name) {
+ if (!contacts.contains(name)) {
+ QMessageBox::information(this, tr("Edit Successful"),
+ tr("\"%1\" has been edited in your address book.").arg(oldName));
+ contacts.remove(oldName);
+ contacts.insert(name, address);
+ } else {
+ QMessageBox::information(this, tr("Edit Unsuccessful"),
+ tr("Sorry, \"%1\" is already in your address book.").arg(name));
+ }
+ } else if (oldAddress != address) {
+ QMessageBox::information(this, tr("Edit Successful"),
+ tr("\"%1\" has been edited in your address book.").arg(name));
+ contacts[name] = address;
+ }
+ }
+
+ updateInterface(NavigationMode);
+}
+
+void AddressBook::cancel()
+{
+ nameLine->setText(oldName);
+ addressText->setText(oldAddress);
+ updateInterface(NavigationMode);
+}
+
+void AddressBook::removeContact()
+{
+ QString name = nameLine->text();
+ QString address = addressText->toPlainText();
+
+ if (contacts.contains(name)) {
+
+ int button = QMessageBox::question(this,
+ tr("Confirm Remove"),
+ tr("Are you sure you want to remove \"%1\"?").arg(name),
+ QMessageBox::Yes | QMessageBox::No);
+
+ if (button == QMessageBox::Yes) {
+
+ previous();
+ contacts.remove(name);
+
+ QMessageBox::information(this, tr("Remove Successful"),
+ tr("\"%1\" has been removed from your address book.").arg(name));
+ }
+ }
+
+ updateInterface(NavigationMode);
+}
+
+void AddressBook::next()
+{
+ QString name = nameLine->text();
+ QMap<QString, QString>::iterator i = contacts.find(name);
+
+ if (i != contacts.end())
+ i++;
+
+ if (i == contacts.end())
+ i = contacts.begin();
+
+ nameLine->setText(i.key());
+ addressText->setText(i.value());
+}
+
+void AddressBook::previous()
+{
+ QString name = nameLine->text();
+ QMap<QString, QString>::iterator i = contacts.find(name);
+
+ if (i == contacts.end()) {
+ nameLine->clear();
+ addressText->clear();
+ return;
+ }
+
+ if (i == contacts.begin())
+ i = contacts.end();
+
+ i--;
+ nameLine->setText(i.key());
+ addressText->setText(i.value());
+}
+//! [findContact() function]
+void AddressBook::findContact()
+{
+ dialog->show();
+
+ if (dialog->exec() == QDialog::Accepted) {
+ QString contactName = dialog->getFindText();
+
+ if (contacts.contains(contactName)) {
+ nameLine->setText(contactName);
+ addressText->setText(contacts.value(contactName));
+ } else {
+ QMessageBox::information(this, tr("Contact Not Found"),
+ tr("Sorry, \"%1\" is not in your address book.").arg(contactName));
+ return;
+ }
+ }
+
+ updateInterface(NavigationMode);
+}
+//! [findContact() function]
+
+void AddressBook::updateInterface(Mode mode)
+{
+ currentMode = mode;
+
+ switch (currentMode) {
+
+ case AddingMode:
+ case EditingMode:
+
+ nameLine->setReadOnly(false);
+ nameLine->setFocus(Qt::OtherFocusReason);
+ addressText->setReadOnly(false);
+
+ addButton->setEnabled(false);
+ editButton->setEnabled(false);
+ removeButton->setEnabled(false);
+
+ nextButton->setEnabled(false);
+ previousButton->setEnabled(false);
+
+ submitButton->show();
+ cancelButton->show();
+ break;
+
+ case NavigationMode:
+
+ if (contacts.isEmpty()) {
+ nameLine->clear();
+ addressText->clear();
+ }
+
+ nameLine->setReadOnly(true);
+ addressText->setReadOnly(true);
+ addButton->setEnabled(true);
+
+ int number = contacts.size();
+ editButton->setEnabled(number >= 1);
+ removeButton->setEnabled(number >= 1);
+ findButton->setEnabled(number > 2);
+ nextButton->setEnabled(number > 1);
+ previousButton->setEnabled(number > 1);
+
+ submitButton->hide();
+ cancelButton->hide();
+ break;
+ }
+}
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part5/addressbook.h b/tests/manual/examples/widgets/tutorials/addressbook/part5/addressbook.h
new file mode 100644
index 0000000000..93da08038c
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part5/addressbook.h
@@ -0,0 +1,65 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef ADDRESSBOOK_H
+#define ADDRESSBOOK_H
+
+#include <QWidget>
+#include <QMap>
+//! [include finddialog's header]
+#include "finddialog.h"
+//! [include finddialog's header]
+QT_BEGIN_NAMESPACE
+class QPushButton;
+class QLabel;
+class QLineEdit;
+class QTextEdit;
+QT_END_NAMESPACE
+
+
+class AddressBook : public QWidget
+{
+ Q_OBJECT
+
+public:
+ AddressBook(QWidget *parent = nullptr);
+ enum Mode { NavigationMode, AddingMode, EditingMode };
+
+public slots:
+ void addContact();
+ void editContact();
+ void submitContact();
+ void cancel();
+ void removeContact();
+//! [findContact() declaration]
+ void findContact();
+//! [findContact() declaration]
+ void next();
+ void previous();
+
+private:
+ void updateInterface(Mode mode);
+
+ QPushButton *addButton;
+ QPushButton *editButton;
+ QPushButton *removeButton;
+//! [findButton declaration]
+ QPushButton *findButton;
+//! [findButton declaration]
+ QPushButton *submitButton;
+ QPushButton *cancelButton;
+ QPushButton *nextButton;
+ QPushButton *previousButton;
+ QLineEdit *nameLine;
+ QTextEdit *addressText;
+
+ QMap<QString, QString> contacts;
+//! [FindDialog declaration]
+ FindDialog *dialog;
+//! [FindDialog declaration]
+ QString oldName;
+ QString oldAddress;
+ Mode currentMode;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part5/finddialog.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part5/finddialog.cpp
new file mode 100644
index 0000000000..d5daa661d0
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part5/finddialog.cpp
@@ -0,0 +1,51 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "finddialog.h"
+
+//! [constructor]
+FindDialog::FindDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ QLabel *findLabel = new QLabel(tr("Enter the name of a contact:"));
+ lineEdit = new QLineEdit;
+
+ findButton = new QPushButton(tr("&Find"));
+ findText = "";
+
+ QHBoxLayout *layout = new QHBoxLayout;
+ layout->addWidget(findLabel);
+ layout->addWidget(lineEdit);
+ layout->addWidget(findButton);
+
+ setLayout(layout);
+ setWindowTitle(tr("Find a Contact"));
+ connect(findButton, &QPushButton::clicked,
+ this, &FindDialog::findClicked);
+ connect(findButton, &QPushButton::clicked,
+ this, &FindDialog::accept);
+}
+//! [constructor]
+//! [findClicked() function]
+void FindDialog::findClicked()
+{
+ QString text = lineEdit->text();
+
+ if (text.isEmpty()) {
+ QMessageBox::information(this, tr("Empty Field"),
+ tr("Please enter a name."));
+ return;
+ } else {
+ findText = text;
+ lineEdit->clear();
+ hide();
+ }
+}
+//! [findClicked() function]
+//! [getFindText() function]
+QString FindDialog::getFindText()
+{
+ return findText;
+}
+//! [getFindText() function]
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part5/finddialog.h b/tests/manual/examples/widgets/tutorials/addressbook/part5/finddialog.h
new file mode 100644
index 0000000000..7cedcffa60
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part5/finddialog.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef FINDDIALOG_H
+#define FINDDIALOG_H
+//! [FindDialog header]
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+class QLineEdit;
+class QPushButton;
+QT_END_NAMESPACE
+
+class FindDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ FindDialog(QWidget *parent = nullptr);
+ QString getFindText();
+
+public slots:
+ void findClicked();
+
+private:
+ QPushButton *findButton;
+ QLineEdit *lineEdit;
+ QString findText;
+};
+//! [FindDialog header]
+#endif
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part5/main.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part5/main.cpp
new file mode 100644
index 0000000000..1f3aac3397
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part5/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "addressbook.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ AddressBook addressBook;
+ addressBook.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part5/part5.pro b/tests/manual/examples/widgets/tutorials/addressbook/part5/part5.pro
new file mode 100644
index 0000000000..da5469f655
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part5/part5.pro
@@ -0,0 +1,13 @@
+QT += widgets
+
+SOURCES = addressbook.cpp \
+ finddialog.cpp \
+ main.cpp
+HEADERS = addressbook.h \
+ finddialog.h
+
+QMAKE_PROJECT_NAME = ab_part5
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook/part5
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part6/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/part6/CMakeLists.txt
new file mode 100644
index 0000000000..007e0c5171
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part6/CMakeLists.txt
@@ -0,0 +1,38 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(part6 LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/tutorials/addressbook/part6")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(part6
+ addressbook.cpp addressbook.h
+ finddialog.cpp finddialog.h
+ main.cpp
+)
+
+set_target_properties(part6 PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(part6 PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS part6
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part6/addressbook.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part6/addressbook.cpp
new file mode 100644
index 0000000000..455ccc899a
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part6/addressbook.cpp
@@ -0,0 +1,366 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "addressbook.h"
+
+AddressBook::AddressBook(QWidget *parent)
+ : QWidget(parent)
+{
+ QLabel *nameLabel = new QLabel(tr("Name:"));
+ nameLine = new QLineEdit;
+ nameLine->setReadOnly(true);
+
+ QLabel *addressLabel = new QLabel(tr("Address:"));
+ addressText = new QTextEdit;
+ addressText->setReadOnly(true);
+
+ addButton = new QPushButton(tr("&Add"));
+
+ editButton = new QPushButton(tr("&Edit"));
+ editButton->setEnabled(false);
+ removeButton = new QPushButton(tr("&Remove"));
+ removeButton->setEnabled(false);
+ findButton = new QPushButton(tr("&Find"));
+ findButton->setEnabled(false);
+ submitButton = new QPushButton(tr("&Submit"));
+ submitButton->hide();
+ cancelButton = new QPushButton(tr("&Cancel"));
+ cancelButton->hide();
+
+ nextButton = new QPushButton(tr("&Next"));
+ nextButton->setEnabled(false);
+ previousButton = new QPushButton(tr("&Previous"));
+ previousButton->setEnabled(false);
+
+ loadButton = new QPushButton(tr("&Load..."));
+//! [tooltip 1]
+ loadButton->setToolTip(tr("Load contacts from a file"));
+//! [tooltip 1]
+ saveButton = new QPushButton(tr("&Save..."));
+//! [tooltip 2]
+ saveButton->setToolTip(tr("Save contacts to a file"));
+//! [tooltip 2]
+ saveButton->setEnabled(false);
+
+ dialog = new FindDialog(this);
+
+ connect(addButton, &QPushButton::clicked,
+ this, &AddressBook::addContact);
+ connect(submitButton, &QPushButton::clicked,
+ this, &AddressBook::submitContact);
+ connect(editButton, &QPushButton::clicked,
+ this, &AddressBook::editContact);
+ connect(removeButton, &QPushButton::clicked,
+ this, &AddressBook::removeContact);
+ connect(cancelButton, &QPushButton::clicked,
+ this, &AddressBook::cancel);
+ connect(findButton, &QPushButton::clicked,
+ this, &AddressBook::findContact);
+ connect(nextButton, &QPushButton::clicked,
+ this, &AddressBook::next);
+ connect(previousButton, &QPushButton::clicked,
+ this, &AddressBook::previous);
+ connect(loadButton, &QPushButton::clicked,
+ this, &AddressBook::loadFromFile);
+ connect(saveButton, &QPushButton::clicked,
+ this, &AddressBook::saveToFile);
+
+ QVBoxLayout *buttonLayout1 = new QVBoxLayout;
+ buttonLayout1->addWidget(addButton);
+ buttonLayout1->addWidget(editButton);
+ buttonLayout1->addWidget(removeButton);
+ buttonLayout1->addWidget(findButton);
+ buttonLayout1->addWidget(submitButton);
+ buttonLayout1->addWidget(cancelButton);
+ buttonLayout1->addWidget(loadButton);
+ buttonLayout1->addWidget(saveButton);
+ buttonLayout1->addStretch();
+
+ QHBoxLayout *buttonLayout2 = new QHBoxLayout;
+ buttonLayout2->addWidget(previousButton);
+ buttonLayout2->addWidget(nextButton);
+
+ QGridLayout *mainLayout = new QGridLayout;
+ mainLayout->addWidget(nameLabel, 0, 0);
+ mainLayout->addWidget(nameLine, 0, 1);
+ mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop);
+ mainLayout->addWidget(addressText, 1, 1);
+ mainLayout->addLayout(buttonLayout1, 1, 2);
+ mainLayout->addLayout(buttonLayout2, 2, 1);
+
+ setLayout(mainLayout);
+ setWindowTitle(tr("Simple Address Book"));
+}
+
+void AddressBook::addContact()
+{
+ oldName = nameLine->text();
+ oldAddress = addressText->toPlainText();
+
+ nameLine->clear();
+ addressText->clear();
+
+ updateInterface(AddingMode);
+}
+
+void AddressBook::editContact()
+{
+ oldName = nameLine->text();
+ oldAddress = addressText->toPlainText();
+
+ updateInterface(EditingMode);
+}
+
+void AddressBook::submitContact()
+{
+ QString name = nameLine->text();
+ QString address = addressText->toPlainText();
+
+ if (name.isEmpty() || address.isEmpty()) {
+ QMessageBox::information(this, tr("Empty Field"),
+ tr("Please enter a name and address."));
+ return;
+ }
+
+ if (currentMode == AddingMode) {
+
+ if (!contacts.contains(name)) {
+ contacts.insert(name, address);
+ QMessageBox::information(this, tr("Add Successful"),
+ tr("\"%1\" has been added to your address book.").arg(name));
+ } else {
+ QMessageBox::information(this, tr("Add Unsuccessful"),
+ tr("Sorry, \"%1\" is already in your address book.").arg(name));
+ }
+ } else if (currentMode == EditingMode) {
+
+ if (oldName != name) {
+ if (!contacts.contains(name)) {
+ QMessageBox::information(this, tr("Edit Successful"),
+ tr("\"%1\" has been edited in your address book.").arg(oldName));
+ contacts.remove(oldName);
+ contacts.insert(name, address);
+ } else {
+ QMessageBox::information(this, tr("Edit Unsuccessful"),
+ tr("Sorry, \"%1\" is already in your address book.").arg(name));
+ }
+ } else if (oldAddress != address) {
+ QMessageBox::information(this, tr("Edit Successful"),
+ tr("\"%1\" has been edited in your address book.").arg(name));
+ contacts[name] = address;
+ }
+ }
+
+ updateInterface(NavigationMode);
+}
+
+void AddressBook::cancel()
+{
+ nameLine->setText(oldName);
+ addressText->setText(oldAddress);
+ updateInterface(NavigationMode);
+}
+
+void AddressBook::removeContact()
+{
+ QString name = nameLine->text();
+ QString address = addressText->toPlainText();
+
+ if (contacts.contains(name)) {
+
+ int button = QMessageBox::question(this,
+ tr("Confirm Remove"),
+ tr("Are you sure you want to remove \"%1\"?").arg(name),
+ QMessageBox::Yes | QMessageBox::No);
+
+ if (button == QMessageBox::Yes) {
+
+ previous();
+ contacts.remove(name);
+
+ QMessageBox::information(this, tr("Remove Successful"),
+ tr("\"%1\" has been removed from your address book.").arg(name));
+ }
+ }
+
+ updateInterface(NavigationMode);
+}
+
+void AddressBook::next()
+{
+ QString name = nameLine->text();
+ QMap<QString, QString>::iterator i = contacts.find(name);
+
+ if (i != contacts.end())
+ i++;
+
+ if (i == contacts.end())
+ i = contacts.begin();
+
+ nameLine->setText(i.key());
+ addressText->setText(i.value());
+}
+
+void AddressBook::previous()
+{
+ QString name = nameLine->text();
+ QMap<QString, QString>::iterator i = contacts.find(name);
+
+ if (i == contacts.end()) {
+ nameLine->clear();
+ addressText->clear();
+ return;
+ }
+
+ if (i == contacts.begin())
+ i = contacts.end();
+
+ i--;
+ nameLine->setText(i.key());
+ addressText->setText(i.value());
+}
+
+void AddressBook::findContact()
+{
+ dialog->show();
+
+ if (dialog->exec() == 1) {
+ QString contactName = dialog->getFindText();
+
+ if (contacts.contains(contactName)) {
+ nameLine->setText(contactName);
+ addressText->setText(contacts.value(contactName));
+ } else {
+ QMessageBox::information(this, tr("Contact Not Found"),
+ tr("Sorry, \"%1\" is not in your address book.").arg(contactName));
+ return;
+ }
+ }
+
+ updateInterface(NavigationMode);
+}
+
+void AddressBook::updateInterface(Mode mode)
+{
+ currentMode = mode;
+
+ switch (currentMode) {
+
+ case AddingMode:
+ case EditingMode:
+
+ nameLine->setReadOnly(false);
+ nameLine->setFocus(Qt::OtherFocusReason);
+ addressText->setReadOnly(false);
+
+ addButton->setEnabled(false);
+ editButton->setEnabled(false);
+ removeButton->setEnabled(false);
+
+ nextButton->setEnabled(false);
+ previousButton->setEnabled(false);
+
+ submitButton->show();
+ cancelButton->show();
+
+ loadButton->setEnabled(false);
+ saveButton->setEnabled(false);
+ break;
+
+ case NavigationMode:
+
+ if (contacts.isEmpty()) {
+ nameLine->clear();
+ addressText->clear();
+ }
+
+ nameLine->setReadOnly(true);
+ addressText->setReadOnly(true);
+ addButton->setEnabled(true);
+
+ int number = contacts.size();
+ editButton->setEnabled(number >= 1);
+ removeButton->setEnabled(number >= 1);
+ findButton->setEnabled(number > 2);
+ nextButton->setEnabled(number > 1);
+ previousButton->setEnabled(number > 1);
+
+ submitButton->hide();
+ cancelButton->hide();
+
+ loadButton->setEnabled(true);
+ saveButton->setEnabled(number >= 1);
+ break;
+ }
+}
+
+//! [saveToFile() function part1]
+void AddressBook::saveToFile()
+{
+ QString fileName = QFileDialog::getSaveFileName(this,
+ tr("Save Address Book"), "",
+ tr("Address Book (*.abk);;All Files (*)"));
+
+//! [saveToFile() function part1]
+//! [saveToFile() function part2]
+ if (fileName.isEmpty())
+ return;
+ else {
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly)) {
+ QMessageBox::information(this, tr("Unable to open file"),
+ file.errorString());
+ return;
+ }
+
+//! [saveToFile() function part2]
+//! [saveToFile() function part3]
+ QDataStream out(&file);
+ out.setVersion(QDataStream::Qt_4_5);
+ out << contacts;
+ }
+}
+//! [saveToFile() function part3]
+
+//! [loadFromFile() function part1]
+void AddressBook::loadFromFile()
+{
+ QString fileName = QFileDialog::getOpenFileName(this,
+ tr("Open Address Book"), "",
+ tr("Address Book (*.abk);;All Files (*)"));
+//! [loadFromFile() function part1]
+
+//! [loadFromFile() function part2]
+ if (fileName.isEmpty())
+ return;
+ else {
+
+ QFile file(fileName);
+
+ if (!file.open(QIODevice::ReadOnly)) {
+ QMessageBox::information(this, tr("Unable to open file"),
+ file.errorString());
+ return;
+ }
+
+ QDataStream in(&file);
+ in.setVersion(QDataStream::Qt_4_5);
+ contacts.clear(); // clear existing contacts
+ in >> contacts;
+//! [loadFromFile() function part2]
+
+//! [loadFromFile() function part3]
+ if (contacts.isEmpty()) {
+ QMessageBox::information(this, tr("No contacts in file"),
+ tr("The file you are attempting to open contains no contacts."));
+ } else {
+ QMap<QString, QString>::iterator i = contacts.begin();
+ nameLine->setText(i.key());
+ addressText->setText(i.value());
+ }
+ }
+
+ updateInterface(NavigationMode);
+}
+//! [loadFromFile() function part3]
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part6/addressbook.h b/tests/manual/examples/widgets/tutorials/addressbook/part6/addressbook.h
new file mode 100644
index 0000000000..26389d7446
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part6/addressbook.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef ADDRESSBOOK_H
+#define ADDRESSBOOK_H
+
+#include <QWidget>
+#include <QMap>
+#include "finddialog.h"
+
+QT_BEGIN_NAMESPACE
+class QPushButton;
+class QLabel;
+class QLineEdit;
+class QTextEdit;
+QT_END_NAMESPACE
+
+
+class AddressBook : public QWidget
+{
+ Q_OBJECT
+
+public:
+ AddressBook(QWidget *parent = nullptr);
+ enum Mode { NavigationMode, AddingMode, EditingMode };
+
+public slots:
+ void addContact();
+ void editContact();
+ void submitContact();
+ void cancel();
+ void removeContact();
+ void findContact();
+ void next();
+ void previous();
+//! [save and load functions declaration]
+ void saveToFile();
+ void loadFromFile();
+//! [save and load functions declaration]
+
+private:
+ void updateInterface(Mode mode);
+
+ QPushButton *addButton;
+ QPushButton *editButton;
+ QPushButton *removeButton;
+ QPushButton *findButton;
+ QPushButton *submitButton;
+ QPushButton *cancelButton;
+ QPushButton *nextButton;
+ QPushButton *previousButton;
+//! [save and load buttons declaration]
+ QPushButton *loadButton;
+ QPushButton *saveButton;
+//! [save and load buttons declaration]
+ QLineEdit *nameLine;
+ QTextEdit *addressText;
+
+ QMap<QString, QString> contacts;
+ FindDialog *dialog;
+ QString oldName;
+ QString oldAddress;
+ Mode currentMode;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part6/finddialog.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part6/finddialog.cpp
new file mode 100644
index 0000000000..90729d9c12
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part6/finddialog.cpp
@@ -0,0 +1,47 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "finddialog.h"
+
+FindDialog::FindDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ QLabel *findLabel = new QLabel(tr("Enter the name of a contact:"));
+ lineEdit = new QLineEdit;
+
+ findButton = new QPushButton(tr("&Find"));
+ findText = "";
+
+ QHBoxLayout *layout = new QHBoxLayout;
+ layout->addWidget(findLabel);
+ layout->addWidget(lineEdit);
+ layout->addWidget(findButton);
+
+ setLayout(layout);
+ setWindowTitle(tr("Find a Contact"));
+ connect(findButton, &QPushButton::clicked,
+ this, &FindDialog::findClicked);
+ connect(findButton, &QPushButton::clicked,
+ this, &FindDialog::accept);
+}
+
+void FindDialog::findClicked()
+{
+ QString text = lineEdit->text();
+
+ if (text.isEmpty()) {
+ QMessageBox::information(this, tr("Empty Field"),
+ tr("Please enter a name."));
+ return;
+ } else {
+ findText = text;
+ lineEdit->clear();
+ hide();
+ }
+}
+
+QString FindDialog::getFindText()
+{
+ return findText;
+}
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part6/finddialog.h b/tests/manual/examples/widgets/tutorials/addressbook/part6/finddialog.h
new file mode 100644
index 0000000000..7c9a3af30f
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part6/finddialog.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef FINDDIALOG_H
+#define FINDDIALOG_H
+
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+class QLineEdit;
+class QPushButton;
+QT_END_NAMESPACE
+
+class FindDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ FindDialog(QWidget *parent = nullptr);
+ QString getFindText();
+
+public slots:
+ void findClicked();
+
+private:
+ QPushButton *findButton;
+ QLineEdit *lineEdit;
+ QString findText;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part6/main.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part6/main.cpp
new file mode 100644
index 0000000000..1f3aac3397
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part6/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "addressbook.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ AddressBook addressBook;
+ addressBook.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part6/part6.pro b/tests/manual/examples/widgets/tutorials/addressbook/part6/part6.pro
new file mode 100644
index 0000000000..6796f30a73
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part6/part6.pro
@@ -0,0 +1,14 @@
+QT += widgets
+requires(qtConfig(filedialog))
+
+SOURCES = addressbook.cpp \
+ finddialog.cpp \
+ main.cpp
+HEADERS = addressbook.h \
+ finddialog.h
+
+QMAKE_PROJECT_NAME = ab_part6
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook/part6
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part7/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/part7/CMakeLists.txt
new file mode 100644
index 0000000000..f480e22c21
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part7/CMakeLists.txt
@@ -0,0 +1,38 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(part7 LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/tutorials/addressbook/part7")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(part7
+ addressbook.cpp addressbook.h
+ finddialog.cpp finddialog.h
+ main.cpp
+)
+
+set_target_properties(part7 PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(part7 PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS part7
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part7/addressbook.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part7/addressbook.cpp
new file mode 100644
index 0000000000..30878d7bbc
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part7/addressbook.cpp
@@ -0,0 +1,419 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "addressbook.h"
+
+AddressBook::AddressBook(QWidget *parent)
+ : QWidget(parent)
+{
+ QLabel *nameLabel = new QLabel(tr("Name:"));
+ nameLine = new QLineEdit;
+ nameLine->setReadOnly(true);
+
+ QLabel *addressLabel = new QLabel(tr("Address:"));
+ addressText = new QTextEdit;
+ addressText->setReadOnly(true);
+
+ addButton = new QPushButton(tr("&Add"));
+
+ editButton = new QPushButton(tr("&Edit"));
+ editButton->setEnabled(false);
+ removeButton = new QPushButton(tr("&Remove"));
+ removeButton->setEnabled(false);
+ findButton = new QPushButton(tr("&Find"));
+ findButton->setEnabled(false);
+ submitButton = new QPushButton(tr("&Submit"));
+ submitButton->hide();
+ cancelButton = new QPushButton(tr("&Cancel"));
+ cancelButton->hide();
+
+ nextButton = new QPushButton(tr("&Next"));
+ nextButton->setEnabled(false);
+ previousButton = new QPushButton(tr("&Previous"));
+ previousButton->setEnabled(false);
+
+ loadButton = new QPushButton(tr("&Load..."));
+ loadButton->setToolTip(tr("Load contacts from a file"));
+ saveButton = new QPushButton(tr("&Save..."));
+ saveButton->setToolTip(tr("Save contacts to a file"));
+ saveButton->setEnabled(false);
+
+ exportButton = new QPushButton(tr("E&xport"));
+ exportButton->setToolTip(tr("Export as vCard"));
+ exportButton->setEnabled(false);
+
+ dialog = new FindDialog(this);
+
+ connect(addButton, &QPushButton::clicked,
+ this, &AddressBook::addContact);
+ connect(submitButton, &QPushButton::clicked,
+ this, &AddressBook::submitContact);
+ connect(editButton, &QPushButton::clicked,
+ this, &AddressBook::editContact);
+ connect(removeButton, &QPushButton::clicked,
+ this, &AddressBook::removeContact);
+ connect(cancelButton, &QPushButton::clicked,
+ this, &AddressBook::cancel);
+ connect(findButton, &QPushButton::clicked,
+ this, &AddressBook::findContact);
+ connect(nextButton, &QPushButton::clicked,
+ this, &AddressBook::next);
+ connect(previousButton, &QPushButton::clicked,
+ this, &AddressBook::previous);
+ connect(loadButton, &QPushButton::clicked,
+ this, &AddressBook::loadFromFile);
+ connect(saveButton, &QPushButton::clicked,
+ this, &AddressBook::saveToFile);
+ connect(exportButton, &QPushButton::clicked,
+ this, &AddressBook::exportAsVCard);
+
+ QVBoxLayout *buttonLayout1 = new QVBoxLayout;
+ buttonLayout1->addWidget(addButton);
+ buttonLayout1->addWidget(editButton);
+ buttonLayout1->addWidget(removeButton);
+ buttonLayout1->addWidget(findButton);
+ buttonLayout1->addWidget(submitButton);
+ buttonLayout1->addWidget(cancelButton);
+ buttonLayout1->addWidget(loadButton);
+ buttonLayout1->addWidget(saveButton);
+ buttonLayout1->addWidget(exportButton);
+ buttonLayout1->addStretch();
+
+ QHBoxLayout *buttonLayout2 = new QHBoxLayout;
+ buttonLayout2->addWidget(previousButton);
+ buttonLayout2->addWidget(nextButton);
+
+ QGridLayout *mainLayout = new QGridLayout;
+ mainLayout->addWidget(nameLabel, 0, 0);
+ mainLayout->addWidget(nameLine, 0, 1);
+ mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop);
+ mainLayout->addWidget(addressText, 1, 1);
+ mainLayout->addLayout(buttonLayout1, 1, 2);
+ mainLayout->addLayout(buttonLayout2, 2, 1);
+
+ setLayout(mainLayout);
+ setWindowTitle(tr("Simple Address Book"));
+}
+
+void AddressBook::addContact()
+{
+ oldName = nameLine->text();
+ oldAddress = addressText->toPlainText();
+
+ nameLine->clear();
+ addressText->clear();
+
+ updateInterface(AddingMode);
+}
+
+void AddressBook::editContact()
+{
+ oldName = nameLine->text();
+ oldAddress = addressText->toPlainText();
+
+ updateInterface(EditingMode);
+}
+
+void AddressBook::submitContact()
+{
+ QString name = nameLine->text();
+ QString address = addressText->toPlainText();
+
+ if (name.isEmpty() || address.isEmpty()) {
+ QMessageBox::information(this, tr("Empty Field"),
+ tr("Please enter a name and address."));
+ return;
+ }
+
+ if (currentMode == AddingMode) {
+
+ if (!contacts.contains(name)) {
+ contacts.insert(name, address);
+ QMessageBox::information(this, tr("Add Successful"),
+ tr("\"%1\" has been added to your address book.").arg(name));
+ } else {
+ QMessageBox::information(this, tr("Add Unsuccessful"),
+ tr("Sorry, \"%1\" is already in your address book.").arg(name));
+ }
+ } else if (currentMode == EditingMode) {
+
+ if (oldName != name) {
+ if (!contacts.contains(name)) {
+ QMessageBox::information(this, tr("Edit Successful"),
+ tr("\"%1\" has been edited in your address book.").arg(oldName));
+ contacts.remove(oldName);
+ contacts.insert(name, address);
+ } else {
+ QMessageBox::information(this, tr("Edit Unsuccessful"),
+ tr("Sorry, \"%1\" is already in your address book.").arg(name));
+ }
+ } else if (oldAddress != address) {
+ QMessageBox::information(this, tr("Edit Successful"),
+ tr("\"%1\" has been edited in your address book.").arg(name));
+ contacts[name] = address;
+ }
+ }
+
+ updateInterface(NavigationMode);
+}
+
+void AddressBook::cancel()
+{
+ nameLine->setText(oldName);
+ addressText->setText(oldAddress);
+ updateInterface(NavigationMode);
+}
+
+void AddressBook::removeContact()
+{
+ QString name = nameLine->text();
+ QString address = addressText->toPlainText();
+
+ if (contacts.contains(name)) {
+
+ int button = QMessageBox::question(this,
+ tr("Confirm Remove"),
+ tr("Are you sure you want to remove \"%1\"?").arg(name),
+ QMessageBox::Yes | QMessageBox::No);
+
+ if (button == QMessageBox::Yes) {
+
+ previous();
+ contacts.remove(name);
+
+ QMessageBox::information(this, tr("Remove Successful"),
+ tr("\"%1\" has been removed from your address book.").arg(name));
+ }
+ }
+
+ updateInterface(NavigationMode);
+}
+
+void AddressBook::next()
+{
+ QString name = nameLine->text();
+ QMap<QString, QString>::iterator i = contacts.find(name);
+
+ if (i != contacts.end())
+ i++;
+
+ if (i == contacts.end())
+ i = contacts.begin();
+
+ nameLine->setText(i.key());
+ addressText->setText(i.value());
+}
+
+void AddressBook::previous()
+{
+ QString name = nameLine->text();
+ QMap<QString, QString>::iterator i = contacts.find(name);
+
+ if (i == contacts.end()) {
+ nameLine->clear();
+ addressText->clear();
+ return;
+ }
+
+ if (i == contacts.begin())
+ i = contacts.end();
+
+ i--;
+ nameLine->setText(i.key());
+ addressText->setText(i.value());
+}
+
+void AddressBook::findContact()
+{
+ dialog->show();
+
+ if (dialog->exec() == 1) {
+ QString contactName = dialog->getFindText();
+
+ if (contacts.contains(contactName)) {
+ nameLine->setText(contactName);
+ addressText->setText(contacts.value(contactName));
+ } else {
+ QMessageBox::information(this, tr("Contact Not Found"),
+ tr("Sorry, \"%1\" is not in your address book.").arg(contactName));
+ return;
+ }
+ }
+
+ updateInterface(NavigationMode);
+}
+void AddressBook::updateInterface(Mode mode)
+{
+ currentMode = mode;
+
+ switch (currentMode) {
+
+ case AddingMode:
+ case EditingMode:
+
+ nameLine->setReadOnly(false);
+ nameLine->setFocus(Qt::OtherFocusReason);
+ addressText->setReadOnly(false);
+
+ addButton->setEnabled(false);
+ editButton->setEnabled(false);
+ removeButton->setEnabled(false);
+
+ nextButton->setEnabled(false);
+ previousButton->setEnabled(false);
+
+ submitButton->show();
+ cancelButton->show();
+
+ loadButton->setEnabled(false);
+ saveButton->setEnabled(false);
+ exportButton->setEnabled(false);
+ break;
+
+ case NavigationMode:
+
+ if (contacts.isEmpty()) {
+ nameLine->clear();
+ addressText->clear();
+ }
+
+ nameLine->setReadOnly(true);
+ addressText->setReadOnly(true);
+ addButton->setEnabled(true);
+
+ int number = contacts.size();
+ editButton->setEnabled(number >= 1);
+ removeButton->setEnabled(number >= 1);
+ findButton->setEnabled(number > 2);
+ nextButton->setEnabled(number > 1);
+ previousButton->setEnabled(number > 1);
+
+ submitButton->hide();
+ cancelButton->hide();
+
+ exportButton->setEnabled(number >= 1);
+
+ loadButton->setEnabled(true);
+ saveButton->setEnabled(number >= 1);
+ break;
+ }
+}
+
+void AddressBook::saveToFile()
+{
+ QString fileName = QFileDialog::getSaveFileName(this,
+ tr("Save Address Book"), "",
+ tr("Address Book (*.abk);;All Files (*)"));
+
+ if (fileName.isEmpty())
+ return;
+ else {
+ QFile file(fileName);
+
+ if (!file.open(QIODevice::WriteOnly)) {
+ QMessageBox::information(this, tr("Unable to open file"),
+ file.errorString());
+ return;
+ }
+
+ QDataStream out(&file);
+ out.setVersion(QDataStream::Qt_4_3);
+ out << contacts;
+ }
+
+ updateInterface(NavigationMode);
+}
+
+void AddressBook::loadFromFile()
+{
+ QString fileName = QFileDialog::getOpenFileName(this,
+ tr("Open Address Book"), "",
+ tr("Address Book (*.abk);;All Files (*)"));
+
+ if (fileName.isEmpty())
+ return;
+ else {
+ QFile file(fileName);
+
+ if (!file.open(QIODevice::ReadOnly)) {
+ QMessageBox::information(this, tr("Unable to open file"),
+ file.errorString());
+ return;
+ }
+
+ QDataStream in(&file);
+ in.setVersion(QDataStream::Qt_4_3);
+ in >> contacts;
+
+ QMap<QString, QString>::iterator i = contacts.begin();
+ nameLine->setText(i.key());
+ addressText->setText(i.value());
+ }
+
+ updateInterface(NavigationMode);
+}
+
+//! [export function part1]
+void AddressBook::exportAsVCard()
+{
+ QString name = nameLine->text();
+ QString address = addressText->toPlainText();
+ QString firstName;
+ QString lastName;
+ QStringList nameList;
+
+ int index = name.indexOf(" ");
+
+ if (index != -1) {
+ nameList = name.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
+ firstName = nameList.first();
+ lastName = nameList.last();
+ } else {
+ firstName = name;
+ lastName = "";
+ }
+
+ QString fileName = QFileDialog::getSaveFileName(this,
+ tr("Export Contact"), "",
+ tr("vCard Files (*.vcf);;All Files (*)"));
+
+ if (fileName.isEmpty())
+ return;
+
+ QFile file(fileName);
+//! [export function part1]
+
+//! [export function part2]
+ if (!file.open(QIODevice::WriteOnly)) {
+ QMessageBox::information(this, tr("Unable to open file"),
+ file.errorString());
+ return;
+ }
+
+ QTextStream out(&file);
+//! [export function part2]
+
+//! [export function part3]
+ out << "BEGIN:VCARD" << '\n';
+ out << "VERSION:2.1" << '\n';
+ out << "N:" << lastName << ';' << firstName << '\n';
+
+ if (!nameList.isEmpty())
+ out << "FN:" << nameList.join(' ') << '\n';
+ else
+ out << "FN:" << firstName << '\n';
+//! [export function part3]
+
+//! [export function part4]
+ address.replace(";", "\\;", Qt::CaseInsensitive);
+ address.replace('\n', ";", Qt::CaseInsensitive);
+ address.replace(",", " ", Qt::CaseInsensitive);
+
+ out << "ADR;HOME:;" << address << '\n';
+ out << "END:VCARD" << '\n';
+
+ QMessageBox::information(this, tr("Export Successful"),
+ tr("\"%1\" has been exported as a vCard.").arg(name));
+}
+//! [export function part4]
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part7/addressbook.h b/tests/manual/examples/widgets/tutorials/addressbook/part7/addressbook.h
new file mode 100644
index 0000000000..c408a97642
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part7/addressbook.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef ADDRESSBOOK_H
+#define ADDRESSBOOK_H
+
+#include <QWidget>
+#include <QMap>
+#include "finddialog.h"
+
+QT_BEGIN_NAMESPACE
+class QPushButton;
+class QLabel;
+class QLineEdit;
+class QTextEdit;
+QT_END_NAMESPACE
+
+
+class AddressBook : public QWidget
+{
+ Q_OBJECT
+
+public:
+ AddressBook(QWidget *parent = nullptr);
+ enum Mode { NavigationMode, AddingMode, EditingMode };
+
+public slots:
+ void addContact();
+ void editContact();
+ void submitContact();
+ void cancel();
+ void removeContact();
+ void findContact();
+ void next();
+ void previous();
+ void saveToFile();
+ void loadFromFile();
+//! [exportAsVCard() declaration]
+ void exportAsVCard();
+//! [exportAsVCard() declaration]
+
+private:
+ void updateInterface(Mode mode);
+
+ QPushButton *addButton;
+ QPushButton *editButton;
+ QPushButton *removeButton;
+ QPushButton *findButton;
+ QPushButton *submitButton;
+ QPushButton *cancelButton;
+ QPushButton *nextButton;
+ QPushButton *previousButton;
+ QPushButton *loadButton;
+ QPushButton *saveButton;
+//! [exportButton declaration]
+ QPushButton *exportButton;
+//! [exportButton declaration]
+ QLineEdit *nameLine;
+ QTextEdit *addressText;
+
+ QMap<QString, QString> contacts;
+ FindDialog *dialog;
+ QString oldName;
+ QString oldAddress;
+ Mode currentMode;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part7/finddialog.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part7/finddialog.cpp
new file mode 100644
index 0000000000..90729d9c12
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part7/finddialog.cpp
@@ -0,0 +1,47 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "finddialog.h"
+
+FindDialog::FindDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ QLabel *findLabel = new QLabel(tr("Enter the name of a contact:"));
+ lineEdit = new QLineEdit;
+
+ findButton = new QPushButton(tr("&Find"));
+ findText = "";
+
+ QHBoxLayout *layout = new QHBoxLayout;
+ layout->addWidget(findLabel);
+ layout->addWidget(lineEdit);
+ layout->addWidget(findButton);
+
+ setLayout(layout);
+ setWindowTitle(tr("Find a Contact"));
+ connect(findButton, &QPushButton::clicked,
+ this, &FindDialog::findClicked);
+ connect(findButton, &QPushButton::clicked,
+ this, &FindDialog::accept);
+}
+
+void FindDialog::findClicked()
+{
+ QString text = lineEdit->text();
+
+ if (text.isEmpty()) {
+ QMessageBox::information(this, tr("Empty Field"),
+ tr("Please enter a name."));
+ return;
+ } else {
+ findText = text;
+ lineEdit->clear();
+ hide();
+ }
+}
+
+QString FindDialog::getFindText()
+{
+ return findText;
+}
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part7/finddialog.h b/tests/manual/examples/widgets/tutorials/addressbook/part7/finddialog.h
new file mode 100644
index 0000000000..7c9a3af30f
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part7/finddialog.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef FINDDIALOG_H
+#define FINDDIALOG_H
+
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+class QLineEdit;
+class QPushButton;
+QT_END_NAMESPACE
+
+class FindDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ FindDialog(QWidget *parent = nullptr);
+ QString getFindText();
+
+public slots:
+ void findClicked();
+
+private:
+ QPushButton *findButton;
+ QLineEdit *lineEdit;
+ QString findText;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part7/main.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part7/main.cpp
new file mode 100644
index 0000000000..1f3aac3397
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part7/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets>
+#include "addressbook.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ AddressBook addressBook;
+ addressBook.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part7/part7.pro b/tests/manual/examples/widgets/tutorials/addressbook/part7/part7.pro
new file mode 100644
index 0000000000..6a99799c67
--- /dev/null
+++ b/tests/manual/examples/widgets/tutorials/addressbook/part7/part7.pro
@@ -0,0 +1,14 @@
+QT += widgets
+requires(qtConfig(filedialog))
+
+SOURCES = addressbook.cpp \
+ finddialog.cpp \
+ main.cpp
+HEADERS = addressbook.h \
+ finddialog.h
+
+QMAKE_PROJECT_NAME = ab_part7
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook/part7
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/widgets/charactermap/CMakeLists.txt b/tests/manual/examples/widgets/widgets/charactermap/CMakeLists.txt
new file mode 100644
index 0000000000..f02ac52689
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/charactermap/CMakeLists.txt
@@ -0,0 +1,38 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(charactermap LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/widgets/charactermap")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(charactermap
+ characterwidget.cpp characterwidget.h
+ main.cpp
+ mainwindow.cpp mainwindow.h
+)
+
+set_target_properties(charactermap PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(charactermap PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS charactermap
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/widgets/charactermap/charactermap.pro b/tests/manual/examples/widgets/widgets/charactermap/charactermap.pro
new file mode 100644
index 0000000000..373aabca73
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/charactermap/charactermap.pro
@@ -0,0 +1,12 @@
+QT += widgets
+requires(qtConfig(combobox))
+
+HEADERS = characterwidget.h \
+ mainwindow.h
+SOURCES = characterwidget.cpp \
+ mainwindow.cpp \
+ main.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/widgets/charactermap
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/widgets/charactermap/characterwidget.cpp b/tests/manual/examples/widgets/widgets/charactermap/characterwidget.cpp
new file mode 100644
index 0000000000..6287a44e80
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/charactermap/characterwidget.cpp
@@ -0,0 +1,145 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "characterwidget.h"
+
+#include <QFontDatabase>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QToolTip>
+
+//! [0]
+CharacterWidget::CharacterWidget(QWidget *parent)
+ : QWidget(parent)
+{
+ calculateSquareSize();
+ setMouseTracking(true);
+}
+//! [0]
+
+//! [1]
+void CharacterWidget::updateFont(const QFont &font)
+{
+ displayFont.setFamily(font.family());
+ calculateSquareSize();
+ adjustSize();
+ update();
+}
+//! [1]
+
+//! [2]
+void CharacterWidget::updateSize(const QString &fontSize)
+{
+ displayFont.setPointSize(fontSize.toInt());
+ calculateSquareSize();
+ adjustSize();
+ update();
+}
+//! [2]
+
+void CharacterWidget::updateStyle(const QString &fontStyle)
+{
+ const QFont::StyleStrategy oldStrategy = displayFont.styleStrategy();
+ displayFont = QFontDatabase::font(displayFont.family(), fontStyle, displayFont.pointSize());
+ displayFont.setStyleStrategy(oldStrategy);
+ calculateSquareSize();
+ adjustSize();
+ update();
+}
+
+void CharacterWidget::updateFontMerging(bool enable)
+{
+ if (enable)
+ displayFont.setStyleStrategy(QFont::PreferDefault);
+ else
+ displayFont.setStyleStrategy(QFont::NoFontMerging);
+ adjustSize();
+ update();
+}
+
+void CharacterWidget::calculateSquareSize()
+{
+ squareSize = qMax(16, 4 + QFontMetrics(displayFont, this).height());
+}
+
+//! [3]
+QSize CharacterWidget::sizeHint() const
+{
+ return QSize(columns*squareSize, (65536 / columns) * squareSize);
+}
+//! [3]
+
+//! [4]
+void CharacterWidget::mouseMoveEvent(QMouseEvent *event)
+{
+ QPoint widgetPosition = mapFromGlobal(event->globalPosition().toPoint());
+ uint key = (widgetPosition.y() / squareSize) * columns + widgetPosition.x() / squareSize;
+
+ QString text = QString::fromLatin1("<p>Character: <span style=\"font-size: 24pt; font-family: %1\">").arg(displayFont.family())
+ + QChar(key)
+ + QString::fromLatin1("</span><p>Value: 0x")
+ + QString::number(key, 16);
+ QToolTip::showText(event->globalPosition().toPoint(), text, this);
+}
+//! [4]
+
+//! [5]
+void CharacterWidget::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton) {
+ lastKey = (event->position().toPoint().y() / squareSize) * columns + event->position().toPoint().x() / squareSize;
+ if (QChar(lastKey).category() != QChar::Other_NotAssigned)
+ emit characterSelected(QString(QChar(lastKey)));
+ update();
+ }
+ else
+ QWidget::mousePressEvent(event);
+}
+//! [5]
+
+//! [6]
+void CharacterWidget::paintEvent(QPaintEvent *event)
+{
+ QPainter painter(this);
+ painter.fillRect(event->rect(), QBrush(Qt::white));
+ painter.setFont(displayFont);
+//! [6]
+
+//! [7]
+ QRect redrawRect = event->rect();
+ int beginRow = redrawRect.top() / squareSize;
+ int endRow = redrawRect.bottom() / squareSize;
+ int beginColumn = redrawRect.left() / squareSize;
+ int endColumn = redrawRect.right() / squareSize;
+//! [7]
+
+//! [8]
+ painter.setPen(QPen(Qt::gray));
+ for (int row = beginRow; row <= endRow; ++row) {
+ for (int column = beginColumn; column <= endColumn; ++column) {
+ painter.drawRect(column * squareSize, row * squareSize, squareSize, squareSize);
+ }
+//! [8] //! [9]
+ }
+//! [9]
+
+//! [10]
+ QFontMetrics fontMetrics(displayFont);
+ painter.setPen(QPen(Qt::black));
+ for (int row = beginRow; row <= endRow; ++row) {
+ for (int column = beginColumn; column <= endColumn; ++column) {
+ int key = row * columns + column;
+ painter.setClipRect(column * squareSize, row * squareSize, squareSize, squareSize);
+
+ if (key == lastKey)
+ painter.fillRect(column * squareSize + 1, row * squareSize + 1,
+ squareSize, squareSize, QBrush(Qt::red));
+
+ painter.drawText(column * squareSize + (squareSize / 2) -
+ fontMetrics.horizontalAdvance(QChar(key)) / 2,
+ row * squareSize + 4 + fontMetrics.ascent(),
+ QString(QChar(key)));
+ }
+ }
+}
+//! [10]
diff --git a/tests/manual/examples/widgets/widgets/charactermap/characterwidget.h b/tests/manual/examples/widgets/widgets/charactermap/characterwidget.h
new file mode 100644
index 0000000000..b8aa76d111
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/charactermap/characterwidget.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef CHARACTERWIDGET_H
+#define CHARACTERWIDGET_H
+
+#include <QFont>
+#include <QSize>
+#include <QString>
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QMouseEvent;
+class QPaintEvent;
+QT_END_NAMESPACE
+
+//! [0]
+class CharacterWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ CharacterWidget(QWidget *parent = nullptr);
+ QSize sizeHint() const override;
+
+public slots:
+ void updateFont(const QFont &font);
+ void updateSize(const QString &fontSize);
+ void updateStyle(const QString &fontStyle);
+ void updateFontMerging(bool enable);
+
+signals:
+ void characterSelected(const QString &character);
+
+protected:
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void paintEvent(QPaintEvent *event) override;
+
+private:
+ void calculateSquareSize();
+
+ QFont displayFont;
+ int columns = 16;
+ int lastKey = -1;
+ int squareSize = 0;
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/charactermap/main.cpp b/tests/manual/examples/widgets/widgets/charactermap/main.cpp
new file mode 100644
index 0000000000..7d7cf3e573
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/charactermap/main.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ MainWindow window;
+ window.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/widgets/charactermap/mainwindow.cpp b/tests/manual/examples/widgets/widgets/charactermap/mainwindow.cpp
new file mode 100644
index 0000000000..fcc00f9642
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/charactermap/mainwindow.cpp
@@ -0,0 +1,264 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+#include "characterwidget.h"
+
+#include <QApplication>
+#include <QBoxLayout>
+#include <QCheckBox>
+#include <QClipboard>
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QFontComboBox>
+#include <QLabel>
+#include <QLineEdit>
+#include <QMenuBar>
+#include <QPlainTextEdit>
+#include <QPushButton>
+#include <QScreen>
+#include <QScrollArea>
+#include <QStatusBar>
+#include <QTextStream>
+
+//! [0]
+Q_DECLARE_METATYPE(QFontComboBox::FontFilter)
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent)
+{
+ QMenu *fileMenu = menuBar()->addMenu(tr("File"));
+ fileMenu->addAction(tr("Quit"), this, &QWidget::close);
+ QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
+ helpMenu->addAction(tr("Show Font Info"), this, &MainWindow::showInfo);
+ helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
+
+ QWidget *centralWidget = new QWidget;
+
+ QLabel *filterLabel = new QLabel(tr("Filter:"));
+ filterCombo = new QComboBox;
+ filterCombo->addItem(tr("All"), QVariant::fromValue(QFontComboBox::AllFonts));
+ filterCombo->addItem(tr("Scalable"), QVariant::fromValue(QFontComboBox::ScalableFonts));
+ filterCombo->addItem(tr("Monospaced"), QVariant::fromValue(QFontComboBox::MonospacedFonts));
+ filterCombo->addItem(tr("Proportional"), QVariant::fromValue(QFontComboBox::ProportionalFonts));
+ filterCombo->setCurrentIndex(0);
+ connect(filterCombo, &QComboBox::currentIndexChanged,
+ this, &MainWindow::filterChanged);
+
+ QLabel *fontLabel = new QLabel(tr("Font:"));
+ fontCombo = new QFontComboBox;
+ QLabel *sizeLabel = new QLabel(tr("Size:"));
+ sizeCombo = new QComboBox;
+ QLabel *styleLabel = new QLabel(tr("Style:"));
+ styleCombo = new QComboBox;
+ QLabel *fontMergingLabel = new QLabel(tr("Automatic Font Merging:"));
+ fontMerging = new QCheckBox;
+ fontMerging->setChecked(true);
+
+ scrollArea = new QScrollArea;
+ characterWidget = new CharacterWidget;
+ scrollArea->setWidget(characterWidget);
+//! [0]
+
+//! [1]
+ findStyles(fontCombo->currentFont());
+//! [1]
+ findSizes(fontCombo->currentFont());
+
+//! [2]
+ lineEdit = new QLineEdit;
+ lineEdit->setClearButtonEnabled(true);
+#ifndef QT_NO_CLIPBOARD
+ QPushButton *clipboardButton = new QPushButton(tr("&To clipboard"));
+//! [2]
+
+#endif
+
+//! [4]
+ connect(fontCombo, &QFontComboBox::currentFontChanged,
+ this, &MainWindow::findStyles);
+ connect(fontCombo, &QFontComboBox::currentFontChanged,
+ this, &MainWindow::findSizes);
+ connect(fontCombo, &QFontComboBox::currentFontChanged,
+ characterWidget, &CharacterWidget::updateFont);
+ connect(sizeCombo, &QComboBox::currentTextChanged,
+ characterWidget, &CharacterWidget::updateSize);
+ connect(styleCombo, &QComboBox::currentTextChanged,
+ characterWidget, &CharacterWidget::updateStyle);
+//! [4] //! [5]
+ connect(characterWidget, &CharacterWidget::characterSelected,
+ this, &MainWindow::insertCharacter);
+
+#ifndef QT_NO_CLIPBOARD
+ connect(clipboardButton, &QAbstractButton::clicked, this, &MainWindow::updateClipboard);
+#endif
+//! [5]
+ connect(fontMerging, &QAbstractButton::toggled, characterWidget, &CharacterWidget::updateFontMerging);
+
+//! [6]
+ QHBoxLayout *controlsLayout = new QHBoxLayout;
+ controlsLayout->addWidget(filterLabel);
+ controlsLayout->addWidget(filterCombo, 1);
+ controlsLayout->addWidget(fontLabel);
+ controlsLayout->addWidget(fontCombo, 1);
+ controlsLayout->addWidget(sizeLabel);
+ controlsLayout->addWidget(sizeCombo, 1);
+ controlsLayout->addWidget(styleLabel);
+ controlsLayout->addWidget(styleCombo, 1);
+ controlsLayout->addWidget(fontMergingLabel);
+ controlsLayout->addWidget(fontMerging, 1);
+ controlsLayout->addStretch(1);
+
+ QHBoxLayout *lineLayout = new QHBoxLayout;
+ lineLayout->addWidget(lineEdit, 1);
+ lineLayout->addSpacing(12);
+#ifndef QT_NO_CLIPBOARD
+ lineLayout->addWidget(clipboardButton);
+#endif
+
+ QVBoxLayout *centralLayout = new QVBoxLayout;
+ centralLayout->addLayout(controlsLayout);
+ centralLayout->addWidget(scrollArea, 1);
+ centralLayout->addSpacing(4);
+ centralLayout->addLayout(lineLayout);
+ centralWidget->setLayout(centralLayout);
+
+ setCentralWidget(centralWidget);
+ setWindowTitle(tr("Character Map"));
+}
+//! [6]
+
+//! [7]
+void MainWindow::findStyles(const QFont &font)
+{
+ QString currentItem = styleCombo->currentText();
+ styleCombo->clear();
+//! [7]
+
+//! [8]
+ const QStringList styles = QFontDatabase::styles(font.family());
+ for (const QString &style : styles)
+ styleCombo->addItem(style);
+
+ int styleIndex = styleCombo->findText(currentItem);
+
+ if (styleIndex == -1)
+ styleCombo->setCurrentIndex(0);
+ else
+ styleCombo->setCurrentIndex(styleIndex);
+}
+//! [8]
+
+void MainWindow::filterChanged(int f)
+{
+ const QFontComboBox::FontFilter filter =
+ qvariant_cast<QFontComboBox::FontFilter>(filterCombo->itemData(f));
+ fontCombo->setFontFilters(filter);
+ statusBar()->showMessage(tr("%n font(s) found", nullptr, fontCombo->count()));
+}
+
+void MainWindow::findSizes(const QFont &font)
+{
+ QString currentSize = sizeCombo->currentText();
+
+ {
+ const QSignalBlocker blocker(sizeCombo);
+ // sizeCombo signals are now blocked until end of scope
+ sizeCombo->clear();
+
+ if (QFontDatabase::isSmoothlyScalable(font.family(), QFontDatabase::styleString(font))) {
+ const QList<int> sizes = QFontDatabase::standardSizes();
+ for (const int size : sizes) {
+ sizeCombo->addItem(QVariant(size).toString());
+ sizeCombo->setEditable(true);
+ }
+
+ } else {
+ const QList<int> sizes = QFontDatabase::smoothSizes(font.family(), QFontDatabase::styleString(font));
+ for (const int size : sizes ) {
+ sizeCombo->addItem(QVariant(size).toString());
+ sizeCombo->setEditable(false);
+ }
+ }
+ }
+
+ int sizeIndex = sizeCombo->findText(currentSize);
+
+ if(sizeIndex == -1)
+ sizeCombo->setCurrentIndex(qMax(0, sizeCombo->count() / 3));
+ else
+ sizeCombo->setCurrentIndex(sizeIndex);
+}
+
+//! [9]
+void MainWindow::insertCharacter(const QString &character)
+{
+ lineEdit->insert(character);
+}
+//! [9]
+
+//! [10]
+#ifndef QT_NO_CLIPBOARD
+void MainWindow::updateClipboard()
+{
+//! [11]
+ QGuiApplication::clipboard()->setText(lineEdit->text(), QClipboard::Clipboard);
+//! [11]
+ QGuiApplication::clipboard()->setText(lineEdit->text(), QClipboard::Selection);
+}
+#endif
+
+class FontInfoDialog : public QDialog
+{
+public:
+ explicit FontInfoDialog(QWidget *parent = nullptr);
+
+private:
+ QString text() const;
+};
+
+FontInfoDialog::FontInfoDialog(QWidget *parent) : QDialog(parent)
+{
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ QVBoxLayout *mainLayout = new QVBoxLayout(this);
+ QPlainTextEdit *textEdit = new QPlainTextEdit(text(), this);
+ textEdit->setReadOnly(true);
+ textEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
+ mainLayout->addWidget(textEdit);
+ QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, this);
+ connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+ mainLayout->addWidget(buttonBox);
+}
+
+QString FontInfoDialog::text() const
+{
+ QString text;
+ QTextStream str(&text);
+ const QFont defaultFont = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
+ const QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
+ const QFont titleFont = QFontDatabase::systemFont(QFontDatabase::TitleFont);
+ const QFont smallestReadableFont = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont);
+
+ str << "Qt " << QT_VERSION_STR << " on " << QGuiApplication::platformName()
+ << ", " << logicalDpiX() << "DPI";
+ if (!qFuzzyCompare(devicePixelRatio(), qreal(1)))
+ str << ", device pixel ratio: " << devicePixelRatio();
+ str << "\n\nDefault font : " << defaultFont.family() << ", " << defaultFont.pointSizeF() << "pt\n"
+ << "Fixed font : " << fixedFont.family() << ", " << fixedFont.pointSizeF() << "pt\n"
+ << "Title font : " << titleFont.family() << ", " << titleFont.pointSizeF() << "pt\n"
+ << "Smallest font: " << smallestReadableFont.family() << ", " << smallestReadableFont.pointSizeF() << "pt\n";
+
+ return text;
+}
+
+void MainWindow::showInfo()
+{
+ const QRect screenGeometry = screen()->geometry();
+ FontInfoDialog *dialog = new FontInfoDialog(this);
+ dialog->setWindowTitle(tr("Fonts"));
+ dialog->setAttribute(Qt::WA_DeleteOnClose);
+ dialog->resize(screenGeometry.width() / 4, screenGeometry.height() / 4);
+ dialog->show();
+}
+
+//! [10]
diff --git a/tests/manual/examples/widgets/widgets/charactermap/mainwindow.h b/tests/manual/examples/widgets/widgets/charactermap/mainwindow.h
new file mode 100644
index 0000000000..b8f3c166eb
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/charactermap/mainwindow.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+class QClipboard;
+class QComboBox;
+class QFontComboBox;
+class QLineEdit;
+class QScrollArea;
+class QCheckBox;
+QT_END_NAMESPACE
+class CharacterWidget;
+
+//! [0]
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow(QWidget *parent = nullptr);
+
+public slots:
+ void filterChanged(int);
+ void findStyles(const QFont &font);
+ void findSizes(const QFont &font);
+ void insertCharacter(const QString &character);
+#ifndef QT_NO_CLIPBOARD
+ void updateClipboard();
+#endif
+ void showInfo();
+
+private:
+ CharacterWidget *characterWidget;
+ QComboBox *filterCombo;
+ QComboBox *styleCombo;
+ QComboBox *sizeCombo;
+ QFontComboBox *fontCombo;
+ QLineEdit *lineEdit;
+ QScrollArea *scrollArea;
+ QCheckBox *fontMerging;
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/digitalclock/CMakeLists.txt b/tests/manual/examples/widgets/widgets/digitalclock/CMakeLists.txt
new file mode 100644
index 0000000000..04d9c53ed3
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/digitalclock/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(digitalclock LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/widgets/digitalclock")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(digitalclock
+ digitalclock.cpp digitalclock.h
+ main.cpp
+)
+
+set_target_properties(digitalclock PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(digitalclock PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS digitalclock
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/widgets/digitalclock/digitalclock.cpp b/tests/manual/examples/widgets/widgets/digitalclock/digitalclock.cpp
new file mode 100644
index 0000000000..4eec982c25
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/digitalclock/digitalclock.cpp
@@ -0,0 +1,36 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "digitalclock.h"
+
+#include <QTime>
+#include <QTimer>
+
+//! [0]
+DigitalClock::DigitalClock(QWidget *parent)
+ : QLCDNumber(parent)
+{
+ setSegmentStyle(Filled);
+
+ QTimer *timer = new QTimer(this);
+ connect(timer, &QTimer::timeout, this, &DigitalClock::showTime);
+ timer->start(1000);
+
+ showTime();
+
+ setWindowTitle(tr("Digital Clock"));
+ resize(150, 60);
+}
+//! [0]
+
+//! [1]
+void DigitalClock::showTime()
+//! [1] //! [2]
+{
+ QTime time = QTime::currentTime();
+ QString text = time.toString("hh:mm");
+ if ((time.second() % 2) == 0)
+ text[2] = ' ';
+ display(text);
+}
+//! [2]
diff --git a/tests/manual/examples/widgets/widgets/digitalclock/digitalclock.h b/tests/manual/examples/widgets/widgets/digitalclock/digitalclock.h
new file mode 100644
index 0000000000..7b028cb06e
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/digitalclock/digitalclock.h
@@ -0,0 +1,22 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef DIGITALCLOCK_H
+#define DIGITALCLOCK_H
+
+#include <QLCDNumber>
+
+//! [0]
+class DigitalClock : public QLCDNumber
+{
+ Q_OBJECT
+
+public:
+ DigitalClock(QWidget *parent = nullptr);
+
+private slots:
+ void showTime();
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/digitalclock/digitalclock.pro b/tests/manual/examples/widgets/widgets/digitalclock/digitalclock.pro
new file mode 100644
index 0000000000..4e4bc0f557
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/digitalclock/digitalclock.pro
@@ -0,0 +1,9 @@
+QT += widgets
+
+HEADERS = digitalclock.h
+SOURCES = digitalclock.cpp \
+ main.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/widgets/digitalclock
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/widgets/digitalclock/main.cpp b/tests/manual/examples/widgets/widgets/digitalclock/main.cpp
new file mode 100644
index 0000000000..d930e72f23
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/digitalclock/main.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "digitalclock.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ DigitalClock clock;
+ clock.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/widgets/icons/CMakeLists.txt b/tests/manual/examples/widgets/widgets/icons/CMakeLists.txt
new file mode 100644
index 0000000000..f36e47bef6
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/CMakeLists.txt
@@ -0,0 +1,44 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(icons LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/widgets/icons")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(icons
+ iconpreviewarea.cpp iconpreviewarea.h
+ iconsizespinbox.cpp iconsizespinbox.h
+ imagedelegate.cpp imagedelegate.h
+ main.cpp
+ mainwindow.cpp mainwindow.h
+)
+
+set_target_properties(icons PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_compile_definitions(icons PRIVATE
+ SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}"
+)
+
+target_link_libraries(icons PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS icons
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/widgets/icons/iconpreviewarea.cpp b/tests/manual/examples/widgets/widgets/icons/iconpreviewarea.cpp
new file mode 100644
index 0000000000..42a5fa7914
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/iconpreviewarea.cpp
@@ -0,0 +1,130 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "iconpreviewarea.h"
+
+#include <QGridLayout>
+#include <QLabel>
+#include <QWindow>
+
+//! [0]
+IconPreviewArea::IconPreviewArea(QWidget *parent)
+ : QWidget(parent)
+{
+ QGridLayout *mainLayout = new QGridLayout(this);
+
+ for (int row = 0; row < NumStates; ++row) {
+ stateLabels[row] = createHeaderLabel(IconPreviewArea::iconStateNames().at(row));
+ mainLayout->addWidget(stateLabels[row], row + 1, 0);
+ }
+ Q_ASSERT(NumStates == 2);
+
+ for (int column = 0; column < NumModes; ++column) {
+ modeLabels[column] = createHeaderLabel(IconPreviewArea::iconModeNames().at(column));
+ mainLayout->addWidget(modeLabels[column], 0, column + 1);
+ }
+ Q_ASSERT(NumModes == 4);
+
+ for (int column = 0; column < NumModes; ++column) {
+ for (int row = 0; row < NumStates; ++row) {
+ pixmapLabels[column][row] = createPixmapLabel();
+ mainLayout->addWidget(pixmapLabels[column][row], row + 1, column + 1);
+ }
+ }
+}
+//! [0]
+
+//! [42]
+QList<QIcon::Mode> IconPreviewArea::iconModes()
+{
+ static const QList<QIcon::Mode> result = { QIcon::Normal, QIcon::Active, QIcon::Disabled,
+ QIcon::Selected };
+ return result;
+}
+
+QList<QIcon::State> IconPreviewArea::iconStates()
+{
+ static const QList<QIcon::State> result = { QIcon::Off, QIcon::On };
+ return result;
+}
+
+QStringList IconPreviewArea::iconModeNames()
+{
+ static const QStringList result = {tr("Normal"), tr("Active"), tr("Disabled"), tr("Selected")};
+ return result;
+}
+
+QStringList IconPreviewArea::iconStateNames()
+{
+ static const QStringList result = {tr("Off"), tr("On")};
+ return result;
+}
+//! [42]
+
+//! [1]
+void IconPreviewArea::setIcon(const QIcon &icon)
+{
+ this->icon = icon;
+ updatePixmapLabels();
+}
+//! [1]
+
+//! [2]
+void IconPreviewArea::setSize(const QSize &size)
+{
+ if (size != this->size) {
+ this->size = size;
+ updatePixmapLabels();
+ }
+}
+//! [2]
+
+//! [3]
+QLabel *IconPreviewArea::createHeaderLabel(const QString &text)
+{
+ QLabel *label = new QLabel(tr("<b>%1</b>").arg(text));
+ label->setAlignment(Qt::AlignCenter);
+ return label;
+}
+//! [3]
+
+//! [4]
+QLabel *IconPreviewArea::createPixmapLabel()
+{
+ QLabel *label = new QLabel;
+ label->setEnabled(false);
+ label->setAlignment(Qt::AlignCenter);
+ label->setFrameShape(QFrame::Box);
+ label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ label->setBackgroundRole(QPalette::Base);
+ label->setAutoFillBackground(true);
+ label->setMinimumSize(132, 132);
+ return label;
+}
+//! [4]
+
+//! [5]
+void IconPreviewArea::updatePixmapLabels()
+{
+ for (int column = 0; column < NumModes; ++column) {
+ for (int row = 0; row < NumStates; ++row) {
+ const QPixmap pixmap =
+ icon.pixmap(size, devicePixelRatio(), IconPreviewArea::iconModes().at(column),
+ IconPreviewArea::iconStates().at(row));
+ QLabel *pixmapLabel = pixmapLabels[column][row];
+ pixmapLabel->setPixmap(pixmap);
+ pixmapLabel->setEnabled(!pixmap.isNull());
+ QString toolTip;
+ if (!pixmap.isNull()) {
+ const QSize actualSize = icon.actualSize(size);
+ toolTip =
+ tr("Size: %1x%2\nActual size: %3x%4\nDevice pixel ratio: %5")
+ .arg(size.width()).arg(size.height())
+ .arg(actualSize.width()).arg(actualSize.height())
+ .arg(pixmap.devicePixelRatio());
+ }
+ pixmapLabel->setToolTip(toolTip);
+ }
+ }
+}
+//! [5]
diff --git a/tests/manual/examples/widgets/widgets/icons/iconpreviewarea.h b/tests/manual/examples/widgets/widgets/icons/iconpreviewarea.h
new file mode 100644
index 0000000000..c59ebc42ba
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/iconpreviewarea.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef ICONPREVIEWAREA_H
+#define ICONPREVIEWAREA_H
+
+#include <QIcon>
+#include <QWidget>
+#include <QStringList>
+#include <QList>
+
+QT_BEGIN_NAMESPACE
+class QLabel;
+QT_END_NAMESPACE
+
+//! [0]
+class IconPreviewArea : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit IconPreviewArea(QWidget *parent = nullptr);
+
+ void setIcon(const QIcon &icon);
+ void setSize(const QSize &size);
+
+ static QList<QIcon::Mode> iconModes();
+ static QList<QIcon::State> iconStates();
+ static QStringList iconModeNames();
+ static QStringList iconStateNames();
+
+private:
+ QLabel *createHeaderLabel(const QString &text);
+ QLabel *createPixmapLabel();
+ void updatePixmapLabels();
+
+ enum { NumModes = 4, NumStates = 2 };
+
+ QIcon icon;
+ QSize size;
+ QLabel *stateLabels[NumStates];
+ QLabel *modeLabels[NumModes];
+ QLabel *pixmapLabels[NumModes][NumStates];
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/icons/icons.pro b/tests/manual/examples/widgets/widgets/icons/icons.pro
new file mode 100644
index 0000000000..243ce6b092
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/icons.pro
@@ -0,0 +1,20 @@
+QT += widgets
+requires(qtConfig(combobox))
+
+HEADERS = iconpreviewarea.h \
+ iconsizespinbox.h \
+ imagedelegate.h \
+ mainwindow.h
+SOURCES = iconpreviewarea.cpp \
+ iconsizespinbox.cpp \
+ imagedelegate.cpp \
+ main.cpp \
+ mainwindow.cpp
+
+DEFINES += SRCDIR=\\\"$$PWD/\\\"
+
+EXAMPLE_FILES = images/*
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/widgets/icons
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/widgets/icons/iconsizespinbox.cpp b/tests/manual/examples/widgets/widgets/icons/iconsizespinbox.cpp
new file mode 100644
index 0000000000..16c88f4dd0
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/iconsizespinbox.cpp
@@ -0,0 +1,33 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "iconsizespinbox.h"
+
+#include <QRegularExpression>
+
+//! [0]
+IconSizeSpinBox::IconSizeSpinBox(QWidget *parent)
+ : QSpinBox(parent)
+{
+}
+//! [0]
+
+//! [1]
+int IconSizeSpinBox::valueFromText(const QString &text) const
+{
+ static const QRegularExpression regExp(tr("(\\d+)(\\s*[xx]\\s*\\d+)?"));
+ Q_ASSERT(regExp.isValid());
+
+ const QRegularExpressionMatch match = regExp.match(text);
+ if (match.isValid())
+ return match.captured(1).toInt();
+ return 0;
+}
+//! [1]
+
+//! [2]
+QString IconSizeSpinBox::textFromValue(int value) const
+{
+ return tr("%1 x %1").arg(value);
+}
+//! [2]
diff --git a/tests/manual/examples/widgets/widgets/icons/iconsizespinbox.h b/tests/manual/examples/widgets/widgets/icons/iconsizespinbox.h
new file mode 100644
index 0000000000..ac304b4794
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/iconsizespinbox.h
@@ -0,0 +1,22 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef ICONSIZESPINBOX_H
+#define ICONSIZESPINBOX_H
+
+#include <QSpinBox>
+
+//! [0]
+class IconSizeSpinBox : public QSpinBox
+{
+ Q_OBJECT
+
+public:
+ explicit IconSizeSpinBox(QWidget *parent = nullptr);
+
+ int valueFromText(const QString &text) const override;
+ QString textFromValue(int value) const override;
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/icons/imagedelegate.cpp b/tests/manual/examples/widgets/widgets/icons/imagedelegate.cpp
new file mode 100644
index 0000000000..ec3c227c71
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/imagedelegate.cpp
@@ -0,0 +1,64 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "imagedelegate.h"
+#include "iconpreviewarea.h"
+
+#include <QComboBox>
+
+//! [0]
+ImageDelegate::ImageDelegate(QObject *parent)
+ : QStyledItemDelegate(parent)
+{}
+//! [0]
+
+//! [1]
+QWidget *ImageDelegate::createEditor(QWidget *parent,
+ const QStyleOptionViewItem & /* option */,
+ const QModelIndex &index) const
+{
+ QComboBox *comboBox = new QComboBox(parent);
+ if (index.column() == 1)
+ comboBox->addItems(IconPreviewArea::iconModeNames());
+ else if (index.column() == 2)
+ comboBox->addItems(IconPreviewArea::iconStateNames());
+
+ connect(comboBox, &QComboBox::activated,
+ this, &ImageDelegate::emitCommitData);
+
+ return comboBox;
+}
+//! [1]
+
+//! [2]
+void ImageDelegate::setEditorData(QWidget *editor,
+ const QModelIndex &index) const
+{
+ QComboBox *comboBox = qobject_cast<QComboBox *>(editor);
+ if (!comboBox)
+ return;
+
+ int pos = comboBox->findText(index.model()->data(index).toString(),
+ Qt::MatchExactly);
+ comboBox->setCurrentIndex(pos);
+}
+//! [2]
+
+//! [3]
+void ImageDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
+ const QModelIndex &index) const
+{
+ QComboBox *comboBox = qobject_cast<QComboBox *>(editor);
+ if (!comboBox)
+ return;
+
+ model->setData(index, comboBox->currentText());
+}
+//! [3]
+
+//! [4]
+void ImageDelegate::emitCommitData()
+{
+ emit commitData(qobject_cast<QWidget *>(sender()));
+}
+//! [4]
diff --git a/tests/manual/examples/widgets/widgets/icons/imagedelegate.h b/tests/manual/examples/widgets/widgets/icons/imagedelegate.h
new file mode 100644
index 0000000000..29707e68c6
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/imagedelegate.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef IMAGEDELEGATE_H
+#define IMAGEDELEGATE_H
+
+#include <QStyledItemDelegate>
+
+//! [0]
+class ImageDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+
+public:
+ explicit ImageDelegate(QObject *parent = nullptr);
+//! [0]
+
+//! [1]
+ QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+ void setEditorData(QWidget *editor, const QModelIndex &index) const override;
+ void setModelData(QWidget *editor, QAbstractItemModel *model,
+ const QModelIndex &index) const override;
+
+//! [1] //! [2]
+private slots:
+ void emitCommitData();
+//! [2]
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/icons/images/designer.png b/tests/manual/examples/widgets/widgets/icons/images/designer.png
new file mode 100644
index 0000000000..9f8578b49e
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/images/designer.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/icons/images/find_disabled.png b/tests/manual/examples/widgets/widgets/icons/images/find_disabled.png
new file mode 100644
index 0000000000..e85e33fe05
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/images/find_disabled.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/icons/images/find_normal.png b/tests/manual/examples/widgets/widgets/icons/images/find_normal.png
new file mode 100644
index 0000000000..728c27f905
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/images/find_normal.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/icons/images/monkey_off_128x128.png b/tests/manual/examples/widgets/widgets/icons/images/monkey_off_128x128.png
new file mode 100644
index 0000000000..f878267287
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/images/monkey_off_128x128.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/icons/images/monkey_off_16x16.png b/tests/manual/examples/widgets/widgets/icons/images/monkey_off_16x16.png
new file mode 100644
index 0000000000..0ac57d7d4f
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/images/monkey_off_16x16.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/icons/images/monkey_off_32x32.png b/tests/manual/examples/widgets/widgets/icons/images/monkey_off_32x32.png
new file mode 100644
index 0000000000..1f7fab694c
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/images/monkey_off_32x32.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/icons/images/monkey_off_64x64.png b/tests/manual/examples/widgets/widgets/icons/images/monkey_off_64x64.png
new file mode 100644
index 0000000000..47d8f6de35
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/images/monkey_off_64x64.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/icons/images/monkey_on_128x128.png b/tests/manual/examples/widgets/widgets/icons/images/monkey_on_128x128.png
new file mode 100644
index 0000000000..8e1c7468bf
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/images/monkey_on_128x128.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/icons/images/monkey_on_16x16.png b/tests/manual/examples/widgets/widgets/icons/images/monkey_on_16x16.png
new file mode 100644
index 0000000000..082e8527f1
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/images/monkey_on_16x16.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/icons/images/monkey_on_32x32.png b/tests/manual/examples/widgets/widgets/icons/images/monkey_on_32x32.png
new file mode 100644
index 0000000000..bf9acc6cae
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/images/monkey_on_32x32.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/icons/images/monkey_on_64x64.png b/tests/manual/examples/widgets/widgets/icons/images/monkey_on_64x64.png
new file mode 100644
index 0000000000..990f604d98
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/images/monkey_on_64x64.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/icons/images/qt_extended_16x16.png b/tests/manual/examples/widgets/widgets/icons/images/qt_extended_16x16.png
new file mode 100644
index 0000000000..30bcb45ed2
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/images/qt_extended_16x16.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/icons/images/qt_extended_32x32.png b/tests/manual/examples/widgets/widgets/icons/images/qt_extended_32x32.png
new file mode 100644
index 0000000000..d609c1e1e5
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/images/qt_extended_32x32.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/icons/images/qt_extended_48x48.png b/tests/manual/examples/widgets/widgets/icons/images/qt_extended_48x48.png
new file mode 100644
index 0000000000..0e524fed5f
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/images/qt_extended_48x48.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/icons/main.cpp b/tests/manual/examples/widgets/widgets/icons/main.cpp
new file mode 100644
index 0000000000..b7ed18ff94
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/main.cpp
@@ -0,0 +1,34 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+#include <QCommandLineParser>
+#include <QScreen>
+
+#include "mainwindow.h"
+
+//! [45]
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ QCoreApplication::setApplicationName(MainWindow::tr("Icons"));
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+ QCommandLineParser commandLineParser;
+ commandLineParser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
+ commandLineParser.addHelpOption();
+ commandLineParser.addVersionOption();
+ commandLineParser.addPositionalArgument(MainWindow::tr("[file]"), MainWindow::tr("Icon file(s) to open."));
+ commandLineParser.process(QCoreApplication::arguments());
+
+ MainWindow mainWin;
+ if (!commandLineParser.positionalArguments().isEmpty())
+ mainWin.loadImages(commandLineParser.positionalArguments());
+
+ const QRect availableGeometry = mainWin.screen()->availableGeometry();
+ mainWin.resize(availableGeometry.width() / 2, availableGeometry.height() * 2 / 3);
+ mainWin.move((availableGeometry.width() - mainWin.width()) / 2, (availableGeometry.height() - mainWin.height()) / 2);
+
+ mainWin.show();
+ return app.exec();
+}
+//! [45]
diff --git a/tests/manual/examples/widgets/widgets/icons/mainwindow.cpp b/tests/manual/examples/widgets/widgets/icons/mainwindow.cpp
new file mode 100644
index 0000000000..f893575f76
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/mainwindow.cpp
@@ -0,0 +1,478 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+#include "iconpreviewarea.h"
+#include "iconsizespinbox.h"
+#include "imagedelegate.h"
+
+#include <QActionGroup>
+#include <QApplication>
+#include <QButtonGroup>
+#include <QCheckBox>
+#include <QFileDialog>
+#include <QHeaderView>
+#include <QFormLayout>
+#include <QGridLayout>
+#include <QGroupBox>
+#include <QImageReader>
+#include <QLabel>
+#include <QMenuBar>
+#include <QMessageBox>
+#include <QRadioButton>
+#include <QScreen>
+#include <QStandardPaths>
+#include <QStyleFactory>
+#include <QTableWidget>
+#include <QWindow>
+
+//! [40]
+enum { OtherSize = QStyle::PM_CustomBase };
+//! [40]
+
+//! [0]
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent)
+{
+ QWidget *centralWidget = new QWidget(this);
+ setCentralWidget(centralWidget);
+
+ createActions();
+
+ QGridLayout *mainLayout = new QGridLayout(centralWidget);
+
+ QGroupBox *previewGroupBox = new QGroupBox(tr("Preview"));
+ previewArea = new IconPreviewArea(previewGroupBox);
+ QVBoxLayout *previewLayout = new QVBoxLayout(previewGroupBox);
+ previewLayout->addWidget(previewArea);
+
+ mainLayout->addWidget(previewGroupBox, 0, 0, 1, 2);
+ mainLayout->addWidget(createImagesGroupBox(), 1, 0);
+ QVBoxLayout *vBox = new QVBoxLayout;
+ vBox->addWidget(createIconSizeGroupBox());
+ vBox->addWidget(createHighDpiIconSizeGroupBox());
+ vBox->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
+ mainLayout->addLayout(vBox, 1, 1);
+ createContextMenu();
+
+ setWindowTitle(tr("Icons"));
+ checkCurrentStyle();
+ sizeButtonGroup->button(OtherSize)->click();
+}
+//! [0]
+
+//! [44]
+void MainWindow::show()
+{
+ QMainWindow::show();
+ connect(windowHandle(), &QWindow::screenChanged, this, &MainWindow::screenChanged);
+ screenChanged();
+}
+//! [44]
+
+//! [1]
+void MainWindow::about()
+{
+ QMessageBox::about(this, tr("About Icons"),
+ tr("The <b>Icons</b> example illustrates how Qt renders an icon in "
+ "different modes (active, normal, disabled, and selected) and "
+ "states (on and off) based on a set of images."));
+}
+//! [1]
+
+//! [2]
+void MainWindow::changeStyle(bool checked)
+{
+ if (!checked)
+ return;
+
+ const QAction *action = qobject_cast<QAction *>(sender());
+//! [2] //! [3]
+ QStyle *style = QStyleFactory::create(action->data().toString());
+//! [3] //! [4]
+ Q_ASSERT(style);
+ QApplication::setStyle(style);
+
+ const QList<QAbstractButton*> buttons = sizeButtonGroup->buttons();
+ for (QAbstractButton *button : buttons) {
+ const QStyle::PixelMetric metric = static_cast<QStyle::PixelMetric>(sizeButtonGroup->id(button));
+ const int value = style->pixelMetric(metric);
+ switch (metric) {
+ case QStyle::PM_SmallIconSize:
+ button->setText(tr("Small (%1 x %1)").arg(value));
+ break;
+ case QStyle::PM_LargeIconSize:
+ button->setText(tr("Large (%1 x %1)").arg(value));
+ break;
+ case QStyle::PM_ToolBarIconSize:
+ button->setText(tr("Toolbars (%1 x %1)").arg(value));
+ break;
+ case QStyle::PM_ListViewIconSize:
+ button->setText(tr("List views (%1 x %1)").arg(value));
+ break;
+ case QStyle::PM_IconViewIconSize:
+ button->setText(tr("Icon views (%1 x %1)").arg(value));
+ break;
+ case QStyle::PM_TabBarIconSize:
+ button->setText(tr("Tab bars (%1 x %1)").arg(value));
+ break;
+ default:
+ break;
+ }
+ }
+
+ triggerChangeSize();
+}
+//! [4]
+
+//! [5]
+void MainWindow::changeSize(QAbstractButton *button, bool checked)
+{
+ if (!checked)
+ return;
+
+ const int index = sizeButtonGroup->id(button);
+ const bool other = index == int(OtherSize);
+ const int extent = other
+ ? otherSpinBox->value()
+ : QApplication::style()->pixelMetric(static_cast<QStyle::PixelMetric>(index));
+
+ previewArea->setSize(QSize(extent, extent));
+ otherSpinBox->setEnabled(other);
+}
+
+void MainWindow::triggerChangeSize()
+{
+ changeSize(sizeButtonGroup->checkedButton(), true);
+}
+//! [5]
+
+//! [6]
+void MainWindow::changeIcon()
+{
+ QIcon icon;
+
+ for (int row = 0; row < imagesTable->rowCount(); ++row) {
+ const QTableWidgetItem *fileItem = imagesTable->item(row, 0);
+ const QTableWidgetItem *modeItem = imagesTable->item(row, 1);
+ const QTableWidgetItem *stateItem = imagesTable->item(row, 2);
+
+ if (fileItem->checkState() == Qt::Checked) {
+ const int modeIndex = IconPreviewArea::iconModeNames().indexOf(modeItem->text());
+ Q_ASSERT(modeIndex >= 0);
+ const int stateIndex = IconPreviewArea::iconStateNames().indexOf(stateItem->text());
+ Q_ASSERT(stateIndex >= 0);
+ const QIcon::Mode mode = IconPreviewArea::iconModes().at(modeIndex);
+ const QIcon::State state = IconPreviewArea::iconStates().at(stateIndex);
+//! [6]
+
+//! [8]
+ const QString fileName = fileItem->data(Qt::UserRole).toString();
+ QImage image(fileName);
+ if (!image.isNull())
+ icon.addPixmap(QPixmap::fromImage(image), mode, state);
+//! [8]
+ }
+ }
+//! [11]
+ previewArea->setIcon(icon);
+//! [11]
+}
+
+void MainWindow::addSampleImages()
+{
+ addImages(QLatin1String(SRCDIR) + QLatin1String("/images"));
+}
+
+void MainWindow::addOtherImages()
+{
+ static bool firstInvocation = true;
+ QString directory;
+ if (firstInvocation) {
+ firstInvocation = false;
+ directory = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).value(0, QString());
+ }
+ addImages(directory);
+}
+
+//! [12]
+void MainWindow::addImages(const QString &directory)
+{
+ QFileDialog fileDialog(this, tr("Open Images"), directory);
+ QStringList mimeTypeFilters;
+ const QList<QByteArray> mimeTypes = QImageReader::supportedMimeTypes();
+ for (const QByteArray &mimeTypeName : mimeTypes)
+ mimeTypeFilters.append(mimeTypeName);
+ mimeTypeFilters.sort();
+ fileDialog.setMimeTypeFilters(mimeTypeFilters);
+ fileDialog.selectMimeTypeFilter(QLatin1String("image/png"));
+ fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
+ fileDialog.setFileMode(QFileDialog::ExistingFiles);
+ if (!nativeFileDialogAct->isChecked())
+ fileDialog.setOption(QFileDialog::DontUseNativeDialog);
+ if (fileDialog.exec() == QDialog::Accepted)
+ loadImages(fileDialog.selectedFiles());
+//! [12]
+}
+
+void MainWindow::loadImages(const QStringList &fileNames)
+{
+ for (const QString &fileName : fileNames) {
+ const int row = imagesTable->rowCount();
+ imagesTable->setRowCount(row + 1);
+//! [13]
+ const QFileInfo fileInfo(fileName);
+ const QString imageName = fileInfo.baseName();
+ const QString fileName2x = fileInfo.absolutePath()
+ + QLatin1Char('/') + imageName + QLatin1String("@2x.") + fileInfo.suffix();
+ const QFileInfo fileInfo2x(fileName2x);
+ const QImage image(fileName);
+ const QString toolTip =
+ tr("Directory: %1\nFile: %2\nFile@2x: %3\nSize: %4x%5")
+ .arg(QDir::toNativeSeparators(fileInfo.absolutePath()), fileInfo.fileName())
+ .arg(fileInfo2x.exists() ? fileInfo2x.fileName() : tr("<None>"))
+ .arg(image.width()).arg(image.height());
+ QTableWidgetItem *fileItem = new QTableWidgetItem(imageName);
+ fileItem->setData(Qt::UserRole, fileName);
+ fileItem->setIcon(QPixmap::fromImage(image));
+ fileItem->setFlags((fileItem->flags() | Qt::ItemIsUserCheckable) & ~Qt::ItemIsEditable);
+ fileItem->setToolTip(toolTip);
+//! [13]
+
+//! [15]
+ QIcon::Mode mode = QIcon::Normal;
+ QIcon::State state = QIcon::Off;
+ if (guessModeStateAct->isChecked()) {
+ if (imageName.contains(QLatin1String("_act"), Qt::CaseInsensitive))
+ mode = QIcon::Active;
+ else if (imageName.contains(QLatin1String("_dis"), Qt::CaseInsensitive))
+ mode = QIcon::Disabled;
+ else if (imageName.contains(QLatin1String("_sel"), Qt::CaseInsensitive))
+ mode = QIcon::Selected;
+
+ if (imageName.contains(QLatin1String("_on"), Qt::CaseInsensitive))
+ state = QIcon::On;
+//! [15]
+ }
+
+//! [18]
+ imagesTable->setItem(row, 0, fileItem);
+ QTableWidgetItem *modeItem =
+ new QTableWidgetItem(IconPreviewArea::iconModeNames().at(IconPreviewArea::iconModes().indexOf(mode)));
+ modeItem->setToolTip(toolTip);
+ imagesTable->setItem(row, 1, modeItem);
+ QTableWidgetItem *stateItem =
+ new QTableWidgetItem(IconPreviewArea::iconStateNames().at(IconPreviewArea::iconStates().indexOf(state)));
+ stateItem->setToolTip(toolTip);
+ imagesTable->setItem(row, 2, stateItem);
+ imagesTable->openPersistentEditor(modeItem);
+ imagesTable->openPersistentEditor(stateItem);
+
+ fileItem->setCheckState(Qt::Checked);
+//! [18]
+ }
+}
+
+//! [20]
+void MainWindow::removeAllImages()
+{
+ imagesTable->setRowCount(0);
+ changeIcon();
+}
+//! [20]
+
+//! [21]
+QWidget *MainWindow::createImagesGroupBox()
+{
+ QGroupBox *imagesGroupBox = new QGroupBox(tr("Images"));
+
+ imagesTable = new QTableWidget;
+ imagesTable->setSelectionMode(QAbstractItemView::NoSelection);
+ imagesTable->setItemDelegate(new ImageDelegate(this));
+//! [21]
+
+//! [22]
+ const QStringList labels({tr("Image"), tr("Mode"), tr("State")});
+
+ imagesTable->horizontalHeader()->setDefaultSectionSize(90);
+ imagesTable->setColumnCount(3);
+ imagesTable->setHorizontalHeaderLabels(labels);
+ imagesTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
+ imagesTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
+ imagesTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);
+ imagesTable->verticalHeader()->hide();
+//! [22]
+
+//! [24]
+ connect(imagesTable, &QTableWidget::itemChanged,
+ this, &MainWindow::changeIcon);
+
+ QVBoxLayout *layout = new QVBoxLayout(imagesGroupBox);
+ layout->addWidget(imagesTable);
+ return imagesGroupBox;
+//! [24]
+}
+
+//! [26]
+QWidget *MainWindow::createIconSizeGroupBox()
+{
+ QGroupBox *iconSizeGroupBox = new QGroupBox(tr("Icon Size"));
+
+ sizeButtonGroup = new QButtonGroup(this);
+ sizeButtonGroup->setExclusive(true);
+
+ connect(sizeButtonGroup, &QButtonGroup::buttonToggled,
+ this, &MainWindow::changeSize);
+
+ QRadioButton *smallRadioButton = new QRadioButton;
+ sizeButtonGroup->addButton(smallRadioButton, QStyle::PM_SmallIconSize);
+ QRadioButton *largeRadioButton = new QRadioButton;
+ sizeButtonGroup->addButton(largeRadioButton, QStyle::PM_LargeIconSize);
+ QRadioButton *toolBarRadioButton = new QRadioButton;
+ sizeButtonGroup->addButton(toolBarRadioButton, QStyle::PM_ToolBarIconSize);
+ QRadioButton *listViewRadioButton = new QRadioButton;
+ sizeButtonGroup->addButton(listViewRadioButton, QStyle::PM_ListViewIconSize);
+ QRadioButton *iconViewRadioButton = new QRadioButton;
+ sizeButtonGroup->addButton(iconViewRadioButton, QStyle::PM_IconViewIconSize);
+ QRadioButton *tabBarRadioButton = new QRadioButton;
+ sizeButtonGroup->addButton(tabBarRadioButton, QStyle::PM_TabBarIconSize);
+ QRadioButton *otherRadioButton = new QRadioButton(tr("Other:"));
+ sizeButtonGroup->addButton(otherRadioButton, OtherSize);
+ otherSpinBox = new IconSizeSpinBox;
+ otherSpinBox->setRange(8, 256);
+ const QString spinBoxToolTip =
+ tr("Enter a custom size within %1..%2")
+ .arg(otherSpinBox->minimum()).arg(otherSpinBox->maximum());
+ otherSpinBox->setValue(64);
+ otherSpinBox->setToolTip(spinBoxToolTip);
+ otherRadioButton->setToolTip(spinBoxToolTip);
+//! [26]
+
+//! [27]
+ connect(otherSpinBox, &QSpinBox::valueChanged,
+ this, &MainWindow::triggerChangeSize);
+
+ QHBoxLayout *otherSizeLayout = new QHBoxLayout;
+ otherSizeLayout->addWidget(otherRadioButton);
+ otherSizeLayout->addWidget(otherSpinBox);
+ otherSizeLayout->addStretch();
+
+ QGridLayout *layout = new QGridLayout(iconSizeGroupBox);
+ layout->addWidget(smallRadioButton, 0, 0);
+ layout->addWidget(largeRadioButton, 1, 0);
+ layout->addWidget(toolBarRadioButton, 2, 0);
+ layout->addWidget(listViewRadioButton, 0, 1);
+ layout->addWidget(iconViewRadioButton, 1, 1);
+ layout->addWidget(tabBarRadioButton, 2, 1);
+ layout->addLayout(otherSizeLayout, 3, 0, 1, 2);
+ layout->setRowStretch(4, 1);
+ return iconSizeGroupBox;
+//! [27]
+}
+
+void MainWindow::screenChanged()
+{
+ devicePixelRatioLabel->setText(QString::number(devicePixelRatio()));
+ if (const QWindow *window = windowHandle()) {
+ const QScreen *screen = window->screen();
+ const QString screenDescription =
+ tr("\"%1\" (%2x%3)").arg(screen->name())
+ .arg(screen->geometry().width()).arg(screen->geometry().height());
+ screenNameLabel->setText(screenDescription);
+ }
+ changeIcon();
+}
+
+QWidget *MainWindow::createHighDpiIconSizeGroupBox()
+{
+ QGroupBox *highDpiGroupBox = new QGroupBox(tr("High DPI Scaling"));
+ QFormLayout *layout = new QFormLayout(highDpiGroupBox);
+ devicePixelRatioLabel = new QLabel(highDpiGroupBox);
+ screenNameLabel = new QLabel(highDpiGroupBox);
+ layout->addRow(tr("Screen:"), screenNameLabel);
+ layout->addRow(tr("Device pixel ratio:"), devicePixelRatioLabel);
+ return highDpiGroupBox;
+}
+
+//! [28]
+void MainWindow::createActions()
+{
+ QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
+
+ addSampleImagesAct = new QAction(tr("Add &Sample Images..."), this);
+ addSampleImagesAct->setShortcut(tr("Ctrl+A"));
+ connect(addSampleImagesAct, &QAction::triggered, this, &MainWindow::addSampleImages);
+ fileMenu->addAction(addSampleImagesAct);
+
+ addOtherImagesAct = new QAction(tr("&Add Images..."), this);
+ addOtherImagesAct->setShortcut(QKeySequence::Open);
+ connect(addOtherImagesAct, &QAction::triggered, this, &MainWindow::addOtherImages);
+ fileMenu->addAction(addOtherImagesAct);
+
+ removeAllImagesAct = new QAction(tr("&Remove All Images"), this);
+ removeAllImagesAct->setShortcut(tr("Ctrl+R"));
+ connect(removeAllImagesAct, &QAction::triggered,
+ this, &MainWindow::removeAllImages);
+ fileMenu->addAction(removeAllImagesAct);
+
+ fileMenu->addSeparator();
+
+ QAction *exitAct = fileMenu->addAction(tr("&Quit"), qApp, &QCoreApplication::quit);
+ exitAct->setShortcuts(QKeySequence::Quit);
+
+ QMenu *viewMenu = menuBar()->addMenu(tr("&View"));
+
+ styleActionGroup = new QActionGroup(this);
+ const QStringList styleKeys = QStyleFactory::keys();
+ for (const QString &styleName : styleKeys) {
+ QAction *action = new QAction(tr("%1 Style").arg(styleName), styleActionGroup);
+ action->setData(styleName);
+ action->setCheckable(true);
+ connect(action, &QAction::triggered, this, &MainWindow::changeStyle);
+ viewMenu->addAction(action);
+ }
+
+ QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings"));
+
+ guessModeStateAct = new QAction(tr("&Guess Image Mode/State"), this);
+ guessModeStateAct->setCheckable(true);
+ guessModeStateAct->setChecked(true);
+ settingsMenu->addAction(guessModeStateAct);
+
+ nativeFileDialogAct = new QAction(tr("&Use Native File Dialog"), this);
+ nativeFileDialogAct->setCheckable(true);
+ nativeFileDialogAct->setChecked(true);
+ settingsMenu->addAction(nativeFileDialogAct);
+
+ QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
+ helpMenu->addAction(tr("&About"), this, &MainWindow::about);
+ helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
+}
+//! [28]
+
+//! [30]
+void MainWindow::createContextMenu()
+{
+ imagesTable->setContextMenuPolicy(Qt::ActionsContextMenu);
+ imagesTable->addAction(addSampleImagesAct);
+ imagesTable->addAction(addOtherImagesAct);
+ imagesTable->addAction(removeAllImagesAct);
+}
+//! [30]
+
+//! [31]
+void MainWindow::checkCurrentStyle()
+{
+ const QList<QAction *> actions = styleActionGroup->actions();
+ for (QAction *action : actions) {
+ const QString styleName = action->data().toString();
+ const std::unique_ptr<QStyle> candidate{QStyleFactory::create(styleName)};
+ Q_ASSERT(candidate);
+ if (candidate->metaObject()->className()
+ == QApplication::style()->metaObject()->className()) {
+ action->trigger();
+ return;
+ }
+ }
+}
+//! [31]
diff --git a/tests/manual/examples/widgets/widgets/icons/mainwindow.h b/tests/manual/examples/widgets/widgets/icons/mainwindow.h
new file mode 100644
index 0000000000..fb6e0493f7
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/icons/mainwindow.h
@@ -0,0 +1,74 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QIcon>
+#include <QList>
+#include <QMainWindow>
+#include <QPixmap>
+
+QT_BEGIN_NAMESPACE
+class QAction;
+class QActionGroup;
+class QLabel;
+class QButtonGroup;
+class QTableWidget;
+class QAbstractButton;
+QT_END_NAMESPACE
+class IconPreviewArea;
+class IconSizeSpinBox;
+
+//! [0]
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow(QWidget *parent = nullptr);
+
+ void loadImages(const QStringList &fileNames);
+
+ void show();
+
+private slots:
+ void about();
+ void changeStyle(bool checked);
+ void changeSize(QAbstractButton *button, bool);
+ void triggerChangeSize();
+ void changeIcon();
+ void addSampleImages();
+ void addOtherImages();
+ void removeAllImages();
+ void screenChanged();
+
+private:
+ QWidget *createImagesGroupBox();
+ QWidget *createIconSizeGroupBox();
+ QWidget *createHighDpiIconSizeGroupBox();
+ void createActions();
+ void createContextMenu();
+ void checkCurrentStyle();
+ void addImages(const QString &directory);
+
+ IconPreviewArea *previewArea;
+
+ QTableWidget *imagesTable;
+
+ QButtonGroup *sizeButtonGroup;
+ IconSizeSpinBox *otherSpinBox;
+
+ QLabel *devicePixelRatioLabel;
+ QLabel *screenNameLabel;
+
+ QAction *addOtherImagesAct;
+ QAction *addSampleImagesAct;
+ QAction *removeAllImagesAct;
+ QAction *guessModeStateAct;
+ QAction *nativeFileDialogAct;
+ QActionGroup *styleActionGroup;
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/imageviewer/CMakeLists.txt b/tests/manual/examples/widgets/widgets/imageviewer/CMakeLists.txt
new file mode 100644
index 0000000000..fd2f051210
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/imageviewer/CMakeLists.txt
@@ -0,0 +1,44 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(imageviewer LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/widgets/imageviewer")
+
+find_package(Qt6
+ REQUIRED COMPONENTS Core Gui Widgets
+ OPTIONAL_COMPONENTS PrintSupport
+)
+
+qt_standard_project_setup()
+
+qt_add_executable(imageviewer
+ imageviewer.cpp imageviewer.h
+ main.cpp
+)
+
+set_target_properties(imageviewer PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(imageviewer PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+if (TARGET Qt6::PrintSupport)
+ target_link_libraries(imageviewer PRIVATE Qt6::PrintSupport)
+endif()
+
+install(TARGETS imageviewer
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/widgets/imageviewer/imageviewer.cpp b/tests/manual/examples/widgets/widgets/imageviewer/imageviewer.cpp
new file mode 100644
index 0000000000..72a93dbf1b
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/imageviewer/imageviewer.cpp
@@ -0,0 +1,364 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "imageviewer.h"
+
+#include <QApplication>
+#include <QClipboard>
+#include <QColorSpace>
+#include <QDir>
+#include <QFileDialog>
+#include <QImageReader>
+#include <QImageWriter>
+#include <QLabel>
+#include <QMenuBar>
+#include <QMessageBox>
+#include <QMimeData>
+#include <QPainter>
+#include <QScreen>
+#include <QScrollArea>
+#include <QScrollBar>
+#include <QStandardPaths>
+#include <QStatusBar>
+
+#if defined(QT_PRINTSUPPORT_LIB)
+# include <QtPrintSupport/qtprintsupportglobal.h>
+
+# if QT_CONFIG(printdialog)
+# include <QPrintDialog>
+# endif
+#endif
+
+//! [0]
+ImageViewer::ImageViewer(QWidget *parent)
+ : QMainWindow(parent), imageLabel(new QLabel)
+ , scrollArea(new QScrollArea)
+{
+ imageLabel->setBackgroundRole(QPalette::Base);
+ imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+ imageLabel->setScaledContents(true);
+
+ scrollArea->setBackgroundRole(QPalette::Dark);
+ scrollArea->setWidget(imageLabel);
+ scrollArea->setVisible(false);
+ setCentralWidget(scrollArea);
+
+ createActions();
+
+ resize(QGuiApplication::primaryScreen()->availableSize() * 3 / 5);
+}
+
+//! [0]
+//! [2]
+
+bool ImageViewer::loadFile(const QString &fileName)
+{
+ QImageReader reader(fileName);
+ reader.setAutoTransform(true);
+ const QImage newImage = reader.read();
+ if (newImage.isNull()) {
+ QMessageBox::information(this, QGuiApplication::applicationDisplayName(),
+ tr("Cannot load %1: %2")
+ .arg(QDir::toNativeSeparators(fileName), reader.errorString()));
+ return false;
+ }
+//! [2]
+
+ setImage(newImage);
+
+ setWindowFilePath(fileName);
+
+ const QString description = image.colorSpace().isValid()
+ ? image.colorSpace().description() : tr("unknown");
+ const QString message = tr("Opened \"%1\", %2x%3, Depth: %4 (%5)")
+ .arg(QDir::toNativeSeparators(fileName)).arg(image.width()).arg(image.height())
+ .arg(image.depth()).arg(description);
+ statusBar()->showMessage(message);
+ return true;
+}
+
+void ImageViewer::setImage(const QImage &newImage)
+{
+ if (newImage.colorSpace().isValid())
+ image = newImage.convertedToColorSpace(QColorSpace::SRgb);
+ else
+ image = newImage;
+ imageLabel->setPixmap(QPixmap::fromImage(image, Qt::NoFormatConversion));
+//! [4]
+ scaleFactor = 1.0;
+
+ scrollArea->setVisible(true);
+ printAct->setEnabled(true);
+ fitToWindowAct->setEnabled(true);
+ updateActions();
+
+ if (!fitToWindowAct->isChecked())
+ imageLabel->adjustSize();
+}
+
+//! [4]
+
+bool ImageViewer::saveFile(const QString &fileName)
+{
+ QImageWriter writer(fileName);
+
+ if (!writer.write(image)) {
+ QMessageBox::information(this, QGuiApplication::applicationDisplayName(),
+ tr("Cannot write %1: %2")
+ .arg(QDir::toNativeSeparators(fileName), writer.errorString()));
+ return false;
+ }
+ const QString message = tr("Wrote \"%1\"").arg(QDir::toNativeSeparators(fileName));
+ statusBar()->showMessage(message);
+ return true;
+}
+
+//! [1]
+
+static void initializeImageFileDialog(QFileDialog &dialog, QFileDialog::AcceptMode acceptMode)
+{
+ static bool firstDialog = true;
+
+ if (firstDialog) {
+ firstDialog = false;
+ const QStringList picturesLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
+ dialog.setDirectory(picturesLocations.isEmpty() ? QDir::currentPath() : picturesLocations.last());
+ }
+
+ QStringList mimeTypeFilters;
+ const QByteArrayList supportedMimeTypes = acceptMode == QFileDialog::AcceptOpen
+ ? QImageReader::supportedMimeTypes() : QImageWriter::supportedMimeTypes();
+ for (const QByteArray &mimeTypeName : supportedMimeTypes)
+ mimeTypeFilters.append(mimeTypeName);
+ mimeTypeFilters.sort();
+ dialog.setMimeTypeFilters(mimeTypeFilters);
+ dialog.selectMimeTypeFilter("image/jpeg");
+ dialog.setAcceptMode(acceptMode);
+ if (acceptMode == QFileDialog::AcceptSave)
+ dialog.setDefaultSuffix("jpg");
+}
+
+void ImageViewer::open()
+{
+ QFileDialog dialog(this, tr("Open File"));
+ initializeImageFileDialog(dialog, QFileDialog::AcceptOpen);
+
+ while (dialog.exec() == QDialog::Accepted && !loadFile(dialog.selectedFiles().constFirst())) {}
+}
+//! [1]
+
+void ImageViewer::saveAs()
+{
+ QFileDialog dialog(this, tr("Save File As"));
+ initializeImageFileDialog(dialog, QFileDialog::AcceptSave);
+
+ while (dialog.exec() == QDialog::Accepted && !saveFile(dialog.selectedFiles().constFirst())) {}
+}
+
+//! [5]
+void ImageViewer::print()
+//! [5] //! [6]
+{
+ Q_ASSERT(!imageLabel->pixmap().isNull());
+#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog)
+//! [6] //! [7]
+ QPrintDialog dialog(&printer, this);
+//! [7] //! [8]
+ if (dialog.exec()) {
+ QPainter painter(&printer);
+ QPixmap pixmap = imageLabel->pixmap();
+ QRect rect = painter.viewport();
+ QSize size = pixmap.size();
+ size.scale(rect.size(), Qt::KeepAspectRatio);
+ painter.setViewport(rect.x(), rect.y(), size.width(), size.height());
+ painter.setWindow(pixmap.rect());
+ painter.drawPixmap(0, 0, pixmap);
+ }
+#endif
+}
+//! [8]
+
+void ImageViewer::copy()
+{
+#ifndef QT_NO_CLIPBOARD
+ QGuiApplication::clipboard()->setImage(image);
+#endif // !QT_NO_CLIPBOARD
+}
+
+#ifndef QT_NO_CLIPBOARD
+static QImage clipboardImage()
+{
+ if (const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData()) {
+ if (mimeData->hasImage()) {
+ const QImage image = qvariant_cast<QImage>(mimeData->imageData());
+ if (!image.isNull())
+ return image;
+ }
+ }
+ return QImage();
+}
+#endif // !QT_NO_CLIPBOARD
+
+void ImageViewer::paste()
+{
+#ifndef QT_NO_CLIPBOARD
+ const QImage newImage = clipboardImage();
+ if (newImage.isNull()) {
+ statusBar()->showMessage(tr("No image in clipboard"));
+ } else {
+ setImage(newImage);
+ setWindowFilePath(QString());
+ const QString message = tr("Obtained image from clipboard, %1x%2, Depth: %3")
+ .arg(newImage.width()).arg(newImage.height()).arg(newImage.depth());
+ statusBar()->showMessage(message);
+ }
+#endif // !QT_NO_CLIPBOARD
+}
+
+//! [9]
+void ImageViewer::zoomIn()
+//! [9] //! [10]
+{
+ scaleImage(1.25);
+}
+
+void ImageViewer::zoomOut()
+{
+ scaleImage(0.8);
+}
+
+//! [10] //! [11]
+void ImageViewer::normalSize()
+//! [11] //! [12]
+{
+ imageLabel->adjustSize();
+ scaleFactor = 1.0;
+}
+//! [12]
+
+//! [13]
+void ImageViewer::fitToWindow()
+//! [13] //! [14]
+{
+ bool fitToWindow = fitToWindowAct->isChecked();
+ scrollArea->setWidgetResizable(fitToWindow);
+ if (!fitToWindow)
+ normalSize();
+ updateActions();
+}
+//! [14]
+
+
+//! [15]
+void ImageViewer::about()
+//! [15] //! [16]
+{
+ QMessageBox::about(this, tr("About Image Viewer"),
+ tr("<p>The <b>Image Viewer</b> example shows how to combine QLabel "
+ "and QScrollArea to display an image. QLabel is typically used "
+ "for displaying a text, but it can also display an image. "
+ "QScrollArea provides a scrolling view around another widget. "
+ "If the child widget exceeds the size of the frame, QScrollArea "
+ "automatically provides scroll bars. </p><p>The example "
+ "demonstrates how QLabel's ability to scale its contents "
+ "(QLabel::scaledContents), and QScrollArea's ability to "
+ "automatically resize its contents "
+ "(QScrollArea::widgetResizable), can be used to implement "
+ "zooming and scaling features. </p><p>In addition the example "
+ "shows how to use QPainter to print an image.</p>"));
+}
+//! [16]
+
+//! [17]
+void ImageViewer::createActions()
+//! [17] //! [18]
+{
+ QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
+
+ QAction *openAct = fileMenu->addAction(tr("&Open..."), this, &ImageViewer::open);
+ openAct->setShortcut(QKeySequence::Open);
+
+ saveAsAct = fileMenu->addAction(tr("&Save As..."), this, &ImageViewer::saveAs);
+ saveAsAct->setEnabled(false);
+
+ printAct = fileMenu->addAction(tr("&Print..."), this, &ImageViewer::print);
+ printAct->setShortcut(QKeySequence::Print);
+ printAct->setEnabled(false);
+
+ fileMenu->addSeparator();
+
+ QAction *exitAct = fileMenu->addAction(tr("E&xit"), this, &QWidget::close);
+ exitAct->setShortcut(tr("Ctrl+Q"));
+
+ QMenu *editMenu = menuBar()->addMenu(tr("&Edit"));
+
+ copyAct = editMenu->addAction(tr("&Copy"), this, &ImageViewer::copy);
+ copyAct->setShortcut(QKeySequence::Copy);
+ copyAct->setEnabled(false);
+
+ QAction *pasteAct = editMenu->addAction(tr("&Paste"), this, &ImageViewer::paste);
+ pasteAct->setShortcut(QKeySequence::Paste);
+
+ QMenu *viewMenu = menuBar()->addMenu(tr("&View"));
+
+ zoomInAct = viewMenu->addAction(tr("Zoom &In (25%)"), this, &ImageViewer::zoomIn);
+ zoomInAct->setShortcut(QKeySequence::ZoomIn);
+ zoomInAct->setEnabled(false);
+
+ zoomOutAct = viewMenu->addAction(tr("Zoom &Out (25%)"), this, &ImageViewer::zoomOut);
+ zoomOutAct->setShortcut(QKeySequence::ZoomOut);
+ zoomOutAct->setEnabled(false);
+
+ normalSizeAct = viewMenu->addAction(tr("&Normal Size"), this, &ImageViewer::normalSize);
+ normalSizeAct->setShortcut(tr("Ctrl+S"));
+ normalSizeAct->setEnabled(false);
+
+ viewMenu->addSeparator();
+
+ fitToWindowAct = viewMenu->addAction(tr("&Fit to Window"), this, &ImageViewer::fitToWindow);
+ fitToWindowAct->setEnabled(false);
+ fitToWindowAct->setCheckable(true);
+ fitToWindowAct->setShortcut(tr("Ctrl+F"));
+
+ QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
+
+ helpMenu->addAction(tr("&About"), this, &ImageViewer::about);
+ helpMenu->addAction(tr("About &Qt"), this, &QApplication::aboutQt);
+}
+//! [18]
+
+//! [21]
+void ImageViewer::updateActions()
+//! [21] //! [22]
+{
+ saveAsAct->setEnabled(!image.isNull());
+ copyAct->setEnabled(!image.isNull());
+ zoomInAct->setEnabled(!fitToWindowAct->isChecked());
+ zoomOutAct->setEnabled(!fitToWindowAct->isChecked());
+ normalSizeAct->setEnabled(!fitToWindowAct->isChecked());
+}
+//! [22]
+
+//! [23]
+void ImageViewer::scaleImage(double factor)
+//! [23] //! [24]
+{
+ scaleFactor *= factor;
+ imageLabel->resize(scaleFactor * imageLabel->pixmap().size());
+
+ adjustScrollBar(scrollArea->horizontalScrollBar(), factor);
+ adjustScrollBar(scrollArea->verticalScrollBar(), factor);
+
+ zoomInAct->setEnabled(scaleFactor < 3.0);
+ zoomOutAct->setEnabled(scaleFactor > 0.333);
+}
+//! [24]
+
+//! [25]
+void ImageViewer::adjustScrollBar(QScrollBar *scrollBar, double factor)
+//! [25] //! [26]
+{
+ scrollBar->setValue(int(factor * scrollBar->value()
+ + ((factor - 1) * scrollBar->pageStep()/2)));
+}
+//! [26]
diff --git a/tests/manual/examples/widgets/widgets/imageviewer/imageviewer.h b/tests/manual/examples/widgets/widgets/imageviewer/imageviewer.h
new file mode 100644
index 0000000000..e9e6858c74
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/imageviewer/imageviewer.h
@@ -0,0 +1,74 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef IMAGEVIEWER_H
+#define IMAGEVIEWER_H
+
+#include <QMainWindow>
+#include <QImage>
+#if defined(QT_PRINTSUPPORT_LIB)
+# include <QtPrintSupport/qtprintsupportglobal.h>
+
+# if QT_CONFIG(printer)
+# include <QPrinter>
+# endif
+#endif
+
+QT_BEGIN_NAMESPACE
+class QAction;
+class QLabel;
+class QMenu;
+class QScrollArea;
+class QScrollBar;
+QT_END_NAMESPACE
+
+//! [0]
+class ImageViewer : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ ImageViewer(QWidget *parent = nullptr);
+ bool loadFile(const QString &);
+
+private slots:
+ void open();
+ void saveAs();
+ void print();
+ void copy();
+ void paste();
+ void zoomIn();
+ void zoomOut();
+ void normalSize();
+ void fitToWindow();
+ void about();
+
+private:
+ void createActions();
+ void createMenus();
+ void updateActions();
+ bool saveFile(const QString &fileName);
+ void setImage(const QImage &newImage);
+ void scaleImage(double factor);
+ void adjustScrollBar(QScrollBar *scrollBar, double factor);
+
+ QImage image;
+ QLabel *imageLabel;
+ QScrollArea *scrollArea;
+ double scaleFactor = 1;
+
+#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printer)
+ QPrinter printer;
+#endif
+
+ QAction *saveAsAct;
+ QAction *printAct;
+ QAction *copyAct;
+ QAction *zoomInAct;
+ QAction *zoomOutAct;
+ QAction *normalSizeAct;
+ QAction *fitToWindowAct;
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/imageviewer/imageviewer.pro b/tests/manual/examples/widgets/widgets/imageviewer/imageviewer.pro
new file mode 100644
index 0000000000..d3db63ec20
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/imageviewer/imageviewer.pro
@@ -0,0 +1,11 @@
+QT += widgets
+requires(qtConfig(filedialog))
+qtHaveModule(printsupport): QT += printsupport
+
+HEADERS = imageviewer.h
+SOURCES = imageviewer.cpp \
+ main.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/widgets/imageviewer
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/widgets/imageviewer/main.cpp b/tests/manual/examples/widgets/widgets/imageviewer/main.cpp
new file mode 100644
index 0000000000..013f7d8689
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/imageviewer/main.cpp
@@ -0,0 +1,24 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+#include <QCommandLineParser>
+
+#include "imageviewer.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ QGuiApplication::setApplicationDisplayName(ImageViewer::tr("Image Viewer"));
+ QCommandLineParser commandLineParser;
+ commandLineParser.addHelpOption();
+ commandLineParser.addPositionalArgument(ImageViewer::tr("[file]"), ImageViewer::tr("Image file to open."));
+ commandLineParser.process(QCoreApplication::arguments());
+ ImageViewer imageViewer;
+ if (!commandLineParser.positionalArguments().isEmpty()
+ && !imageViewer.loadFile(commandLineParser.positionalArguments().constFirst())) {
+ return -1;
+ }
+ imageViewer.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/widgets/movie/CMakeLists.txt b/tests/manual/examples/widgets/widgets/movie/CMakeLists.txt
new file mode 100644
index 0000000000..49b766a11d
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/movie/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(movie LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/widgets/movie")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(movie
+ main.cpp
+ movieplayer.cpp movieplayer.h
+)
+
+set_target_properties(movie PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(movie PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS movie
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/widgets/movie/animation.gif b/tests/manual/examples/widgets/widgets/movie/animation.gif
new file mode 100644
index 0000000000..f674369efc
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/movie/animation.gif
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/movie/main.cpp b/tests/manual/examples/widgets/widgets/movie/main.cpp
new file mode 100644
index 0000000000..e5d4d3f040
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/movie/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "movieplayer.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ MoviePlayer player;
+ player.show();
+ player.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/widgets/movie/movie.pro b/tests/manual/examples/widgets/widgets/movie/movie.pro
new file mode 100644
index 0000000000..f9f89109e4
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/movie/movie.pro
@@ -0,0 +1,12 @@
+QT += widgets
+requires(qtConfig(filedialog))
+
+HEADERS = movieplayer.h
+SOURCES = main.cpp \
+ movieplayer.cpp
+
+EXAMPLE_FILES = animation.gif
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/widgets/movie
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/widgets/movie/movieplayer.cpp b/tests/manual/examples/widgets/widgets/movie/movieplayer.cpp
new file mode 100644
index 0000000000..6f6ec14bb1
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/movie/movieplayer.cpp
@@ -0,0 +1,180 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "movieplayer.h"
+
+#include <QCheckBox>
+#include <QFileDialog>
+#include <QLabel>
+#include <QMovie>
+#include <QSlider>
+#include <QSpinBox>
+#include <QStyle>
+#include <QToolButton>
+#include <QVBoxLayout>
+
+MoviePlayer::MoviePlayer(QWidget *parent)
+ : QWidget(parent)
+{
+ movie = new QMovie(this);
+ movie->setCacheMode(QMovie::CacheAll);
+
+ movieLabel = new QLabel(tr("No movie loaded"));
+ movieLabel->setAlignment(Qt::AlignCenter);
+ movieLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+ movieLabel->setBackgroundRole(QPalette::Dark);
+ movieLabel->setAutoFillBackground(true);
+
+ currentMovieDirectory = "movies";
+
+ createControls();
+ createButtons();
+
+ connect(movie, &QMovie::frameChanged, this, &MoviePlayer::updateFrameSlider);
+ connect(movie, &QMovie::stateChanged, this, &MoviePlayer::updateButtons);
+ connect(fitCheckBox, &QCheckBox::clicked, this, &MoviePlayer::fitToWindow);
+ connect(frameSlider, &QSlider::valueChanged, this, &MoviePlayer::goToFrame);
+ connect(speedSpinBox, &QSpinBox::valueChanged,
+ movie, &QMovie::setSpeed);
+
+ mainLayout = new QVBoxLayout;
+ mainLayout->addWidget(movieLabel);
+ mainLayout->addLayout(controlsLayout);
+ mainLayout->addLayout(buttonsLayout);
+ setLayout(mainLayout);
+
+ updateFrameSlider();
+ updateButtons();
+
+ setWindowTitle(tr("Movie Player"));
+ resize(400, 400);
+}
+
+void MoviePlayer::open()
+{
+ QString fileName = QFileDialog::getOpenFileName(this, tr("Open a Movie"),
+ currentMovieDirectory);
+ if (!fileName.isEmpty())
+ openFile(fileName);
+}
+
+void MoviePlayer::openFile(const QString &fileName)
+{
+ currentMovieDirectory = QFileInfo(fileName).path();
+
+ movie->stop();
+ movieLabel->setMovie(movie);
+ movie->setFileName(fileName);
+ movie->start();
+
+ updateFrameSlider();
+ updateButtons();
+}
+
+void MoviePlayer::goToFrame(int frame)
+{
+ movie->jumpToFrame(frame);
+}
+
+void MoviePlayer::fitToWindow()
+{
+ movieLabel->setScaledContents(fitCheckBox->isChecked());
+}
+
+void MoviePlayer::updateFrameSlider()
+{
+ bool hasFrames = (movie->currentFrameNumber() >= 0);
+
+ if (hasFrames) {
+ if (movie->frameCount() > 0) {
+ frameSlider->setMaximum(movie->frameCount() - 1);
+ } else {
+ if (movie->currentFrameNumber() > frameSlider->maximum())
+ frameSlider->setMaximum(movie->currentFrameNumber());
+ }
+ frameSlider->setValue(movie->currentFrameNumber());
+ } else {
+ frameSlider->setMaximum(0);
+ }
+ frameLabel->setEnabled(hasFrames);
+ frameSlider->setEnabled(hasFrames);
+}
+
+void MoviePlayer::updateButtons()
+{
+ playButton->setEnabled(movie->isValid() && movie->frameCount() != 1
+ && movie->state() == QMovie::NotRunning);
+ pauseButton->setEnabled(movie->state() != QMovie::NotRunning);
+ pauseButton->setChecked(movie->state() == QMovie::Paused);
+ stopButton->setEnabled(movie->state() != QMovie::NotRunning);
+}
+
+void MoviePlayer::createControls()
+{
+ fitCheckBox = new QCheckBox(tr("Fit to Window"));
+
+ frameLabel = new QLabel(tr("Current frame:"));
+
+ frameSlider = new QSlider(Qt::Horizontal);
+ frameSlider->setTickPosition(QSlider::TicksBelow);
+ frameSlider->setTickInterval(10);
+
+ speedLabel = new QLabel(tr("Speed:"));
+
+ speedSpinBox = new QSpinBox;
+ speedSpinBox->setRange(1, 9999);
+ speedSpinBox->setValue(100);
+ speedSpinBox->setSuffix(tr("%"));
+
+ controlsLayout = new QGridLayout;
+ controlsLayout->addWidget(fitCheckBox, 0, 0, 1, 2);
+ controlsLayout->addWidget(frameLabel, 1, 0);
+ controlsLayout->addWidget(frameSlider, 1, 1, 1, 2);
+ controlsLayout->addWidget(speedLabel, 2, 0);
+ controlsLayout->addWidget(speedSpinBox, 2, 1);
+}
+
+void MoviePlayer::createButtons()
+{
+ QSize iconSize(36, 36);
+
+ openButton = new QToolButton;
+ openButton->setIcon(style()->standardIcon(QStyle::SP_DialogOpenButton));
+ openButton->setIconSize(iconSize);
+ openButton->setToolTip(tr("Open File"));
+ connect(openButton, &QToolButton::clicked, this, &MoviePlayer::open);
+
+ playButton = new QToolButton;
+ playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
+ playButton->setIconSize(iconSize);
+ playButton->setToolTip(tr("Play"));
+ connect(playButton, &QToolButton::clicked, movie, &QMovie::start);
+
+ pauseButton = new QToolButton;
+ pauseButton->setCheckable(true);
+ pauseButton->setIcon(style()->standardIcon(QStyle::SP_MediaPause));
+ pauseButton->setIconSize(iconSize);
+ pauseButton->setToolTip(tr("Pause"));
+ connect(pauseButton, &QToolButton::clicked, movie, &QMovie::setPaused);
+
+ stopButton = new QToolButton;
+ stopButton->setIcon(style()->standardIcon(QStyle::SP_MediaStop));
+ stopButton->setIconSize(iconSize);
+ stopButton->setToolTip(tr("Stop"));
+ connect(stopButton, &QToolButton::clicked, movie, &QMovie::stop);
+
+ quitButton = new QToolButton;
+ quitButton->setIcon(style()->standardIcon(QStyle::SP_DialogCloseButton));
+ quitButton->setIconSize(iconSize);
+ quitButton->setToolTip(tr("Quit"));
+ connect(quitButton, &QToolButton::clicked, this, &MoviePlayer::close);
+
+ buttonsLayout = new QHBoxLayout;
+ buttonsLayout->addStretch();
+ buttonsLayout->addWidget(openButton);
+ buttonsLayout->addWidget(playButton);
+ buttonsLayout->addWidget(pauseButton);
+ buttonsLayout->addWidget(stopButton);
+ buttonsLayout->addWidget(quitButton);
+ buttonsLayout->addStretch();
+}
diff --git a/tests/manual/examples/widgets/widgets/movie/movieplayer.h b/tests/manual/examples/widgets/widgets/movie/movieplayer.h
new file mode 100644
index 0000000000..362fd50535
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/movie/movieplayer.h
@@ -0,0 +1,59 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MOVIEPLAYER_H
+#define MOVIEPLAYER_H
+
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QCheckBox;
+class QGridLayout;
+class QHBoxLayout;
+class QLabel;
+class QMovie;
+class QSlider;
+class QSpinBox;
+class QToolButton;
+class QVBoxLayout;
+QT_END_NAMESPACE
+
+class MoviePlayer : public QWidget
+{
+ Q_OBJECT
+
+public:
+ MoviePlayer(QWidget *parent = nullptr);
+ void openFile(const QString &fileName);
+
+private slots:
+ void open();
+ void goToFrame(int frame);
+ void fitToWindow();
+ void updateButtons();
+ void updateFrameSlider();
+
+private:
+ void createControls();
+ void createButtons();
+
+ QString currentMovieDirectory;
+ QLabel *movieLabel;
+ QMovie *movie;
+ QToolButton *openButton;
+ QToolButton *playButton;
+ QToolButton *pauseButton;
+ QToolButton *stopButton;
+ QToolButton *quitButton;
+ QCheckBox *fitCheckBox;
+ QSlider *frameSlider;
+ QSpinBox *speedSpinBox;
+ QLabel *frameLabel;
+ QLabel *speedLabel;
+
+ QGridLayout *controlsLayout;
+ QHBoxLayout *buttonsLayout;
+ QVBoxLayout *mainLayout;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/styles/CMakeLists.txt b/tests/manual/examples/widgets/widgets/styles/CMakeLists.txt
new file mode 100644
index 0000000000..ff1d64b9d9
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/styles/CMakeLists.txt
@@ -0,0 +1,51 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(styles LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/widgets/styles")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(styles
+ main.cpp
+ norwegianwoodstyle.cpp norwegianwoodstyle.h
+ widgetgallery.cpp widgetgallery.h
+)
+
+set_target_properties(styles PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(styles PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(styles_resource_files
+ "images/woodbackground.png"
+ "images/woodbutton.png"
+)
+
+qt_add_resources(styles "styles"
+ PREFIX
+ "/"
+ FILES
+ ${styles_resource_files}
+)
+
+install(TARGETS styles
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/widgets/styles/images/woodbackground.png b/tests/manual/examples/widgets/widgets/styles/images/woodbackground.png
new file mode 100644
index 0000000000..8be3366bb4
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/styles/images/woodbackground.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/styles/images/woodbutton.png b/tests/manual/examples/widgets/widgets/styles/images/woodbutton.png
new file mode 100644
index 0000000000..adb59ef633
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/styles/images/woodbutton.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/styles/main.cpp b/tests/manual/examples/widgets/widgets/styles/main.cpp
new file mode 100644
index 0000000000..5c33895ea4
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/styles/main.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "norwegianwoodstyle.h"
+#include "widgetgallery.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication::setStyle(new NorwegianWoodStyle);
+
+ QApplication app(argc, argv);
+ WidgetGallery gallery;
+ gallery.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/widgets/styles/norwegianwoodstyle.cpp b/tests/manual/examples/widgets/widgets/styles/norwegianwoodstyle.cpp
new file mode 100644
index 0000000000..a8969dfd54
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/styles/norwegianwoodstyle.cpp
@@ -0,0 +1,310 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "norwegianwoodstyle.h"
+
+#include <QComboBox>
+#include <QPainter>
+#include <QPainterPath>
+#include <QPushButton>
+#include <QStyleFactory>
+
+NorwegianWoodStyle::NorwegianWoodStyle() :
+ QProxyStyle(QStyleFactory::create("windows"))
+{
+ setObjectName("NorwegianWood");
+}
+
+//! [0]
+QPalette NorwegianWoodStyle::standardPalette() const
+{
+ if (!m_standardPalette.isBrushSet(QPalette::Disabled, QPalette::Mid)) {
+ QColor brown(212, 140, 95);
+ QColor beige(236, 182, 120);
+ QColor slightlyOpaqueBlack(0, 0, 0, 63);
+
+ QImage backgroundImage(":/images/woodbackground.png");
+ QImage buttonImage(":/images/woodbutton.png");
+ QImage midImage = buttonImage.convertToFormat(QImage::Format_RGB32);
+
+ QPainter painter;
+ painter.begin(&midImage);
+ painter.setPen(Qt::NoPen);
+ painter.fillRect(midImage.rect(), slightlyOpaqueBlack);
+ painter.end();
+ //! [0]
+
+ //! [1]
+ QPalette palette(brown);
+
+ palette.setBrush(QPalette::BrightText, Qt::white);
+ palette.setBrush(QPalette::Base, beige);
+ palette.setBrush(QPalette::Highlight, Qt::darkGreen);
+ setTexture(palette, QPalette::Button, buttonImage);
+ setTexture(palette, QPalette::Mid, midImage);
+ setTexture(palette, QPalette::Window, backgroundImage);
+
+ QBrush brush = palette.window();
+ brush.setColor(brush.color().darker());
+
+ palette.setBrush(QPalette::Disabled, QPalette::WindowText, brush);
+ palette.setBrush(QPalette::Disabled, QPalette::Text, brush);
+ palette.setBrush(QPalette::Disabled, QPalette::ButtonText, brush);
+ palette.setBrush(QPalette::Disabled, QPalette::Base, brush);
+ palette.setBrush(QPalette::Disabled, QPalette::Button, brush);
+ palette.setBrush(QPalette::Disabled, QPalette::Mid, brush);
+
+ m_standardPalette = palette;
+ }
+
+ return m_standardPalette;
+}
+//! [1]
+
+//! [3]
+void NorwegianWoodStyle::polish(QWidget *widget)
+//! [3] //! [4]
+{
+ if (qobject_cast<QPushButton *>(widget)
+ || qobject_cast<QComboBox *>(widget))
+ widget->setAttribute(Qt::WA_Hover, true);
+}
+//! [4]
+
+//! [5]
+void NorwegianWoodStyle::unpolish(QWidget *widget)
+//! [5] //! [6]
+{
+ if (qobject_cast<QPushButton *>(widget)
+ || qobject_cast<QComboBox *>(widget))
+ widget->setAttribute(Qt::WA_Hover, false);
+}
+//! [6]
+
+//! [7]
+int NorwegianWoodStyle::pixelMetric(PixelMetric metric,
+//! [7] //! [8]
+ const QStyleOption *option,
+ const QWidget *widget) const
+{
+ switch (metric) {
+ case PM_ComboBoxFrameWidth:
+ return 8;
+ case PM_ScrollBarExtent:
+ return QProxyStyle::pixelMetric(metric, option, widget) + 4;
+ default:
+ return QProxyStyle::pixelMetric(metric, option, widget);
+ }
+}
+//! [8]
+
+//! [9]
+int NorwegianWoodStyle::styleHint(StyleHint hint, const QStyleOption *option,
+//! [9] //! [10]
+ const QWidget *widget,
+ QStyleHintReturn *returnData) const
+{
+ switch (hint) {
+ case SH_DitherDisabledText:
+ return int(false);
+ case SH_EtchDisabledText:
+ return int(true);
+ default:
+ return QProxyStyle::styleHint(hint, option, widget, returnData);
+ }
+}
+//! [10]
+
+//! [11]
+void NorwegianWoodStyle::drawPrimitive(PrimitiveElement element,
+//! [11] //! [12]
+ const QStyleOption *option,
+ QPainter *painter,
+ const QWidget *widget) const
+{
+ switch (element) {
+ case PE_PanelButtonCommand:
+ {
+ int delta = (option->state & State_MouseOver) ? 64 : 0;
+ QColor slightlyOpaqueBlack(0, 0, 0, 63);
+ QColor semiTransparentWhite(255, 255, 255, 127 + delta);
+ QColor semiTransparentBlack(0, 0, 0, 127 - delta);
+
+ int x, y, width, height;
+ option->rect.getRect(&x, &y, &width, &height);
+//! [12]
+
+//! [13]
+ QPainterPath roundRect = roundRectPath(option->rect);
+//! [13] //! [14]
+ int radius = qMin(width, height) / 2;
+//! [14]
+
+//! [15]
+ QBrush brush;
+//! [15] //! [16]
+ bool darker;
+
+ const QStyleOptionButton *buttonOption =
+ qstyleoption_cast<const QStyleOptionButton *>(option);
+ if (buttonOption
+ && (buttonOption->features & QStyleOptionButton::Flat)) {
+ brush = option->palette.window();
+ darker = (option->state & (State_Sunken | State_On));
+ } else {
+ if (option->state & (State_Sunken | State_On)) {
+ brush = option->palette.mid();
+ darker = !(option->state & State_Sunken);
+ } else {
+ brush = option->palette.button();
+ darker = false;
+//! [16] //! [17]
+ }
+//! [17] //! [18]
+ }
+//! [18]
+
+//! [19]
+ painter->save();
+//! [19] //! [20]
+ painter->setRenderHint(QPainter::Antialiasing, true);
+//! [20] //! [21]
+ painter->fillPath(roundRect, brush);
+//! [21] //! [22]
+ if (darker)
+//! [22] //! [23]
+ painter->fillPath(roundRect, slightlyOpaqueBlack);
+//! [23]
+
+//! [24]
+ int penWidth;
+//! [24] //! [25]
+ if (radius < 10)
+ penWidth = 3;
+ else if (radius < 20)
+ penWidth = 5;
+ else
+ penWidth = 7;
+
+ QPen topPen(semiTransparentWhite, penWidth);
+ QPen bottomPen(semiTransparentBlack, penWidth);
+
+ if (option->state & (State_Sunken | State_On))
+ qSwap(topPen, bottomPen);
+//! [25]
+
+//! [26]
+ int x1 = x;
+ int x2 = x + radius;
+ int x3 = x + width - radius;
+ int x4 = x + width;
+
+ if (option->direction == Qt::RightToLeft) {
+ qSwap(x1, x4);
+ qSwap(x2, x3);
+ }
+
+ QPolygon topHalf;
+ topHalf << QPoint(x1, y)
+ << QPoint(x4, y)
+ << QPoint(x3, y + radius)
+ << QPoint(x2, y + height - radius)
+ << QPoint(x1, y + height);
+
+ painter->setClipPath(roundRect);
+ painter->setClipRegion(topHalf, Qt::IntersectClip);
+ painter->setPen(topPen);
+ painter->drawPath(roundRect);
+//! [26] //! [32]
+
+ QPolygon bottomHalf = topHalf;
+ bottomHalf[0] = QPoint(x4, y + height);
+
+ painter->setClipPath(roundRect);
+ painter->setClipRegion(bottomHalf, Qt::IntersectClip);
+ painter->setPen(bottomPen);
+ painter->drawPath(roundRect);
+
+ painter->setPen(option->palette.windowText().color());
+ painter->setClipping(false);
+ painter->drawPath(roundRect);
+
+ painter->restore();
+ }
+ break;
+//! [32] //! [33]
+ default:
+//! [33] //! [34]
+ QProxyStyle::drawPrimitive(element, option, painter, widget);
+ }
+}
+//! [34]
+
+//! [35]
+void NorwegianWoodStyle::drawControl(ControlElement element,
+//! [35] //! [36]
+ const QStyleOption *option,
+ QPainter *painter,
+ const QWidget *widget) const
+{
+ switch (element) {
+ case CE_PushButtonLabel:
+ {
+ QStyleOptionButton myButtonOption;
+ const QStyleOptionButton *buttonOption =
+ qstyleoption_cast<const QStyleOptionButton *>(option);
+ if (buttonOption) {
+ myButtonOption = *buttonOption;
+ if (myButtonOption.palette.currentColorGroup()
+ != QPalette::Disabled) {
+ if (myButtonOption.state & (State_Sunken | State_On)) {
+ myButtonOption.palette.setBrush(QPalette::ButtonText,
+ myButtonOption.palette.brightText());
+ }
+ }
+ }
+ QProxyStyle::drawControl(element, &myButtonOption, painter, widget);
+ }
+ break;
+ default:
+ QProxyStyle::drawControl(element, option, painter, widget);
+ }
+}
+//! [36]
+
+//! [37]
+void NorwegianWoodStyle::setTexture(QPalette &palette, QPalette::ColorRole role,
+//! [37] //! [38]
+ const QImage &image)
+{
+ for (int i = 0; i < QPalette::NColorGroups; ++i) {
+ QBrush brush(image);
+ brush.setColor(palette.brush(QPalette::ColorGroup(i), role).color());
+ palette.setBrush(QPalette::ColorGroup(i), role, brush);
+ }
+}
+//! [38]
+
+//! [39]
+QPainterPath NorwegianWoodStyle::roundRectPath(const QRect &rect)
+//! [39] //! [40]
+{
+ int radius = qMin(rect.width(), rect.height()) / 2;
+ int diam = 2 * radius;
+
+ int x1, y1, x2, y2;
+ rect.getCoords(&x1, &y1, &x2, &y2);
+
+ QPainterPath path;
+ path.moveTo(x2, y1 + radius);
+ path.arcTo(QRect(x2 - diam, y1, diam, diam), 0.0, +90.0);
+ path.lineTo(x1 + radius, y1);
+ path.arcTo(QRect(x1, y1, diam, diam), 90.0, +90.0);
+ path.lineTo(x1, y2 - radius);
+ path.arcTo(QRect(x1, y2 - diam, diam, diam), 180.0, +90.0);
+ path.lineTo(x1 + radius, y2);
+ path.arcTo(QRect(x2 - diam, y2 - diam, diam, diam), 270.0, +90.0);
+ path.closeSubpath();
+ return path;
+}
+//! [40]
diff --git a/tests/manual/examples/widgets/widgets/styles/norwegianwoodstyle.h b/tests/manual/examples/widgets/widgets/styles/norwegianwoodstyle.h
new file mode 100644
index 0000000000..5af2171f35
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/styles/norwegianwoodstyle.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef NORWEGIANWOODSTYLE_H
+#define NORWEGIANWOODSTYLE_H
+
+#include <QProxyStyle>
+#include <QPalette>
+
+QT_BEGIN_NAMESPACE
+class QPainterPath;
+QT_END_NAMESPACE
+
+//! [0]
+class NorwegianWoodStyle : public QProxyStyle
+{
+ Q_OBJECT
+
+public:
+ NorwegianWoodStyle();
+
+ QPalette standardPalette() const override;
+
+ void polish(QWidget *widget) override;
+ void unpolish(QWidget *widget) override;
+ int pixelMetric(PixelMetric metric, const QStyleOption *option,
+ const QWidget *widget) const override;
+ int styleHint(StyleHint hint, const QStyleOption *option,
+ const QWidget *widget, QStyleHintReturn *returnData) const override;
+ void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const override;
+ void drawControl(ControlElement control, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const override;
+
+private:
+ static void setTexture(QPalette &palette, QPalette::ColorRole role,
+ const QImage &image);
+ static QPainterPath roundRectPath(const QRect &rect);
+ mutable QPalette m_standardPalette;
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/styles/styles.pro b/tests/manual/examples/widgets/widgets/styles/styles.pro
new file mode 100644
index 0000000000..87dee7e04c
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/styles/styles.pro
@@ -0,0 +1,13 @@
+QT += widgets
+requires(qtConfig(combobox))
+
+HEADERS = norwegianwoodstyle.h \
+ widgetgallery.h
+SOURCES = main.cpp \
+ norwegianwoodstyle.cpp \
+ widgetgallery.cpp
+RESOURCES = styles.qrc
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/widgets/styles
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/widgets/styles/styles.qrc b/tests/manual/examples/widgets/widgets/styles/styles.qrc
new file mode 100644
index 0000000000..4fdad8d914
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/styles/styles.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>images/woodbutton.png</file>
+ <file>images/woodbackground.png</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/widgets/styles/widgetgallery.cpp b/tests/manual/examples/widgets/widgets/styles/widgetgallery.cpp
new file mode 100644
index 0000000000..83cb99a52c
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/styles/widgetgallery.cpp
@@ -0,0 +1,277 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "widgetgallery.h"
+#include "norwegianwoodstyle.h"
+
+#include <QApplication>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QDateTimeEdit>
+#include <QDial>
+#include <QGridLayout>
+#include <QGroupBox>
+#include <QLabel>
+#include <QLineEdit>
+#include <QProgressBar>
+#include <QPushButton>
+#include <QRadioButton>
+#include <QScrollBar>
+#include <QSpinBox>
+#include <QStyle>
+#include <QStyleFactory>
+#include <QTableWidget>
+#include <QTextEdit>
+#include <QTimer>
+
+//! [0]
+WidgetGallery::WidgetGallery(QWidget *parent)
+ : QDialog(parent)
+{
+ styleComboBox = new QComboBox;
+ const QString defaultStyleName = QApplication::style()->objectName();
+ QStringList styleNames = QStyleFactory::keys();
+ styleNames.append("NorwegianWood");
+ for (int i = 1, size = styleNames.size(); i < size; ++i) {
+ if (defaultStyleName.compare(styleNames.at(i), Qt::CaseInsensitive) == 0) {
+ styleNames.swapItemsAt(0, i);
+ break;
+ }
+ }
+ styleComboBox->addItems(styleNames);
+
+ styleLabel = new QLabel(tr("&Style:"));
+ styleLabel->setBuddy(styleComboBox);
+
+ useStylePaletteCheckBox = new QCheckBox(tr("&Use style's standard palette"));
+ useStylePaletteCheckBox->setChecked(true);
+
+ disableWidgetsCheckBox = new QCheckBox(tr("&Disable widgets"));
+
+ createTopLeftGroupBox();
+ createTopRightGroupBox();
+ createBottomLeftTabWidget();
+ createBottomRightGroupBox();
+ createProgressBar();
+//! [0]
+
+//! [1]
+ connect(styleComboBox, &QComboBox::textActivated,
+//! [1] //! [2]
+ this, &WidgetGallery::changeStyle);
+ connect(useStylePaletteCheckBox, &QCheckBox::toggled,
+ this, &WidgetGallery::changePalette);
+ connect(disableWidgetsCheckBox, &QCheckBox::toggled,
+ topLeftGroupBox, &QGroupBox::setDisabled);
+ connect(disableWidgetsCheckBox, &QCheckBox::toggled,
+ topRightGroupBox, &QGroupBox::setDisabled);
+ connect(disableWidgetsCheckBox, &QCheckBox::toggled,
+ bottomLeftTabWidget, &QGroupBox::setDisabled);
+ connect(disableWidgetsCheckBox, &QCheckBox::toggled,
+ bottomRightGroupBox, &QGroupBox::setDisabled);
+//! [2]
+
+//! [3]
+ QHBoxLayout *topLayout = new QHBoxLayout;
+//! [3] //! [4]
+ topLayout->addWidget(styleLabel);
+ topLayout->addWidget(styleComboBox);
+ topLayout->addStretch(1);
+ topLayout->addWidget(useStylePaletteCheckBox);
+ topLayout->addWidget(disableWidgetsCheckBox);
+
+ QGridLayout *mainLayout = new QGridLayout;
+ mainLayout->addLayout(topLayout, 0, 0, 1, 2);
+ mainLayout->addWidget(topLeftGroupBox, 1, 0);
+ mainLayout->addWidget(topRightGroupBox, 1, 1);
+ mainLayout->addWidget(bottomLeftTabWidget, 2, 0);
+ mainLayout->addWidget(bottomRightGroupBox, 2, 1);
+ mainLayout->addWidget(progressBar, 3, 0, 1, 2);
+ mainLayout->setRowStretch(1, 1);
+ mainLayout->setRowStretch(2, 1);
+ mainLayout->setColumnStretch(0, 1);
+ mainLayout->setColumnStretch(1, 1);
+ setLayout(mainLayout);
+
+ setWindowTitle(tr("Styles"));
+ styleChanged();
+}
+//! [4]
+
+//! [5]
+void WidgetGallery::changeStyle(const QString &styleName)
+//! [5] //! [6]
+{
+ if (styleName == "NorwegianWood")
+ QApplication::setStyle(new NorwegianWoodStyle);
+ else
+ QApplication::setStyle(QStyleFactory::create(styleName));
+}
+//! [6]
+
+//! [7]
+void WidgetGallery::changePalette()
+//! [7] //! [8]
+{
+ QApplication::setPalette(useStylePaletteCheckBox->isChecked() ?
+ QApplication::style()->standardPalette() : QPalette());
+}
+//! [8]
+
+void WidgetGallery::changeEvent(QEvent *event)
+{
+ if (event->type() == QEvent::StyleChange)
+ styleChanged();
+}
+
+void WidgetGallery::styleChanged()
+{
+ auto styleName = QApplication::style()->objectName();
+ for (int i = 0; i < styleComboBox->count(); ++i) {
+ if (QString::compare(styleComboBox->itemText(i), styleName, Qt::CaseInsensitive) == 0) {
+ styleComboBox->setCurrentIndex(i);
+ break;
+ }
+ }
+
+ changePalette();
+}
+
+//! [9]
+void WidgetGallery::advanceProgressBar()
+//! [9] //! [10]
+{
+ int curVal = progressBar->value();
+ int maxVal = progressBar->maximum();
+ progressBar->setValue(curVal + (maxVal - curVal) / 100);
+}
+//! [10]
+
+//! [11]
+void WidgetGallery::createTopLeftGroupBox()
+//! [11] //! [12]
+{
+ topLeftGroupBox = new QGroupBox(tr("Group 1"));
+
+ radioButton1 = new QRadioButton(tr("Radio button 1"));
+ radioButton2 = new QRadioButton(tr("Radio button 2"));
+ radioButton3 = new QRadioButton(tr("Radio button 3"));
+ radioButton1->setChecked(true);
+
+ checkBox = new QCheckBox(tr("Tri-state check box"));
+ checkBox->setTristate(true);
+ checkBox->setCheckState(Qt::PartiallyChecked);
+
+ QVBoxLayout *layout = new QVBoxLayout;
+ layout->addWidget(radioButton1);
+ layout->addWidget(radioButton2);
+ layout->addWidget(radioButton3);
+ layout->addWidget(checkBox);
+ layout->addStretch(1);
+ topLeftGroupBox->setLayout(layout);
+}
+//! [12]
+
+void WidgetGallery::createTopRightGroupBox()
+{
+ topRightGroupBox = new QGroupBox(tr("Group 2"));
+
+ defaultPushButton = new QPushButton(tr("Default Push Button"));
+ defaultPushButton->setDefault(true);
+
+ togglePushButton = new QPushButton(tr("Toggle Push Button"));
+ togglePushButton->setCheckable(true);
+ togglePushButton->setChecked(true);
+
+ flatPushButton = new QPushButton(tr("Flat Push Button"));
+ flatPushButton->setFlat(true);
+
+ QVBoxLayout *layout = new QVBoxLayout;
+ layout->addWidget(defaultPushButton);
+ layout->addWidget(togglePushButton);
+ layout->addWidget(flatPushButton);
+ layout->addStretch(1);
+ topRightGroupBox->setLayout(layout);
+}
+
+void WidgetGallery::createBottomLeftTabWidget()
+{
+ bottomLeftTabWidget = new QTabWidget;
+ bottomLeftTabWidget->setSizePolicy(QSizePolicy::Preferred,
+ QSizePolicy::Ignored);
+
+ QWidget *tab1 = new QWidget;
+ tableWidget = new QTableWidget(10, 10);
+
+ QHBoxLayout *tab1hbox = new QHBoxLayout;
+ tab1hbox->setContentsMargins(5,5, 5, 5);
+ tab1hbox->addWidget(tableWidget);
+ tab1->setLayout(tab1hbox);
+
+ QWidget *tab2 = new QWidget;
+ textEdit = new QTextEdit;
+
+ textEdit->setPlainText(tr("Twinkle, twinkle, little star,\n"
+ "How I wonder what you are.\n"
+ "Up above the world so high,\n"
+ "Like a diamond in the sky.\n"
+ "Twinkle, twinkle, little star,\n"
+ "How I wonder what you are!\n"));
+
+ QHBoxLayout *tab2hbox = new QHBoxLayout;
+ tab2hbox->setContentsMargins(5, 5, 5, 5);
+ tab2hbox->addWidget(textEdit);
+ tab2->setLayout(tab2hbox);
+
+ bottomLeftTabWidget->addTab(tab1, tr("&Table"));
+ bottomLeftTabWidget->addTab(tab2, tr("Text &Edit"));
+}
+
+void WidgetGallery::createBottomRightGroupBox()
+{
+ bottomRightGroupBox = new QGroupBox(tr("Group 3"));
+ bottomRightGroupBox->setCheckable(true);
+ bottomRightGroupBox->setChecked(true);
+
+ lineEdit = new QLineEdit("s3cRe7");
+ lineEdit->setEchoMode(QLineEdit::Password);
+
+ spinBox = new QSpinBox(bottomRightGroupBox);
+ spinBox->setValue(50);
+
+ dateTimeEdit = new QDateTimeEdit(bottomRightGroupBox);
+ dateTimeEdit->setDateTime(QDateTime::currentDateTime());
+
+ slider = new QSlider(Qt::Horizontal, bottomRightGroupBox);
+ slider->setValue(40);
+
+ scrollBar = new QScrollBar(Qt::Horizontal, bottomRightGroupBox);
+ scrollBar->setValue(60);
+
+ dial = new QDial(bottomRightGroupBox);
+ dial->setValue(30);
+ dial->setNotchesVisible(true);
+
+ QGridLayout *layout = new QGridLayout;
+ layout->addWidget(lineEdit, 0, 0, 1, 2);
+ layout->addWidget(spinBox, 1, 0, 1, 2);
+ layout->addWidget(dateTimeEdit, 2, 0, 1, 2);
+ layout->addWidget(slider, 3, 0);
+ layout->addWidget(scrollBar, 4, 0);
+ layout->addWidget(dial, 3, 1, 2, 1);
+ layout->setRowStretch(5, 1);
+ bottomRightGroupBox->setLayout(layout);
+}
+
+//! [13]
+void WidgetGallery::createProgressBar()
+{
+ progressBar = new QProgressBar;
+ progressBar->setRange(0, 10000);
+ progressBar->setValue(0);
+
+ QTimer *timer = new QTimer(this);
+ connect(timer, &QTimer::timeout, this, &WidgetGallery::advanceProgressBar);
+ timer->start(1000);
+}
+//! [13]
diff --git a/tests/manual/examples/widgets/widgets/styles/widgetgallery.h b/tests/manual/examples/widgets/widgets/styles/widgetgallery.h
new file mode 100644
index 0000000000..f4c43e7b4f
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/styles/widgetgallery.h
@@ -0,0 +1,86 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef WIDGETGALLERY_H
+#define WIDGETGALLERY_H
+
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+class QCheckBox;
+class QComboBox;
+class QDateTimeEdit;
+class QDial;
+class QGroupBox;
+class QLabel;
+class QLineEdit;
+class QProgressBar;
+class QPushButton;
+class QRadioButton;
+class QScrollBar;
+class QSlider;
+class QSpinBox;
+class QTabWidget;
+class QTableWidget;
+class QTextEdit;
+QT_END_NAMESPACE
+
+//! [0]
+class WidgetGallery : public QDialog
+{
+ Q_OBJECT
+
+public:
+ WidgetGallery(QWidget *parent = nullptr);
+
+protected:
+ void changeEvent(QEvent *) override;
+
+private slots:
+ void changeStyle(const QString &styleName);
+ void styleChanged();
+ void changePalette();
+ void advanceProgressBar();
+
+private:
+ void createTopLeftGroupBox();
+ void createTopRightGroupBox();
+ void createBottomLeftTabWidget();
+ void createBottomRightGroupBox();
+ void createProgressBar();
+
+ QLabel *styleLabel;
+ QComboBox *styleComboBox;
+ QCheckBox *useStylePaletteCheckBox;
+ QCheckBox *disableWidgetsCheckBox;
+//! [0]
+
+ QGroupBox *topLeftGroupBox;
+ QRadioButton *radioButton1;
+ QRadioButton *radioButton2;
+ QRadioButton *radioButton3;
+ QCheckBox *checkBox;
+
+ QGroupBox *topRightGroupBox;
+ QPushButton *defaultPushButton;
+ QPushButton *togglePushButton;
+ QPushButton *flatPushButton;
+
+ QTabWidget *bottomLeftTabWidget;
+ QTableWidget *tableWidget;
+ QTextEdit *textEdit;
+
+ QGroupBox *bottomRightGroupBox;
+ QLineEdit *lineEdit;
+ QSpinBox *spinBox;
+ QDateTimeEdit *dateTimeEdit;
+ QSlider *slider;
+ QScrollBar *scrollBar;
+ QDial *dial;
+
+ QProgressBar *progressBar;
+//! [1]
+};
+//! [1]
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/CMakeLists.txt b/tests/manual/examples/widgets/widgets/stylesheet/CMakeLists.txt
new file mode 100644
index 0000000000..7937f6076f
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/CMakeLists.txt
@@ -0,0 +1,84 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(stylesheet LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/widgets/stylesheet")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(stylesheet
+ main.cpp
+ mainwindow.cpp mainwindow.h mainwindow.ui
+ stylesheeteditor.cpp stylesheeteditor.h stylesheeteditor.ui
+)
+
+set_target_properties(stylesheet PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(stylesheet PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(stylesheet_resource_files
+ "images/checkbox_checked.png"
+ "images/checkbox_checked_hover.png"
+ "images/checkbox_checked_pressed.png"
+ "images/checkbox_unchecked.png"
+ "images/checkbox_unchecked_hover.png"
+ "images/checkbox_unchecked_pressed.png"
+ "images/down_arrow.png"
+ "images/down_arrow_disabled.png"
+ "images/frame.png"
+ "images/pagefold.png"
+ "images/pushbutton.png"
+ "images/pushbutton_hover.png"
+ "images/pushbutton_pressed.png"
+ "images/radiobutton_checked.png"
+ "images/radiobutton_checked_hover.png"
+ "images/radiobutton_checked_pressed.png"
+ "images/radiobutton_unchecked.png"
+ "images/radiobutton_unchecked_hover.png"
+ "images/radiobutton_unchecked_pressed.png"
+ "images/sizegrip.png"
+ "images/spindown.png"
+ "images/spindown_hover.png"
+ "images/spindown_off.png"
+ "images/spindown_pressed.png"
+ "images/spinup.png"
+ "images/spinup_hover.png"
+ "images/spinup_off.png"
+ "images/spinup_pressed.png"
+ "images/up_arrow.png"
+ "images/up_arrow_disabled.png"
+ "layouts/default.ui"
+ "layouts/pagefold.ui"
+ "qss/coffee.qss"
+ "qss/default.qss"
+ "qss/pagefold.qss"
+)
+
+qt_add_resources(stylesheet "stylesheet"
+ PREFIX
+ "/"
+ FILES
+ ${stylesheet_resource_files}
+)
+
+install(TARGETS stylesheet
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_checked.png b/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_checked.png
new file mode 100644
index 0000000000..cbf06f6513
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_checked.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_checked_hover.png b/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_checked_hover.png
new file mode 100644
index 0000000000..fb4d4d3fdb
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_checked_hover.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_checked_pressed.png b/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_checked_pressed.png
new file mode 100644
index 0000000000..852fcc0306
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_checked_pressed.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_unchecked.png b/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_unchecked.png
new file mode 100644
index 0000000000..5f5465582a
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_unchecked.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_unchecked_hover.png b/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_unchecked_hover.png
new file mode 100644
index 0000000000..687364497e
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_unchecked_hover.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_unchecked_pressed.png b/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_unchecked_pressed.png
new file mode 100644
index 0000000000..6a768c44de
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/checkbox_unchecked_pressed.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/down_arrow.png b/tests/manual/examples/widgets/widgets/stylesheet/images/down_arrow.png
new file mode 100644
index 0000000000..85004aeaf6
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/down_arrow.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/down_arrow_disabled.png b/tests/manual/examples/widgets/widgets/stylesheet/images/down_arrow_disabled.png
new file mode 100644
index 0000000000..d9eefed4c8
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/down_arrow_disabled.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/frame.png b/tests/manual/examples/widgets/widgets/stylesheet/images/frame.png
new file mode 100644
index 0000000000..05a600502e
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/frame.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/pagefold.png b/tests/manual/examples/widgets/widgets/stylesheet/images/pagefold.png
new file mode 100644
index 0000000000..69be8f2a38
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/pagefold.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/pushbutton.png b/tests/manual/examples/widgets/widgets/stylesheet/images/pushbutton.png
new file mode 100644
index 0000000000..c7529ba02d
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/pushbutton.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/pushbutton_hover.png b/tests/manual/examples/widgets/widgets/stylesheet/images/pushbutton_hover.png
new file mode 100644
index 0000000000..af0ef9d561
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/pushbutton_hover.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/pushbutton_pressed.png b/tests/manual/examples/widgets/widgets/stylesheet/images/pushbutton_pressed.png
new file mode 100644
index 0000000000..f7381f752a
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/pushbutton_pressed.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_checked.png b/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_checked.png
new file mode 100644
index 0000000000..8ab9157578
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_checked.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_checked_hover.png b/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_checked_hover.png
new file mode 100644
index 0000000000..d68cb009a7
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_checked_hover.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_checked_pressed.png b/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_checked_pressed.png
new file mode 100644
index 0000000000..e3cd5a59e4
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_checked_pressed.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_unchecked.png b/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_unchecked.png
new file mode 100644
index 0000000000..321a9a1558
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_unchecked.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_unchecked_hover.png b/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_unchecked_hover.png
new file mode 100644
index 0000000000..666a3b0bdd
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_unchecked_hover.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_unchecked_pressed.png b/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_unchecked_pressed.png
new file mode 100644
index 0000000000..c4b0567aa9
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/radiobutton_unchecked_pressed.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/sizegrip.png b/tests/manual/examples/widgets/widgets/stylesheet/images/sizegrip.png
new file mode 100644
index 0000000000..350583aaac
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/sizegrip.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/spindown.png b/tests/manual/examples/widgets/widgets/stylesheet/images/spindown.png
new file mode 100644
index 0000000000..7ff3c64926
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/spindown.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/spindown_hover.png b/tests/manual/examples/widgets/widgets/stylesheet/images/spindown_hover.png
new file mode 100644
index 0000000000..1486c4dfe4
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/spindown_hover.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/spindown_off.png b/tests/manual/examples/widgets/widgets/stylesheet/images/spindown_off.png
new file mode 100644
index 0000000000..a90ab3f038
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/spindown_off.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/spindown_pressed.png b/tests/manual/examples/widgets/widgets/stylesheet/images/spindown_pressed.png
new file mode 100644
index 0000000000..f6271cbde2
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/spindown_pressed.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/spinup.png b/tests/manual/examples/widgets/widgets/stylesheet/images/spinup.png
new file mode 100644
index 0000000000..1069dd00df
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/spinup.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/spinup_hover.png b/tests/manual/examples/widgets/widgets/stylesheet/images/spinup_hover.png
new file mode 100644
index 0000000000..884c8d77c5
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/spinup_hover.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/spinup_off.png b/tests/manual/examples/widgets/widgets/stylesheet/images/spinup_off.png
new file mode 100644
index 0000000000..02dad1fb29
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/spinup_off.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/spinup_pressed.png b/tests/manual/examples/widgets/widgets/stylesheet/images/spinup_pressed.png
new file mode 100644
index 0000000000..b1843e2a1f
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/spinup_pressed.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/up_arrow.png b/tests/manual/examples/widgets/widgets/stylesheet/images/up_arrow.png
new file mode 100644
index 0000000000..e7f7ddb3a6
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/up_arrow.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/images/up_arrow_disabled.png b/tests/manual/examples/widgets/widgets/stylesheet/images/up_arrow_disabled.png
new file mode 100644
index 0000000000..4d2c27770f
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/images/up_arrow_disabled.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/layouts/default.ui b/tests/manual/examples/widgets/widgets/stylesheet/layouts/default.ui
new file mode 100644
index 0000000000..431a40956e
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/layouts/default.ui
@@ -0,0 +1,329 @@
+<ui version="4.0" >
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>388</width>
+ <height>413</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralwidget" >
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="nameLabel" >
+ <property name="text" >
+ <string>&amp;Name:</string>
+ </property>
+ <property name="buddy" >
+ <cstring>nameCombo</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="3" >
+ <widget class="QComboBox" name="nameCombo" >
+ <property name="toolTip" >
+ <string>Specify your name</string>
+ </property>
+ <property name="editable" >
+ <bool>true</bool>
+ </property>
+ <property name="currentIndex" >
+ <number>-1</number>
+ </property>
+ <item>
+ <property name="text" >
+ <string>Girish</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Jasmin</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Simon</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Zack</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="3" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="2" >
+ <widget class="QRadioButton" name="femaleRadioButton" >
+ <property name="toolTip" >
+ <string>Check this if you are female</string>
+ </property>
+ <property name="text" >
+ <string>&amp;Female</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0" colspan="4" >
+ <widget class="QCheckBox" name="agreeCheckBox" >
+ <property name="toolTip" >
+ <string>Please read the license before checking this</string>
+ </property>
+ <property name="text" >
+ <string>I &amp;accept the terms and conditions</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QRadioButton" name="maleRadioButton" >
+ <property name="toolTip" >
+ <string>Check this if you are male</string>
+ </property>
+ <property name="text" >
+ <string>&amp;Male</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="genderLabel" >
+ <property name="text" >
+ <string>Gender:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" colspan="3" >
+ <widget class="QSpinBox" name="ageSpinBox" >
+ <property name="toolTip" >
+ <string>Specify your age</string>
+ </property>
+ <property name="statusTip" >
+ <string>Specify your age here</string>
+ </property>
+ <property name="minimum" >
+ <number>12</number>
+ </property>
+ <property name="value" >
+ <number>22</number>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="2" colspan="2" >
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="ageLabel" >
+ <property name="text" >
+ <string>&amp;Age:</string>
+ </property>
+ <property name="buddy" >
+ <cstring>ageSpinBox</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="passwordLabel" >
+ <property name="text" >
+ <string>&amp;Password:</string>
+ </property>
+ <property name="buddy" >
+ <cstring>passwordEdit</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" colspan="3" >
+ <widget class="QLineEdit" name="passwordEdit" >
+ <property name="toolTip" >
+ <string>Specify your password</string>
+ </property>
+ <property name="statusTip" >
+ <string>Specify your password here</string>
+ </property>
+ <property name="text" >
+ <string>Password</string>
+ </property>
+ <property name="echoMode" >
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Profession</string>
+ </property>
+ <property name="buddy" >
+ <cstring>professionList</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="countryLabel" >
+ <property name="text" >
+ <string>&amp;Country</string>
+ </property>
+ <property name="buddy" >
+ <cstring>professionList</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1" colspan="3" >
+ <widget class="QListWidget" name="professionList" >
+ <property name="toolTip" >
+ <string>Select your profession</string>
+ </property>
+ <property name="statusTip" >
+ <string>Select your profession</string>
+ </property>
+ <property name="whatsThis" >
+ <string>Select your profession</string>
+ </property>
+ <property name="currentRow" >
+ <number>1</number>
+ </property>
+ <item>
+ <property name="text" >
+ <string>Developer</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Student</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Fisherman</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="4" column="1" colspan="3" >
+ <widget class="QComboBox" name="countryCombo" >
+ <property name="toolTip" >
+ <string>Specify your country</string>
+ </property>
+ <property name="statusTip" >
+ <string>Specify your country here</string>
+ </property>
+ <property name="currentIndex" >
+ <number>2</number>
+ </property>
+ <item>
+ <property name="text" >
+ <string>Germany</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>India</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Norway</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>United States Of America</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>United Kingdom</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>388</width>
+ <height>21</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menu_File" >
+ <property name="title" >
+ <string>&amp;File</string>
+ </property>
+ <addaction name="editStyleAction" />
+ <addaction name="separator" />
+ <addaction name="exitAction" />
+ </widget>
+ <widget class="QMenu" name="menu_Help" >
+ <property name="title" >
+ <string>&amp;Help</string>
+ </property>
+ <addaction name="aboutAction" />
+ <addaction name="separator" />
+ <addaction name="aboutQtAction" />
+ </widget>
+ <addaction name="menu_File" />
+ <addaction name="menu_Help" />
+ </widget>
+ <widget class="QStatusBar" name="statusbar" />
+ <action name="exitAction" >
+ <property name="text" >
+ <string>&amp;Exit</string>
+ </property>
+ </action>
+ <action name="aboutQtAction" >
+ <property name="text" >
+ <string>About Qt</string>
+ </property>
+ </action>
+ <action name="editStyleAction" >
+ <property name="text" >
+ <string>Edit &amp;Style</string>
+ </property>
+ </action>
+ <action name="aboutAction" >
+ <property name="text" >
+ <string>About</string>
+ </property>
+ </action>
+ </widget>
+ <tabstops>
+ <tabstop>maleRadioButton</tabstop>
+ <tabstop>femaleRadioButton</tabstop>
+ <tabstop>ageSpinBox</tabstop>
+ <tabstop>passwordEdit</tabstop>
+ <tabstop>professionList</tabstop>
+ <tabstop>agreeCheckBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/layouts/pagefold.ui b/tests/manual/examples/widgets/widgets/stylesheet/layouts/pagefold.ui
new file mode 100644
index 0000000000..7ae313c331
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/layouts/pagefold.ui
@@ -0,0 +1,349 @@
+<ui version="4.0" >
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>392</width>
+ <height>412</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralwidget" >
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="mainFrame" >
+ <property name="frameShape" >
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow" >
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="0" column="1" colspan="3" >
+ <widget class="QComboBox" name="nameCombo" >
+ <property name="toolTip" >
+ <string>Specify your name</string>
+ </property>
+ <property name="editable" >
+ <bool>true</bool>
+ </property>
+ <property name="currentIndex" >
+ <number>-1</number>
+ </property>
+ <item>
+ <property name="text" >
+ <string>Girish</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Jasmin</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Simon</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Zack</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="3" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="2" >
+ <widget class="QRadioButton" name="femaleRadioButton" >
+ <property name="styleSheet" >
+ <string>Check this if you are female</string>
+ </property>
+ <property name="text" >
+ <string>&amp;Female</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="genderLabel" >
+ <property name="text" >
+ <string>Gender:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="ageLabel" >
+ <property name="text" >
+ <string>&amp;Age:</string>
+ </property>
+ <property name="buddy" >
+ <cstring>ageSpinBox</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QRadioButton" name="maleRadioButton" >
+ <property name="toolTip" >
+ <string>Check this if you are male</string>
+ </property>
+ <property name="text" >
+ <string>&amp;Male</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="nameLabel" >
+ <property name="text" >
+ <string>&amp;Name:</string>
+ </property>
+ <property name="buddy" >
+ <cstring>nameCombo</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="passwordLabel" >
+ <property name="text" >
+ <string>&amp;Password:</string>
+ </property>
+ <property name="buddy" >
+ <cstring>passwordEdit</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" colspan="3" >
+ <widget class="QSpinBox" name="ageSpinBox" >
+ <property name="toolTip" >
+ <string>Specify your age</string>
+ </property>
+ <property name="statusTip" >
+ <string>Specify your age</string>
+ </property>
+ <property name="minimum" >
+ <number>12</number>
+ </property>
+ <property name="value" >
+ <number>22</number>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="2" colspan="2" >
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0" colspan="4" >
+ <widget class="QCheckBox" name="agreeCheckBox" >
+ <property name="toolTip" >
+ <string>Please read the LICENSE file before checking</string>
+ </property>
+ <property name="text" >
+ <string>I &amp;accept the terms and &amp;conditions</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" colspan="3" >
+ <widget class="QLineEdit" name="passwordEdit" >
+ <property name="toolTip" >
+ <string>Specify your password</string>
+ </property>
+ <property name="statusTip" >
+ <string>Specify your password</string>
+ </property>
+ <property name="text" >
+ <string>Password</string>
+ </property>
+ <property name="echoMode" >
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1" colspan="3" >
+ <widget class="QListWidget" name="professionList" >
+ <property name="toolTip" >
+ <string>Select your profession</string>
+ </property>
+ <property name="statusTip" >
+ <string>Specify your name here</string>
+ </property>
+ <property name="whatsThis" >
+ <string>Specify your name here</string>
+ </property>
+ <property name="currentRow" >
+ <number>0</number>
+ </property>
+ <item>
+ <property name="text" >
+ <string>Developer</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Student</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Fisherman</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Profession:</string>
+ </property>
+ <property name="buddy" >
+ <cstring>professionList</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" colspan="3" >
+ <widget class="QComboBox" name="countryCombo" >
+ <property name="toolTip" >
+ <string>Specify country of origin</string>
+ </property>
+ <property name="statusTip" >
+ <string>Specify country of origin</string>
+ </property>
+ <property name="currentIndex" >
+ <number>6</number>
+ </property>
+ <item>
+ <property name="text" >
+ <string>Egypt</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>France</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Germany</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>India</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Italy</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Korea</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Norway</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="countryLabel" >
+ <property name="text" >
+ <string>Pro&amp;fession</string>
+ </property>
+ <property name="buddy" >
+ <cstring>professionList</cstring>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>392</width>
+ <height>25</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menu_File" >
+ <property name="title" >
+ <string>&amp;File</string>
+ </property>
+ <addaction name="editStyleAction" />
+ <addaction name="separator" />
+ <addaction name="exitAction" />
+ </widget>
+ <widget class="QMenu" name="menu_Help" >
+ <property name="title" >
+ <string>&amp;Help</string>
+ </property>
+ <addaction name="aboutAction" />
+ <addaction name="separator" />
+ <addaction name="aboutQtAction" />
+ </widget>
+ <addaction name="menu_File" />
+ <addaction name="menu_Help" />
+ </widget>
+ <widget class="QStatusBar" name="statusbar" />
+ <action name="exitAction" >
+ <property name="text" >
+ <string>&amp;Exit</string>
+ </property>
+ </action>
+ <action name="aboutQtAction" >
+ <property name="text" >
+ <string>About Qt</string>
+ </property>
+ </action>
+ <action name="editStyleAction" >
+ <property name="text" >
+ <string>Edit &amp;Style</string>
+ </property>
+ </action>
+ <action name="aboutAction" >
+ <property name="text" >
+ <string>About</string>
+ </property>
+ </action>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/main.cpp b/tests/manual/examples/widgets/widgets/stylesheet/main.cpp
new file mode 100644
index 0000000000..7d7cf3e573
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/main.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ MainWindow window;
+ window.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/mainwindow.cpp b/tests/manual/examples/widgets/widgets/stylesheet/mainwindow.cpp
new file mode 100644
index 0000000000..a43613c757
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/mainwindow.cpp
@@ -0,0 +1,41 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+#include "stylesheeteditor.h"
+
+#include <QMessageBox>
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent)
+{
+ ui.setupUi(this);
+
+ ui.nameLabel->setProperty("class", "mandatory QLabel");
+ ui.nameCombo->lineEdit()->setPlaceholderText(tr("Last, First"));
+
+ styleSheetEditor = new StyleSheetEditor(this);
+
+ statusBar()->addWidget(new QLabel(tr("Ready")));
+
+ connect(ui.exitAction, &QAction::triggered, qApp, &QApplication::quit);
+ connect(ui.aboutQtAction, &QAction::triggered, qApp, &QApplication::aboutQt);
+}
+
+void MainWindow::on_editStyleAction_triggered()
+{
+ styleSheetEditor->show();
+ styleSheetEditor->activateWindow();
+}
+
+void MainWindow::on_aboutAction_triggered()
+{
+ const QString url = QStringLiteral("http://doc.qt.io/qt-%1/stylesheet.html")
+ .arg(QT_VERSION_MAJOR);
+ QMessageBox::about(this, tr("About Style sheet"),
+ tr("The <b>Style Sheet</b> example shows how widgets can be styled "
+ "using <a href=\"%1\">Qt "
+ "Style Sheets</a>. Click <b>File|Edit Style Sheet</b> to pop up the "
+ "style editor, and either choose an existing style sheet or design "
+ "your own.").arg(url));
+}
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/mainwindow.h b/tests/manual/examples/widgets/widgets/stylesheet/mainwindow.h
new file mode 100644
index 0000000000..877636bddf
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/mainwindow.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+
+#include "ui_mainwindow.h"
+
+class StyleSheetEditor;
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow(QWidget *parent = nullptr);
+
+private slots:
+ void on_editStyleAction_triggered();
+ void on_aboutAction_triggered();
+
+private:
+ StyleSheetEditor *styleSheetEditor;
+ Ui::MainWindow ui;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/mainwindow.ui b/tests/manual/examples/widgets/widgets/stylesheet/mainwindow.ui
new file mode 100644
index 0000000000..cc29257afb
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/mainwindow.ui
@@ -0,0 +1,356 @@
+<ui version="4.0" >
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>413</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Style Sheet</string>
+ </property>
+ <widget class="QWidget" name="centralwidget" >
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="mainFrame" >
+ <property name="frameShape" >
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow" >
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="6" column="0" colspan="5" >
+ <widget class="QCheckBox" name="agreeCheckBox" >
+ <property name="toolTip" >
+ <string>Please read the LICENSE file before checking</string>
+ </property>
+ <property name="text" >
+ <string>I accept the terms and &amp;conditions</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Profession:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing</set>
+ </property>
+ <property name="buddy" >
+ <cstring>professionList</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="nameLabel" >
+ <property name="text" >
+ <string>&amp;Name:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="buddy" >
+ <cstring>nameCombo</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QRadioButton" name="maleRadioButton" >
+ <property name="toolTip" >
+ <string>Check this if you are male</string>
+ </property>
+ <property name="text" >
+ <string>&amp;Male</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="passwordLabel" >
+ <property name="text" >
+ <string>&amp;Password:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="buddy" >
+ <cstring>passwordEdit</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" colspan="4" >
+ <widget class="QComboBox" name="countryCombo" >
+ <property name="toolTip" >
+ <string>Specify country of origin</string>
+ </property>
+ <property name="statusTip" >
+ <string>Specify country of origin</string>
+ </property>
+ <property name="currentIndex" >
+ <number>6</number>
+ </property>
+ <item>
+ <property name="text" >
+ <string>Egypt</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>France</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Germany</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>India</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Italy</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Norway</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Pakistan</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="ageLabel" >
+ <property name="text" >
+ <string>&amp;Age:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="buddy" >
+ <cstring>ageSpinBox</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="countryLabel" >
+ <property name="text" >
+ <string>Country:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="buddy" >
+ <cstring>countryCombo</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="genderLabel" >
+ <property name="text" >
+ <string>Gender:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" colspan="4" >
+ <widget class="QLineEdit" name="passwordEdit" >
+ <property name="toolTip" >
+ <string>Specify your password</string>
+ </property>
+ <property name="statusTip" >
+ <string>Specify your password</string>
+ </property>
+ <property name="text" >
+ <string>Password</string>
+ </property>
+ <property name="echoMode" >
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2" colspan="2" >
+ <widget class="QRadioButton" name="femaleRadioButton" >
+ <property name="toolTip">
+ <string>Check this if you are female</string>
+ </property>
+ <property name="text" >
+ <string>&amp;Female</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" colspan="2" >
+ <widget class="QSpinBox" name="ageSpinBox" >
+ <property name="toolTip" >
+ <string>Specify your age</string>
+ </property>
+ <property name="statusTip" >
+ <string>Specify your age</string>
+ </property>
+ <property name="minimum" >
+ <number>12</number>
+ </property>
+ <property name="value" >
+ <number>22</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="4" >
+ <widget class="QComboBox" name="nameCombo" >
+ <property name="toolTip" >
+ <string>Specify your name</string>
+ </property>
+ <property name="editable" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="3" colspan="2" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>61</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="7" column="3" colspan="2" >
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1" colspan="4" >
+ <widget class="QListWidget" name="professionList" >
+ <property name="toolTip" >
+ <string>Select your profession</string>
+ </property>
+ <property name="statusTip" >
+ <string>Specify your name here</string>
+ </property>
+ <property name="whatsThis" >
+ <string>Specify your name here</string>
+ </property>
+ <property name="currentRow" >
+ <number>0</number>
+ </property>
+ <item>
+ <property name="text" >
+ <string>Developer</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Student</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Fisherman</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>29</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menu_File" >
+ <property name="title" >
+ <string>&amp;File</string>
+ </property>
+ <addaction name="editStyleAction" />
+ <addaction name="separator" />
+ <addaction name="exitAction" />
+ </widget>
+ <widget class="QMenu" name="menu_Help" >
+ <property name="title" >
+ <string>&amp;Help</string>
+ </property>
+ <addaction name="aboutAction" />
+ <addaction name="aboutQtAction" />
+ </widget>
+ <addaction name="menu_File" />
+ <addaction name="menu_Help" />
+ </widget>
+ <widget class="QStatusBar" name="statusbar" />
+ <action name="exitAction" >
+ <property name="text" >
+ <string>&amp;Exit</string>
+ </property>
+ </action>
+ <action name="aboutQtAction" >
+ <property name="text" >
+ <string>About Qt</string>
+ </property>
+ </action>
+ <action name="editStyleAction" >
+ <property name="text" >
+ <string>Edit &amp;Style...</string>
+ </property>
+ </action>
+ <action name="aboutAction" >
+ <property name="text" >
+ <string>About</string>
+ </property>
+ </action>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/qss/coffee.qss b/tests/manual/examples/widgets/widgets/stylesheet/qss/coffee.qss
new file mode 100644
index 0000000000..8f72a15ee5
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/qss/coffee.qss
@@ -0,0 +1,117 @@
+.QWidget {
+ background-color: beige;
+}
+
+/* Nice Windows-XP-style password character. */
+QLineEdit[echoMode="2"] {
+ lineedit-password-character: 9679;
+}
+
+/* We provide a min-width and min-height for push buttons
+ so that they look elegant regardless of the width of the text. */
+QPushButton {
+ background-color: palegoldenrod;
+ border-width: 2px;
+ border-color: darkkhaki;
+ border-style: solid;
+ border-radius: 5;
+ padding: 3px;
+ min-width: 9ex;
+ min-height: 2.5ex;
+}
+
+QPushButton:hover {
+ background-color: khaki;
+}
+
+/* Increase the padding, so the text is shifted when the button is
+ pressed. */
+QPushButton:pressed {
+ padding-left: 5px;
+ padding-top: 5px;
+ background-color: #d0d67c;
+}
+
+QLabel, QAbstractButton {
+ font: bold;
+}
+
+/* Mark mandatory fields with a brownish color. */
+.mandatory {
+ color: brown;
+}
+
+/* Bold text on status bar looks awful. */
+QStatusBar QLabel {
+ font: normal;
+}
+
+QStatusBar::item {
+ border-width: 1;
+ border-color: darkkhaki;
+ border-style: solid;
+ border-radius: 2;
+}
+
+QComboBox, QLineEdit, QSpinBox, QTextEdit, QListView {
+ background-color: cornsilk;
+ selection-color: #0a214c;
+ selection-background-color: #C19A6B;
+}
+
+/* Make placeholder text a matching semi-transparent color */
+QComboBox, QLineEdit {
+ placeholder-text-color: #80C19A6B;
+}
+
+QListView {
+ show-decoration-selected: 1;
+}
+
+QListView::item:hover {
+ background-color: wheat;
+}
+
+/* We reserve 1 pixel space in padding. When we get the focus,
+ we kill the padding and enlarge the border. This makes the items
+ glow. */
+QLineEdit, QFrame {
+ border-width: 2px;
+ padding: 1px;
+ border-style: solid;
+ border-color: darkkhaki;
+ border-radius: 5px;
+}
+
+/* As mentioned above, eliminate the padding and increase the border. */
+QLineEdit:focus, QFrame:focus {
+ border-width: 3px;
+ padding: 0px;
+}
+
+/* A QLabel is a QFrame ... */
+QLabel {
+ border: none;
+ padding: 0;
+ background: none;
+}
+
+/* A QToolTip is a QLabel ... */
+QToolTip {
+ border: 2px solid darkkhaki;
+ padding: 5px;
+ border-radius: 3px;
+ opacity: 200;
+}
+
+/* Nice to have the background color change when hovered. */
+QRadioButton:hover, QCheckBox:hover {
+ background-color: wheat;
+}
+
+/* Force the dialog's buttons to follow the Windows guidelines. */
+QDialogButtonBox {
+ button-layout: 0;
+}
+
+
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/qss/default.qss b/tests/manual/examples/widgets/widgets/stylesheet/qss/default.qss
new file mode 100644
index 0000000000..84eb5e0bc2
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/qss/default.qss
@@ -0,0 +1 @@
+/* empty stylesheet */
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/qss/pagefold.qss b/tests/manual/examples/widgets/widgets/stylesheet/qss/pagefold.qss
new file mode 100644
index 0000000000..3701fc713c
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/qss/pagefold.qss
@@ -0,0 +1,299 @@
+/* Customize any plain widget that is a child of a QMainWindow. */
+QMainWindow > .QWidget {
+ background-color: gainsboro;
+ background-image: url(:/images/pagefold.png);
+ background-position: top right;
+ background-repeat: no-repeat
+}
+
+/* Provide a padding for the layout inside the frame. The frame
+ exists only to provide a padding for the top-right image, so we
+ explicitly disable the border. */
+#mainFrame {
+ padding-right: 30px;
+ border-style: none;
+ border-image: none; /* since we set a border-image below */
+}
+
+/* mainFrame won't have this border-image since we have
+ explicitly set it to 'none' using a more specific selector. */
+QFrame, QLineEdit, QComboBox[editable="true"], QSpinBox {
+ border-image: url(:/images/frame.png) 4;
+ border-width: 3;
+}
+
+QLabel {
+ border: none;
+ border-image: none;
+ padding: 0;
+ background: none;
+}
+
+/* Make text in message boxes selectable. */
+QMessageBox {
+ /* LinksAccessibleByMouse | TextSelectableByMouse */
+ messagebox-text-interaction-flags: 5;
+}
+
+/* Set the selection colors for all widgets. */
+QWidget {
+ selection-color: black;
+ selection-background-color: Silver;
+ color: black;
+}
+
+/* Make the entire row selected in item views. */
+QAbstractItemView {
+ show-decoration-selected: 1;
+}
+
+/* Nice WindowsXP-style password character for password line edits. */
+QLineEdit[echoMode="2"] {
+ lineedit-password-character: 9679;
+}
+
+/* Customize tooltips. */
+QToolTip {
+ background-color: rgb(200,200,255);
+ border-color: darkslategray;
+ border-width: 1px;
+ border-style: solid;
+ padding: 3px;
+ font: bold;
+ border-radius: 3px;
+ opacity: 200;
+}
+
+/* Customize radio buttons. */
+
+QRadioButton {
+ spacing: 5px;
+}
+
+QRadioButton::indicator {
+ width: 13px;
+ height: 13px;
+}
+
+QRadioButton::indicator::unchecked {
+ image: url(:/images/radiobutton_unchecked.png);
+}
+
+QRadioButton::indicator:unchecked:hover {
+ image: url(:/images/radiobutton_unchecked_hover.png);
+}
+
+QRadioButton::indicator:unchecked:pressed {
+ image: url(:/images/radiobutton_unchecked_pressed.png);
+}
+
+QRadioButton::indicator::checked {
+ image: url(:/images/radiobutton_checked.png);
+}
+
+QRadioButton::indicator:checked:hover {
+ image: url(:/images/radiobutton_checked_hover.png);
+}
+
+QRadioButton::indicator:checked:pressed {
+ image: url(:/images/radiobutton_checked_pressed.png);
+}
+
+/* Customize arrows. */
+
+*::down-arrow, *::menu-indicator {
+ image: url(:/images/down_arrow.png);
+ width: 7px;
+ height: 7px;
+}
+
+*::down-arrow:disabled, *::down-arrow:off {
+ image: url(:/images/down_arrow_disabled.png);
+}
+
+*::up-arrow {
+ image: url(:/images/up_arrow.png);
+ width: 7px;
+ height: 7px;
+}
+
+*::up-arrow:disabled, *::up-arrow:off {
+ image: url(:/images/up_arrow_disabled.png);
+}
+
+/* Customize push buttons and comboboxes. Our read-only combobox
+ is very similar to a push button, so they share the same border image. */
+
+QPushButton {
+ min-width: 4em;
+}
+
+QPushButton, QComboBox[editable="false"],
+QComboBox[editable="true"]::drop-down {
+ border-image: url(:/images/pushbutton.png) 5;
+ border-width: 5;
+}
+
+QPushButton:hover, QComboBox[editable="false"]:hover,
+QComboBox[editable="true"]::drop-down:hover, QMenuBar::item:hover {
+ border-image: url(:/images/pushbutton_hover.png) 5;
+ border-width: 5;
+}
+
+QPushButton:pressed, QComboBox[editable="false"]:on,
+QComboBox[editable="true"]::drop-down:on, QMenuBar::item:on {
+ border-image: url(:/images/pushbutton_pressed.png) 5;
+ border-width: 5;
+}
+
+/* Customize read-only comboboxes. */
+
+QComboBox[editable="false"] {
+ padding-left: 3px;
+ padding-right: 20px; /* space for the arrow */
+}
+
+QComboBox[editable="false"]::drop-down {
+ subcontrol-origin: padding;
+ subcontrol-position: top right;
+ width: 15px;
+ border-left-style: solid;
+ border-left-color: darkgray;
+ border-left-width: 1px;
+}
+
+QComboBox[editable="false"]::down-arrow {
+ subcontrol-origin: content;
+ subcontrol-position: center;
+ position: relative;
+ left: 1px; /* 1 pixel dropdown border */
+}
+
+/* The combobox arrow is on when the popup is open. */
+QComboBox[editable="false"]::down-arrow:on {
+ position: relative;
+ top: 1px;
+ left: 2px;
+}
+
+/* Customize editable comboboxes. */
+
+QComboBox[editable="true"] {
+ padding-right: 16px;
+}
+
+QComboBox[editable="true"]::drop-down {
+ subcontrol-origin: border;
+ subcontrol-position: top right;
+ width: 13px;
+ position: absolute;
+ top: 2px;
+ bottom: 2px;
+ right: 2px;
+}
+
+QComboBox[editable="true"]::drop-down,
+QComboBox[editable="true"]::drop-down:hover,
+QComboBox[editable="true"]::drop-down:on {
+ border-width: 0px;
+ border-left-width: 3px; /* we need only left and center part */
+}
+
+/* Shift the arrow when it's open. */
+QComboBox[editable="true"]::down-arrow:on {
+ position: relative;
+ top: 1px;
+ left: 1px;
+}
+
+/* Customize check boxes. */
+QCheckBox {
+ spacing: 5px;
+}
+
+QCheckBox::indicator {
+ width: 13px;
+ height: 13px;
+}
+
+QCheckBox::indicator:unchecked {
+ image: url(:/images/checkbox_unchecked.png);
+}
+
+QCheckBox::indicator:unchecked:hover {
+ image: url(:/images/checkbox_unchecked_hover.png);
+}
+
+QCheckBox::indicator:unchecked:pressed {
+ image: url(:/images/checkbox_unchecked_pressed.png);
+}
+
+QCheckBox::indicator:checked {
+ image: url(:/images/checkbox_checked.png);
+}
+
+QCheckBox::indicator:checked:hover {
+ image: url(:/images/checkbox_checked_hover.png);
+}
+
+QCheckBox::indicator:checked:pressed {
+ image: url(:/images/checkbox_checked_pressed.png);
+}
+
+/* Customize the size grip. */
+QSizeGrip {
+ image: url(:/images/sizegrip.png);
+ width: 16px;
+ height: 16px;
+}
+
+/* Customize the menu bar. */
+QMenuBar {
+ border-image: none;
+ border-style: none;
+ border-width: 1px;
+ border-bottom-style: solid;
+ border-bottom-color: darkslategray;
+ padding: 2px;
+}
+
+/* Customize spin boxes. */
+
+QSpinBox {
+ padding-right: 15px;
+}
+
+QSpinBox::up-button {
+ subcontrol-origin: border;
+ subcontrol-position: top right;
+
+ width: 16px; /* 16 + 2*1px border-width = 15px padding + 3px parent border */
+ border-image: url(:/images/spinup.png) 1;
+ border-width: 1px;
+}
+
+QSpinBox::up-button:hover {
+ border-image: url(:/images/spinup_hover.png) 1;
+}
+
+QSpinBox::up-button:pressed {
+ border-image: url(:/images/spinup_pressed.png) 1;
+}
+
+QSpinBox::down-button {
+ subcontrol-origin: border;
+ subcontrol-position: bottom right;
+
+ width: 16px;
+ border-image: url(:/images/spindown.png) 1;
+ border-width: 1px;
+ border-top-width: 0;
+}
+
+QSpinBox::down-button:hover {
+ border-image: url(:/images/spindown_hover.png) 1;
+}
+
+QSpinBox::down-button:pressed {
+ border-image: url(:/images/spindown_pressed.png) 1;
+}
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/stylesheet.pro b/tests/manual/examples/widgets/widgets/stylesheet/stylesheet.pro
new file mode 100644
index 0000000000..0fe3187f63
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/stylesheet.pro
@@ -0,0 +1,15 @@
+QT += widgets
+requires(qtConfig(combobox))
+
+HEADERS = mainwindow.h \
+ stylesheeteditor.h
+FORMS = mainwindow.ui \
+ stylesheeteditor.ui
+RESOURCES = stylesheet.qrc
+SOURCES = main.cpp \
+ mainwindow.cpp \
+ stylesheeteditor.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/widgets/stylesheet
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/stylesheet.qrc b/tests/manual/examples/widgets/widgets/stylesheet/stylesheet.qrc
new file mode 100644
index 0000000000..e1e61a920a
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/stylesheet.qrc
@@ -0,0 +1,39 @@
+<RCC>
+<qresource prefix="/">
+ <file>layouts/default.ui</file>
+ <file>layouts/pagefold.ui</file>
+ <file>qss/coffee.qss</file>
+ <file>qss/default.qss</file>
+ <file>qss/pagefold.qss</file>
+ <file>images/pagefold.png</file>
+ <file>images/frame.png</file>
+ <file>images/radiobutton_unchecked.png</file>
+ <file>images/radiobutton_unchecked_pressed.png</file>
+ <file>images/radiobutton_unchecked_hover.png</file>
+ <file>images/radiobutton_checked.png</file>
+ <file>images/radiobutton_checked_pressed.png</file>
+ <file>images/radiobutton_checked_hover.png</file>
+ <file>images/pushbutton.png</file>
+ <file>images/pushbutton_hover.png</file>
+ <file>images/pushbutton_pressed.png</file>
+ <file>images/checkbox_unchecked.png</file>
+ <file>images/checkbox_unchecked_pressed.png</file>
+ <file>images/checkbox_unchecked_hover.png</file>
+ <file>images/checkbox_checked.png</file>
+ <file>images/checkbox_checked_pressed.png</file>
+ <file>images/checkbox_checked_hover.png</file>
+ <file>images/down_arrow.png</file>
+ <file>images/down_arrow_disabled.png</file>
+ <file>images/up_arrow.png</file>
+ <file>images/up_arrow_disabled.png</file>
+ <file>images/sizegrip.png</file>
+ <file>images/spinup.png</file>
+ <file>images/spinup_off.png</file>
+ <file>images/spinup_hover.png</file>
+ <file>images/spinup_pressed.png</file>
+ <file>images/spindown.png</file>
+ <file>images/spindown_off.png</file>
+ <file>images/spindown_hover.png</file>
+ <file>images/spindown_pressed.png</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/stylesheeteditor.cpp b/tests/manual/examples/widgets/widgets/stylesheet/stylesheeteditor.cpp
new file mode 100644
index 0000000000..84fda79a16
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/stylesheeteditor.cpp
@@ -0,0 +1,64 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "stylesheeteditor.h"
+
+#include <QFile>
+#include <QRegularExpression>
+#include <QStyleFactory>
+
+StyleSheetEditor::StyleSheetEditor(QWidget *parent)
+ : QDialog(parent)
+{
+ ui.setupUi(this);
+
+ connect(ui.styleCombo, &QComboBox::textActivated, this, &StyleSheetEditor::setStyleName);
+ connect(ui.styleSheetCombo, &QComboBox::textActivated, this, &StyleSheetEditor::setStyleSheetName);
+ connect(ui.styleTextEdit, &QTextEdit::textChanged, this, &StyleSheetEditor::setModified);
+ connect(ui.applyButton, &QAbstractButton::clicked, this, &StyleSheetEditor::apply);
+
+ QRegularExpression regExp("^.(.*)\\+?Style$");
+ QString defaultStyle = QApplication::style()->metaObject()->className();
+ QRegularExpressionMatch match = regExp.match(defaultStyle);
+
+ if (match.hasMatch())
+ defaultStyle = match.captured(1);
+
+ ui.styleCombo->addItems(QStyleFactory::keys());
+ ui.styleCombo->setCurrentIndex(ui.styleCombo->findText(defaultStyle, Qt::MatchContains));
+ ui.styleSheetCombo->setCurrentIndex(ui.styleSheetCombo->findText("Coffee"));
+ loadStyleSheet("Coffee");
+}
+
+void StyleSheetEditor::setStyleName(const QString &styleName)
+{
+ qApp->setStyle(styleName);
+ ui.applyButton->setEnabled(false);
+}
+
+void StyleSheetEditor::setStyleSheetName(const QString &sheetName)
+{
+ loadStyleSheet(sheetName);
+}
+
+void StyleSheetEditor::setModified()
+{
+ ui.applyButton->setEnabled(true);
+}
+
+void StyleSheetEditor::apply()
+{
+ qApp->setStyleSheet(ui.styleTextEdit->toPlainText());
+ ui.applyButton->setEnabled(false);
+}
+
+void StyleSheetEditor::loadStyleSheet(const QString &sheetName)
+{
+ QFile file(":/qss/" + sheetName.toLower() + ".qss");
+ file.open(QFile::ReadOnly);
+ QString styleSheet = QString::fromLatin1(file.readAll());
+
+ ui.styleTextEdit->setPlainText(styleSheet);
+ qApp->setStyleSheet(styleSheet);
+ ui.applyButton->setEnabled(false);
+}
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/stylesheeteditor.h b/tests/manual/examples/widgets/widgets/stylesheet/stylesheeteditor.h
new file mode 100644
index 0000000000..e61534a4f9
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/stylesheeteditor.h
@@ -0,0 +1,30 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef STYLESHEETEDITOR_H
+#define STYLESHEETEDITOR_H
+
+#include <QDialog>
+
+#include "ui_stylesheeteditor.h"
+
+class StyleSheetEditor : public QDialog
+{
+ Q_OBJECT
+
+public:
+ StyleSheetEditor(QWidget *parent = nullptr);
+
+private slots:
+ void setStyleName(const QString &styleName);
+ void setStyleSheetName(const QString &styleSheetName);
+ void setModified();
+ void apply();
+
+private:
+ void loadStyleSheet(const QString &sheetName);
+
+ Ui::StyleSheetEditor ui;
+};
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/stylesheet/stylesheeteditor.ui b/tests/manual/examples/widgets/widgets/stylesheet/stylesheeteditor.ui
new file mode 100644
index 0000000000..34cd7f6d04
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/stylesheet/stylesheeteditor.ui
@@ -0,0 +1,171 @@
+<ui version="4.0" >
+ <class>StyleSheetEditor</class>
+ <widget class="QWidget" name="StyleSheetEditor" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>445</width>
+ <height>289</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Style Editor</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="0" column="6" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType" >
+ <enum>QSizePolicy::MinimumExpanding</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>32</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="0" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType" >
+ <enum>QSizePolicy::MinimumExpanding</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>32</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="5" >
+ <widget class="QComboBox" name="styleSheetCombo" >
+ <item>
+ <property name="text" >
+ <string>Default</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Coffee</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Pagefold</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="0" column="3" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType" >
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>10</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="2" >
+ <widget class="QComboBox" name="styleCombo" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLabel" name="label_7" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" >
+ <string>Style:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="7" >
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>321</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="applyButton" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>&amp;Apply</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0" colspan="7" >
+ <widget class="QTextEdit" name="styleTextEdit" />
+ </item>
+ <item row="0" column="4" >
+ <widget class="QLabel" name="label_8" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" >
+ <string>Style Sheet:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tests/manual/examples/widgets/widgets/tetrix/CMakeLists.txt b/tests/manual/examples/widgets/widgets/tetrix/CMakeLists.txt
new file mode 100644
index 0000000000..c591684ee6
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/tetrix/CMakeLists.txt
@@ -0,0 +1,39 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(tetrix LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/widgets/tetrix")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(tetrix
+ main.cpp
+ tetrixboard.cpp tetrixboard.h
+ tetrixpiece.cpp tetrixpiece.h
+ tetrixwindow.cpp tetrixwindow.h
+)
+
+set_target_properties(tetrix PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(tetrix PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS tetrix
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/widgets/tetrix/main.cpp b/tests/manual/examples/widgets/widgets/tetrix/main.cpp
new file mode 100644
index 0000000000..24791bde26
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/tetrix/main.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "tetrixwindow.h"
+
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ TetrixWindow window;
+ window.show();
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/widgets/tetrix/tetrix.pro b/tests/manual/examples/widgets/widgets/tetrix/tetrix.pro
new file mode 100644
index 0000000000..bb9f855fb0
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/tetrix/tetrix.pro
@@ -0,0 +1,13 @@
+QT += widgets
+
+HEADERS = tetrixboard.h \
+ tetrixpiece.h \
+ tetrixwindow.h
+SOURCES = main.cpp \
+ tetrixboard.cpp \
+ tetrixpiece.cpp \
+ tetrixwindow.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/widgets/tetrix
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/widgets/tetrix/tetrixboard.cpp b/tests/manual/examples/widgets/widgets/tetrix/tetrixboard.cpp
new file mode 100644
index 0000000000..0c5771ad89
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/tetrix/tetrixboard.cpp
@@ -0,0 +1,371 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "tetrixboard.h"
+
+#include <QKeyEvent>
+#include <QLabel>
+#include <QPainter>
+
+//! [0]
+TetrixBoard::TetrixBoard(QWidget *parent)
+ : QFrame(parent), isStarted(false), isPaused(false)
+{
+ setFrameStyle(QFrame::Panel | QFrame::Sunken);
+ setFocusPolicy(Qt::StrongFocus);
+ clearBoard();
+
+ nextPiece.setRandomShape();
+}
+//! [0]
+
+//! [1]
+void TetrixBoard::setNextPieceLabel(QLabel *label)
+{
+ nextPieceLabel = label;
+}
+//! [1]
+
+//! [2]
+QSize TetrixBoard::sizeHint() const
+{
+ return QSize(BoardWidth * 15 + frameWidth() * 2,
+ BoardHeight * 15 + frameWidth() * 2);
+}
+
+QSize TetrixBoard::minimumSizeHint() const
+//! [2] //! [3]
+{
+ return QSize(BoardWidth * 5 + frameWidth() * 2,
+ BoardHeight * 5 + frameWidth() * 2);
+}
+//! [3]
+
+//! [4]
+void TetrixBoard::start()
+{
+ if (isPaused)
+ return;
+
+ isStarted = true;
+ isWaitingAfterLine = false;
+ numLinesRemoved = 0;
+ numPiecesDropped = 0;
+ score = 0;
+ level = 1;
+ clearBoard();
+
+ emit linesRemovedChanged(numLinesRemoved);
+ emit scoreChanged(score);
+ emit levelChanged(level);
+
+ newPiece();
+ timer.start(timeoutTime(), this);
+}
+//! [4]
+
+//! [5]
+void TetrixBoard::pause()
+{
+ if (!isStarted)
+ return;
+
+ isPaused = !isPaused;
+ if (isPaused) {
+ timer.stop();
+ } else {
+ timer.start(timeoutTime(), this);
+ }
+ update();
+//! [5] //! [6]
+}
+//! [6]
+
+//! [7]
+void TetrixBoard::paintEvent(QPaintEvent *event)
+{
+ QFrame::paintEvent(event);
+
+ QPainter painter(this);
+ QRect rect = contentsRect();
+//! [7]
+
+ if (isPaused) {
+ painter.drawText(rect, Qt::AlignCenter, tr("Pause"));
+ return;
+ }
+
+//! [8]
+ int boardTop = rect.bottom() - BoardHeight*squareHeight();
+
+ for (int i = 0; i < BoardHeight; ++i) {
+ for (int j = 0; j < BoardWidth; ++j) {
+ TetrixShape shape = shapeAt(j, BoardHeight - i - 1);
+ if (shape != NoShape)
+ drawSquare(painter, rect.left() + j * squareWidth(),
+ boardTop + i * squareHeight(), shape);
+ }
+//! [8] //! [9]
+ }
+//! [9]
+
+//! [10]
+ if (curPiece.shape() != NoShape) {
+ for (int i = 0; i < 4; ++i) {
+ int x = curX + curPiece.x(i);
+ int y = curY - curPiece.y(i);
+ drawSquare(painter, rect.left() + x * squareWidth(),
+ boardTop + (BoardHeight - y - 1) * squareHeight(),
+ curPiece.shape());
+ }
+//! [10] //! [11]
+ }
+//! [11] //! [12]
+}
+//! [12]
+
+//! [13]
+void TetrixBoard::keyPressEvent(QKeyEvent *event)
+{
+ if (!isStarted || isPaused || curPiece.shape() == NoShape) {
+ QFrame::keyPressEvent(event);
+ return;
+ }
+//! [13]
+
+//! [14]
+ switch (event->key()) {
+ case Qt::Key_Left:
+ tryMove(curPiece, curX - 1, curY);
+ break;
+ case Qt::Key_Right:
+ tryMove(curPiece, curX + 1, curY);
+ break;
+ case Qt::Key_Down:
+ tryMove(curPiece.rotatedRight(), curX, curY);
+ break;
+ case Qt::Key_Up:
+ tryMove(curPiece.rotatedLeft(), curX, curY);
+ break;
+ case Qt::Key_Space:
+ dropDown();
+ break;
+ case Qt::Key_D:
+ oneLineDown();
+ break;
+ default:
+ QFrame::keyPressEvent(event);
+ }
+//! [14]
+}
+
+//! [15]
+void TetrixBoard::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == timer.timerId()) {
+ if (isWaitingAfterLine) {
+ isWaitingAfterLine = false;
+ newPiece();
+ timer.start(timeoutTime(), this);
+ } else {
+ oneLineDown();
+ }
+ } else {
+ QFrame::timerEvent(event);
+//! [15] //! [16]
+ }
+//! [16] //! [17]
+}
+//! [17]
+
+//! [18]
+void TetrixBoard::clearBoard()
+{
+ for (int i = 0; i < BoardHeight * BoardWidth; ++i)
+ board[i] = NoShape;
+}
+//! [18]
+
+//! [19]
+void TetrixBoard::dropDown()
+{
+ int dropHeight = 0;
+ int newY = curY;
+ while (newY > 0) {
+ if (!tryMove(curPiece, curX, newY - 1))
+ break;
+ --newY;
+ ++dropHeight;
+ }
+ pieceDropped(dropHeight);
+//! [19] //! [20]
+}
+//! [20]
+
+//! [21]
+void TetrixBoard::oneLineDown()
+{
+ if (!tryMove(curPiece, curX, curY - 1))
+ pieceDropped(0);
+}
+//! [21]
+
+//! [22]
+void TetrixBoard::pieceDropped(int dropHeight)
+{
+ for (int i = 0; i < 4; ++i) {
+ int x = curX + curPiece.x(i);
+ int y = curY - curPiece.y(i);
+ shapeAt(x, y) = curPiece.shape();
+ }
+
+ ++numPiecesDropped;
+ if (numPiecesDropped % 25 == 0) {
+ ++level;
+ timer.start(timeoutTime(), this);
+ emit levelChanged(level);
+ }
+
+ score += dropHeight + 7;
+ emit scoreChanged(score);
+ removeFullLines();
+
+ if (!isWaitingAfterLine)
+ newPiece();
+//! [22] //! [23]
+}
+//! [23]
+
+//! [24]
+void TetrixBoard::removeFullLines()
+{
+ int numFullLines = 0;
+
+ for (int i = BoardHeight - 1; i >= 0; --i) {
+ bool lineIsFull = true;
+
+ for (int j = 0; j < BoardWidth; ++j) {
+ if (shapeAt(j, i) == NoShape) {
+ lineIsFull = false;
+ break;
+ }
+ }
+
+ if (lineIsFull) {
+//! [24] //! [25]
+ ++numFullLines;
+ for (int k = i; k < BoardHeight - 1; ++k) {
+ for (int j = 0; j < BoardWidth; ++j)
+ shapeAt(j, k) = shapeAt(j, k + 1);
+ }
+//! [25] //! [26]
+ for (int j = 0; j < BoardWidth; ++j)
+ shapeAt(j, BoardHeight - 1) = NoShape;
+ }
+//! [26] //! [27]
+ }
+//! [27]
+
+//! [28]
+ if (numFullLines > 0) {
+ numLinesRemoved += numFullLines;
+ score += 10 * numFullLines;
+ emit linesRemovedChanged(numLinesRemoved);
+ emit scoreChanged(score);
+
+ timer.start(500, this);
+ isWaitingAfterLine = true;
+ curPiece.setShape(NoShape);
+ update();
+ }
+//! [28] //! [29]
+}
+//! [29]
+
+//! [30]
+void TetrixBoard::newPiece()
+{
+ curPiece = nextPiece;
+ nextPiece.setRandomShape();
+ showNextPiece();
+ curX = BoardWidth / 2 + 1;
+ curY = BoardHeight - 1 + curPiece.minY();
+
+ if (!tryMove(curPiece, curX, curY)) {
+ curPiece.setShape(NoShape);
+ timer.stop();
+ isStarted = false;
+ }
+//! [30] //! [31]
+}
+//! [31]
+
+//! [32]
+void TetrixBoard::showNextPiece()
+{
+ if (!nextPieceLabel)
+ return;
+
+ int dx = nextPiece.maxX() - nextPiece.minX() + 1;
+ int dy = nextPiece.maxY() - nextPiece.minY() + 1;
+
+ QPixmap pixmap(dx * squareWidth(), dy * squareHeight());
+ QPainter painter(&pixmap);
+ painter.fillRect(pixmap.rect(), nextPieceLabel->palette().window());
+
+ for (int i = 0; i < 4; ++i) {
+ int x = nextPiece.x(i) - nextPiece.minX();
+ int y = nextPiece.y(i) - nextPiece.minY();
+ drawSquare(painter, x * squareWidth(), y * squareHeight(),
+ nextPiece.shape());
+ }
+ nextPieceLabel->setPixmap(pixmap);
+//! [32] //! [33]
+}
+//! [33]
+
+//! [34]
+bool TetrixBoard::tryMove(const TetrixPiece &newPiece, int newX, int newY)
+{
+ for (int i = 0; i < 4; ++i) {
+ int x = newX + newPiece.x(i);
+ int y = newY - newPiece.y(i);
+ if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight)
+ return false;
+ if (shapeAt(x, y) != NoShape)
+ return false;
+ }
+//! [34]
+
+//! [35]
+ curPiece = newPiece;
+ curX = newX;
+ curY = newY;
+ update();
+ return true;
+}
+//! [35]
+
+//! [36]
+void TetrixBoard::drawSquare(QPainter &painter, int x, int y, TetrixShape shape)
+{
+ static constexpr QRgb colorTable[8] = {
+ 0x000000, 0xCC6666, 0x66CC66, 0x6666CC,
+ 0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00
+ };
+
+ QColor color = colorTable[int(shape)];
+ painter.fillRect(x + 1, y + 1, squareWidth() - 2, squareHeight() - 2,
+ color);
+
+ painter.setPen(color.lighter());
+ painter.drawLine(x, y + squareHeight() - 1, x, y);
+ painter.drawLine(x, y, x + squareWidth() - 1, y);
+
+ painter.setPen(color.darker());
+ painter.drawLine(x + 1, y + squareHeight() - 1,
+ x + squareWidth() - 1, y + squareHeight() - 1);
+ painter.drawLine(x + squareWidth() - 1, y + squareHeight() - 1,
+ x + squareWidth() - 1, y + 1);
+}
+//! [36]
diff --git a/tests/manual/examples/widgets/widgets/tetrix/tetrixboard.h b/tests/manual/examples/widgets/widgets/tetrix/tetrixboard.h
new file mode 100644
index 0000000000..c2fe3fd01e
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/tetrix/tetrixboard.h
@@ -0,0 +1,79 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef TETRIXBOARD_H
+#define TETRIXBOARD_H
+
+#include <QBasicTimer>
+#include <QFrame>
+#include <QPointer>
+
+#include "tetrixpiece.h"
+
+QT_BEGIN_NAMESPACE
+class QLabel;
+QT_END_NAMESPACE
+
+//! [0]
+class TetrixBoard : public QFrame
+{
+ Q_OBJECT
+
+public:
+ TetrixBoard(QWidget *parent = nullptr);
+
+ void setNextPieceLabel(QLabel *label);
+ QSize sizeHint() const override;
+ QSize minimumSizeHint() const override;
+
+public slots:
+ void start();
+ void pause();
+
+signals:
+ void scoreChanged(int score);
+ void levelChanged(int level);
+ void linesRemovedChanged(int numLines);
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+ void timerEvent(QTimerEvent *event) override;
+//! [0]
+
+//! [1]
+private:
+ enum { BoardWidth = 10, BoardHeight = 22 };
+
+ TetrixShape &shapeAt(int x, int y) { return board[(y * BoardWidth) + x]; }
+ int timeoutTime() { return 1000 / (1 + level); }
+ int squareWidth() { return contentsRect().width() / BoardWidth; }
+ int squareHeight() { return contentsRect().height() / BoardHeight; }
+ void clearBoard();
+ void dropDown();
+ void oneLineDown();
+ void pieceDropped(int dropHeight);
+ void removeFullLines();
+ void newPiece();
+ void showNextPiece();
+ bool tryMove(const TetrixPiece &newPiece, int newX, int newY);
+ void drawSquare(QPainter &painter, int x, int y, TetrixShape shape);
+
+ QBasicTimer timer;
+ QPointer<QLabel> nextPieceLabel;
+ bool isStarted;
+ bool isPaused;
+ bool isWaitingAfterLine;
+ TetrixPiece curPiece;
+ TetrixPiece nextPiece;
+ int curX;
+ int curY;
+ int numLinesRemoved;
+ int numPiecesDropped;
+ int score;
+ int level;
+ TetrixShape board[BoardWidth * BoardHeight];
+};
+//! [1]
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/tetrix/tetrixpiece.cpp b/tests/manual/examples/widgets/widgets/tetrix/tetrixpiece.cpp
new file mode 100644
index 0000000000..5dc9e11c49
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/tetrix/tetrixpiece.cpp
@@ -0,0 +1,106 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "tetrixpiece.h"
+
+#include <QtCore>
+
+//! [0]
+void TetrixPiece::setRandomShape()
+{
+ setShape(TetrixShape(QRandomGenerator::global()->bounded(7) + 1));
+}
+//! [0]
+
+//! [1]
+void TetrixPiece::setShape(TetrixShape shape)
+{
+ static constexpr int coordsTable[8][4][2] = {
+ { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
+ { { 0, -1 }, { 0, 0 }, { -1, 0 }, { -1, 1 } },
+ { { 0, -1 }, { 0, 0 }, { 1, 0 }, { 1, 1 } },
+ { { 0, -1 }, { 0, 0 }, { 0, 1 }, { 0, 2 } },
+ { { -1, 0 }, { 0, 0 }, { 1, 0 }, { 0, 1 } },
+ { { 0, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } },
+ { { -1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } },
+ { { 1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } }
+ };
+
+ for (int i = 0; i < 4 ; i++) {
+ for (int j = 0; j < 2; ++j)
+ coords[i][j] = coordsTable[shape][i][j];
+ }
+ pieceShape = shape;
+//! [1] //! [2]
+}
+//! [2]
+
+//! [3]
+int TetrixPiece::minX() const
+{
+ int min = coords[0][0];
+ for (int i = 1; i < 4; ++i)
+ min = qMin(min, coords[i][0]);
+ return min;
+}
+
+int TetrixPiece::maxX() const
+//! [3] //! [4]
+{
+ int max = coords[0][0];
+ for (int i = 1; i < 4; ++i)
+ max = qMax(max, coords[i][0]);
+ return max;
+}
+//! [4]
+
+//! [5]
+int TetrixPiece::minY() const
+{
+ int min = coords[0][1];
+ for (int i = 1; i < 4; ++i)
+ min = qMin(min, coords[i][1]);
+ return min;
+}
+
+int TetrixPiece::maxY() const
+//! [5] //! [6]
+{
+ int max = coords[0][1];
+ for (int i = 1; i < 4; ++i)
+ max = qMax(max, coords[i][1]);
+ return max;
+}
+//! [6]
+
+//! [7]
+TetrixPiece TetrixPiece::rotatedLeft() const
+{
+ if (pieceShape == SquareShape)
+ return *this;
+
+ TetrixPiece result;
+ result.pieceShape = pieceShape;
+ for (int i = 0; i < 4; ++i) {
+ result.setX(i, y(i));
+ result.setY(i, -x(i));
+ }
+//! [7]
+ return result;
+}
+
+//! [9]
+TetrixPiece TetrixPiece::rotatedRight() const
+{
+ if (pieceShape == SquareShape)
+ return *this;
+
+ TetrixPiece result;
+ result.pieceShape = pieceShape;
+ for (int i = 0; i < 4; ++i) {
+ result.setX(i, -y(i));
+ result.setY(i, x(i));
+ }
+//! [9]
+ return result;
+}
diff --git a/tests/manual/examples/widgets/widgets/tetrix/tetrixpiece.h b/tests/manual/examples/widgets/widgets/tetrix/tetrixpiece.h
new file mode 100644
index 0000000000..86a513a76e
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/tetrix/tetrixpiece.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef TETRIXPIECE_H
+#define TETRIXPIECE_H
+
+enum TetrixShape { NoShape, ZShape, SShape, LineShape, TShape, SquareShape,
+ LShape, MirroredLShape };
+
+//! [0]
+class TetrixPiece
+{
+public:
+ TetrixPiece() { setShape(NoShape); }
+
+ void setRandomShape();
+ void setShape(TetrixShape shape);
+
+ TetrixShape shape() const { return pieceShape; }
+ int x(int index) const { return coords[index][0]; }
+ int y(int index) const { return coords[index][1]; }
+ int minX() const;
+ int maxX() const;
+ int minY() const;
+ int maxY() const;
+ TetrixPiece rotatedLeft() const;
+ TetrixPiece rotatedRight() const;
+
+private:
+ void setX(int index, int x) { coords[index][0] = x; }
+ void setY(int index, int y) { coords[index][1] = y; }
+
+ TetrixShape pieceShape;
+ int coords[4][2];
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/tetrix/tetrixwindow.cpp b/tests/manual/examples/widgets/widgets/tetrix/tetrixwindow.cpp
new file mode 100644
index 0000000000..ae10a778e7
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/tetrix/tetrixwindow.cpp
@@ -0,0 +1,82 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "tetrixboard.h"
+#include "tetrixwindow.h"
+
+#include <QCoreApplication>
+#include <QGridLayout>
+#include <QLabel>
+#include <QLCDNumber>
+#include <QPushButton>
+
+//! [0]
+TetrixWindow::TetrixWindow(QWidget *parent)
+ : QWidget(parent), board(new TetrixBoard)
+{
+//! [0]
+ nextPieceLabel = new QLabel;
+ nextPieceLabel->setFrameStyle(QFrame::Box | QFrame::Raised);
+ nextPieceLabel->setAlignment(Qt::AlignCenter);
+ board->setNextPieceLabel(nextPieceLabel);
+//! [1]
+ scoreLcd = new QLCDNumber(5);
+ scoreLcd->setSegmentStyle(QLCDNumber::Filled);
+//! [1]
+ levelLcd = new QLCDNumber(2);
+ levelLcd->setSegmentStyle(QLCDNumber::Filled);
+ linesLcd = new QLCDNumber(5);
+ linesLcd->setSegmentStyle(QLCDNumber::Filled);
+
+//! [2]
+ startButton = new QPushButton(tr("&Start"));
+ startButton->setFocusPolicy(Qt::NoFocus);
+ quitButton = new QPushButton(tr("&Quit"));
+ quitButton->setFocusPolicy(Qt::NoFocus);
+ pauseButton = new QPushButton(tr("&Pause"));
+//! [2] //! [3]
+ pauseButton->setFocusPolicy(Qt::NoFocus);
+//! [3] //! [4]
+
+ connect(startButton, &QPushButton::clicked, board, &TetrixBoard::start);
+//! [4] //! [5]
+ connect(quitButton , &QPushButton::clicked, qApp, &QCoreApplication::quit);
+ connect(pauseButton, &QPushButton::clicked, board, &TetrixBoard::pause);
+ connect(board, &TetrixBoard::scoreChanged,
+ scoreLcd, qOverload<int>(&QLCDNumber::display));
+ connect(board, &TetrixBoard::levelChanged,
+ levelLcd, qOverload<int>(&QLCDNumber::display));
+ connect(board, &TetrixBoard::linesRemovedChanged,
+ linesLcd, qOverload<int>(&QLCDNumber::display));
+//! [5]
+
+//! [6]
+ QGridLayout *layout = new QGridLayout;
+ layout->addWidget(createLabel(tr("NEXT")), 0, 0);
+ layout->addWidget(nextPieceLabel, 1, 0);
+ layout->addWidget(createLabel(tr("LEVEL")), 2, 0);
+ layout->addWidget(levelLcd, 3, 0);
+ layout->addWidget(startButton, 4, 0);
+ layout->addWidget(board, 0, 1, 6, 1);
+ layout->addWidget(createLabel(tr("SCORE")), 0, 2);
+ layout->addWidget(scoreLcd, 1, 2);
+ layout->addWidget(createLabel(tr("LINES REMOVED")), 2, 2);
+ layout->addWidget(linesLcd, 3, 2);
+ layout->addWidget(quitButton, 4, 2);
+ layout->addWidget(pauseButton, 5, 2);
+ setLayout(layout);
+
+ setWindowTitle(tr("Tetrix"));
+ resize(550, 370);
+}
+//! [6]
+
+//! [7]
+QLabel *TetrixWindow::createLabel(const QString &text)
+{
+ QLabel *label = new QLabel(text);
+ label->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
+ return label;
+}
+//! [7]
+
diff --git a/tests/manual/examples/widgets/widgets/tetrix/tetrixwindow.h b/tests/manual/examples/widgets/widgets/tetrix/tetrixwindow.h
new file mode 100644
index 0000000000..2ed468717d
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/tetrix/tetrixwindow.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef TETRIXWINDOW_H
+#define TETRIXWINDOW_H
+
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QLCDNumber;
+class QLabel;
+class QPushButton;
+QT_END_NAMESPACE
+class TetrixBoard;
+
+//! [0]
+class TetrixWindow : public QWidget
+{
+ Q_OBJECT
+
+public:
+ TetrixWindow(QWidget *parent = nullptr);
+
+private:
+ QLabel *createLabel(const QString &text);
+
+ TetrixBoard *board;
+ QLabel *nextPieceLabel;
+ QLCDNumber *scoreLcd;
+ QLCDNumber *levelLcd;
+ QLCDNumber *linesLcd;
+ QPushButton *startButton;
+ QPushButton *quitButton;
+ QPushButton *pauseButton;
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/widgets/validators/CMakeLists.txt b/tests/manual/examples/widgets/widgets/validators/CMakeLists.txt
new file mode 100644
index 0000000000..653b8eba78
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/validators/CMakeLists.txt
@@ -0,0 +1,55 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(validators LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/widgets/validators")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(validators
+ ledwidget.cpp ledwidget.h
+ localeselector.cpp localeselector.h
+ main.cpp
+ validators.ui
+ validatorwidget.cpp validatorwidget.h
+)
+
+set_target_properties(validators PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(validators PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+# Resources:
+set(validators_resource_files
+ "ledoff.png"
+ "ledon.png"
+)
+
+qt_add_resources(validators "validators"
+ PREFIX
+ "/"
+ FILES
+ ${validators_resource_files}
+)
+
+install(TARGETS validators
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/widgets/validators/ledoff.png b/tests/manual/examples/widgets/widgets/validators/ledoff.png
new file mode 100644
index 0000000000..8b1f2ed123
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/validators/ledoff.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/validators/ledon.png b/tests/manual/examples/widgets/widgets/validators/ledon.png
new file mode 100644
index 0000000000..601c34d5a8
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/validators/ledon.png
Binary files differ
diff --git a/tests/manual/examples/widgets/widgets/validators/ledwidget.cpp b/tests/manual/examples/widgets/widgets/validators/ledwidget.cpp
new file mode 100644
index 0000000000..5b95595a75
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/validators/ledwidget.cpp
@@ -0,0 +1,25 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "ledwidget.h"
+
+LEDWidget::LEDWidget(QWidget *parent)
+ : QLabel(parent), onPixmap(":/ledon.png"), offPixmap(":/ledoff.png")
+{
+ setPixmap(offPixmap);
+ flashTimer.setInterval(200);
+ flashTimer.setSingleShot(true);
+ connect(&flashTimer, &QTimer::timeout, this, &LEDWidget::extinguish);
+};
+
+void LEDWidget::extinguish()
+{
+ setPixmap(offPixmap);
+}
+
+void LEDWidget::flash()
+{
+ setPixmap(onPixmap);
+ flashTimer.start();
+}
+
diff --git a/tests/manual/examples/widgets/widgets/validators/ledwidget.h b/tests/manual/examples/widgets/widgets/validators/ledwidget.h
new file mode 100644
index 0000000000..81215b668f
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/validators/ledwidget.h
@@ -0,0 +1,27 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef LEDWIDGET_H
+#define LEDWIDGET_H
+
+#include <QLabel>
+#include <QPixmap>
+#include <QTimer>
+
+class LEDWidget : public QLabel
+{
+ Q_OBJECT
+public:
+ LEDWidget(QWidget *parent = nullptr);
+public slots:
+ void flash();
+
+private slots:
+ void extinguish();
+
+private:
+ QPixmap onPixmap, offPixmap;
+ QTimer flashTimer;
+};
+
+#endif // LEDWIDGET_H
diff --git a/tests/manual/examples/widgets/widgets/validators/localeselector.cpp b/tests/manual/examples/widgets/widgets/validators/localeselector.cpp
new file mode 100644
index 0000000000..c74faa8c9d
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/validators/localeselector.cpp
@@ -0,0 +1,48 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "localeselector.h"
+
+#include <QLocale>
+
+LocaleSelector::LocaleSelector(QWidget *parent)
+ : QComboBox(parent)
+{
+ int curIndex = -1;
+ int index = 0;
+ for (int _lang = QLocale::C; _lang <= QLocale::LastLanguage; ++_lang) {
+ QLocale::Language lang = static_cast<QLocale::Language>(_lang);
+ const QList<QLocale> locales =
+ QLocale::matchingLocales(lang, QLocale::AnyScript, QLocale::AnyTerritory);
+ for (const QLocale &l : locales) {
+ QString label = QLocale::languageToString(l.language());
+ label += QLatin1Char('/');
+ label += QLocale::territoryToString(l.territory());
+ // distinguish locales by script, if there are more than one script for a language/territory pair
+ if (QLocale::matchingLocales(l.language(), QLocale::AnyScript, l.territory()).size() > 1)
+ label += QLatin1String(" (") + QLocale::scriptToString(l.script()) + QLatin1Char(')');
+
+ addItem(label, QVariant::fromValue(l));
+
+ if (l.language() == locale().language() && l.territory() == locale().territory()
+ && (locale().script() == QLocale::AnyScript || l.script() == locale().script())) {
+ curIndex = index;
+ }
+ ++index;
+ }
+ }
+ if (curIndex != -1)
+ setCurrentIndex(curIndex);
+
+ connect(this, QOverload<int>::of(&LocaleSelector::activated),
+ this, &LocaleSelector::emitLocaleSelected);
+}
+
+void LocaleSelector::emitLocaleSelected(int index)
+{
+ QVariant v = itemData(index);
+ if (!v.isValid())
+ return;
+ const QLocale l = qvariant_cast<QLocale>(v);
+ emit localeSelected(l);
+}
diff --git a/tests/manual/examples/widgets/widgets/validators/localeselector.h b/tests/manual/examples/widgets/widgets/validators/localeselector.h
new file mode 100644
index 0000000000..c684e18d40
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/validators/localeselector.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef LOCALESELECTOR_H
+#define LOCALESELECTOR_H
+
+#include <QComboBox>
+
+class LocaleSelector : public QComboBox
+{
+ Q_OBJECT
+
+public:
+ LocaleSelector(QWidget *parent = nullptr);
+
+signals:
+ void localeSelected(const QLocale &locale);
+
+private slots:
+ void emitLocaleSelected(int index);
+};
+
+#endif //LOCALESELECTOR_H
diff --git a/tests/manual/examples/widgets/widgets/validators/main.cpp b/tests/manual/examples/widgets/widgets/validators/main.cpp
new file mode 100644
index 0000000000..c09d428416
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/validators/main.cpp
@@ -0,0 +1,16 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "validatorwidget.h"
+
+#include <QApplication>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+ ValidatorWidget w;
+ w.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/widgets/validators/validators.pro b/tests/manual/examples/widgets/widgets/validators/validators.pro
new file mode 100644
index 0000000000..029cf95aca
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/validators/validators.pro
@@ -0,0 +1,12 @@
+QT += widgets
+requires(qtConfig(combobox))
+
+FORMS += validators.ui
+RESOURCES += validators.qrc
+
+SOURCES += main.cpp ledwidget.cpp localeselector.cpp validatorwidget.cpp
+HEADERS += ledwidget.h localeselector.h validatorwidget.h
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/widgets/validators
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/widgets/validators/validators.qrc b/tests/manual/examples/widgets/widgets/validators/validators.qrc
new file mode 100644
index 0000000000..94874317a8
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/validators/validators.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/" >
+ <file>ledoff.png</file>
+ <file>ledon.png</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/examples/widgets/widgets/validators/validators.ui b/tests/manual/examples/widgets/widgets/validators/validators.ui
new file mode 100644
index 0000000000..cd984e6b97
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/validators/validators.ui
@@ -0,0 +1,468 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ValidatorsForm</class>
+ <widget class="QWidget" name="ValidatorsForm">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>526</width>
+ <height>443</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Validators</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="LocaleSelector" name="localeSelector"/>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>QIntValidator</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QGridLayout">
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Min:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="minVal">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>-1000000</number>
+ </property>
+ <property name="maximum">
+ <number>1000000</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Max:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="maxVal">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>-1000000</number>
+ </property>
+ <property name="maximum">
+ <number>1000000</number>
+ </property>
+ <property name="value">
+ <number>1000</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QFrame" name="frame">
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="LEDWidget" name="ledWidget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="validators.qrc">:/ledoff.png</pixmap>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>editingFinished()</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="editor"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>QDoubleValidator</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QGridLayout">
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Min:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QDoubleSpinBox" name="doubleMinVal">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <double>-100000.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>100000.000000000000000</double>
+ </property>
+ <property name="value">
+ <double>0.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Format:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QComboBox" name="doubleFormat">
+ <item>
+ <property name="text">
+ <string>Standard</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Scientific</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Max:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDoubleSpinBox" name="doubleMaxVal">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <double>-100000.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>100000.000000000000000</double>
+ </property>
+ <property name="value">
+ <double>1000.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Decimals:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QSpinBox" name="doubleDecimals">
+ <property name="value">
+ <number>2</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QFrame" name="frame_2">
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="LEDWidget" name="doubleLedWidget">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="validators.qrc">:/ledoff.png</pixmap>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>editingFinished()</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="doubleEditor"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>111</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="pushButton">
+ <property name="text">
+ <string>Quit</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>LEDWidget</class>
+ <extends>QLabel</extends>
+ <header>ledwidget.h</header>
+ </customwidget>
+ <customwidget>
+ <class>LocaleSelector</class>
+ <extends>QComboBox</extends>
+ <header>localeselector.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="validators.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>pushButton</sender>
+ <signal>clicked()</signal>
+ <receiver>ValidatorsForm</receiver>
+ <slot>close()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>94</x>
+ <y>274</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>131</x>
+ <y>260</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/tests/manual/examples/widgets/widgets/validators/validatorwidget.cpp b/tests/manual/examples/widgets/widgets/validators/validatorwidget.cpp
new file mode 100644
index 0000000000..7782342221
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/validators/validatorwidget.cpp
@@ -0,0 +1,75 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "validatorwidget.h"
+
+#include <QIntValidator>
+
+ValidatorWidget::ValidatorWidget(QWidget *parent)
+ : QWidget(parent)
+{
+ setupUi(this);
+
+ connect(localeSelector, &LocaleSelector::localeSelected,
+ this, &ValidatorWidget::setLocale);
+ connect(localeSelector, &LocaleSelector::localeSelected,
+ this, &ValidatorWidget::updateValidator);
+ connect(localeSelector, &LocaleSelector::localeSelected,
+ this, &ValidatorWidget::updateDoubleValidator);
+
+ connect(minVal, &QSpinBox::editingFinished,
+ this, &ValidatorWidget::updateValidator);
+ connect(maxVal, &QSpinBox::editingFinished,
+ this, &ValidatorWidget::updateValidator);
+ connect(editor, &QLineEdit::editingFinished,
+ ledWidget, &LEDWidget::flash);
+
+ connect(doubleMaxVal, &QDoubleSpinBox::editingFinished,
+ this, &ValidatorWidget::updateDoubleValidator);
+ connect(doubleMinVal, &QDoubleSpinBox::editingFinished,
+ this, &ValidatorWidget::updateDoubleValidator);
+ connect(doubleDecimals, &QSpinBox::valueChanged,
+ this, &ValidatorWidget::updateDoubleValidator);
+ connect(doubleFormat, &QComboBox::activated,
+ this, &ValidatorWidget::updateDoubleValidator);
+ connect(doubleEditor, &QLineEdit::editingFinished,
+ doubleLedWidget, &LEDWidget::flash);
+
+ updateValidator();
+ updateDoubleValidator();
+}
+
+void ValidatorWidget::updateValidator()
+{
+ QIntValidator *v = new QIntValidator(minVal->value(), maxVal->value(), this);
+ v->setLocale(locale());
+ delete editor->validator();
+ editor->setValidator(v);
+
+ QString s = editor->text();
+ int i = 0;
+ if (v->validate(s, i) == QValidator::Invalid) {
+ editor->clear();
+ } else {
+ editor->setText(s);
+ }
+}
+
+void ValidatorWidget::updateDoubleValidator()
+{
+ QDoubleValidator *v
+ = new QDoubleValidator(doubleMinVal->value(), doubleMaxVal->value(),
+ doubleDecimals->value(), this);
+ v->setNotation(static_cast<QDoubleValidator::Notation>(doubleFormat->currentIndex()));
+ v->setLocale(locale());
+ delete doubleEditor->validator();
+ doubleEditor->setValidator(v);
+
+ QString s = doubleEditor->text();
+ int i = 0;
+ if (v->validate(s, i) == QValidator::Invalid) {
+ doubleEditor->clear();
+ } else {
+ doubleEditor->setText(s);
+ }
+}
diff --git a/tests/manual/examples/widgets/widgets/validators/validatorwidget.h b/tests/manual/examples/widgets/widgets/validators/validatorwidget.h
new file mode 100644
index 0000000000..d186c5863b
--- /dev/null
+++ b/tests/manual/examples/widgets/widgets/validators/validatorwidget.h
@@ -0,0 +1,22 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef VALIDATORWIDGET_H
+#define VALIDATORWIDGET_H
+
+#include <QWidget>
+
+#include "ui_validators.h"
+
+class ValidatorWidget : public QWidget, public Ui::ValidatorsForm
+{
+ Q_OBJECT
+public:
+ ValidatorWidget(QWidget *parent = nullptr);
+
+private slots:
+ void updateValidator();
+ void updateDoubleValidator();
+};
+
+#endif // VALIDATORWIDGET_H
diff --git a/tests/manual/examples/widgets/wiggly/CMakeLists.txt b/tests/manual/examples/widgets/wiggly/CMakeLists.txt
index c529c20bfa..fe68396f92 100644
--- a/tests/manual/examples/widgets/wiggly/CMakeLists.txt
+++ b/tests/manual/examples/widgets/wiggly/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(wiggly LANGUAGES CXX)
diff --git a/tests/manual/examples/widgets/windowcontainer/CMakeLists.txt b/tests/manual/examples/widgets/windowcontainer/CMakeLists.txt
new file mode 100644
index 0000000000..8e58ff0c84
--- /dev/null
+++ b/tests/manual/examples/widgets/windowcontainer/CMakeLists.txt
@@ -0,0 +1,42 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(windowcontainer LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/windowcontainer")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(windowcontainer
+ ../../opengl/openglwindow/openglwindow.cpp ../../opengl/openglwindow/openglwindow.h
+ windowcontainer.cpp
+)
+
+set_target_properties(windowcontainer PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_include_directories(windowcontainer PRIVATE
+ ../../opengl/openglwindow
+)
+
+target_link_libraries(windowcontainer PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::OpenGL
+ Qt6::Widgets
+)
+
+install(TARGETS windowcontainer
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/windowcontainer/windowcontainer.cpp b/tests/manual/examples/widgets/windowcontainer/windowcontainer.cpp
new file mode 100644
index 0000000000..fa66684294
--- /dev/null
+++ b/tests/manual/examples/widgets/windowcontainer/windowcontainer.cpp
@@ -0,0 +1,136 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "openglwindow.h"
+
+#include <QApplication>
+#include <QFocusEvent>
+#include <QHBoxLayout>
+#include <QKeyEvent>
+#include <QLineEdit>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QWidget>
+
+
+// Making use of the class from the openglwindow example
+class Window : public OpenGLWindow
+{
+ Q_OBJECT
+public:
+ using OpenGLWindow::OpenGLWindow;
+
+ void render(QPainter *p) override
+ {
+ QLinearGradient g(0, 0, 0, height());
+ g.setColorAt(0, QColor("lightsteelblue"));
+ g.setColorAt(1, Qt::black);
+ p->fillRect(0, 0, width(), height(), g);
+
+ p->setPen(Qt::white);
+
+ p->drawText(20, 30, QLatin1String("This is an OpenGL based QWindow"));
+
+ if (m_key.trimmed().length() > 0) {
+ QRect bounds = p->boundingRect(QRect(0, 0, width(), height()), Qt::AlignTop | Qt::AlignLeft, m_key);
+ p->save();
+ p->translate(width() / 2.0, height() / 2.0);
+ p->scale(10, 10);
+ p->translate(-bounds.width() / 2.0, -bounds.height() / 2.0);
+ p->drawText(bounds, Qt::AlignCenter, m_key);
+ p->restore();
+ }
+
+ if (m_focus)
+ p->drawText(20, height() - 20, QLatin1String("Window has focus!"));
+
+ p->setRenderHint(QPainter::Antialiasing);
+ p->drawPolyline(m_polygon);
+ }
+
+ void mousePressEvent(QMouseEvent *e) override
+ {
+ if (!m_mouseDown) {
+ m_mouseDown = true;
+ m_polygon.clear();
+ m_polygon.append(e->position().toPoint());
+ renderLater();
+ }
+ }
+
+ void mouseMoveEvent(QMouseEvent *e) override
+ {
+ if (m_mouseDown) {
+ m_polygon.append(e->position().toPoint());
+ renderLater();
+ }
+ }
+
+ void mouseReleaseEvent(QMouseEvent *e) override
+ {
+ if (m_mouseDown) {
+ m_mouseDown = false;
+ m_polygon.append(e->position().toPoint());
+ renderLater();
+ }
+ }
+
+ void focusInEvent(QFocusEvent *) override
+ {
+ m_focus = true;
+ renderLater();
+ }
+
+ void focusOutEvent(QFocusEvent *) override
+ {
+ m_focus = false;
+ m_polygon.clear();
+ renderLater();
+ }
+
+ void keyPressEvent(QKeyEvent *e) override
+ {
+ m_key = e->text();
+ renderLater();
+ }
+
+ void keyReleaseEvent(QKeyEvent *) override
+ {
+ m_key = QString();
+ renderLater();
+ }
+private:
+ QPolygon m_polygon;
+ QString m_key;
+ bool m_mouseDown = false;
+ bool m_focus = false;
+};
+
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ QWidget *widget = new QWidget;
+ QHBoxLayout *layout = new QHBoxLayout(widget);
+
+ Window *window = new Window;
+
+ QWidget *container = QWidget::createWindowContainer(window);
+ container->setMinimumSize(300, 300);
+ container->setMaximumSize(600, 600);
+ container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ container->setFocusPolicy(Qt::StrongFocus);
+
+ window->setGeometry(100, 100, 300, 200);
+
+ layout->addWidget(new QLineEdit(QLatin1String("A QLineEdit")));
+ layout->addWidget(container);
+ layout->addWidget(new QLineEdit(QLatin1String("A QLabel")));
+
+ widget->show();
+
+ return app.exec();
+}
+
+#include "windowcontainer.moc"
diff --git a/tests/manual/examples/widgets/windowcontainer/windowcontainer.pro b/tests/manual/examples/widgets/windowcontainer/windowcontainer.pro
new file mode 100644
index 0000000000..664ac938a2
--- /dev/null
+++ b/tests/manual/examples/widgets/windowcontainer/windowcontainer.pro
@@ -0,0 +1,9 @@
+SOURCES = windowcontainer.cpp
+
+QT += widgets
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/windowcontainer
+INSTALLS += target
+
+include(../../opengl/openglwindow/openglwindow.pri)
diff --git a/tests/manual/filetest/main.cpp b/tests/manual/filetest/main.cpp
index db495242db..d30b6c62c2 100644
--- a/tests/manual/filetest/main.cpp
+++ b/tests/manual/filetest/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QDateTime>
#include <QDebug>
@@ -22,7 +22,8 @@ static const char usage2[] =" [KEYWORD] [ARGUMENTS]\n\n"
" cp SOURCE TARGET copy files using QFile::copy\n"
" rm FILE remove file using QFile::remove\n"
" rmr DIR remove directory recursively\n"
-" using QDir::removeRecursively\n";
+" using QDir::removeRecursively\n"
+" trash FILES moves the file or directory to trash\n";
std::ostream &operator<<(std::ostream &o, const QString &str)
{
@@ -181,6 +182,17 @@ static int rmr(const char *dirName)
return 0;
}
+static int trash(const char *filename)
+{
+ QFile f(QString::fromLocal8Bit(filename));
+ if (!f.moveToTrash()) {
+ qWarning().nospace() << "Failed to trash " << f.fileName()
+ << ": " << f.errorString();
+ return -1;
+ }
+ return 0;
+}
+
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
@@ -206,6 +218,9 @@ int main(int argc, char *argv[])
if (argc == 3 && !qstrcmp(argv[1], "rmr"))
return rmr(argv[2]);
+ if (argc == 3 && !qstrcmp(argv[1], "trash"))
+ return trash(argv[2]);
+
std::cerr << usage1 << argv[0] << usage2;
return 0;
}
diff --git a/tests/manual/findfiles/findfiles.qdoc b/tests/manual/findfiles/findfiles.qdoc
index bed831d14d..ec00698703 100644
--- a/tests/manual/findfiles/findfiles.qdoc
+++ b/tests/manual/findfiles/findfiles.qdoc
@@ -86,7 +86,7 @@
and a stretchable space in a separate \l QHBoxLayout first, to make the
buttons appear in the \c Window widget's bottom right corner.
- Alternatively, we could have used Qt Designer to construct a UI file,
+ Alternatively, we could have used \QD to construct a UI file,
and \l {uic} to generate this code.
\snippet dialogs/findfiles/window.cpp 1
diff --git a/tests/manual/findfiles/main.cpp b/tests/manual/findfiles/main.cpp
index 27409403a5..8c25b7e031 100644
--- a/tests/manual/findfiles/main.cpp
+++ b/tests/manual/findfiles/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
diff --git a/tests/manual/findfiles/window.cpp b/tests/manual/findfiles/window.cpp
index f3e7253c75..278fb7dd62 100644
--- a/tests/manual/findfiles/window.cpp
+++ b/tests/manual/findfiles/window.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
diff --git a/tests/manual/findfiles/window.h b/tests/manual/findfiles/window.h
index 2c2e3100c0..ff166bf63d 100644
--- a/tests/manual/findfiles/window.h
+++ b/tests/manual/findfiles/window.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WINDOW_H
#define WINDOW_H
diff --git a/tests/manual/fontfeatures/fontfeatures.pro b/tests/manual/fontfeatures/fontfeatures.pro
new file mode 100644
index 0000000000..62769072b4
--- /dev/null
+++ b/tests/manual/fontfeatures/fontfeatures.pro
@@ -0,0 +1,17 @@
+TEMPLATE = app
+TARGET = fontfeatures
+INCLUDEPATH += .
+QT += widgets
+
+# You can make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# Please consult the documentation of the deprecated API in order to know
+# how to port your code away from it.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_UP_TO=0x060000 # disables all APIs deprecated in Qt 6.0.0 and earlier
+
+# Input
+HEADERS += mainwindow.h
+FORMS += mainwindow.ui
+SOURCES += main.cpp \
+ mainwindow.cpp \
diff --git a/tests/manual/fontfeatures/main.cpp b/tests/manual/fontfeatures/main.cpp
new file mode 100644
index 0000000000..38c32b1bd3
--- /dev/null
+++ b/tests/manual/fontfeatures/main.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "mainwindow.h"
+
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ MainWindow w;
+ w.show();
+ return a.exec();
+}
diff --git a/tests/manual/fontfeatures/mainwindow.cpp b/tests/manual/fontfeatures/mainwindow.cpp
new file mode 100644
index 0000000000..5e61a14d5e
--- /dev/null
+++ b/tests/manual/fontfeatures/mainwindow.cpp
@@ -0,0 +1,225 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent)
+ , ui(new Ui::MainWindow)
+{
+ ui->setupUi(this);
+
+ setup();
+ updateSampleText();
+}
+
+MainWindow::~MainWindow()
+{
+ delete ui;
+}
+
+void MainWindow::updateSampleText()
+{
+ QFont font = ui->fontComboBox->currentFont();
+ font.setPixelSize(54);
+
+ for (int i = 0; i < ui->lwFeatures->count(); ++i) {
+ QListWidgetItem *it = ui->lwFeatures->item(i);
+ if (it->checkState() != Qt::PartiallyChecked) {
+ if (const auto maybeTag = QFont::Tag::fromString(it->text().toLatin1()))
+ font.setFeature(*maybeTag, !!it->checkState());
+ }
+ }
+
+ ui->lSampleDisplay->setFont(font);
+ ui->lSampleDisplay->setText(ui->leSampleText->text());
+}
+
+void MainWindow::enableAll()
+{
+ for (int i = 0; i < ui->lwFeatures->count(); ++i) {
+ QListWidgetItem *it = ui->lwFeatures->item(i);
+ it->setCheckState(Qt::Checked);
+ }
+}
+
+void MainWindow::disableAll()
+{
+ for (int i = 0; i < ui->lwFeatures->count(); ++i) {
+ QListWidgetItem *it = ui->lwFeatures->item(i);
+ it->setCheckState(Qt::Unchecked);
+ }
+}
+
+void MainWindow::reset()
+{
+ for (int i = 0; i < ui->lwFeatures->count(); ++i) {
+ QListWidgetItem *it = ui->lwFeatures->item(i);
+ it->setCheckState(Qt::PartiallyChecked);
+ }
+}
+
+void MainWindow::setup()
+{
+ connect(ui->fontComboBox, &QFontComboBox::currentFontChanged, this, &MainWindow::updateSampleText);
+ connect(ui->leSampleText, &QLineEdit::textChanged, this, &MainWindow::updateSampleText);
+ connect(ui->lwFeatures, &QListWidget::itemChanged, this, &MainWindow::updateSampleText);
+ connect(ui->pbEnableAll, &QPushButton::clicked, this, &MainWindow::enableAll);
+ connect(ui->pbDisableAll, &QPushButton::clicked, this, &MainWindow::disableAll);
+ connect(ui->pbReset, &QPushButton::clicked, this, &MainWindow::reset);
+
+ QList<QByteArray> featureList =
+ {
+ "aalt",
+ "abvf",
+ "abvm",
+ "abvs",
+ "afrc",
+ "akhn",
+ "blwf",
+ "blwm",
+ "blws",
+ "calt",
+ "case",
+ "ccmp",
+ "cfar",
+ "chws",
+ "cjct",
+ "clig",
+ "cpct",
+ "cpsp",
+ "cswh",
+ "curs",
+ "cv01",
+ "c2pc",
+ "c2sc",
+ "dist",
+ "dlig",
+ "dnom",
+ "dtls",
+ "expt",
+ "falt",
+ "fin2",
+ "fin3",
+ "fina",
+ "flac",
+ "frac",
+ "fwid",
+ "half",
+ "haln",
+ "halt",
+ "hist",
+ "hkna",
+ "hlig",
+ "hngl",
+ "hojo",
+ "hwid",
+ "init",
+ "isol",
+ "ital",
+ "jalt",
+ "jp78",
+ "jp83",
+ "jp90",
+ "jp04",
+ "kern",
+ "lfbd",
+ "liga",
+ "ljmo",
+ "lnum",
+ "locl",
+ "ltra",
+ "ltrm",
+ "mark",
+ "med2",
+ "medi",
+ "mgrk",
+ "mkmk",
+ "mset",
+ "nalt",
+ "nlck",
+ "nukt",
+ "numr",
+ "onum",
+ "opbd",
+ "ordn",
+ "ornm",
+ "palt",
+ "pcap",
+ "pkna",
+ "pnum",
+ "pref",
+ "pres",
+ "pstf",
+ "psts",
+ "pwid",
+ "qwid",
+ "rand",
+ "rclt",
+ "rkrf",
+ "rlig",
+ "rphf",
+ "rtbd",
+ "rtla",
+ "rtlm",
+ "ruby",
+ "rvrn",
+ "salt",
+ "sinf",
+ "size",
+ "smcp",
+ "smpl",
+ "ss01",
+ "ss02",
+ "ss03",
+ "ss04",
+ "ss05",
+ "ss06",
+ "ss07",
+ "ss08",
+ "ss09",
+ "ss10",
+ "ss11",
+ "ss12",
+ "ss13",
+ "ss14",
+ "ss15",
+ "ss16",
+ "ss17",
+ "ss18",
+ "ss19",
+ "ss20",
+ "ssty",
+ "stch",
+ "subs",
+ "sups",
+ "swsh",
+ "titl",
+ "tjmo",
+ "tnam",
+ "tnum",
+ "trad",
+ "twid",
+ "unic",
+ "valt",
+ "vatu",
+ "vchw",
+ "vert",
+ "vhal",
+ "vjmo",
+ "vkna",
+ "vkrn",
+ "vpal",
+ "vrt2",
+ "vrtr",
+ "zero"
+ };
+
+ for (auto it = featureList.constBegin(); it != featureList.constEnd(); ++it) {
+ QListWidgetItem *item = new QListWidgetItem(*it);
+ item->setFlags(Qt::ItemIsUserTristate | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
+ item->setCheckState(Qt::PartiallyChecked);
+ ui->lwFeatures->addItem(item);
+ }
+}
diff --git a/tests/manual/fontfeatures/mainwindow.h b/tests/manual/fontfeatures/mainwindow.h
new file mode 100644
index 0000000000..8df130edd6
--- /dev/null
+++ b/tests/manual/fontfeatures/mainwindow.h
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+
+QT_BEGIN_NAMESPACE
+namespace Ui { class MainWindow; }
+QT_END_NAMESPACE
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow(QWidget *parent = nullptr);
+ ~MainWindow();
+
+private slots:
+ void updateSampleText();
+ void enableAll();
+ void disableAll();
+ void reset();
+
+private:
+ Ui::MainWindow *ui;
+
+ void setup();
+};
+#endif // MAINWINDOW_H
diff --git a/tests/manual/fontfeatures/mainwindow.ui b/tests/manual/fontfeatures/mainwindow.ui
new file mode 100644
index 0000000000..17f56c5a01
--- /dev/null
+++ b/tests/manual/fontfeatures/mainwindow.ui
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>600</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QFontComboBox" name="fontComboBox"/>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Sample text:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="leSampleText">
+ <property name="text">
+ <string>Foobar</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QPushButton" name="pbEnableAll">
+ <property name="text">
+ <string>Enable all</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="pbDisableAll">
+ <property name="text">
+ <string>Disable all</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="pbReset">
+ <property name="text">
+ <string>Reset</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QListWidget" name="lwFeatures">
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="flow">
+ <enum>QListView::TopToBottom</enum>
+ </property>
+ <property name="isWrapping" stdset="0">
+ <bool>false</bool>
+ </property>
+ <property name="viewMode">
+ <enum>QListView::ListMode</enum>
+ </property>
+ <property name="uniformItemSizes">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLabel" name="lSampleDisplay">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>LABEL</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>21</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tests/manual/foreignwindows/main.cpp b/tests/manual/foreignwindows/main.cpp
index 1264656390..1fb4b0c167 100644
--- a/tests/manual/foreignwindows/main.cpp
+++ b/tests/manual/foreignwindows/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui/QAction>
#include <QtWidgets/QApplication>
diff --git a/tests/manual/gestures/graphicsview/gestures.cpp b/tests/manual/gestures/graphicsview/gestures.cpp
index 287e5253e4..5a46246143 100644
--- a/tests/manual/gestures/graphicsview/gestures.cpp
+++ b/tests/manual/gestures/graphicsview/gestures.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "gestures.h"
diff --git a/tests/manual/gestures/graphicsview/gestures.h b/tests/manual/gestures/graphicsview/gestures.h
index 972fc1a424..525e9f04f6 100644
--- a/tests/manual/gestures/graphicsview/gestures.h
+++ b/tests/manual/gestures/graphicsview/gestures.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef GESTURE_H
#define GESTURE_H
diff --git a/tests/manual/gestures/graphicsview/imageitem.cpp b/tests/manual/gestures/graphicsview/imageitem.cpp
index 8bde9b5d45..6e93232f96 100644
--- a/tests/manual/gestures/graphicsview/imageitem.cpp
+++ b/tests/manual/gestures/graphicsview/imageitem.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "imageitem.h"
#include "gestures.h"
diff --git a/tests/manual/gestures/graphicsview/imageitem.h b/tests/manual/gestures/graphicsview/imageitem.h
index 08cdabb78c..f3dd4cec9f 100644
--- a/tests/manual/gestures/graphicsview/imageitem.h
+++ b/tests/manual/gestures/graphicsview/imageitem.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef IMAGEITEM_H
#define IMAGEITEM_H
diff --git a/tests/manual/gestures/graphicsview/main.cpp b/tests/manual/gestures/graphicsview/main.cpp
index 615c5f4d43..69d97bd0bc 100644
--- a/tests/manual/gestures/graphicsview/main.cpp
+++ b/tests/manual/gestures/graphicsview/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QMainWindow>
@@ -143,8 +143,8 @@ MainWindow::MainWindow()
void MainWindow::setDirectory(const QString &path)
{
QDir dir(path);
- QStringList files = dir.entryList(QDir::Files | QDir::Readable | QDir::NoDotAndDotDot);
- foreach(const QString &file, files) {
+ const QStringList files = dir.entryList(QDir::Files | QDir::Readable | QDir::NoDotAndDotDot);
+ for (const QString &file : files) {
QImageReader img(path + QLatin1Char('/') +file);
QImage image = img.read();
if (!image.isNull()) {
diff --git a/tests/manual/gestures/graphicsview/mousepangesturerecognizer.cpp b/tests/manual/gestures/graphicsview/mousepangesturerecognizer.cpp
index 69333ebe44..45580f8e88 100644
--- a/tests/manual/gestures/graphicsview/mousepangesturerecognizer.cpp
+++ b/tests/manual/gestures/graphicsview/mousepangesturerecognizer.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mousepangesturerecognizer.h"
diff --git a/tests/manual/gestures/graphicsview/mousepangesturerecognizer.h b/tests/manual/gestures/graphicsview/mousepangesturerecognizer.h
index c51a9c23eb..512171f1ae 100644
--- a/tests/manual/gestures/graphicsview/mousepangesturerecognizer.h
+++ b/tests/manual/gestures/graphicsview/mousepangesturerecognizer.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MOUSEPANGESTURERECOGNIZER_H
#define MOUSEPANGESTURERECOGNIZER_H
diff --git a/tests/manual/gestures/scrollarea/CMakeLists.txt b/tests/manual/gestures/scrollarea/CMakeLists.txt
index 7ea3ed5b6b..02ea63d040 100644
--- a/tests/manual/gestures/scrollarea/CMakeLists.txt
+++ b/tests/manual/gestures/scrollarea/CMakeLists.txt
@@ -10,6 +10,8 @@ qt_internal_add_manual_test(scrollarea
SOURCES
main.cpp
mousepangesturerecognizer.cpp mousepangesturerecognizer.h
+ NO_PCH_SOURCES
+ main.cpp # undef QT_NO_FOREACH
LIBRARIES
Qt::Gui
Qt::Widgets
diff --git a/tests/manual/gestures/scrollarea/main.cpp b/tests/manual/gestures/scrollarea/main.cpp
index e8764a5ded..911c3530cf 100644
--- a/tests/manual/gestures/scrollarea/main.cpp
+++ b/tests/manual/gestures/scrollarea/main.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include <QApplication>
#include <QSlider>
diff --git a/tests/manual/gestures/scrollarea/mousepangesturerecognizer.cpp b/tests/manual/gestures/scrollarea/mousepangesturerecognizer.cpp
index 0f77e235c3..c6d4295868 100644
--- a/tests/manual/gestures/scrollarea/mousepangesturerecognizer.cpp
+++ b/tests/manual/gestures/scrollarea/mousepangesturerecognizer.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mousepangesturerecognizer.h"
diff --git a/tests/manual/gestures/scrollarea/mousepangesturerecognizer.h b/tests/manual/gestures/scrollarea/mousepangesturerecognizer.h
index c51a9c23eb..512171f1ae 100644
--- a/tests/manual/gestures/scrollarea/mousepangesturerecognizer.h
+++ b/tests/manual/gestures/scrollarea/mousepangesturerecognizer.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MOUSEPANGESTURERECOGNIZER_H
#define MOUSEPANGESTURERECOGNIZER_H
diff --git a/tests/manual/graphicsframecapture/CMakeLists.txt b/tests/manual/graphicsframecapture/CMakeLists.txt
new file mode 100644
index 0000000000..8d5fc5952d
--- /dev/null
+++ b/tests/manual/graphicsframecapture/CMakeLists.txt
@@ -0,0 +1,35 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## graphicsframecapture Binary:
+#####################################################################
+
+qt_internal_add_manual_test(graphicsframecapture
+ SOURCES
+ examplewindow.cpp examplewindow.h
+ main.cpp
+ window.cpp window.h
+ LIBRARIES
+ Qt::Gui
+ Qt::GuiPrivate
+)
+
+# Resources:
+set_source_files_properties("../rhi/shared/color.frag.qsb"
+ PROPERTIES QT_RESOURCE_ALIAS "color.frag.qsb"
+)
+set_source_files_properties("../rhi/shared/color.vert.qsb"
+ PROPERTIES QT_RESOURCE_ALIAS "color.vert.qsb"
+)
+set(graphicsframecapture_resource_files
+ "../rhi/shared/color.frag.qsb"
+ "../rhi/shared/color.vert.qsb"
+)
+
+qt_internal_add_resource(graphicsframecapture "graphicsframecapture"
+ PREFIX
+ "/"
+ FILES
+ ${graphicsframecapture_resource_files}
+)
diff --git a/tests/manual/graphicsframecapture/examplewindow.cpp b/tests/manual/graphicsframecapture/examplewindow.cpp
new file mode 100644
index 0000000000..6bc49a81ce
--- /dev/null
+++ b/tests/manual/graphicsframecapture/examplewindow.cpp
@@ -0,0 +1,116 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "examplewindow.h"
+#include <QFile>
+#include <rhi/qshader.h>
+
+static float vertexData[] = {
+ // Y up (note clipSpaceCorrMatrix in m_proj), CCW
+ -0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
+ -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
+ 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
+
+ 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
+ -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
+ 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
+};
+
+ExampleWindow::ExampleWindow(QRhi::Implementation graphicsApi)
+ : Window(graphicsApi)
+{
+}
+
+QShader ExampleWindow::getShader(const QString &name)
+{
+ QFile f(name);
+ if (f.open(QIODevice::ReadOnly))
+ return QShader::fromSerialized(f.readAll());
+
+ return QShader();
+}
+
+void ExampleWindow::customInit()
+{
+ m_vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData)));
+ m_vbuf->create();
+ m_vbufReady = false;
+
+ m_ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68));
+ m_ubuf->create();
+
+ m_srb.reset(m_rhi->newShaderResourceBindings());
+ m_srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
+ m_ubuf.get())
+ });
+ m_srb->create();
+
+ m_ps.reset(m_rhi->newGraphicsPipeline());
+
+ QRhiGraphicsPipeline::TargetBlend premulAlphaBlend;
+ premulAlphaBlend.enable = true;
+ m_ps->setTargetBlends({ premulAlphaBlend });
+
+ const QShader vs = getShader(QLatin1String(":/color.vert.qsb"));
+ if (!vs.isValid())
+ qFatal("Failed to load shader pack (vertex)");
+ const QShader fs = getShader(QLatin1String(":/color.frag.qsb"));
+ if (!fs.isValid())
+ qFatal("Failed to load shader pack (fragment)");
+
+ m_ps->setShaderStages({
+ { QRhiShaderStage::Vertex, vs },
+ { QRhiShaderStage::Fragment, fs }
+ });
+
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 5 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
+ });
+
+ m_ps->setVertexInputLayout(inputLayout);
+ m_ps->setShaderResourceBindings(m_srb.get());
+ m_ps->setRenderPassDescriptor(m_rp.get());
+
+ m_ps->create();
+}
+
+// called once per frame
+void ExampleWindow::customRender()
+{
+ QRhiResourceUpdateBatch *u = m_rhi->nextResourceUpdateBatch();
+ if (!m_vbufReady) {
+ m_vbufReady = true;
+ u->uploadStaticBuffer(m_vbuf.get(), vertexData);
+ }
+ m_rotation += 1.0f;
+ QMatrix4x4 mvp = m_proj;
+ mvp.rotate(m_rotation, 0, 0, 1);
+ u->updateDynamicBuffer(m_ubuf.get(), 0, 64, mvp.constData());
+ m_opacity += m_opacityDir * 0.005f;
+ if (m_opacity < 0.0f || m_opacity > 1.0f) {
+ m_opacityDir *= -1;
+ m_opacity = qBound(0.0f, m_opacity, 1.0f);
+ }
+ u->updateDynamicBuffer(m_ubuf.get(), 64, 4, &m_opacity);
+
+ QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
+ const QSize outputSizeInPixels = m_sc->currentPixelSize();
+
+ cb->beginPass(m_sc->currentFrameRenderTarget(), QColor::fromRgbF(0.0f, 0.0f, 0.0f, 1.0f), { 1.0f, 0 }, u);
+
+ cb->setGraphicsPipeline(m_ps.get());
+ cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
+ cb->setShaderResources();
+
+ const QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf.get(), 0);
+ cb->setVertexInput(0, 1, &vbufBinding);
+ cb->draw(6);
+
+ cb->endPass();
+}
diff --git a/tests/manual/graphicsframecapture/examplewindow.h b/tests/manual/graphicsframecapture/examplewindow.h
new file mode 100644
index 0000000000..88442efb31
--- /dev/null
+++ b/tests/manual/graphicsframecapture/examplewindow.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef EXAMPLEWINDOW_H
+#define EXAMPLEWINDOW_H
+
+#include "window.h"
+
+class ExampleWindow : public Window
+{
+public:
+ ExampleWindow(QRhi::Implementation graphicsApi);
+
+ void customInit() override;
+ void customRender() override;
+
+private:
+ QShader getShader(const QString &name);
+
+ std::unique_ptr<QRhiBuffer> m_vbuf;
+ bool m_vbufReady = false;
+ std::unique_ptr<QRhiBuffer> m_ubuf;
+ std::unique_ptr<QRhiShaderResourceBindings> m_srb;
+ std::unique_ptr<QRhiGraphicsPipeline> m_ps;
+
+ float m_rotation = 0;
+ float m_opacity = 1;
+ int m_opacityDir = -1;
+};
+
+#endif
diff --git a/tests/manual/graphicsframecapture/main.cpp b/tests/manual/graphicsframecapture/main.cpp
new file mode 100644
index 0000000000..b13f2f4184
--- /dev/null
+++ b/tests/manual/graphicsframecapture/main.cpp
@@ -0,0 +1,128 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+// This is a compact, minimal demo of deciding the backend at runtime while
+// using the exact same shaders and rendering code without any branching
+// whatsoever once the QWindow is up and the RHI is initialized.
+
+#include <QGuiApplication>
+#include <QCommandLineParser>
+#include "examplewindow.h"
+
+
+QString graphicsApiName(QRhi::Implementation graphicsApi);
+QRhi::Implementation graphicsApiFromCmd(const QGuiApplication &app);
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+ QRhi::Implementation graphicsApi = graphicsApiFromCmd(app);
+
+ QSurfaceFormat fmt;
+ fmt.setDepthBufferSize(24);
+ fmt.setStencilBufferSize(8);
+ QSurfaceFormat::setDefaultFormat(fmt);
+
+ ExampleWindow w(graphicsApi);
+
+#if QT_CONFIG(vulkan)
+ QVulkanInstance inst;
+ if (graphicsApi == QRhi::Vulkan) {
+ inst.setLayers({ "VK_LAYER_KHRONOS_validation" });
+ inst.setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
+ if (!inst.create()) {
+ qWarning("Failed to create Vulkan instance, switching to OpenGL");
+ graphicsApi = QRhi::OpenGLES2;
+ }
+ }
+#endif
+
+#if QT_CONFIG(vulkan)
+ if (graphicsApi == QRhi::Vulkan)
+ w.setVulkanInstance(&inst);
+#endif
+ w.resize(1280, 720);
+ w.setTitle(QCoreApplication::applicationName() + QLatin1String(" - ") + graphicsApiName(graphicsApi));
+ w.show();
+
+ int ret = app.exec();
+
+ // Window::event() will not get invoked when the
+ // PlatformSurfaceAboutToBeDestroyed event is sent during the QWindow
+ // destruction. That happens only when exiting via app::quit() instead of
+ // the more common QWindow::close(). Take care of it: if the QPlatformWindow
+ // is still around (there was no close() yet), get rid of the swapchain
+ // while it's not too late.
+ if (w.handle())
+ w.releaseSwapChain();
+
+ return ret;
+}
+
+QString graphicsApiName(QRhi::Implementation graphicsApi)
+{
+ switch (graphicsApi) {
+ case QRhi::Null:
+ return QLatin1String("Null (no output)");
+ case QRhi::OpenGLES2:
+ return QLatin1String("OpenGL 2.x");
+ case QRhi::Vulkan:
+ return QLatin1String("Vulkan");
+ case QRhi::D3D11:
+ return QLatin1String("Direct3D 11");
+ case QRhi::D3D12:
+ return QLatin1String("Direct3D 12");
+ case QRhi::Metal:
+ return QLatin1String("Metal");
+ default:
+ break;
+ }
+ return QString();
+}
+
+QRhi::Implementation graphicsApiFromCmd(const QGuiApplication &app) {
+ QRhi::Implementation graphicsApi;
+#if defined(Q_OS_WIN)
+ graphicsApi = QRhi::D3D11;
+#elif QT_CONFIG(metal)
+ graphicsApi = QRhi::Metal;
+#elif QT_CONFIG(vulkan)
+ graphicsApi = QRhi::Vulkan;
+#else
+ graphicsApi = QRhi::OpenGLES2;
+#endif
+
+ QCommandLineParser cmdLineParser;
+ cmdLineParser.addHelpOption();
+ QCommandLineOption nullOption({ "n", "null" }, QLatin1String("Null"));
+ cmdLineParser.addOption(nullOption);
+ QCommandLineOption glOption({ "g", "opengl" }, QLatin1String("OpenGL (2.x)"));
+ cmdLineParser.addOption(glOption);
+ QCommandLineOption vkOption({ "v", "vulkan" }, QLatin1String("Vulkan"));
+ cmdLineParser.addOption(vkOption);
+ QCommandLineOption d3d11Option({ "d", "d3d11" }, QLatin1String("Direct3D 11"));
+ cmdLineParser.addOption(d3d11Option);
+ QCommandLineOption d3d12Option({ "D", "d3d12" }, QLatin1String("Direct3D 12"));
+ cmdLineParser.addOption(d3d12Option);
+ QCommandLineOption mtlOption({ "m", "metal" }, QLatin1String("Metal"));
+ cmdLineParser.addOption(mtlOption);
+
+ cmdLineParser.process(app);
+ if (cmdLineParser.isSet(nullOption))
+ graphicsApi = QRhi::Null;
+ if (cmdLineParser.isSet(glOption))
+ graphicsApi = QRhi::OpenGLES2;
+ if (cmdLineParser.isSet(vkOption))
+ graphicsApi = QRhi::Vulkan;
+ if (cmdLineParser.isSet(d3d11Option))
+ graphicsApi = QRhi::D3D11;
+ if (cmdLineParser.isSet(d3d12Option))
+ graphicsApi = QRhi::D3D12;
+ if (cmdLineParser.isSet(mtlOption))
+ graphicsApi = QRhi::Metal;
+
+ qDebug("Selected graphics API is %s", qPrintable(graphicsApiName(graphicsApi)));
+ qDebug("This is a multi-api example, use command line arguments to override:\n%s", qPrintable(cmdLineParser.helpText()));
+
+ return graphicsApi;
+}
diff --git a/tests/manual/graphicsframecapture/window.cpp b/tests/manual/graphicsframecapture/window.cpp
new file mode 100644
index 0000000000..92a5483634
--- /dev/null
+++ b/tests/manual/graphicsframecapture/window.cpp
@@ -0,0 +1,256 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "window.h"
+#include <QPlatformSurfaceEvent>
+#include <QTimer>
+
+Window::Window(QRhi::Implementation graphicsApi)
+ : m_graphicsApi(graphicsApi)
+{
+ m_capturer.reset(new QGraphicsFrameCapture);
+#if QT_CONFIG(metal)
+ qDebug("This example uses Metal Capture Manager In App API to capture frames. Press F9 to capture a frame and F10 to open it for analysis");
+#else
+ qDebug("This example uses RenderDoc In App API to capture frames. Press F9 to capture a frame and F10 to open it for analysis");
+ qDebug("On Linux with OpenGL, make sure that librenderdoc.so is loaded using LD_PRELOAD");
+#endif
+ qDebug("The Frame Capturer API will save captures in this directory : %s", qPrintable(m_capturer->capturePath()));
+
+ switch (graphicsApi) {
+ case QRhi::OpenGLES2:
+ setSurfaceType(OpenGLSurface);
+ break;
+ case QRhi::Vulkan:
+ setSurfaceType(VulkanSurface);
+ break;
+ case QRhi::D3D11:
+ case QRhi::D3D12:
+ setSurfaceType(Direct3DSurface);
+ break;
+ case QRhi::Metal:
+ setSurfaceType(MetalSurface);
+ break;
+ default:
+ break;
+ }
+}
+
+void Window::exposeEvent(QExposeEvent *)
+{
+ // initialize and start rendering when the window becomes usable for graphics purposes
+ if (isExposed() && !m_running) {
+ qDebug("init");
+ m_running = true;
+ init();
+ resizeSwapChain();
+ }
+
+ const QSize surfaceSize = m_hasSwapChain ? m_sc->surfacePixelSize() : QSize();
+
+ // stop pushing frames when not exposed (or size is 0)
+ if ((!isExposed() || (m_hasSwapChain && surfaceSize.isEmpty())) && m_running && !m_notExposed) {
+ qDebug("not exposed");
+ m_notExposed = true;
+ }
+
+ // Continue when exposed again and the surface has a valid size. Note that
+ // surfaceSize can be (0, 0) even though size() reports a valid one, hence
+ // trusting surfacePixelSize() and not QWindow.
+ if (isExposed() && m_running && m_notExposed && !surfaceSize.isEmpty()) {
+ qDebug("exposed again");
+ m_notExposed = false;
+ m_newlyExposed = true;
+ }
+
+ // always render a frame on exposeEvent() (when exposed) in order to update
+ // immediately on window resize.
+ if (isExposed() && !surfaceSize.isEmpty())
+ render();
+}
+
+bool Window::event(QEvent *e)
+{
+
+ switch (e->type()) {
+ case QEvent::UpdateRequest:
+ render();
+ break;
+
+ case QEvent::PlatformSurface:
+ // this is the proper time to tear down the swapchain (while the native window and surface are still around)
+ if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed)
+ releaseSwapChain();
+ break;
+
+ case QEvent::KeyRelease:
+ if (static_cast<QKeyEvent *>(e)->key() == Qt::Key::Key_F9 && !static_cast<QKeyEvent *>(e)->isAutoRepeat()) {
+ m_shouldCapture = true;
+ return true;
+ }
+ else if (static_cast<QKeyEvent *>(e)->key() == Qt::Key::Key_F10 && !static_cast<QKeyEvent *>(e)->isAutoRepeat()) {
+ if (!m_capturer.isNull())
+ m_capturer->openCapture();
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return QWindow::event(e);
+
+}
+
+void Window::init()
+{
+ QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers;
+
+ if (m_graphicsApi == QRhi::Null) {
+ QRhiNullInitParams params;
+ m_rhi.reset(QRhi::create(QRhi::Null, &params, rhiFlags));
+ }
+
+#if QT_CONFIG(opengl)
+ if (m_graphicsApi == QRhi::OpenGLES2) {
+ m_fallbackSurface.reset(QRhiGles2InitParams::newFallbackSurface());
+ QRhiGles2InitParams params;
+ params.fallbackSurface = m_fallbackSurface.get();
+ params.window = this;
+ m_rhi.reset(QRhi::create(QRhi::OpenGLES2, &params, rhiFlags));
+ }
+#endif
+
+#if QT_CONFIG(vulkan)
+ if (m_graphicsApi == QRhi::Vulkan) {
+ QRhiVulkanInitParams params;
+ params.inst = vulkanInstance();
+ params.window = this;
+ m_rhi.reset(QRhi::create(QRhi::Vulkan, &params, rhiFlags));
+ }
+#endif
+
+#ifdef Q_OS_WIN
+ if (m_graphicsApi == QRhi::D3D11) {
+ QRhiD3D11InitParams params;
+ params.enableDebugLayer = true;
+ m_rhi.reset(QRhi::create(QRhi::D3D11, &params, rhiFlags));
+ } else if (m_graphicsApi == QRhi::D3D12) {
+ QRhiD3D12InitParams params;
+ params.enableDebugLayer = true;
+ m_rhi.reset(QRhi::create(QRhi::D3D12, &params, rhiFlags));
+ }
+#endif
+
+#if QT_CONFIG(metal)
+ if (m_graphicsApi == QRhi::Metal) {
+ QRhiMetalInitParams params;
+ m_rhi.reset(QRhi::create(QRhi::Metal, &params, rhiFlags));
+ }
+#endif
+
+ if (!m_rhi)
+ qFatal("Failed to create RHI backend");
+
+ m_sc.reset(m_rhi->newSwapChain());
+ m_ds.reset(m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
+ QSize(), // no need to set the size here, due to UsedWithSwapChainOnly
+ 1,
+ QRhiRenderBuffer::UsedWithSwapChainOnly));
+ m_sc->setWindow(this);
+ m_sc->setDepthStencil(m_ds.get());
+ m_rp.reset(m_sc->newCompatibleRenderPassDescriptor());
+ m_sc->setRenderPassDescriptor(m_rp.get());
+
+ m_capturer->setRhi(m_rhi.get());
+
+ customInit();
+}
+
+void Window::resizeSwapChain()
+{
+ m_hasSwapChain = m_sc->createOrResize(); // also handles m_ds
+
+ const QSize outputSize = m_sc->currentPixelSize();
+ m_proj = m_rhi->clipSpaceCorrMatrix();
+ m_proj.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 1000.0f);
+ m_proj.translate(0, 0, -4);
+}
+
+void Window::releaseSwapChain()
+{
+ if (m_hasSwapChain) {
+ m_hasSwapChain = false;
+ m_sc->destroy();
+ }
+}
+
+void Window::render()
+{
+ if (!m_hasSwapChain || m_notExposed)
+ return;
+
+ // If the window got resized or newly exposed, resize the swapchain. (the
+ // newly-exposed case is not actually required by some platforms, but
+ // f.ex. Vulkan on Windows seems to need it)
+ //
+ // This (exposeEvent + the logic here) is the only safe way to perform
+ // resize handling. Note the usage of the RHI's surfacePixelSize(), and
+ // never QWindow::size(). (the two may or may not be the same under the hood,
+ // depending on the backend and platform)
+ //
+ if (m_sc->currentPixelSize() != m_sc->surfacePixelSize() || m_newlyExposed) {
+ resizeSwapChain();
+ if (!m_hasSwapChain)
+ return;
+ m_newlyExposed = false;
+ }
+
+ if (m_shouldCapture)
+ m_capturer->startCaptureFrame();
+
+ QRhi::FrameOpResult r = m_rhi->beginFrame(m_sc.get());
+ if (r == QRhi::FrameOpSwapChainOutOfDate) {
+ resizeSwapChain();
+ if (!m_hasSwapChain)
+ return;
+ r = m_rhi->beginFrame(m_sc.get());
+ }
+ if (r != QRhi::FrameOpSuccess) {
+ qDebug("beginFrame failed with %d, retry", r);
+ requestUpdate();
+ return;
+ }
+
+ customRender();
+
+ m_rhi->endFrame(m_sc.get());
+
+ if (m_shouldCapture) {
+ m_capturer->endCaptureFrame();
+ m_shouldCapture = false;
+ }
+
+// Always request the next frame via requestUpdate(). On some platforms this is backed
+// by a platform-specific solution, e.g. CVDisplayLink on macOS, which is potentially
+// more efficient than a timer, queued metacalls, etc.
+//
+// However, the rendering behavior is identical no matter how the next round of
+// rendering is triggered: the rendering thread is throttled to the presentation rate
+// (either in beginFrame() or endFrame()) so the triangle should rotate at the exact
+// same speed no matter which approach is taken here.
+
+#if 1
+ requestUpdate();
+#else
+ QTimer::singleShot(0, this, [this] { render(); });
+#endif
+}
+
+void Window::customInit()
+{
+}
+
+void Window::customRender()
+{
+}
diff --git a/tests/manual/graphicsframecapture/window.h b/tests/manual/graphicsframecapture/window.h
new file mode 100644
index 0000000000..d7e75c6f98
--- /dev/null
+++ b/tests/manual/graphicsframecapture/window.h
@@ -0,0 +1,55 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include <QWindow>
+#include <QOffscreenSurface>
+#include <rhi/qrhi.h>
+#include <QtGui/private/qgraphicsframecapture_p.h>
+
+class Window : public QWindow
+{
+public:
+ Window(QRhi::Implementation graphicsApi);
+
+ void releaseSwapChain();
+
+protected:
+ virtual void customInit();
+ virtual void customRender();
+
+ // destruction order matters to a certain degree: the fallbackSurface must
+ // outlive the rhi, the rhi must outlive all other resources. The resources
+ // need no special order when destroying.
+#if QT_CONFIG(opengl)
+ std::unique_ptr<QOffscreenSurface> m_fallbackSurface;
+#endif
+ std::unique_ptr<QRhi> m_rhi;
+ std::unique_ptr<QRhiSwapChain> m_sc;
+ std::unique_ptr<QRhiRenderBuffer> m_ds;
+ std::unique_ptr<QRhiRenderPassDescriptor> m_rp;
+
+ bool m_hasSwapChain = false;
+ QMatrix4x4 m_proj;
+
+private:
+ void init();
+ void resizeSwapChain();
+ void render();
+
+ void exposeEvent(QExposeEvent *) override;
+ bool event(QEvent *) override;
+
+ QRhi::Implementation m_graphicsApi;
+
+ bool m_running = false;
+ bool m_notExposed = false;
+ bool m_newlyExposed = false;
+
+ QScopedPointer<QGraphicsFrameCapture> m_capturer;
+ bool m_shouldCapture = false;
+};
+
+#endif
diff --git a/tests/manual/highdpi/CMakeLists.txt b/tests/manual/highdpi/CMakeLists.txt
index d32e2a52fc..1d5d0ad1ff 100644
--- a/tests/manual/highdpi/CMakeLists.txt
+++ b/tests/manual/highdpi/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(dprgadget)
add_subdirectory(kitchensink)
diff --git a/tests/manual/highdpi/dprgadget/CMakeLists.txt b/tests/manual/highdpi/dprgadget/CMakeLists.txt
index 0d23fab040..5cd0d5812e 100644
--- a/tests/manual/highdpi/dprgadget/CMakeLists.txt
+++ b/tests/manual/highdpi/dprgadget/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## dprgadget Binary:
@@ -6,7 +8,7 @@
qt_internal_add_manual_test(dprgadget
SOURCES
main.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::Widgets
Qt::GuiPrivate
diff --git a/tests/manual/highdpi/dprgadget/main.cpp b/tests/manual/highdpi/dprgadget/main.cpp
index b0a4191890..26a653e849 100644
--- a/tests/manual/highdpi/dprgadget/main.cpp
+++ b/tests/manual/highdpi/dprgadget/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/qtversion.h>
#include <QtGui/qpa/qplatformscreen.h>
diff --git a/tests/manual/highdpi/kitchensink/CMakeLists.txt b/tests/manual/highdpi/kitchensink/CMakeLists.txt
index 046a7a2117..e7558c19e0 100644
--- a/tests/manual/highdpi/kitchensink/CMakeLists.txt
+++ b/tests/manual/highdpi/kitchensink/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## kitchensink Binary:
@@ -11,7 +13,7 @@ qt_internal_add_manual_test(kitchensink
HAVE_SCREEN_BASE_DPI
INCLUDE_DIRECTORIES
.
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::GuiPrivate
Qt::Widgets
diff --git a/tests/manual/highdpi/kitchensink/dragwidget.cpp b/tests/manual/highdpi/kitchensink/dragwidget.cpp
index 22393f0416..4088a56a80 100644
--- a/tests/manual/highdpi/kitchensink/dragwidget.cpp
+++ b/tests/manual/highdpi/kitchensink/dragwidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
#include "dragwidget.h"
@@ -84,8 +84,8 @@ void DragWidget::dropEvent(QDropEvent *event)
{
if (event->mimeData()->hasText()) {
const QMimeData *mime = event->mimeData();
- QStringList pieces = mime->text().split(QRegularExpression("\\s+"),
- Qt::SkipEmptyParts);
+ const QStringList pieces = mime->text().split(QRegularExpression("\\s+"),
+ Qt::SkipEmptyParts);
QPoint position = event->pos();
QPoint hotSpot;
@@ -98,7 +98,7 @@ void DragWidget::dropEvent(QDropEvent *event)
dropTimer.start(500, this);
update();
- foreach (QString piece, pieces) {
+ for (const QString &piece : pieces) {
FramedLabel *newLabel = new FramedLabel(piece, this);
newLabel->move(position - hotSpot);
newLabel->show();
diff --git a/tests/manual/highdpi/kitchensink/dragwidget.h b/tests/manual/highdpi/kitchensink/dragwidget.h
index e7b9ae1b6f..d444f7f0a6 100644
--- a/tests/manual/highdpi/kitchensink/dragwidget.h
+++ b/tests/manual/highdpi/kitchensink/dragwidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DRAGWIDGET_H
#define DRAGWIDGET_H
diff --git a/tests/manual/highdpi/kitchensink/main.cpp b/tests/manual/highdpi/kitchensink/main.cpp
index fcc9af7928..5fe46828bd 100644
--- a/tests/manual/highdpi/kitchensink/main.cpp
+++ b/tests/manual/highdpi/kitchensink/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QMainWindow>
#include <QMenuBar>
diff --git a/tests/manual/highdpi/pixelgadget/CMakeLists.txt b/tests/manual/highdpi/pixelgadget/CMakeLists.txt
index 9a1f67aeb1..f808786b2a 100644
--- a/tests/manual/highdpi/pixelgadget/CMakeLists.txt
+++ b/tests/manual/highdpi/pixelgadget/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## pixelgadget Binary:
@@ -7,7 +9,7 @@ qt_internal_add_manual_test(pixelgadget
GUI
SOURCES
main.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::Widgets
)
diff --git a/tests/manual/highdpi/pixelgadget/main.cpp b/tests/manual/highdpi/pixelgadget/main.cpp
index 3f419adaf5..51174bc6d9 100644
--- a/tests/manual/highdpi/pixelgadget/main.cpp
+++ b/tests/manual/highdpi/pixelgadget/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui>
#include <QtWidgets>
diff --git a/tests/manual/highdpi/screengadget/CMakeLists.txt b/tests/manual/highdpi/screengadget/CMakeLists.txt
index f85ea159c2..f4a7aad306 100644
--- a/tests/manual/highdpi/screengadget/CMakeLists.txt
+++ b/tests/manual/highdpi/screengadget/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## screengadget Binary:
@@ -7,7 +9,7 @@ qt_internal_add_manual_test(screengadget
GUI
SOURCES
main.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::Widgets
Qt::GuiPrivate
diff --git a/tests/manual/highdpi/screengadget/main.cpp b/tests/manual/highdpi/screengadget/main.cpp
index fdd940724f..68343a31c5 100644
--- a/tests/manual/highdpi/screengadget/main.cpp
+++ b/tests/manual/highdpi/screengadget/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui>
#include <QtWidgets>
diff --git a/tests/manual/iconbrowser/CMakeLists.txt b/tests/manual/iconbrowser/CMakeLists.txt
new file mode 100644
index 0000000000..b0fefba5db
--- /dev/null
+++ b/tests/manual/iconbrowser/CMakeLists.txt
@@ -0,0 +1,64 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+find_package(Qt6 REQUIRED COMPONENTS Gui Widgets)
+
+qt_internal_add_manual_test(iconbrowser
+ GUI
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::Widgets
+ Qt::WidgetsPrivate
+)
+
+if (TARGET Qt::Quick)
+find_package(Qt6 COMPONENTS QuickWidgets REQUIRED)
+
+qt_add_qml_module(iconbrowser
+ URI main
+ VERSION 1.0
+ QML_FILES "Main.qml"
+ NO_RESOURCE_TARGET_PATH
+)
+target_link_libraries(iconbrowser PRIVATE
+ Qt6::QuickWidgets
+)
+endif()
+
+if (ANDROID)
+ set(font_filename "MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].ttf")
+ if (QT_ALLOW_DOWNLOAD)
+ include(FetchContent)
+
+ FetchContent_Declare(
+ MaterialIcons
+ URL
+ "https://github.com/google/material-design-icons/raw/master/variablefont/${font_filename}"
+ DOWNLOAD_DIR ${CMAKE_CURRENT_BINARY_DIR}
+ DOWNLOAD_NAME "${font_filename}"
+ DOWNLOAD_NO_EXTRACT TRUE
+ )
+
+ FetchContent_MakeAvailable(MaterialIcons)
+ endif()
+
+ if (EXISTS "${CMAKE_CURRENT_BINARY_DIR}/${font_filename}")
+ set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/${font_filename}"
+ PROPERTIES QT_RESOURCE_ALIAS ${font_filename})
+ target_compile_definitions(iconbrowser PRIVATE "ICONBROWSER_RESOURCE")
+ qt_add_resources(iconbrowser "icons"
+ PREFIX
+ "/qt-project.org/icons"
+ FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/${font_filename}"
+ )
+ else()
+ message(WARNING "Font file ${font_filename} not found and not downloaded!\n"
+ "Make sure the font file ${font_filename} is available in ${CMAKE_CURRENT_BINARY_DIR}.\n"
+ "Consider configuring with -DQT_ALLOW_DOWNLOAD=ON to download the font automatically.")
+ endif()
+endif()
+
diff --git a/tests/manual/iconbrowser/Main.qml b/tests/manual/iconbrowser/Main.qml
new file mode 100644
index 0000000000..fd16df58ec
--- /dev/null
+++ b/tests/manual/iconbrowser/Main.qml
@@ -0,0 +1,31 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+Rectangle {
+ anchors.fill: parent
+ Column {
+ Row {
+ ToolButton {
+ id: normalButton
+ icon.name: iconName.text
+ }
+ ToolButton {
+ id: disabledButton
+ enabled: false
+ icon.name: iconName.text
+ }
+ ToolButton {
+ id: checkedButton
+ checked: true
+ icon.name: iconName.text
+ }
+ }
+ TextField {
+ id: iconName
+ text: "folder"
+ }
+ }
+}
diff --git a/tests/manual/iconbrowser/main.cpp b/tests/manual/iconbrowser/main.cpp
new file mode 100644
index 0000000000..e4749de6cc
--- /dev/null
+++ b/tests/manual/iconbrowser/main.cpp
@@ -0,0 +1,570 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtWidgets>
+
+#include <QtWidgets/private/qapplication_p.h>
+#include <QtGui/qpa/qplatformtheme.h>
+
+#ifdef QT_QUICKWIDGETS_LIB
+#include <QQuickWidget>
+#endif
+
+using namespace Qt::StringLiterals;
+
+class IconModel : public QAbstractItemModel
+{
+ const QStringList themedIcons = {
+ u"address-book-new"_s,
+ u"application-exit"_s,
+ u"appointment-new"_s,
+ u"call-start"_s,
+ u"call-stop"_s,
+ u"contact-new"_s,
+ u"document-new"_s,
+ u"document-open"_s,
+ u"document-open-recent"_s,
+ u"document-page-setup"_s,
+ u"document-print"_s,
+ u"document-print-preview"_s,
+ u"document-properties"_s,
+ u"document-revert"_s,
+ u"document-save"_s,
+ u"document-save-as"_s,
+ u"document-send"_s,
+ u"edit-clear"_s,
+ u"edit-copy"_s,
+ u"edit-cut"_s,
+ u"edit-delete"_s,
+ u"edit-find"_s,
+ u"edit-find-replace"_s,
+ u"edit-paste"_s,
+ u"edit-redo"_s,
+ u"edit-select-all"_s,
+ u"edit-undo"_s,
+ u"folder-new"_s,
+ u"format-indent-less"_s,
+ u"format-indent-more"_s,
+ u"format-justify-center"_s,
+ u"format-justify-fill"_s,
+ u"format-justify-left"_s,
+ u"format-justify-right"_s,
+ u"format-text-direction-ltr"_s,
+ u"format-text-direction-rtl"_s,
+ u"format-text-bold"_s,
+ u"format-text-italic"_s,
+ u"format-text-underline"_s,
+ u"format-text-strikethrough"_s,
+ u"go-bottom"_s,
+ u"go-down"_s,
+ u"go-first"_s,
+ u"go-home"_s,
+ u"go-jump"_s,
+ u"go-last"_s,
+ u"go-next"_s,
+ u"go-previous"_s,
+ u"go-top"_s,
+ u"go-up"_s,
+ u"help-about"_s,
+ u"help-contents"_s,
+ u"help-faq"_s,
+ u"insert-image"_s,
+ u"insert-link"_s,
+ u"insert-object"_s,
+ u"insert-text"_s,
+ u"list-add"_s,
+ u"list-remove"_s,
+ u"mail-forward"_s,
+ u"mail-mark-important"_s,
+ u"mail-mark-junk"_s,
+ u"mail-mark-notjunk"_s,
+ u"mail-mark-read"_s,
+ u"mail-mark-unread"_s,
+ u"mail-message-new"_s,
+ u"mail-reply-all"_s,
+ u"mail-reply-sender"_s,
+ u"mail-send"_s,
+ u"mail-send-receive"_s,
+ u"media-eject"_s,
+ u"media-playback-pause"_s,
+ u"media-playback-start"_s,
+ u"media-playback-stop"_s,
+ u"media-record"_s,
+ u"media-seek-backward"_s,
+ u"media-seek-forward"_s,
+ u"media-skip-backward"_s,
+ u"media-skip-forward"_s,
+ u"object-flip-horizontal"_s,
+ u"object-flip-vertical"_s,
+ u"object-rotate-left"_s,
+ u"object-rotate-right"_s,
+ u"process-stop"_s,
+ u"system-lock-screen"_s,
+ u"system-log-out"_s,
+ u"system-run"_s,
+ u"system-search"_s,
+ u"system-reboot"_s,
+ u"system-shutdown"_s,
+ u"tools-check-spelling"_s,
+ u"view-fullscreen"_s,
+ u"view-refresh"_s,
+ u"view-restore"_s,
+ u"view-sort-ascending"_s,
+ u"view-sort-descending"_s,
+ u"window-close"_s,
+ u"window-new"_s,
+ u"zoom-fit-best"_s,
+ u"zoom-in"_s,
+ u"zoom-original"_s,
+ u"zoom-out"_s,
+
+
+ u"process-working"_s,
+
+
+ u"accessories-calculator"_s,
+ u"accessories-character-map"_s,
+ u"accessories-dictionary"_s,
+ u"accessories-text-editor"_s,
+ u"help-browser"_s,
+ u"multimedia-volume-control"_s,
+ u"preferences-desktop-accessibility"_s,
+ u"preferences-desktop-font"_s,
+ u"preferences-desktop-keyboard"_s,
+ u"preferences-desktop-locale"_s,
+ u"preferences-desktop-multimedia"_s,
+ u"preferences-desktop-screensaver"_s,
+ u"preferences-desktop-theme"_s,
+ u"preferences-desktop-wallpaper"_s,
+ u"system-file-manager"_s,
+ u"system-software-install"_s,
+ u"system-software-update"_s,
+ u"utilities-system-monitor"_s,
+ u"utilities-terminal"_s,
+
+
+ u"applications-accessories"_s,
+ u"applications-development"_s,
+ u"applications-engineering"_s,
+ u"applications-games"_s,
+ u"applications-graphics"_s,
+ u"applications-internet"_s,
+ u"applications-multimedia"_s,
+ u"applications-office"_s,
+ u"applications-other"_s,
+ u"applications-science"_s,
+ u"applications-system"_s,
+ u"applications-utilities"_s,
+ u"preferences-desktop"_s,
+ u"preferences-desktop-peripherals"_s,
+ u"preferences-desktop-personal"_s,
+ u"preferences-other"_s,
+ u"preferences-system"_s,
+ u"preferences-system-network"_s,
+ u"system-help"_s,
+
+
+ u"audio-card"_s,
+ u"audio-input-microphone"_s,
+ u"battery"_s,
+ u"camera-photo"_s,
+ u"camera-video"_s,
+ u"camera-web"_s,
+ u"computer"_s,
+ u"drive-harddisk"_s,
+ u"drive-optical"_s,
+ u"drive-removable-media"_s,
+ u"input-gaming"_s,
+ u"input-keyboard"_s,
+ u"input-mouse"_s,
+ u"input-tablet"_s,
+ u"media-flash"_s,
+ u"media-floppy"_s,
+ u"media-optical"_s,
+ u"media-tape"_s,
+ u"modem"_s,
+ u"multimedia-player"_s,
+ u"network-wired"_s,
+ u"network-wireless"_s,
+ u"pda"_s,
+ u"phone"_s,
+ u"printer"_s,
+ u"scanner"_s,
+ u"video-display"_s,
+
+
+ u"emblem-default"_s,
+ u"emblem-documents"_s,
+ u"emblem-downloads"_s,
+ u"emblem-favorite"_s,
+ u"emblem-important"_s,
+ u"emblem-mail"_s,
+ u"emblem-photos"_s,
+ u"emblem-readonly"_s,
+ u"emblem-shared"_s,
+ u"emblem-symbolic-link"_s,
+ u"emblem-synchronized"_s,
+ u"emblem-system"_s,
+ u"emblem-unreadable"_s,
+
+
+ u"face-angel"_s,
+ u"face-angry"_s,
+ u"face-cool"_s,
+ u"face-crying"_s,
+ u"face-devilish"_s,
+ u"face-embarrassed"_s,
+ u"face-kiss"_s,
+ u"face-laugh"_s,
+ u"face-monkey"_s,
+ u"face-plain"_s,
+ u"face-raspberry"_s,
+ u"face-sad"_s,
+ u"face-sick"_s,
+ u"face-smile"_s,
+ u"face-smile-big"_s,
+ u"face-smirk"_s,
+ u"face-surprise"_s,
+ u"face-tired"_s,
+ u"face-uncertain"_s,
+ u"face-wink"_s,
+ u"face-worried"_s,
+
+
+ u"flag-aa"_s,
+
+
+ u"application-x-executable"_s,
+ u"audio-x-generic"_s,
+ u"font-x-generic"_s,
+ u"image-x-generic"_s,
+ u"package-x-generic"_s,
+ u"text-html"_s,
+ u"text-x-generic"_s,
+ u"text-x-generic-template"_s,
+ u"text-x-script"_s,
+ u"video-x-generic"_s,
+ u"x-office-address-book"_s,
+ u"x-office-calendar"_s,
+ u"x-office-document"_s,
+ u"x-office-presentation"_s,
+ u"x-office-spreadsheet"_s,
+
+
+ u"folder"_s,
+ u"folder-remote"_s,
+ u"network-server"_s,
+ u"network-workgroup"_s,
+ u"start-here"_s,
+ u"user-bookmarks"_s,
+ u"user-desktop"_s,
+ u"user-home"_s,
+ u"user-trash"_s,
+
+
+ u"appointment-missed"_s,
+ u"appointment-soon"_s,
+ u"audio-volume-high"_s,
+ u"audio-volume-low"_s,
+ u"audio-volume-medium"_s,
+ u"audio-volume-muted"_s,
+ u"battery-caution"_s,
+ u"battery-low"_s,
+ u"dialog-error"_s,
+ u"dialog-information"_s,
+ u"dialog-password"_s,
+ u"dialog-question"_s,
+ u"dialog-warning"_s,
+ u"folder-drag-accept"_s,
+ u"folder-open"_s,
+ u"folder-visiting"_s,
+ u"image-loading"_s,
+ u"image-missing"_s,
+ u"mail-attachment"_s,
+ u"mail-unread"_s,
+ u"mail-read"_s,
+ u"mail-replied"_s,
+ u"mail-signed"_s,
+ u"mail-signed-verified"_s,
+ u"media-playlist-repeat"_s,
+ u"media-playlist-shuffle"_s,
+ u"network-error"_s,
+ u"network-idle"_s,
+ u"network-offline"_s,
+ u"network-receive"_s,
+ u"network-transmit"_s,
+ u"network-transmit-receive"_s,
+ u"printer-error"_s,
+ u"printer-printing"_s,
+ u"security-high"_s,
+ u"security-medium"_s,
+ u"security-low"_s,
+ u"software-update-available"_s,
+ u"software-update-urgent"_s,
+ u"sync-error"_s,
+ u"sync-synchronizing"_s,
+ u"task-due"_s,
+ u"task-past-due"_s,
+ u"user-available"_s,
+ u"user-away"_s,
+ u"user-idle"_s,
+ u"user-offline"_s,
+ u"user-trash-full"_s,
+ u"weather-clear"_s,
+ u"weather-clear-night"_s,
+ u"weather-few-clouds"_s,
+ u"weather-few-clouds-night"_s,
+ u"weather-fog"_s,
+ u"weather-overcast"_s,
+ u"weather-severe-alert"_s,
+ u"weather-showers"_s,
+ u"weather-showers-scattered"_s,
+ u"weather-snow"_s,
+ u"weather-storm"_s,
+ };
+public:
+ using QAbstractItemModel::QAbstractItemModel;
+
+ enum Columns {
+ Name,
+ Style,
+ Theme,
+ Icon
+ };
+
+ int rowCount(const QModelIndex &parent) const override
+ {
+ if (parent.isValid())
+ return 0;
+ return themedIcons.size() + QStyle::NStandardPixmap;
+ }
+ int columnCount(const QModelIndex &parent) const override
+ {
+ if (parent.isValid())
+ return 0;
+ return Icon + 1;
+ }
+ QModelIndex index(int row, int column, const QModelIndex &parent) const override
+ {
+ if (parent.isValid())
+ return {};
+ if (column > columnCount(parent) || row > rowCount(parent))
+ return {};
+ return createIndex(row, column, quintptr(row));
+ }
+ QModelIndex parent(const QModelIndex &) const override
+ {
+ return {};
+ }
+
+ QVariant data(const QModelIndex &index, int role) const override
+ {
+ int row = index.row();
+ const Columns column = Columns(index.column());
+ if (!index.isValid() || row >= rowCount(index.parent()) || column >= columnCount(index.parent()))
+ return {};
+ const bool fromIcon = row < themedIcons.size();
+ if (!fromIcon)
+ row -= themedIcons.size();
+ switch (role) {
+ case Qt::DisplayRole:
+ if (fromIcon) {
+ return themedIcons.at(row);
+ } else {
+ const QMetaObject *styleMO = &QStyle::staticMetaObject;
+ const int pixmapIndex = styleMO->indexOfEnumerator("StandardPixmap");
+ Q_ASSERT(pixmapIndex >= 0);
+ const QMetaEnum pixmapEnum = styleMO->enumerator(pixmapIndex);
+ const QString pixmapName = QString::fromUtf8(pixmapEnum.key(row));
+ return QVariant(pixmapName);
+ }
+ break;
+ case Qt::DecorationRole:
+ switch (index.column()) {
+ case Name:
+ break;
+ case Style:
+ if (fromIcon)
+ break;
+ return QApplication::style()->standardIcon(QStyle::StandardPixmap(row));
+ case Theme:
+ if (fromIcon)
+ break;
+ return QIcon(QApplicationPrivate::platformTheme()->standardPixmap(QPlatformTheme::StandardPixmap(row), {36, 36}));
+ case Icon:
+ if (fromIcon)
+ return QIcon::fromTheme(themedIcons.at(row));
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return {};
+ }
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override
+ {
+ switch (orientation) {
+ case Qt::Vertical:
+ break;
+ case Qt::Horizontal:
+ if (role == Qt::DisplayRole) {
+ switch (section) {
+ case Name:
+ return "Name";
+ case Style:
+ return "Style";
+ case Theme:
+ return "Theme";
+ case Icon:
+ return"Icon";
+ }
+ }
+ }
+ return QAbstractItemModel::headerData(section, orientation, role);
+ }
+};
+
+template<IconModel::Columns Column>
+struct ColumnModel : public QSortFilterProxyModel
+{
+ bool filterAcceptsColumn(int sourceColumn, const QModelIndex &) const override
+ {
+ return sourceColumn == Column;
+ }
+
+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
+ {
+ const QModelIndex sourceIndex = sourceModel()->index(sourceRow, Column, sourceParent);
+ const QIcon iconData = sourceModel()->data(sourceIndex, Qt::DecorationRole).template value<QIcon>();
+ return !iconData.isNull();
+ }
+};
+
+template<IconModel::Columns Column>
+struct IconView : public QListView
+{
+ ColumnModel<Column> proxyModel;
+
+ IconView(QAbstractItemModel *model)
+ {
+ setViewMode(QListView::ListMode);
+ setIconSize(QSize(64, 64));
+ setUniformItemSizes(true);
+ proxyModel.setSourceModel(model);
+ setModel(&proxyModel);
+ }
+};
+
+class IconInspector : public QFrame
+{
+public:
+ IconInspector()
+ {
+ setFrameShape(QFrame::StyledPanel);
+
+ QLineEdit *lineEdit = new QLineEdit;
+ connect(lineEdit, &QLineEdit::textChanged,
+ this, &IconInspector::updateIcon);
+
+ QVBoxLayout *vbox = new QVBoxLayout;
+ vbox->addStretch(10);
+ vbox->addWidget(lineEdit);
+ setLayout(vbox);
+ }
+
+protected:
+ void paintEvent(QPaintEvent *event) override
+ {
+ QPainter painter(this);
+ painter.fillRect(event->rect(), palette().window());
+ if (!icon.isNull()) {
+ const QString modeLabels[] = { u"Normal"_s, u"Disabled"_s, u"Active"_s, u"Selected"_s};
+ const QString stateLabels[] = { u"On"_s, u"Off"_s};
+ const int labelWidth = fontMetrics().horizontalAdvance(u"Disabled"_s);
+ const int labelHeight = fontMetrics().height();
+ int labelYs[4] = {};
+ int labelXs[2] = {};
+
+ painter.save();
+ painter.translate(labelWidth + contentsMargins().left(), labelHeight * 2);
+ const QBrush brush(palette().base().color(), Qt::CrossPattern);
+
+ QPoint point;
+ for (const auto &mode : {QIcon::Normal, QIcon::Disabled, QIcon::Active, QIcon::Selected}) {
+ int height = 0;
+ for (const auto &state : {QIcon::On, QIcon::Off}) {
+ int totalWidth = 0;
+ const int relativeX = point.x();
+ const auto sizes = icon.availableSizes(mode, state);
+ for (const auto &size : sizes) {
+ if (size.width() > 256)
+ continue;
+ const QRect iconRect(point, size);
+ painter.fillRect(iconRect, brush);
+ icon.paint(&painter, iconRect, Qt::AlignCenter, mode, state);
+ totalWidth += size.width();
+ point.rx() += size.width();
+ height = std::max(height, size.height());
+ }
+ labelXs[state] = relativeX + totalWidth / 2;
+ }
+ point.rx() = 0;
+ labelYs[mode] = point.ry() + height / 2;
+ point.ry() += height;
+ }
+ painter.restore();
+
+ painter.translate(contentsMargins().left(), labelHeight);
+ for (const auto &mode : {QIcon::Normal, QIcon::Disabled, QIcon::Active, QIcon::Selected})
+ painter.drawText(QPoint(0, labelYs[mode]), modeLabels[mode]);
+ painter.translate(labelWidth, 0);
+ for (const auto &state : {QIcon::On, QIcon::Off})
+ painter.drawText(QPoint(labelXs[state], 0), stateLabels[state]);
+ }
+ QFrame::paintEvent(event);
+ }
+private:
+ QIcon icon;
+ void updateIcon(const QString &iconName)
+ {
+ icon = QIcon::fromTheme(iconName);
+ update();
+ }
+};
+
+int main(int argc, char* argv[])
+{
+ QApplication app(argc, argv);
+
+#ifdef ICONBROWSER_RESOURCE
+ Q_INIT_RESOURCE(icons);
+#endif
+
+ IconModel model;
+
+ QTabWidget widget;
+ widget.setTabPosition(QTabWidget::West);
+ widget.addTab(new IconInspector, "Inspect");
+ widget.addTab(new IconView<IconModel::Icon>(&model), "QIcon::fromTheme");
+ widget.addTab(new IconView<IconModel::Style>(&model), "QStyle");
+ widget.addTab(new IconView<IconModel::Theme>(&model), "QPlatformTheme");
+
+#ifdef QT_QUICKWIDGETS_LIB
+ QQuickWidget *quickBrowser = new QQuickWidget;
+ quickBrowser->setSource(QUrl(u"qrc:/Main.qml"_s));
+ quickBrowser->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ widget.addTab(quickBrowser, "Qt Quick");
+ QObject::connect(quickBrowser, &QQuickWidget::statusChanged, quickBrowser,
+ [](QQuickWidget::Status status){
+ qDebug() << status;
+ });
+ QObject::connect(quickBrowser, &QQuickWidget::sceneGraphError, quickBrowser,
+ [](QQuickWindow::SceneGraphError error, const QString &message){
+ qDebug() << error << message;
+ });
+#endif
+
+ widget.show();
+ return app.exec();
+}
diff --git a/tests/manual/inputdevices/CMakeLists.txt b/tests/manual/inputdevices/CMakeLists.txt
index 9440705a9b..54dd2556fb 100644
--- a/tests/manual/inputdevices/CMakeLists.txt
+++ b/tests/manual/inputdevices/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
project(inputdevices)
cmake_minimum_required(VERSION 3.19)
diff --git a/tests/manual/inputdevices/inputdevicemodel.cpp b/tests/manual/inputdevices/inputdevicemodel.cpp
index 8168ae64bf..c0b594a7da 100644
--- a/tests/manual/inputdevices/inputdevicemodel.cpp
+++ b/tests/manual/inputdevices/inputdevicemodel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "inputdevicemodel.h"
diff --git a/tests/manual/inputdevices/inputdevicemodel.h b/tests/manual/inputdevices/inputdevicemodel.h
index c68e64e95d..7c72d498e0 100644
--- a/tests/manual/inputdevices/inputdevicemodel.h
+++ b/tests/manual/inputdevices/inputdevicemodel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef INPUTDEVICEMODEL_H
#define INPUTDEVICEMODEL_H
diff --git a/tests/manual/inputdevices/main.cpp b/tests/manual/inputdevices/main.cpp
index 4b4386ae6f..2857fd0fb6 100644
--- a/tests/manual/inputdevices/main.cpp
+++ b/tests/manual/inputdevices/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QLoggingCategory>
diff --git a/tests/manual/inputmethodhints/inputmethodhints.cpp b/tests/manual/inputmethodhints/inputmethodhints.cpp
index e2b6c13a2a..89fa9ab0be 100644
--- a/tests/manual/inputmethodhints/inputmethodhints.cpp
+++ b/tests/manual/inputmethodhints/inputmethodhints.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "inputmethodhints.h"
diff --git a/tests/manual/inputmethodhints/inputmethodhints.h b/tests/manual/inputmethodhints/inputmethodhints.h
index 6343a9e24f..24461d87b0 100644
--- a/tests/manual/inputmethodhints/inputmethodhints.h
+++ b/tests/manual/inputmethodhints/inputmethodhints.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef INPUTMETHODHINTS_H
#define INPUTMETHODHINTS_H
diff --git a/tests/manual/inputmethodhints/main.cpp b/tests/manual/inputmethodhints/main.cpp
index 11d2e00cae..1b511b91f8 100644
--- a/tests/manual/inputmethodhints/main.cpp
+++ b/tests/manual/inputmethodhints/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "inputmethodhints.h"
diff --git a/tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/AppIcon1024x1024.png b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/AppIcon1024x1024.png
new file mode 100644
index 0000000000..91d71a1b45
--- /dev/null
+++ b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/AppIcon1024x1024.png
Binary files differ
diff --git a/tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/AppIcon167x167.png b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/AppIcon167x167.png
new file mode 100644
index 0000000000..c184c7f93d
--- /dev/null
+++ b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/AppIcon167x167.png
Binary files differ
diff --git a/tests/manual/ios_assets/appicon/AppIcon60x60@2x.png b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/AppIcon60x60@2x.png
index 14eb31e401..14eb31e401 100644
--- a/tests/manual/ios_assets/appicon/AppIcon60x60@2x.png
+++ b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/AppIcon60x60@2x.png
Binary files differ
diff --git a/tests/manual/ios_assets/appicon/AppIcon76x76@2x~ipad.png b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/AppIcon76x76@2x~ipad.png
index ffa1d4729a..ffa1d4729a 100644
--- a/tests/manual/ios_assets/appicon/AppIcon76x76@2x~ipad.png
+++ b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/AppIcon76x76@2x~ipad.png
Binary files differ
diff --git a/tests/manual/ios_assets/Assets.xcassets/AppIcon.appiconset/Contents.json b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/Contents.json
index 9221b9bb1a..c7cc581ac4 100644
--- a/tests/manual/ios_assets/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/AppIcon.appiconset/Contents.json
@@ -31,6 +31,7 @@
"size" : "40x40"
},
{
+ "filename" : "AppIcon60x60@2x.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
@@ -76,16 +77,19 @@
"size" : "76x76"
},
{
+ "filename" : "AppIcon76x76@2x~ipad.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
+ "filename" : "AppIcon167x167.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
+ "filename" : "AppIcon1024x1024.png",
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
diff --git a/tests/manual/ios_assets/Assets.xcassets/Contents.json b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/Contents.json
index 73c00596a7..73c00596a7 100644
--- a/tests/manual/ios_assets/Assets.xcassets/Contents.json
+++ b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/Contents.json
diff --git a/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Contents.json b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/Face.imageset/Contents.json
index 2108ce2651..2108ce2651 100644
--- a/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Contents.json
+++ b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/Face.imageset/Contents.json
diff --git a/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-16.png b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/Face.imageset/Face-16.png
index 670beb4fbb..670beb4fbb 100644
--- a/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-16.png
+++ b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/Face.imageset/Face-16.png
Binary files differ
diff --git a/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-32.png b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/Face.imageset/Face-32.png
index ee159b6061..ee159b6061 100644
--- a/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-32.png
+++ b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/Face.imageset/Face-32.png
Binary files differ
diff --git a/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-48.png b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/Face.imageset/Face-48.png
index f0783cd533..f0783cd533 100644
--- a/tests/manual/ios_assets/Assets.xcassets/Face.imageset/Face-48.png
+++ b/tests/manual/ios_assets/AssetsXcode13.0.xcassets/Face.imageset/Face-48.png
Binary files differ
diff --git a/tests/manual/ios_assets/AssetsXcode14.3.xcassets/AppIcon.appiconset/AppIcon1024x1024.png b/tests/manual/ios_assets/AssetsXcode14.3.xcassets/AppIcon.appiconset/AppIcon1024x1024.png
new file mode 100644
index 0000000000..91d71a1b45
--- /dev/null
+++ b/tests/manual/ios_assets/AssetsXcode14.3.xcassets/AppIcon.appiconset/AppIcon1024x1024.png
Binary files differ
diff --git a/tests/manual/ios_assets/AssetsXcode14.3.xcassets/AppIcon.appiconset/Contents.json b/tests/manual/ios_assets/AssetsXcode14.3.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000..7baa233d94
--- /dev/null
+++ b/tests/manual/ios_assets/AssetsXcode14.3.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,104 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "3x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "3x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "2x",
+ "size" : "38x38"
+ },
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "3x",
+ "size" : "38x38"
+ },
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "3x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "2x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "3x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "2x",
+ "size" : "64x64"
+ },
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "3x",
+ "size" : "64x64"
+ },
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "2x",
+ "size" : "68x68"
+ },
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "2x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "2x",
+ "size" : "83.5x83.5"
+ },
+ {
+ "filename" : "AppIcon1024x1024.png",
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/tests/manual/ios_assets/AssetsXcode14.3.xcassets/Contents.json b/tests/manual/ios_assets/AssetsXcode14.3.xcassets/Contents.json
new file mode 100644
index 0000000000..73c00596a7
--- /dev/null
+++ b/tests/manual/ios_assets/AssetsXcode14.3.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Contents.json b/tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Contents.json
new file mode 100644
index 0000000000..2108ce2651
--- /dev/null
+++ b/tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "filename" : "Face-16.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "Face-32.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "Face-48.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Face-16.png b/tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Face-16.png
new file mode 100644
index 0000000000..670beb4fbb
--- /dev/null
+++ b/tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Face-16.png
Binary files differ
diff --git a/tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Face-32.png b/tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Face-32.png
new file mode 100644
index 0000000000..ee159b6061
--- /dev/null
+++ b/tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Face-32.png
Binary files differ
diff --git a/tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Face-48.png b/tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Face-48.png
new file mode 100644
index 0000000000..f0783cd533
--- /dev/null
+++ b/tests/manual/ios_assets/AssetsXcode14.3.xcassets/Face.imageset/Face-48.png
Binary files differ
diff --git a/tests/manual/ios_assets/CMakeLists.txt b/tests/manual/ios_assets/CMakeLists.txt
index a19fe5a2e6..a9abe52b02 100644
--- a/tests/manual/ios_assets/CMakeLists.txt
+++ b/tests/manual/ios_assets/CMakeLists.txt
@@ -22,12 +22,20 @@ target_link_libraries(tst_manual_ios_assets PRIVATE
)
# Custom Info.plist
-set_target_properties(tst_manual_ios_assets
- PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.ios.cmake.plist")
+if(IOS)
+ if(XCODE_VERSION AND XCODE_VERSION VERSION_LESS "14")
+ set(plist_path "${CMAKE_CURRENT_SOURCE_DIR}/Info.ios.cmake.xcode.13.0.plist")
+ else()
+ set(plist_path "${CMAKE_CURRENT_SOURCE_DIR}/Info.ios.cmake.xcode.14.3.plist")
+ endif()
+ set_target_properties(tst_manual_ios_assets
+ PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${plist_path}")
+endif()
# Custom resources
file(GLOB_RECURSE text_files CONFIGURE_DEPENDS "*.txt")
if(text_files)
+ list(FILTER text_files EXCLUDE REGEX CMakeLists.txt)
target_sources(tst_manual_ios_assets PRIVATE ${text_files})
# On iOS the 'Resources' prefix is removed by Xcode because on iOS app bundles are shallow,
# so the final location of the text file will be
@@ -40,22 +48,36 @@ if(text_files)
endif()
# App icons
-file(GLOB_RECURSE app_icons CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/appicon/AppIcon*.png")
-if(IOS AND app_icons)
- target_sources(tst_manual_ios_assets PRIVATE ${app_icons})
- set_source_files_properties(
- ${app_icons}
- PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
-endif()
+# https://developer.apple.com/library/archive/qa/qa1686/_index.html
+# https://help.apple.com/xcode/mac/current/#/dev10510b1f7
+# https://web.archive.org/web/20180124234409/https://developer.apple.com/ios/human-interface-guidelines/icons-and-images/app-icon/
+# https://doc.qt.io/qt-6/ios-platform-notes.html#icons
+# No need to copy the icons into the bundle manually when using Xcode 13+.
+# - rely on Xcode 13 to copy the needed icon files that are specified in the asset catalog (all the
+# required ones should be specified manually)
+# - rely on Xcode 14 to generate the needed icon files based on the 1024x1024 sized image in the
+# asset catalog
-# Asset catalog with images
+# Asset catalog with images and icons.
if(IOS)
enable_language(OBJCXX)
- set(asset_catalog_path "${CMAKE_CURRENT_SOURCE_DIR}/Assets.xcassets")
+ if(XCODE_VERSION AND XCODE_VERSION VERSION_LESS "14")
+ set(asset_catalog_path "${CMAKE_CURRENT_SOURCE_DIR}/AssetsXcode13.0.xcassets")
+ else()
+ set(asset_catalog_path "${CMAKE_CURRENT_SOURCE_DIR}/AssetsXcode14.3.xcassets")
+ endif()
target_sources(tst_manual_ios_assets PRIVATE "${asset_catalog_path}")
set_source_files_properties(
${asset_catalog_path}
PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
+
+ # Make sure asset catalog compilation generates the needed app icons image sizes.
+ # This might not be needed in a future Qt version where qt_add_executable might do it
+ # automatically. Unclear how to do it cleanly though, because specifying the option when
+ # the asset catalog doesn't have an AppIcon set will cause a build failure.
+ set_target_properties(tst_manual_ios_assets PROPERTIES
+ XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME AppIcon)
+
target_sources(tst_manual_ios_assets PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/utils.mm")
endif()
diff --git a/tests/manual/ios_assets/Info.ios.cmake.plist b/tests/manual/ios_assets/Info.ios.cmake.xcode.13.0.plist
index 2b2bb69522..b83c922e24 100644
--- a/tests/manual/ios_assets/Info.ios.cmake.plist
+++ b/tests/manual/ios_assets/Info.ios.cmake.xcode.13.0.plist
@@ -41,7 +41,7 @@
<true/>
<key>UILaunchStoryboardName</key>
- <string>CustomLaunchScreen.storyboard</string>
+ <string>CustomLaunchScreen</string>
<key>UISupportedInterfaceOrientations</key>
<array>
@@ -57,13 +57,10 @@
<dict>
<key>CFBundleIconFiles</key>
<array>
- <string>AppIcon29x29.png</string>
- <string>AppIcon29x29@2x.png</string>
- <string>AppIcon40x40@2x.png</string>
- <string>AppIcon57x57.png</string>
- <string>AppIcon57x57@2x.png</string>
<string>AppIcon60x60@2x.png</string>
</array>
+ <key>CFBundleIconName</key>
+ <string>AppIcon</string>
</dict>
</dict>
<key>CFBundleIcons~ipad</key>
@@ -72,21 +69,6 @@
<dict>
<key>CFBundleIconFiles</key>
<array>
- <string>AppIcon29x29.png</string>
- <string>AppIcon29x29@2x.png</string>
- <string>AppIcon40x40@2x.png</string>
- <string>AppIcon57x57.png</string>
- <string>AppIcon57x57@2x.png</string>
- <string>AppIcon60x60@2x.png</string>
- <string>AppIcon29x29~ipad.png</string>
- <string>AppIcon29x29@2x~ipad.png</string>
- <string>AppIcon40x40~ipad.png</string>
- <string>AppIcon40x40@2x~ipad.png</string>
- <string>AppIcon50x50~ipad.png</string>
- <string>AppIcon50x50@2x~ipad.png</string>
- <string>AppIcon72x72~ipad.png</string>
- <string>AppIcon72x72@2x~ipad.png</string>
- <string>AppIcon76x76~ipad.png</string>
<string>AppIcon76x76@2x~ipad.png</string>
</array>
</dict>
diff --git a/tests/manual/ios_assets/Info.ios.cmake.xcode.14.3.plist b/tests/manual/ios_assets/Info.ios.cmake.xcode.14.3.plist
new file mode 100644
index 0000000000..282ad81c94
--- /dev/null
+++ b/tests/manual/ios_assets/Info.ios.cmake.xcode.14.3.plist
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+
+ <key>CFBundleDisplayName</key>
+ <string>$(PRODUCT_NAME)</string>
+
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+
+ <key>CFBundleVersion</key>
+ <string>$(CURRENT_PROJECT_VERSION)</string>
+
+ <key>CFBundleShortVersionString</key>
+ <string>$(MARKETING_VERSION)</string>
+
+ <key>NSHumanReadableCopyright</key>
+ <string></string>
+
+ <key>CFBundleIconFile</key>
+ <string></string>
+
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleAllowMixedLocalizations</key>
+ <true/>
+
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+
+ <key>UILaunchStoryboardName</key>
+ <string>CustomLaunchScreen</string>
+
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
diff --git a/tests/manual/ios_assets/Info.ios.qmake.plist b/tests/manual/ios_assets/Info.ios.qmake.xcode.13.0.plist
index e97150e47b..7b1454beeb 100644
--- a/tests/manual/ios_assets/Info.ios.qmake.plist
+++ b/tests/manual/ios_assets/Info.ios.qmake.xcode.13.0.plist
@@ -40,13 +40,10 @@
<dict>
<key>CFBundleIconFiles</key>
<array>
- <string>AppIcon29x29.png</string>
- <string>AppIcon29x29@2x.png</string>
- <string>AppIcon40x40@2x.png</string>
- <string>AppIcon57x57.png</string>
- <string>AppIcon57x57@2x.png</string>
<string>AppIcon60x60@2x.png</string>
</array>
+ <key>CFBundleIconName</key>
+ <string>AppIcon</string>
</dict>
</dict>
<key>CFBundleIcons~ipad</key>
@@ -55,21 +52,6 @@
<dict>
<key>CFBundleIconFiles</key>
<array>
- <string>AppIcon29x29.png</string>
- <string>AppIcon29x29@2x.png</string>
- <string>AppIcon40x40@2x.png</string>
- <string>AppIcon57x57.png</string>
- <string>AppIcon57x57@2x.png</string>
- <string>AppIcon60x60@2x.png</string>
- <string>AppIcon29x29~ipad.png</string>
- <string>AppIcon29x29@2x~ipad.png</string>
- <string>AppIcon40x40~ipad.png</string>
- <string>AppIcon40x40@2x~ipad.png</string>
- <string>AppIcon50x50~ipad.png</string>
- <string>AppIcon50x50@2x~ipad.png</string>
- <string>AppIcon72x72~ipad.png</string>
- <string>AppIcon72x72@2x~ipad.png</string>
- <string>AppIcon76x76~ipad.png</string>
<string>AppIcon76x76@2x~ipad.png</string>
</array>
</dict>
diff --git a/tests/manual/ios_assets/Info.ios.qmake.xcode.14.3.plist b/tests/manual/ios_assets/Info.ios.qmake.xcode.14.3.plist
new file mode 100644
index 0000000000..9df52cb2f7
--- /dev/null
+++ b/tests/manual/ios_assets/Info.ios.qmake.xcode.14.3.plist
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDisplayName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string>${ASSETCATALOG_COMPILER_APPICON_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>${QMAKE_SHORT_VERSION}</string>
+ <key>CFBundleSignature</key>
+ <string>${QMAKE_PKGINFO_TYPEINFO}</string>
+ <key>CFBundleVersion</key>
+ <string>${QMAKE_FULL_VERSION}</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>MinimumOSVersion</key>
+ <string>${IPHONEOS_DEPLOYMENT_TARGET}</string>
+ <key>UILaunchStoryboardName</key>
+ <string>CustomLaunchScreen</string>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
diff --git a/tests/manual/ios_assets/appicon/AppIcon29x29.png b/tests/manual/ios_assets/appicon/AppIcon29x29.png
deleted file mode 100644
index f220ad51a3..0000000000
--- a/tests/manual/ios_assets/appicon/AppIcon29x29.png
+++ /dev/null
Binary files differ
diff --git a/tests/manual/ios_assets/appicon/AppIcon29x29@2x.png b/tests/manual/ios_assets/appicon/AppIcon29x29@2x.png
deleted file mode 100644
index 69d8a9a3fb..0000000000
--- a/tests/manual/ios_assets/appicon/AppIcon29x29@2x.png
+++ /dev/null
Binary files differ
diff --git a/tests/manual/ios_assets/appicon/AppIcon29x29@2x~ipad.png b/tests/manual/ios_assets/appicon/AppIcon29x29@2x~ipad.png
deleted file mode 100644
index 69d8a9a3fb..0000000000
--- a/tests/manual/ios_assets/appicon/AppIcon29x29@2x~ipad.png
+++ /dev/null
Binary files differ
diff --git a/tests/manual/ios_assets/appicon/AppIcon29x29~ipad.png b/tests/manual/ios_assets/appicon/AppIcon29x29~ipad.png
deleted file mode 100644
index f220ad51a3..0000000000
--- a/tests/manual/ios_assets/appicon/AppIcon29x29~ipad.png
+++ /dev/null
Binary files differ
diff --git a/tests/manual/ios_assets/appicon/AppIcon40x40@2x.png b/tests/manual/ios_assets/appicon/AppIcon40x40@2x.png
deleted file mode 100644
index 2966b0d866..0000000000
--- a/tests/manual/ios_assets/appicon/AppIcon40x40@2x.png
+++ /dev/null
Binary files differ
diff --git a/tests/manual/ios_assets/appicon/AppIcon40x40@2x~ipad.png b/tests/manual/ios_assets/appicon/AppIcon40x40@2x~ipad.png
deleted file mode 100644
index 2966b0d866..0000000000
--- a/tests/manual/ios_assets/appicon/AppIcon40x40@2x~ipad.png
+++ /dev/null
Binary files differ
diff --git a/tests/manual/ios_assets/appicon/AppIcon40x40~ipad.png b/tests/manual/ios_assets/appicon/AppIcon40x40~ipad.png
deleted file mode 100644
index 9956f67ba6..0000000000
--- a/tests/manual/ios_assets/appicon/AppIcon40x40~ipad.png
+++ /dev/null
Binary files differ
diff --git a/tests/manual/ios_assets/appicon/AppIcon50x50@2x~ipad.png b/tests/manual/ios_assets/appicon/AppIcon50x50@2x~ipad.png
deleted file mode 100644
index 570f8c9a50..0000000000
--- a/tests/manual/ios_assets/appicon/AppIcon50x50@2x~ipad.png
+++ /dev/null
Binary files differ
diff --git a/tests/manual/ios_assets/appicon/AppIcon50x50~ipad.png b/tests/manual/ios_assets/appicon/AppIcon50x50~ipad.png
deleted file mode 100644
index 9d7b8edc4f..0000000000
--- a/tests/manual/ios_assets/appicon/AppIcon50x50~ipad.png
+++ /dev/null
Binary files differ
diff --git a/tests/manual/ios_assets/appicon/AppIcon57x57.png b/tests/manual/ios_assets/appicon/AppIcon57x57.png
deleted file mode 100644
index 5d558bbb08..0000000000
--- a/tests/manual/ios_assets/appicon/AppIcon57x57.png
+++ /dev/null
Binary files differ
diff --git a/tests/manual/ios_assets/appicon/AppIcon57x57@2x.png b/tests/manual/ios_assets/appicon/AppIcon57x57@2x.png
deleted file mode 100644
index ea14c9c0b9..0000000000
--- a/tests/manual/ios_assets/appicon/AppIcon57x57@2x.png
+++ /dev/null
Binary files differ
diff --git a/tests/manual/ios_assets/appicon/AppIcon72x72@2x~ipad.png b/tests/manual/ios_assets/appicon/AppIcon72x72@2x~ipad.png
deleted file mode 100644
index cb988891ff..0000000000
--- a/tests/manual/ios_assets/appicon/AppIcon72x72@2x~ipad.png
+++ /dev/null
Binary files differ
diff --git a/tests/manual/ios_assets/appicon/AppIcon72x72~ipad.png b/tests/manual/ios_assets/appicon/AppIcon72x72~ipad.png
deleted file mode 100644
index a0072bcfa1..0000000000
--- a/tests/manual/ios_assets/appicon/AppIcon72x72~ipad.png
+++ /dev/null
Binary files differ
diff --git a/tests/manual/ios_assets/appicon/AppIcon76x76~ipad.png b/tests/manual/ios_assets/appicon/AppIcon76x76~ipad.png
deleted file mode 100644
index 15a13665f0..0000000000
--- a/tests/manual/ios_assets/appicon/AppIcon76x76~ipad.png
+++ /dev/null
Binary files differ
diff --git a/tests/manual/ios_assets/ios_assets.pro b/tests/manual/ios_assets/ios_assets.pro
index a6a610dcd8..a2aa348771 100644
--- a/tests/manual/ios_assets/ios_assets.pro
+++ b/tests/manual/ios_assets/ios_assets.pro
@@ -6,7 +6,12 @@ TARGET = tst_manual_ios_assets
# Custom Info.plist
ios {
- QMAKE_INFO_PLIST = Info.ios.qmake.plist
+ versionAtLeast(QMAKE_XCODE_VERSION, 14.0) {
+ plist_path = Info.ios.qmake.xcode.14.3.plist
+ } else {
+ plist_path = Info.ios.qmake.xcode.13.0.plist
+ }
+ QMAKE_INFO_PLIST = $$plist_path
}
# Custom resources
@@ -23,19 +28,18 @@ ios {
macos {
textFiles.path = Contents/Resources/textFiles
}
+textFiles.files -= CMakeLists.txt
QMAKE_BUNDLE_DATA += textFiles
-# App icons
+# Asset catalog with images and app icons
ios {
- ios_icon.files = $$files($$PWD/appicon/AppIcon*.png)
- QMAKE_BUNDLE_DATA += ios_icon
-}
-
-# Asset catalog with images
-ios {
- # The asset catalog needs to have an empty AppIcon.appiconset, otherwise Xcode refuses
+ # The asset catalog needs to have at least an empty AppIcon.appiconset, otherwise Xcode refuses
# to compile the asset catalog.
- QMAKE_ASSET_CATALOGS += Assets.xcassets
+ versionAtLeast(QMAKE_XCODE_VERSION, 14.0) {
+ QMAKE_ASSET_CATALOGS += AssetsXcode14.3.xcassets
+ } else {
+ QMAKE_ASSET_CATALOGS += AssetsXcode13.0.xcassets
+ }
SOURCES += utils.mm
LIBS += -framework UIKit
}
diff --git a/tests/manual/ios_assets/main.cpp b/tests/manual/ios_assets/main.cpp
index 020f3f940b..bbad425b62 100644
--- a/tests/manual/ios_assets/main.cpp
+++ b/tests/manual/ios_assets/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QStandardPaths>
#include <QtCore/QDir>
diff --git a/tests/manual/ios_assets/utils.mm b/tests/manual/ios_assets/utils.mm
index c3b657084e..44a333f673 100644
--- a/tests/manual/ios_assets/utils.mm
+++ b/tests/manual/ios_assets/utils.mm
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QString>
diff --git a/tests/manual/keyevents/CMakeLists.txt b/tests/manual/keyevents/CMakeLists.txt
new file mode 100644
index 0000000000..92a9f6f0b4
--- /dev/null
+++ b/tests/manual/keyevents/CMakeLists.txt
@@ -0,0 +1,12 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_manual_test(keyevents
+ GUI
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::Widgets
+)
diff --git a/tests/manual/keyevents/keyevents.pro b/tests/manual/keyevents/keyevents.pro
new file mode 100644
index 0000000000..dd53f0645d
--- /dev/null
+++ b/tests/manual/keyevents/keyevents.pro
@@ -0,0 +1,6 @@
+QT += core gui widgets gui-private
+
+TARGET = keyevents
+TEMPLATE = app
+
+SOURCES += main.cpp
diff --git a/tests/manual/keyevents/main.cpp b/tests/manual/keyevents/main.cpp
new file mode 100644
index 0000000000..888b7f9d3f
--- /dev/null
+++ b/tests/manual/keyevents/main.cpp
@@ -0,0 +1,305 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtWidgets>
+#include <vector>
+#include <memory>
+#include <type_traits>
+
+#include <private/qkeymapper_p.h>
+#include <private/qguiapplication_p.h>
+
+static const QKeySequence keySequences[] = {
+ QKeySequence("Ctrl+C"),
+ QKeySequence("Ctrl++"),
+};
+
+class KeyEventModel : public QAbstractTableModel
+{
+ Q_OBJECT
+public:
+ explicit KeyEventModel(QObject *parent = nullptr)
+ : QAbstractTableModel(parent)
+ {
+ }
+
+ enum Columns {
+ Language,
+ Direction,
+ Type,
+ ScanCode,
+ VirtualKey,
+ Modifiers,
+ Key,
+ Text,
+ PortableText,
+ NativeText,
+ PossibleKeys,
+ FirstKeySequence,
+ LastKeySequence = FirstKeySequence + std::size(keySequences) - 1,
+ KeySequenceEdit,
+ ColumnCount
+ };
+
+ int rowCount(const QModelIndex & = QModelIndex()) const override
+ {
+ return int(m_events.size());
+ }
+
+ int columnCount(const QModelIndex & = QModelIndex()) const override
+ {
+ return ColumnCount;
+ }
+
+ QVariant headerData(int column, Qt::Orientation orientation, int role) const override
+ {
+ if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
+ switch (column) {
+ case Language: return QString("language");
+ case Direction: return QString("direction");
+ case Type: return QString("type");
+ case ScanCode: return QString("nativeScanCode");
+ case VirtualKey: return QString("nativeVirtualKey");
+ case Modifiers: return QString("modifiers");
+ case Key: return QString("key");
+ case Text: return QString("text");
+ case PortableText: return QString("PortableText");
+ case NativeText: return QString("NativeText");
+ case PossibleKeys: return QString("keyCombinations");
+ case KeySequenceEdit: return m_customKeySequence.toString();
+ default: {
+ auto keySequence = keySequences[column - FirstKeySequence];
+ return keySequence.toString();
+ }
+ }
+ }
+ return QVariant();
+ }
+
+ template <typename T>
+ static QString toString(T &&object, int verbosity = QDebug::DefaultVerbosity)
+ {
+ QString buffer;
+ QDebug stream(&buffer);
+ stream.setVerbosity(verbosity);
+ stream.nospace() << std::forward<T>(object);
+ return buffer;
+ }
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
+ {
+ if (role == Qt::DisplayRole) {
+ auto &event = m_events.at(index.row());
+ auto *keyEvent = event.keyEvent.get();
+ switch (int column = index.column()) {
+ case Language: return event.language;
+ case Direction: return toString(event.layoutDirection, QDebug::MinimumVerbosity);
+ case Type: return toString(keyEvent->type(), QDebug::MinimumVerbosity);
+ case ScanCode: return keyEvent->nativeScanCode();
+ case VirtualKey: return keyEvent->nativeVirtualKey();
+ case Modifiers: return toString(keyEvent->modifiers(), QDebug::MinimumVerbosity);
+ case Key: return toString(Qt::Key(keyEvent->key()), QDebug::MinimumVerbosity);
+ case Text: return keyEvent->text();
+ case PortableText: return event.keySequence.toString(QKeySequence::PortableText);
+ case NativeText: return event.keySequence.toString(QKeySequence::NativeText);
+ case PossibleKeys: {
+ QStringList keyCombinations;
+ for (auto combination : event.possibleKeyCombinations)
+ keyCombinations << QKeySequence(combination).toString(QKeySequence::NativeText);
+ static constexpr auto leftToRightOverride = QChar(0x202d);
+ return leftToRightOverride + keyCombinations.join(" ");
+ }
+ default: {
+ QStringList matches;
+ if (event.keySequenceEquals[column - FirstKeySequence])
+ matches << "K";
+ if (event.shortcutMatches[column - FirstKeySequence])
+ matches << "S";
+ return matches.join(" ");
+ }
+ }
+ } else if (role == Qt::TextAlignmentRole) {
+ return Qt::AlignCenter;
+ }
+
+ return QVariant();
+ }
+
+ struct Event {
+ std::unique_ptr<QKeyEvent> keyEvent;
+ QString language;
+ Qt::LayoutDirection layoutDirection;
+ QKeySequence keySequence;
+ using PossibleKeysList = decltype(std::declval<QKeyMapper>().possibleKeys(nullptr));
+ PossibleKeysList possibleKeyCombinations;
+ // Hard-coded key sequences, plus room for KeySequenceEdit
+ bool keySequenceEquals[std::size(keySequences) + 1] = {};
+ bool shortcutMatches[std::size(keySequences) + 1] = {};
+ };
+
+ bool eventFilter(QObject *object, QEvent *event) override
+ {
+ if (!m_enabled)
+ return false;
+
+ switch (auto type = event->type()) {
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease: {
+ auto *keyEvent = static_cast<QKeyEvent*>(event);
+ auto *inputMethod = qGuiApp->inputMethod();
+ auto row = int(m_events.size());
+ beginInsertRows(QModelIndex(), row, row);
+ m_events.push_back({
+ std::unique_ptr<QKeyEvent>(keyEvent->clone()),
+ QLocale::languageToString(inputMethod->locale().language()),
+ inputMethod->inputDirection(),
+ QKeySequence(keyEvent->keyCombination()),
+ QKeyMapper::instance()->possibleKeys(keyEvent)
+ });
+
+ Event &event = m_events.back();
+
+ if (type == QEvent::KeyPress) {
+ for (size_t i = 0; i < std::size(event.keySequenceEquals); ++i) {
+ QKeySequence keySequence = i == std::size(keySequences) ?
+ m_customKeySequence : keySequences[i];
+
+ event.keySequenceEquals[i] = event.keySequence == keySequence;
+
+ QShortcut shortcut(keySequence, object, [&] {
+ event.shortcutMatches[i] = true;
+ }, Qt::ApplicationShortcut);
+ QShortcutMap &shortcutMap = QGuiApplicationPrivate::instance()->shortcutMap;
+ shortcutMap.tryShortcut(keyEvent);
+ }
+ }
+
+ endInsertRows();
+ return false;
+ }
+ case QEvent::ShortcutOverride: {
+ auto *keyEvent = static_cast<QKeyEvent*>(event);
+ if (!keyEvent->matches(QKeySequence::Quit)) {
+ event->accept();
+ return true;
+ }
+ return false;
+ }
+ default:
+ return false;
+ }
+ }
+
+ void reset()
+ {
+ beginResetModel();
+ m_events.clear();
+ endResetModel();
+ }
+
+ Q_SLOT void setCustomKeySequence(const QKeySequence &keySequence)
+ {
+ m_customKeySequence = keySequence;
+ emit headerDataChanged(Qt::Horizontal, KeySequenceEdit, ColumnCount);
+ }
+
+ bool m_enabled = true;
+ std::vector<Event> m_events;
+ QKeySequence m_customKeySequence;
+};
+
+class KeyEventWindow : public QMainWindow
+{
+public:
+ KeyEventWindow()
+ {
+ setWindowTitle(QString("Qt %1 on %2").arg(QT_VERSION_STR).arg(QSysInfo::prettyProductName()));
+ auto *tableView = new QTableView(this);
+ m_keyEventModel = new KeyEventModel(this);
+ tableView->setModel(m_keyEventModel);
+ tableView->installEventFilter(m_keyEventModel);
+
+ tableView->setFocusPolicy(Qt::ClickFocus);
+ tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ tableView->setSelectionMode(QAbstractItemView::NoSelection);
+ tableView->setWordWrap(false);
+
+ QObject::connect(tableView->model(), &QAbstractItemModel::rowsInserted,
+ tableView, &QTableView::scrollToBottom);
+
+ QMenu *menu = menuBar()->addMenu("File");
+ menu->addAction("Save...", this, &KeyEventWindow::save);
+ menu->addAction("Clear", this, [this]{
+ m_keyEventModel->reset();
+ });
+ auto *enableAction = menu->addAction("Enabled", this, [this]{
+ auto *action = static_cast<QAction*>(sender());
+ m_keyEventModel->m_enabled = action->isChecked();
+ });
+ enableAction->setCheckable(true);
+ enableAction->setChecked(true);
+
+ auto *toolBar = addToolBar("Tools");
+ toolBar->setMovable(false);
+ toolBar->addWidget(new QLabel("Key sequence editor:"));
+ auto *keySequenceEdit = new QKeySequenceEdit;
+ keySequenceEdit->setMaximumSequenceLength(1);
+ connect(keySequenceEdit, &QKeySequenceEdit::keySequenceChanged,
+ m_keyEventModel, &KeyEventModel::setCustomKeySequence);
+ keySequenceEdit->installEventFilter(m_keyEventModel);
+ toolBar->addWidget(keySequenceEdit);
+ toolBar->addWidget(new QLabel("Free form text input:"));
+ toolBar->addWidget(new QLineEdit);
+
+ setCentralWidget(tableView);
+ centralWidget()->setFocus();
+ }
+
+ void save()
+ {
+ auto homeDirectory = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
+ QString fileName = QFileDialog::getSaveFileName(this, "Save events",
+ QString("%1/events.csv").arg(homeDirectory));
+
+ QFile file(fileName);
+ if (!file.open(QFile::WriteOnly | QFile::Truncate)) {
+ QMessageBox::critical(this, "Could not open file", file.errorString());
+ return;
+ }
+ QTextStream output(&file);
+ const auto columns = m_keyEventModel->columnCount();
+ for (int c = 0; c < columns; ++c) {
+ output << m_keyEventModel->headerData(c, Qt::Horizontal, Qt::DisplayRole).toString()
+ << ((c < columns - 1) ? ";" : "");
+ }
+ output << "\n";
+ for (int r = 0; r < m_keyEventModel->rowCount(); ++r) {
+ for (int c = 0; c < m_keyEventModel->columnCount(); ++c) {
+ auto index = m_keyEventModel->index(r, c);
+ output << m_keyEventModel->data(index).toString()
+ << ((c < columns - 1) ? ";" : "");
+ }
+ output << "\n";
+ }
+ }
+
+ void keyPressEvent(QKeyEvent *keyEvent) override
+ {
+ if (keyEvent->matches(QKeySequence::Quit))
+ qGuiApp->quit();
+ }
+
+ KeyEventModel *m_keyEventModel = nullptr;
+};
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+
+ KeyEventWindow keyEventWindow;
+ keyEventWindow.showMaximized();
+
+ return a.exec();
+}
+
+#include "main.moc"
diff --git a/tests/manual/keypadnavigation/main.cpp b/tests/manual/keypadnavigation/main.cpp
index 46214ad237..4cfd8975d2 100644
--- a/tests/manual/keypadnavigation/main.cpp
+++ b/tests/manual/keypadnavigation/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QMainWindow>
#include <QApplication>
diff --git a/tests/manual/lance/CMakeLists.txt b/tests/manual/lance/CMakeLists.txt
index 9a4ebb88b2..41eb18b6c2 100644
--- a/tests/manual/lance/CMakeLists.txt
+++ b/tests/manual/lance/CMakeLists.txt
@@ -11,6 +11,8 @@ qt_internal_add_manual_test(lance
interactivewidget.cpp interactivewidget.h
main.cpp
widgets.h
+ NO_PCH_SOURCES
+ interactivewidget.cpp # undef QT_NO_FOREACH
INCLUDE_DIRECTORIES
.
../../baseline/shared
diff --git a/tests/manual/lance/README b/tests/manual/lance/README
index 6e89b29b08..68a9d647bc 100644
--- a/tests/manual/lance/README
+++ b/tests/manual/lance/README
@@ -1,6 +1,6 @@
The "lance" tool can be used to edit and run QPainter script (.qps)
files. They are used in the "lancelot" qpainter regression autotest.
-A collection of scripts can be found in the directory
-tests/auto/lancelot/scripts
+A collection of scripts can be found in subdirectories under
+tests/baseline
See lance -help for options.
diff --git a/tests/manual/lance/interactivewidget.cpp b/tests/manual/lance/interactivewidget.cpp
index cd2084978c..03bfca8566 100644
--- a/tests/manual/lance/interactivewidget.cpp
+++ b/tests/manual/lance/interactivewidget.cpp
@@ -1,5 +1,8 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
+
#include "interactivewidget.h"
#include <QtWidgets>
diff --git a/tests/manual/lance/interactivewidget.h b/tests/manual/lance/interactivewidget.h
index c5f66dc0a6..b11c6cfee2 100644
--- a/tests/manual/lance/interactivewidget.h
+++ b/tests/manual/lance/interactivewidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef INTERACTIVEWIDGET_H
#define INTERACTIVEWIDGET_H
diff --git a/tests/manual/lance/main.cpp b/tests/manual/lance/main.cpp
index e6474b10c8..f39001f3c2 100644
--- a/tests/manual/lance/main.cpp
+++ b/tests/manual/lance/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "interactivewidget.h"
#include "widgets.h"
#include "paintcommands.h"
diff --git a/tests/manual/lance/widgets.h b/tests/manual/lance/widgets.h
index fb8429648e..ae2e75fbdc 100644
--- a/tests/manual/lance/widgets.h
+++ b/tests/manual/lance/widgets.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WIDGETS_H
#define WIDGETS_H
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index 7bb92bb453..ef52a60952 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -5,10 +5,12 @@ SUBDIRS = \
filetest \
embeddedintoforeignwindow \
foreignwindows \
+fontfeatures \
gestures \
highdpi \
inputmethodhints \
keypadnavigation \
+keyevents \
lance \
network_remote_stresstest \
network_stresstest \
diff --git a/tests/manual/markdown/html2md.cpp b/tests/manual/markdown/html2md.cpp
index 7d13034848..f813330242 100644
--- a/tests/manual/markdown/html2md.cpp
+++ b/tests/manual/markdown/html2md.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QCommandLineParser>
#include <QDebug>
diff --git a/tests/manual/network/ssl/client-auth/CMakeLists.txt b/tests/manual/network/ssl/client-auth/CMakeLists.txt
new file mode 100644
index 0000000000..67ecc20bf4
--- /dev/null
+++ b/tests/manual/network/ssl/client-auth/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_manual_test(tst_manual_ssl_client_auth
+ SOURCES
+ tst_manual_ssl_client_auth.cpp
+ LIBRARIES
+ Qt::Network
+)
+
+qt_internal_add_resource(tst_manual_ssl_client_auth "tst_manual_ssl_client_auth"
+ PREFIX
+ "/"
+ FILES
+ "certs/127.0.0.1.pem"
+ "certs/127.0.0.1-key.pem"
+ "certs/127.0.0.1-client.pem"
+ "certs/127.0.0.1-client-key.pem"
+ "certs/accepted-client.pem"
+ "certs/accepted-client-key.pem"
+ "certs/rootCA.pem"
+ BASE
+ "certs"
+)
diff --git a/tests/manual/network/ssl/client-auth/certs/.gitignore b/tests/manual/network/ssl/client-auth/certs/.gitignore
new file mode 100644
index 0000000000..5866f7b609
--- /dev/null
+++ b/tests/manual/network/ssl/client-auth/certs/.gitignore
@@ -0,0 +1,4 @@
+*
+!/.gitignore
+!/generate.sh
+!/accepted-client.conf
diff --git a/tests/manual/network/ssl/client-auth/certs/accepted-client.conf b/tests/manual/network/ssl/client-auth/certs/accepted-client.conf
new file mode 100644
index 0000000000..a88b276efe
--- /dev/null
+++ b/tests/manual/network/ssl/client-auth/certs/accepted-client.conf
@@ -0,0 +1,14 @@
+[req]
+default_md = sha512
+basicConstraints = CA:FALSE
+extendedKeyUsage = clientAuth
+[req]
+distinguished_name = client_distinguished_name
+prompt = no
+[client_distinguished_name]
+C = NO
+ST = Oslo
+L = Oslo
+O = The Qt Project
+OU = The Qt Project
+CN = Fake Qt Project Client Certificate
diff --git a/tests/manual/network/ssl/client-auth/certs/generate.sh b/tests/manual/network/ssl/client-auth/certs/generate.sh
new file mode 100755
index 0000000000..5dbe3b3712
--- /dev/null
+++ b/tests/manual/network/ssl/client-auth/certs/generate.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+# Requires mkcert and openssl
+
+warn () { echo "$@" >&2; }
+die () { warn "$@"; exit 1; }
+
+
+command -v mkcert 1>/dev/null 2>&1 || die "Failed to find mkcert"
+command -v openssl 1>/dev/null 2>&1 || die "Failed to find openssl"
+
+SCRIPT=$(realpath "$0")
+SCRIPTPATH=$(dirname "$SCRIPT")
+
+pushd "$SCRIPTPATH" || die "Unable to pushd to $SCRIPTPATH"
+mkcert 127.0.0.1
+mkcert -client 127.0.0.1
+warn "Remember to run mkcert -install if you haven't already"
+
+# Generate CA
+openssl genrsa -out ca-key.pem 2048
+openssl req -new -x509 -noenc -days 365 -key ca-key.pem -out rootCA.pem
+
+# Generate accepted client certificate
+openssl genrsa -out accepted-client-key.pem 2048
+openssl req -new -sha512 -nodes -key accepted-client-key.pem -out accepted-client.csr -config accepted-client.conf
+openssl x509 -req -sha512 -days 45 -in accepted-client.csr -CA rootCA.pem -CAkey ca-key.pem -CAcreateserial -out accepted-client.pem
+rm accepted-client.csr
+rm rootCA.srl
+
+popd || die "Unable to popd"
diff --git a/tests/manual/network/ssl/client-auth/tst_manual_ssl_client_auth.cpp b/tests/manual/network/ssl/client-auth/tst_manual_ssl_client_auth.cpp
new file mode 100644
index 0000000000..7730105f36
--- /dev/null
+++ b/tests/manual/network/ssl/client-auth/tst_manual_ssl_client_auth.cpp
@@ -0,0 +1,136 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtCore/qcoreapplication.h>
+
+#include <QtCore/qthread.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qdir.h>
+
+#include <QtNetwork/qsslsocket.h>
+#include <QtNetwork/qsslserver.h>
+#include <QtNetwork/qsslconfiguration.h>
+#include <QtNetwork/qsslkey.h>
+
+// Client and/or server presents a certificate signed by a system-trusted CA
+// but the other side presents a certificate signed by a different CA.
+constexpr bool TestServerPresentsIncorrectCa = false;
+constexpr bool TestClientPresentsIncorrectCa = true;
+// Decides whether or not to put the root CA into the global ssl configuration
+// or into the socket's specific ssl configuration.
+constexpr bool UseGlobalConfiguration = true;
+
+class ServerThread : public QThread
+{
+ Q_OBJECT
+public:
+ void run() override
+ {
+ QSslServer server;
+
+ QSslConfiguration config = server.sslConfiguration();
+ if (!UseGlobalConfiguration) {
+ QList<QSslCertificate> certs = QSslCertificate::fromPath(QStringLiteral(":/rootCA.pem"));
+ config.setCaCertificates(certs);
+ }
+ config.setLocalCertificate(QSslCertificate::fromPath(QStringLiteral(":/127.0.0.1.pem"))
+ .first());
+ QFile keyFile(QStringLiteral(":/127.0.0.1-key.pem"));
+ if (!keyFile.open(QIODevice::ReadOnly))
+ qFatal("Failed to open key file");
+ config.setPrivateKey(QSslKey(&keyFile, QSsl::Rsa));
+ config.setPeerVerifyMode(QSslSocket::VerifyPeer);
+ server.setSslConfiguration(config);
+
+ connect(&server, &QSslServer::pendingConnectionAvailable, [&server]() {
+ QSslSocket *socket = static_cast<QSslSocket *>(server.nextPendingConnection());
+ qDebug() << "[s] newConnection" << socket->peerAddress() << socket->peerPort();
+ socket->disconnectFromHost();
+ qApp->quit();
+ });
+ connect(&server, &QSslServer::startedEncryptionHandshake, [](QSslSocket *socket) {
+ qDebug() << "[s] new handshake" << socket->peerAddress() << socket->peerPort();
+ });
+ connect(&server, &QSslServer::errorOccurred,
+ [](QSslSocket *socket, QAbstractSocket::SocketError error) {
+ qDebug() << "[s] errorOccurred" << socket->peerAddress() << socket->peerPort()
+ << error << socket->errorString();
+ });
+ connect(&server, &QSslServer::peerVerifyError,
+ [](QSslSocket *socket, const QSslError &error) {
+ qDebug() << "[s] peerVerifyError" << socket->peerAddress() << socket->peerPort()
+ << error;
+ });
+ server.listen(QHostAddress::LocalHost, 24242);
+
+ exec();
+
+ server.close();
+ }
+};
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ using namespace Qt::StringLiterals;
+
+ if (!QFileInfo(u":/rootCA.pem"_s).exists())
+ qFatal("rootCA.pem not found. Did you run generate.sh in the certs directory?");
+
+ if (UseGlobalConfiguration) {
+ QSslConfiguration config = QSslConfiguration::defaultConfiguration();
+ config.setCaCertificates(QSslCertificate::fromPath(u":/rootCA.pem"_s));
+ QSslConfiguration::setDefaultConfiguration(config);
+ }
+
+ ServerThread serverThread;
+ serverThread.start();
+
+ QSslSocket socket;
+ QSslConfiguration config = socket.sslConfiguration();
+ QString certificatePath;
+ QString keyFileName;
+ if constexpr (TestClientPresentsIncorrectCa) { // true: Present cert signed with incorrect CA: should fail
+ certificatePath = u":/127.0.0.1-client.pem"_s;
+ keyFileName = u":/127.0.0.1-client-key.pem"_s;
+ } else { // false: Use correct CA: should succeed
+ certificatePath = u":/accepted-client.pem"_s;
+ keyFileName = u":/accepted-client-key.pem"_s;
+ }
+ config.setLocalCertificate(QSslCertificate::fromPath(certificatePath).first());
+ if (!UseGlobalConfiguration && TestServerPresentsIncorrectCa) {
+ // Verify server using incorrect CA: should fail
+ config.setCaCertificates(QSslCertificate::fromPath(u":/rootCA.pem"_s));
+ } else if (UseGlobalConfiguration && !TestServerPresentsIncorrectCa) {
+ // Verify server using correct CA, we need to explicitly set the
+ // system CAs when the global config is overridden.
+ config.setCaCertificates(QSslConfiguration::systemCaCertificates());
+ }
+ QFile keyFile(keyFileName);
+ if (!keyFile.open(QIODevice::ReadOnly))
+ qFatal("Failed to open key file");
+ config.setPrivateKey(QSslKey(&keyFile, QSsl::Rsa));
+
+ socket.setSslConfiguration(config);
+
+ QObject::connect(&socket, &QSslSocket::encrypted, []() { qDebug() << "[c] encrypted"; });
+ QObject::connect(&socket, &QSslSocket::errorOccurred,
+ [&socket](QAbstractSocket::SocketError error) {
+ qDebug() << "[c] errorOccurred" << error << socket.errorString();
+ qApp->quit();
+ });
+ QObject::connect(&socket, &QSslSocket::sslErrors, [](const QList<QSslError> &errors) {
+ qDebug() << "[c] sslErrors" << errors;
+ });
+ QObject::connect(&socket, &QSslSocket::connected, []() { qDebug() << "[c] connected"; });
+
+ socket.connectToHostEncrypted(QStringLiteral("127.0.0.1"), 24242);
+
+ const int res = app.exec();
+ serverThread.quit();
+ serverThread.wait();
+ return res;
+}
+
+#include "tst_manual_ssl_client_auth.moc"
diff --git a/tests/manual/network_remote_stresstest/tst_network_remote_stresstest.cpp b/tests/manual/network_remote_stresstest/tst_network_remote_stresstest.cpp
index 390395a9f6..fd4e62c92b 100644
--- a/tests/manual/network_remote_stresstest/tst_network_remote_stresstest.cpp
+++ b/tests/manual/network_remote_stresstest/tst_network_remote_stresstest.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QtTest/qtesteventloop.h>
diff --git a/tests/manual/network_stresstest/minihttpserver.cpp b/tests/manual/network_stresstest/minihttpserver.cpp
index afa496efcc..cd07f585b1 100644
--- a/tests/manual/network_stresstest/minihttpserver.cpp
+++ b/tests/manual/network_stresstest/minihttpserver.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "minihttpserver.h"
diff --git a/tests/manual/network_stresstest/minihttpserver.h b/tests/manual/network_stresstest/minihttpserver.h
index 789e69b824..d630e29c9a 100644
--- a/tests/manual/network_stresstest/minihttpserver.h
+++ b/tests/manual/network_stresstest/minihttpserver.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MINIHTTPSERVER_H
diff --git a/tests/manual/network_stresstest/tst_network_stresstest.cpp b/tests/manual/network_stresstest/tst_network_stresstest.cpp
index 29a7d57a84..5796dd8e77 100644
--- a/tests/manual/network_stresstest/tst_network_stresstest.cpp
+++ b/tests/manual/network_stresstest/tst_network_stresstest.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QtTest/qtesteventloop.h>
diff --git a/tests/manual/permissions/android/AndroidManifest.xml b/tests/manual/permissions/android/AndroidManifest.xml
index 557ec8007e..db577e6c27 100644
--- a/tests/manual/permissions/android/AndroidManifest.xml
+++ b/tests/manual/permissions/android/AndroidManifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="org.qtproject.example"
+ package="org.qtproject.example.tst_manual_permissions"
android:installLocation="auto"
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
android:versionName="-- %%INSERT_VERSION_NAME%% --">
@@ -26,13 +26,11 @@
android:hardwareAccelerated="true"
android:label="-- %%INSERT_APP_NAME%% --"
android:requestLegacyExternalStorage="true"
- android:allowNativeHeapPointerTagging="false"
android:allowBackup="true"
android:fullBackupOnly="false">
<activity
android:name="org.qtproject.qt.android.bindings.QtActivity"
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
- android:label="-- %%INSERT_APP_NAME%% --"
android:launchMode="singleTop"
android:screenOrientation="unspecified"
android:exported="true">
@@ -44,10 +42,6 @@
<meta-data
android:name="android.app.lib_name"
android:value="-- %%INSERT_APP_LIB_NAME%% --" />
-
- <meta-data
- android:name="android.app.extract_android_style"
- android:value="minimal" />
</activity>
</application>
</manifest>
diff --git a/tests/manual/permissions/tst_qpermissions.cpp b/tests/manual/permissions/tst_qpermissions.cpp
index 846a048ccd..7df2fdc2fe 100644
--- a/tests/manual/permissions/tst_qpermissions.cpp
+++ b/tests/manual/permissions/tst_qpermissions.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
diff --git a/tests/manual/qcursor/allcursors/main.cpp b/tests/manual/qcursor/allcursors/main.cpp
index 99c56fde8b..b688308b13 100644
--- a/tests/manual/qcursor/allcursors/main.cpp
+++ b/tests/manual/qcursor/allcursors/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include "mainwindow.h"
diff --git a/tests/manual/qcursor/allcursors/mainwindow.cpp b/tests/manual/qcursor/allcursors/mainwindow.cpp
index 3057e8bd27..917f17520f 100644
--- a/tests/manual/qcursor/allcursors/mainwindow.cpp
+++ b/tests/manual/qcursor/allcursors/mainwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
#include "ui_mainwindow.h"
diff --git a/tests/manual/qcursor/allcursors/mainwindow.h b/tests/manual/qcursor/allcursors/mainwindow.h
index 0f6e20436c..ebe81855f8 100644
--- a/tests/manual/qcursor/allcursors/mainwindow.h
+++ b/tests/manual/qcursor/allcursors/mainwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
diff --git a/tests/manual/qcursor/childwidget/main.cpp b/tests/manual/qcursor/childwidget/main.cpp
index 4a0455c3d7..fd2a010a01 100644
--- a/tests/manual/qcursor/childwidget/main.cpp
+++ b/tests/manual/qcursor/childwidget/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
diff --git a/tests/manual/qcursor/childwindow/main.cpp b/tests/manual/qcursor/childwindow/main.cpp
index ca91cce26d..af84816463 100644
--- a/tests/manual/qcursor/childwindow/main.cpp
+++ b/tests/manual/qcursor/childwindow/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui>
diff --git a/tests/manual/qcursor/childwindowcontainer/main.cpp b/tests/manual/qcursor/childwindowcontainer/main.cpp
index fbf01831fb..ae1e415545 100644
--- a/tests/manual/qcursor/childwindowcontainer/main.cpp
+++ b/tests/manual/qcursor/childwindowcontainer/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
diff --git a/tests/manual/qcursor/grab_override/main.cpp b/tests/manual/qcursor/grab_override/main.cpp
index 99c56fde8b..b688308b13 100644
--- a/tests/manual/qcursor/grab_override/main.cpp
+++ b/tests/manual/qcursor/grab_override/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include "mainwindow.h"
diff --git a/tests/manual/qcursor/grab_override/mainwindow.cpp b/tests/manual/qcursor/grab_override/mainwindow.cpp
index 742e574f2f..4ed3dfe979 100644
--- a/tests/manual/qcursor/grab_override/mainwindow.cpp
+++ b/tests/manual/qcursor/grab_override/mainwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
#include "ui_mainwindow.h"
diff --git a/tests/manual/qcursor/grab_override/mainwindow.h b/tests/manual/qcursor/grab_override/mainwindow.h
index 6caf7b6891..ded9059991 100644
--- a/tests/manual/qcursor/grab_override/mainwindow.h
+++ b/tests/manual/qcursor/grab_override/mainwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
diff --git a/tests/manual/qcursor/qcursorhighdpi/main.cpp b/tests/manual/qcursor/qcursorhighdpi/main.cpp
index 55ba67d825..9997e93967 100644
--- a/tests/manual/qcursor/qcursorhighdpi/main.cpp
+++ b/tests/manual/qcursor/qcursorhighdpi/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QAction>
#include <QApplication>
diff --git a/tests/manual/qdesktopservices/tst_qdesktopservices.cpp b/tests/manual/qdesktopservices/tst_qdesktopservices.cpp
index ca3ba9f41d..96340d5e7c 100644
--- a/tests/manual/qdesktopservices/tst_qdesktopservices.cpp
+++ b/tests/manual/qdesktopservices/tst_qdesktopservices.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Intel Corporation.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QString>
#include <QTest>
diff --git a/tests/manual/qdnslookup/CMakeLists.txt b/tests/manual/qdnslookup/CMakeLists.txt
new file mode 100644
index 0000000000..646e972b30
--- /dev/null
+++ b/tests/manual/qdnslookup/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2023 Intel Corporation.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_manual_test(qdnslookup
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Network
+)
diff --git a/tests/manual/qdnslookup/main.cpp b/tests/manual/qdnslookup/main.cpp
new file mode 100644
index 0000000000..41dda45b8f
--- /dev/null
+++ b/tests/manual/qdnslookup/main.cpp
@@ -0,0 +1,241 @@
+// Copyright (C) 2023 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QMetaEnum>
+#include <QtCore/QTimer>
+#include <QtCore/QUrl>
+#include <QtNetwork/QHostAddress>
+#include <QtNetwork/QHostInfo>
+#include <QtNetwork/QDnsLookup>
+
+#if QT_CONFIG(ssl)
+# include <QtNetwork/QSslCipher>
+# include <QtNetwork/QSslConfiguration>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <chrono>
+
+using namespace Qt::StringLiterals;
+using namespace std::chrono;
+using namespace std::chrono_literals;
+
+static QDnsLookup::Type typeFromString(QString str)
+{
+ // we can use the meta object
+ QMetaEnum me = QMetaEnum::fromType<QDnsLookup::Type>();
+
+ bool ok;
+ int value = me.keyToValue(str.toUpper().toLatin1(), &ok);
+ if (!ok)
+ return QDnsLookup::Type(0);
+ return QDnsLookup::Type(value);
+}
+
+template <typename Enum> QByteArray enumToString(Enum value)
+{
+ QMetaEnum me = QMetaEnum::fromType<Enum>();
+ QByteArray keys = me.valueToKeys(int(value));
+ if (keys.isEmpty())
+ return QByteArrayLiteral("<unknown>");
+
+ // return the last one
+ qsizetype idx = keys.lastIndexOf('|');
+ if (idx > 0)
+ return std::move(keys).sliced(idx + 1);
+ return keys;
+}
+
+static int showHelp(const char *argv0, int exitcode)
+{
+ // like dig
+ printf("%s [@global-server] [domain] [query-type]\n", argv0);
+ return exitcode;
+}
+
+static auto parseServerAddress(QString server)
+{
+ struct R {
+ QHostAddress address;
+ int port;
+ } r;
+
+ // let's use QUrl to help us
+ QUrl url;
+ url.setAuthority(server);
+ if (!url.isValid() || !url.userInfo().isNull())
+ return r; // failed
+
+ r.port = url.port(0);
+ r.address.setAddress(url.host());
+ if (r.address.isNull()) {
+ // try to resolve a hostname
+ QHostInfo hostinfo = QHostInfo::fromName(url.host());
+ const QList<QHostAddress> addresses = hostinfo.addresses();
+ if (!hostinfo.error() && !addresses.isEmpty())
+ r.address = addresses.at(0);
+ }
+ return r;
+}
+
+static void printAnswers(const QDnsLookup &lookup)
+{
+ printf("\n;; ANSWER:\n");
+ static auto printRecordCommon = [](const auto &rr, const char *rrtype) {
+ printf("%-23s %6d IN %s\t", qPrintable(rr.name()), rr.timeToLive(), rrtype);
+ };
+ auto printNameRecords = [](const char *rrtype, const QList<QDnsDomainNameRecord> list) {
+ for (const QDnsDomainNameRecord &rr : list) {
+ printRecordCommon(rr, rrtype);
+ printf("%s\n", qPrintable(rr.value()));
+ }
+ };
+
+ for (const QDnsMailExchangeRecord &rr : lookup.mailExchangeRecords()) {
+ printRecordCommon(rr, "MX");
+ printf("%d %s\n", rr.preference(), qPrintable(rr.exchange()));
+ }
+ for (const QDnsServiceRecord &rr : lookup.serviceRecords()) {
+ printRecordCommon(rr, "SRV");
+ printf("%d %d %d %s\n", rr.priority(), rr.weight(), rr.port(),
+ qPrintable(rr.target()));
+ }
+ printNameRecords("NS", lookup.nameServerRecords());
+ printNameRecords("PTR", lookup.pointerRecords());
+ printNameRecords("CNAME", lookup.canonicalNameRecords());
+
+ for (const QDnsHostAddressRecord &rr : lookup.hostAddressRecords()) {
+ QHostAddress addr = rr.value();
+ printRecordCommon(rr, addr.protocol() == QHostAddress::IPv6Protocol ? "AAAA" : "A");
+ printf("%s\n", qPrintable(addr.toString()));
+ }
+
+ for (const QDnsTextRecord &rr : lookup.textRecords()) {
+ printRecordCommon(rr, "TXT");
+ for (const QByteArray &data : rr.values())
+ printf("%s ", qPrintable(QDebug::toString(data)));
+ puts("");
+ }
+
+ for (const QDnsTlsAssociationRecord &rr : lookup.tlsAssociationRecords()) {
+ printRecordCommon(rr, "TLSA");
+ printf("( %u %u %u ; %s %s %s\n\t%s )\n", quint8(rr.usage()), quint8(rr.selector()),
+ quint8(rr.matchType()), enumToString(rr.usage()).constData(),
+ enumToString(rr.selector()).constData(), enumToString(rr.matchType()).constData(),
+ rr.value().toHex().toUpper().constData());
+ }
+}
+
+static void printResults(const QDnsLookup &lookup, QElapsedTimer::Duration duration)
+{
+ if (QDnsLookup::Error error = lookup.error())
+ printf(";; status: %s (%s)", QMetaEnum::fromType<QDnsLookup::Error>().valueToKey(error),
+ qPrintable(lookup.errorString()));
+ else
+ printf(";; status: NoError");
+ if (lookup.isAuthenticData())
+ printf("; AuthenticData");
+ puts("");
+
+ QMetaEnum me = QMetaEnum::fromType<QDnsLookup::Type>();
+ printf(";; QUESTION:\n");
+ printf(";%-30s IN %s\n", qPrintable(lookup.name()),
+ me.valueToKey(lookup.type()));
+
+ if (lookup.error() == QDnsLookup::NoError)
+ printAnswers(lookup);
+
+ printf("\n;; Query time: %lld ms\n", qint64(duration_cast<milliseconds>(duration).count()));
+ if (QHostAddress server = lookup.nameserver(); !server.isNull()) {
+ quint16 port = lookup.nameserverPort();
+ if (port == 0)
+ port = QDnsLookup::defaultPortForProtocol(lookup.nameserverProtocol());
+ printf(";; SERVER: %s#%d", qPrintable(server.toString()), port);
+#if QT_CONFIG(ssl)
+ if (lookup.nameserverProtocol() != QDnsLookup::Standard) {
+ if (QSslConfiguration conf = lookup.sslConfiguration(); !conf.isNull()) {
+ printf(" (%s %s)", enumToString(conf.sessionProtocol()).constData(),
+ qPrintable(conf.sessionCipher().name()));
+ }
+ }
+#endif
+ puts("");
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ QDnsLookup::Type type = {};
+ QDnsLookup::Protocol protocol = QDnsLookup::Standard;
+ QString domain, server;
+ const QStringList args = QCoreApplication::arguments().sliced(1);
+ for (const QString &arg : args) {
+ if (arg.startsWith(u'@')) {
+ server = arg.mid(1);
+ continue;
+ }
+ if (arg == u"-h")
+ return showHelp(argv[0], EXIT_SUCCESS);
+ if (arg == "+tls") {
+ protocol = QDnsLookup::DnsOverTls;
+ continue;
+ } else if (arg == "+notls") {
+ protocol = QDnsLookup::Standard;
+ continue;
+ }
+
+ if (domain.isNull()) {
+ domain = arg;
+ continue;
+ }
+ if (type != QDnsLookup::Type{})
+ return showHelp(argv[0], EXIT_FAILURE);
+
+ type = typeFromString(arg);
+ if (type == QDnsLookup::Type{}) {
+ fprintf(stderr, "%s: unknown DNS record type '%s'. Valid types are:\n",
+ argv[0], qPrintable(arg));
+ QMetaEnum me = QMetaEnum::fromType<QDnsLookup::Type>();
+ for (int i = 0; i < me.keyCount(); ++i)
+ fprintf(stderr, " %s\n", me.key(i));
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (domain.isEmpty())
+ domain = u"qt-project.org"_s;
+ if (type == QDnsLookup::Type{})
+ type = QDnsLookup::A;
+
+ QDnsLookup lookup(type, domain);
+ if (!server.isEmpty()) {
+ auto addr = parseServerAddress(server);
+ if (addr.address.isNull()) {
+ fprintf(stderr, "%s: could not parse name server address '%s'\n",
+ argv[0], qPrintable(server));
+ return EXIT_FAILURE;
+ }
+ lookup.setNameserver(protocol, addr.address, addr.port);
+ }
+
+ // execute the lookup
+ QObject::connect(&lookup, &QDnsLookup::finished, qApp, &QCoreApplication::quit);
+ QTimer::singleShot(15s, []() { qApp->exit(EXIT_FAILURE); });
+
+ QElapsedTimer timer;
+ timer.start();
+ lookup.lookup();
+ if (a.exec() == EXIT_FAILURE)
+ return EXIT_FAILURE;
+ printf("; <<>> QDnsLookup " QT_VERSION_STR " <<>> %s %s\n",
+ qPrintable(QCoreApplication::applicationName()), qPrintable(args.join(u' ')));
+ printResults(lookup, timer.durationElapsed());
+ return 0;
+}
diff --git a/tests/manual/qglyphruns/controller.cpp b/tests/manual/qglyphruns/controller.cpp
index d63c23de32..0037d11b5b 100644
--- a/tests/manual/qglyphruns/controller.cpp
+++ b/tests/manual/qglyphruns/controller.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "controller.h"
#include "ui_controller.h"
diff --git a/tests/manual/qglyphruns/controller.h b/tests/manual/qglyphruns/controller.h
index 9771ae135f..75289165f8 100644
--- a/tests/manual/qglyphruns/controller.h
+++ b/tests/manual/qglyphruns/controller.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef CONTROLLER_H
#define CONTROLLER_H
diff --git a/tests/manual/qglyphruns/glyphruninspector.cpp b/tests/manual/qglyphruns/glyphruninspector.cpp
index 13a8df0181..0e35a1f94b 100644
--- a/tests/manual/qglyphruns/glyphruninspector.cpp
+++ b/tests/manual/qglyphruns/glyphruninspector.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "glyphruninspector.h"
#include "singleglyphrun.h"
diff --git a/tests/manual/qglyphruns/glyphruninspector.h b/tests/manual/qglyphruns/glyphruninspector.h
index 4b3943cf6a..d2aa6033db 100644
--- a/tests/manual/qglyphruns/glyphruninspector.h
+++ b/tests/manual/qglyphruns/glyphruninspector.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef GLYPHRUNINSPECTOR_H
#define GLYPHRUNINSPECTOR_H
diff --git a/tests/manual/qglyphruns/main.cpp b/tests/manual/qglyphruns/main.cpp
index 6f76b66d14..0f192e051f 100644
--- a/tests/manual/qglyphruns/main.cpp
+++ b/tests/manual/qglyphruns/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "controller.h"
diff --git a/tests/manual/qglyphruns/singleglyphrun.cpp b/tests/manual/qglyphruns/singleglyphrun.cpp
index 2836cfadd9..9a5bd3e139 100644
--- a/tests/manual/qglyphruns/singleglyphrun.cpp
+++ b/tests/manual/qglyphruns/singleglyphrun.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "singleglyphrun.h"
#include "ui_singleglyphrun.h"
diff --git a/tests/manual/qglyphruns/singleglyphrun.h b/tests/manual/qglyphruns/singleglyphrun.h
index c400640335..138d820ad4 100644
--- a/tests/manual/qglyphruns/singleglyphrun.h
+++ b/tests/manual/qglyphruns/singleglyphrun.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef SINGLEGLYPHRUN_H
#define SINGLEGLYPHRUN_H
diff --git a/tests/manual/qglyphruns/view.cpp b/tests/manual/qglyphruns/view.cpp
index 95c05c608d..0e7d6dd478 100644
--- a/tests/manual/qglyphruns/view.cpp
+++ b/tests/manual/qglyphruns/view.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "view.h"
#include <QTextLayout>
diff --git a/tests/manual/qglyphruns/view.h b/tests/manual/qglyphruns/view.h
index 0eaaebe829..a8c3c1aed9 100644
--- a/tests/manual/qglyphruns/view.h
+++ b/tests/manual/qglyphruns/view.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef VIEW_H
#define VIEW_H
diff --git a/tests/manual/qgraphicsitem/main.cpp b/tests/manual/qgraphicsitem/main.cpp
index be2f1557df..9a852f4fab 100644
--- a/tests/manual/qgraphicsitem/main.cpp
+++ b/tests/manual/qgraphicsitem/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QGraphicsView>
diff --git a/tests/manual/qgraphicsitemgroup/CMakeLists.txt b/tests/manual/qgraphicsitemgroup/CMakeLists.txt
index 7a3cb00e8e..d6a158fac5 100644
--- a/tests/manual/qgraphicsitemgroup/CMakeLists.txt
+++ b/tests/manual/qgraphicsitemgroup/CMakeLists.txt
@@ -11,6 +11,8 @@ qt_internal_add_manual_test(qgraphicsitemgroup
customitem.cpp customitem.h
main.cpp
widget.cpp widget.h widget.ui
+ NO_PCH_SOURCES
+ widget.cpp # undef QT_NO_FOREACH
LIBRARIES
Qt::Gui
Qt::Widgets
diff --git a/tests/manual/qgraphicsitemgroup/customitem.cpp b/tests/manual/qgraphicsitemgroup/customitem.cpp
index 77b023337e..5df16291ad 100644
--- a/tests/manual/qgraphicsitemgroup/customitem.cpp
+++ b/tests/manual/qgraphicsitemgroup/customitem.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "customitem.h"
@@ -9,10 +9,10 @@
QList<CustomGroup*> CustomScene::selectedCustomGroups() const
{
- QList<QGraphicsItem*> all = selectedItems();
+ const QList<QGraphicsItem*> all = selectedItems();
QList<CustomGroup*> groups;
- foreach (QGraphicsItem *item, all) {
+ for (QGraphicsItem *item : all) {
CustomGroup* group = qgraphicsitem_cast<CustomGroup*>(item);
if (group)
groups.append(group);
@@ -23,10 +23,10 @@ QList<CustomGroup*> CustomScene::selectedCustomGroups() const
QList<CustomItem*> CustomScene::selectedCustomItems() const
{
- QList<QGraphicsItem*> all = selectedItems();
+ const QList<QGraphicsItem*> all = selectedItems();
QList<CustomItem*> items;
- foreach (QGraphicsItem *item, all) {
+ for (QGraphicsItem *item : all) {
CustomItem* citem = qgraphicsitem_cast<CustomItem*>(item);
if (citem)
items.append(citem);
diff --git a/tests/manual/qgraphicsitemgroup/customitem.h b/tests/manual/qgraphicsitemgroup/customitem.h
index 30a41b0699..78f195629d 100644
--- a/tests/manual/qgraphicsitemgroup/customitem.h
+++ b/tests/manual/qgraphicsitemgroup/customitem.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef CUSTOMITEM_H
#define CUSTOMITEM_H
diff --git a/tests/manual/qgraphicsitemgroup/main.cpp b/tests/manual/qgraphicsitemgroup/main.cpp
index 30add62d6b..d586e84d20 100644
--- a/tests/manual/qgraphicsitemgroup/main.cpp
+++ b/tests/manual/qgraphicsitemgroup/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include "widget.h"
diff --git a/tests/manual/qgraphicsitemgroup/widget.cpp b/tests/manual/qgraphicsitemgroup/widget.cpp
index d6005601ca..25362d52a4 100644
--- a/tests/manual/qgraphicsitemgroup/widget.cpp
+++ b/tests/manual/qgraphicsitemgroup/widget.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "widget.h"
#include "ui_widget.h"
@@ -95,15 +97,15 @@ void Widget::on_scaleItem_valueChanged(int value)
void Widget::on_group_clicked()
{
- QList<QGraphicsItem*> all = scene->selectedItems();
+ const QList<QGraphicsItem*> all = scene->selectedItems();
if (all.size() < 2)
return;
- QList<CustomItem*> items = scene->selectedCustomItems();
+ const QList<CustomItem*> items = scene->selectedCustomItems();
QList<CustomGroup*> groups = scene->selectedCustomGroups();
if (groups.size() == 1) {
- foreach (CustomItem *item, items) {
+ for (CustomItem *item : items) {
item->setSelected(false);
groups[0]->addToGroup(item);
}
@@ -113,7 +115,7 @@ void Widget::on_group_clicked()
CustomGroup* group = new CustomGroup;
scene->addItem(group);
- foreach (QGraphicsItem *item, all) {
+ for (QGraphicsItem *item : all) {
item->setSelected(false);
group->addToGroup(item);
}
@@ -124,9 +126,9 @@ void Widget::on_group_clicked()
void Widget::on_dismantle_clicked()
{
- QList<CustomGroup*> groups = scene->selectedCustomGroups();
+ const QList<CustomGroup*> groups = scene->selectedCustomGroups();
- foreach (CustomGroup *group, groups) {
+ for (CustomGroup *group : groups) {
foreach (QGraphicsItem *item, group->childItems())
group->removeFromGroup(item);
diff --git a/tests/manual/qgraphicsitemgroup/widget.h b/tests/manual/qgraphicsitemgroup/widget.h
index 8286258c24..51b2943282 100644
--- a/tests/manual/qgraphicsitemgroup/widget.h
+++ b/tests/manual/qgraphicsitemgroup/widget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WIDGET_H
#define WIDGET_H
diff --git a/tests/manual/qgraphicslayout/anchorlayout/main.cpp b/tests/manual/qgraphicslayout/anchorlayout/main.cpp
index ee8f94f236..93f712753b 100644
--- a/tests/manual/qgraphicslayout/anchorlayout/main.cpp
+++ b/tests/manual/qgraphicslayout/anchorlayout/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
diff --git a/tests/manual/qgraphicslayout/flicker/main.cpp b/tests/manual/qgraphicslayout/flicker/main.cpp
index cfd1d2ff09..f7a227a401 100644
--- a/tests/manual/qgraphicslayout/flicker/main.cpp
+++ b/tests/manual/qgraphicslayout/flicker/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QMainWindow>
#include <QApplication>
diff --git a/tests/manual/qgraphicslayout/flicker/window.cpp b/tests/manual/qgraphicslayout/flicker/window.cpp
index 200b437ca0..a30c1e0cf6 100644
--- a/tests/manual/qgraphicslayout/flicker/window.cpp
+++ b/tests/manual/qgraphicslayout/flicker/window.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "window.h"
diff --git a/tests/manual/qgraphicslayout/flicker/window.h b/tests/manual/qgraphicslayout/flicker/window.h
index 1d4402b2e4..d54c69050f 100644
--- a/tests/manual/qgraphicslayout/flicker/window.h
+++ b/tests/manual/qgraphicslayout/flicker/window.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WINDOW_H
#define WINDOW_H
diff --git a/tests/manual/qgraphicslayout/weatheranchorlayout/main.cpp b/tests/manual/qgraphicslayout/weatheranchorlayout/main.cpp
index 1151349f24..bdf7c00942 100644
--- a/tests/manual/qgraphicslayout/weatheranchorlayout/main.cpp
+++ b/tests/manual/qgraphicslayout/weatheranchorlayout/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QLabel>
diff --git a/tests/manual/qhttpnetworkconnection/main.cpp b/tests/manual/qhttpnetworkconnection/main.cpp
index f29f823f0c..9be3b32fa2 100644
--- a/tests/manual/qhttpnetworkconnection/main.cpp
+++ b/tests/manual/qhttpnetworkconnection/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This file contains benchmarks for QNetworkReply functions.
#include <QDebug>
diff --git a/tests/manual/qimagereader/CMakeLists.txt b/tests/manual/qimagereader/CMakeLists.txt
index 60a111c82e..abfd7c6697 100644
--- a/tests/manual/qimagereader/CMakeLists.txt
+++ b/tests/manual/qimagereader/CMakeLists.txt
@@ -13,3 +13,12 @@ qt_internal_add_manual_test(qimagereader
Qt::Gui
Qt::Widgets
)
+set(qimagereader_resource_files
+ "Qt_logostrap_CMYK.jpg"
+)
+qt_add_resources(qimagereader "qimagereader"
+ PREFIX
+ "/"
+ FILES
+ ${qimagereader_resource_files}
+)
diff --git a/tests/manual/qimagereader/main.cpp b/tests/manual/qimagereader/main.cpp
index 9c2111ca20..c4ea74b6d2 100644
--- a/tests/manual/qimagereader/main.cpp
+++ b/tests/manual/qimagereader/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QWidget>
#include <QHBoxLayout>
@@ -17,7 +17,7 @@ public:
}
- virtual void paintEvent(QPaintEvent * /*event*/)
+ void paintEvent(QPaintEvent * /*event*/) override
{
QPainter painter(this);
QImageReader reader(fileName);
@@ -54,8 +54,8 @@ int main(int argc, char** argv)
mainWidget.setWindowTitle("Colors in images are identical?");
mainWidget.setMinimumSize(400,400);
QHBoxLayout *l = new QHBoxLayout;
- MyWidget *w1 = new MyWidget(&mainWidget,"Qt_logostrap_CMYK.jpg", false);
- MyWidget *w2 = new MyWidget(&mainWidget,"Qt_logostrap_CMYK.jpg", true);
+ MyWidget *w1 = new MyWidget(&mainWidget,":/Qt_logostrap_CMYK.jpg", false);
+ MyWidget *w2 = new MyWidget(&mainWidget,":/Qt_logostrap_CMYK.jpg", true);
l->addWidget(w1);
l->addWidget(w2);
mainWidget.setLayout(l);
diff --git a/tests/manual/qimagereader/qimagereader.pro b/tests/manual/qimagereader/qimagereader.pro
index 3523257f3e..9a5ad4de13 100644
--- a/tests/manual/qimagereader/qimagereader.pro
+++ b/tests/manual/qimagereader/qimagereader.pro
@@ -1,2 +1,3 @@
QT += widgets
SOURCES += main.cpp
+RESOURCES += qimagereader.qrc
diff --git a/tests/manual/qimagereader/qimagereader.qrc b/tests/manual/qimagereader/qimagereader.qrc
new file mode 100644
index 0000000000..30a11fd17d
--- /dev/null
+++ b/tests/manual/qimagereader/qimagereader.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/" >
+ <file>Qt_logostrap_CMYK.jpg</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/qlayout/gridwidget.cpp b/tests/manual/qlayout/gridwidget.cpp
index cce754b59e..1dda49654c 100644
--- a/tests/manual/qlayout/gridwidget.cpp
+++ b/tests/manual/qlayout/gridwidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "gridwidget.h"
#include <QGridLayout>
diff --git a/tests/manual/qlayout/gridwidget.h b/tests/manual/qlayout/gridwidget.h
index c05f67fd12..3a957c9fa5 100644
--- a/tests/manual/qlayout/gridwidget.h
+++ b/tests/manual/qlayout/gridwidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef GRIDWIDGET_H
#define GRIDWIDGET_H
diff --git a/tests/manual/qlayout/hbwidget.cpp b/tests/manual/qlayout/hbwidget.cpp
index 37c9b1eb17..a3bcffc315 100644
--- a/tests/manual/qlayout/hbwidget.cpp
+++ b/tests/manual/qlayout/hbwidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "hbwidget.h"
#include <QHBoxLayout>
diff --git a/tests/manual/qlayout/hbwidget.h b/tests/manual/qlayout/hbwidget.h
index d053d2b9e1..61ee916a85 100644
--- a/tests/manual/qlayout/hbwidget.h
+++ b/tests/manual/qlayout/hbwidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef HBWIDGET_H
#define HBWIDGET_H
diff --git a/tests/manual/qlayout/main.cpp b/tests/manual/qlayout/main.cpp
index f1f5fb41ae..9bcf883e36 100644
--- a/tests/manual/qlayout/main.cpp
+++ b/tests/manual/qlayout/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include "mainwindow.h"
diff --git a/tests/manual/qlayout/mainwindow.cpp b/tests/manual/qlayout/mainwindow.cpp
index 6cb6ed4cc6..24a3d6bb27 100644
--- a/tests/manual/qlayout/mainwindow.cpp
+++ b/tests/manual/qlayout/mainwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
#include "hbwidget.h"
diff --git a/tests/manual/qlayout/mainwindow.h b/tests/manual/qlayout/mainwindow.h
index 82a34d17ef..efcf2259a8 100644
--- a/tests/manual/qlayout/mainwindow.h
+++ b/tests/manual/qlayout/mainwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
diff --git a/tests/manual/qlayout/vbwidget.cpp b/tests/manual/qlayout/vbwidget.cpp
index 7b0f7f08da..c99f533d64 100644
--- a/tests/manual/qlayout/vbwidget.cpp
+++ b/tests/manual/qlayout/vbwidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "vbwidget.h"
#include <QVBoxLayout>
diff --git a/tests/manual/qlayout/vbwidget.h b/tests/manual/qlayout/vbwidget.h
index 30f43f3f39..9292852307 100644
--- a/tests/manual/qlayout/vbwidget.h
+++ b/tests/manual/qlayout/vbwidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef VBWIDGET_H
#define VBWIDGET_H
diff --git a/tests/manual/qlocale/CMakeLists.txt b/tests/manual/qlocale/CMakeLists.txt
index a5f9ac4cf4..e749565129 100644
--- a/tests/manual/qlocale/CMakeLists.txt
+++ b/tests/manual/qlocale/CMakeLists.txt
@@ -17,6 +17,8 @@ qt_internal_add_manual_test(qlocale
miscellaneous.cpp miscellaneous.h
numberformats.cpp numberformats.h
window.cpp window.h
+ NO_PCH_SOURCES
+ languages.cpp # undef QT_NO_FOREACH
LIBRARIES
Qt::Gui
Qt::Widgets
diff --git a/tests/manual/qlocale/calendar.cpp b/tests/manual/qlocale/calendar.cpp
index c4a2385032..c79e4ae02c 100644
--- a/tests/manual/qlocale/calendar.cpp
+++ b/tests/manual/qlocale/calendar.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "calendar.h"
diff --git a/tests/manual/qlocale/calendar.h b/tests/manual/qlocale/calendar.h
index cf2a33d9be..a8b35a5989 100644
--- a/tests/manual/qlocale/calendar.h
+++ b/tests/manual/qlocale/calendar.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef CALENDAR_H
#define CALENDAR_H
diff --git a/tests/manual/qlocale/currency.cpp b/tests/manual/qlocale/currency.cpp
index 6cd0799403..8e0d126f84 100644
--- a/tests/manual/qlocale/currency.cpp
+++ b/tests/manual/qlocale/currency.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "currency.h"
#include <QLineEdit>
diff --git a/tests/manual/qlocale/currency.h b/tests/manual/qlocale/currency.h
index 0e03a3a174..a528e406ee 100644
--- a/tests/manual/qlocale/currency.h
+++ b/tests/manual/qlocale/currency.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef CURRENCY_H
#define CURRENCY_H
diff --git a/tests/manual/qlocale/dateformats.cpp b/tests/manual/qlocale/dateformats.cpp
index fbc3e5064b..c47a96c8e5 100644
--- a/tests/manual/qlocale/dateformats.cpp
+++ b/tests/manual/qlocale/dateformats.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "dateformats.h"
diff --git a/tests/manual/qlocale/dateformats.h b/tests/manual/qlocale/dateformats.h
index caf32fe85d..ddfc5d8548 100644
--- a/tests/manual/qlocale/dateformats.h
+++ b/tests/manual/qlocale/dateformats.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DATEFORMATS_H
#define DATEFORMATS_H
diff --git a/tests/manual/qlocale/info.cpp b/tests/manual/qlocale/info.cpp
index e49df18d60..86de7e5b69 100644
--- a/tests/manual/qlocale/info.cpp
+++ b/tests/manual/qlocale/info.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "info.h"
diff --git a/tests/manual/qlocale/info.h b/tests/manual/qlocale/info.h
index dd87b054eb..820a2435ad 100644
--- a/tests/manual/qlocale/info.h
+++ b/tests/manual/qlocale/info.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef INFO_H
#define INFO_H
diff --git a/tests/manual/qlocale/languages.cpp b/tests/manual/qlocale/languages.cpp
index 75e4a0ee9f..f5085fcd3e 100644
--- a/tests/manual/qlocale/languages.cpp
+++ b/tests/manual/qlocale/languages.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "languages.h"
diff --git a/tests/manual/qlocale/languages.h b/tests/manual/qlocale/languages.h
index ab2cdb9f72..b1fc997156 100644
--- a/tests/manual/qlocale/languages.h
+++ b/tests/manual/qlocale/languages.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef LANGUAGES_H
#define LANGUAGES_H
diff --git a/tests/manual/qlocale/main.cpp b/tests/manual/qlocale/main.cpp
index 653e8a11d1..6a108e7757 100644
--- a/tests/manual/qlocale/main.cpp
+++ b/tests/manual/qlocale/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
diff --git a/tests/manual/qlocale/miscellaneous.cpp b/tests/manual/qlocale/miscellaneous.cpp
index d1ea008369..6eb33972bf 100644
--- a/tests/manual/qlocale/miscellaneous.cpp
+++ b/tests/manual/qlocale/miscellaneous.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "miscellaneous.h"
diff --git a/tests/manual/qlocale/miscellaneous.h b/tests/manual/qlocale/miscellaneous.h
index aa3a398ab7..a1a5c810ac 100644
--- a/tests/manual/qlocale/miscellaneous.h
+++ b/tests/manual/qlocale/miscellaneous.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MISCELLANEOUS_H
#define MISCELLANEOUS_H
diff --git a/tests/manual/qlocale/numberformats.cpp b/tests/manual/qlocale/numberformats.cpp
index 3666cb744e..8545b8c6fc 100644
--- a/tests/manual/qlocale/numberformats.cpp
+++ b/tests/manual/qlocale/numberformats.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "numberformats.h"
diff --git a/tests/manual/qlocale/numberformats.h b/tests/manual/qlocale/numberformats.h
index 7c5148f19c..7bb44f46e5 100644
--- a/tests/manual/qlocale/numberformats.h
+++ b/tests/manual/qlocale/numberformats.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef NUMBERFORMATS_H
#define NUMBERFORMATS_H
diff --git a/tests/manual/qlocale/window.cpp b/tests/manual/qlocale/window.cpp
index 463360b9cd..48fdec5f4d 100644
--- a/tests/manual/qlocale/window.cpp
+++ b/tests/manual/qlocale/window.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "window.h"
@@ -16,8 +16,8 @@ Window::Window()
localeCombo->addItem("System", QLocale::system());
- QList<QLocale> locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyTerritory);
- foreach (const QLocale &locale, locales) {
+ const QList<QLocale> locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyTerritory);
+ for (const QLocale &locale : locales) {
QString label = QLocale::languageToString(locale.language());
label += QLatin1Char('/');
if (locale.script() != QLocale::AnyScript) {
diff --git a/tests/manual/qlocale/window.h b/tests/manual/qlocale/window.h
index 58a7d75dbe..c12651348a 100644
--- a/tests/manual/qlocale/window.h
+++ b/tests/manual/qlocale/window.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WINDOW_H
#define WINDOW_H
diff --git a/tests/manual/qmetatype/declare_metatype_noninline.cpp b/tests/manual/qmetatype/declare_metatype_noninline.cpp
index 7e430f73b6..dcb34e75ac 100644
--- a/tests/manual/qmetatype/declare_metatype_noninline.cpp
+++ b/tests/manual/qmetatype/declare_metatype_noninline.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "declare_metatype_noninline.h"
diff --git a/tests/manual/qmetatype/declare_metatype_noninline.h b/tests/manual/qmetatype/declare_metatype_noninline.h
index 9fc4e5c76c..8b2e34744c 100644
--- a/tests/manual/qmetatype/declare_metatype_noninline.h
+++ b/tests/manual/qmetatype/declare_metatype_noninline.h
@@ -1,5 +1,5 @@
// Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DECLARE_METATYPE_NONINLINE_H
#define DECLARE_METATYPE_NONINLINE_H
diff --git a/tests/manual/qmetatype/tst_qmetatype.cpp b/tests/manual/qmetatype/tst_qmetatype.cpp
index 5319df1329..21c139200c 100644
--- a/tests/manual/qmetatype/tst_qmetatype.cpp
+++ b/tests/manual/qmetatype/tst_qmetatype.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore>
diff --git a/tests/manual/qmimedatabase/main.cpp b/tests/manual/qmimedatabase/main.cpp
index 7b2c6dddd6..a28a64a2e7 100644
--- a/tests/manual/qmimedatabase/main.cpp
+++ b/tests/manual/qmimedatabase/main.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include <QtCore/QMimeDatabase>
#include <QtCore/QMimeType>
diff --git a/tests/manual/qnetconmonitor/tst_qnetconmonitor.cpp b/tests/manual/qnetconmonitor/tst_qnetconmonitor.cpp
index 3e6696faa5..a052932dd2 100644
--- a/tests/manual/qnetconmonitor/tst_qnetconmonitor.cpp
+++ b/tests/manual/qnetconmonitor/tst_qnetconmonitor.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdeadlinetimer.h>
diff --git a/tests/manual/qnetworkaccessmanager/qget/CMakeLists.txt b/tests/manual/qnetworkaccessmanager/qget/CMakeLists.txt
index 62925f8229..e870ab2b62 100644
--- a/tests/manual/qnetworkaccessmanager/qget/CMakeLists.txt
+++ b/tests/manual/qnetworkaccessmanager/qget/CMakeLists.txt
@@ -10,6 +10,9 @@ qt_internal_add_manual_test(qget
downloadmanager.cpp
qget.cpp qget.h
transferitem.cpp
+ NO_PCH_SOURCES
+ downloadmanager.cpp # undef QT_NO_FOREACH
+ qget.cpp # undef QT_NO_FOREACH
LIBRARIES
Qt::Network
)
diff --git a/tests/manual/qnetworkaccessmanager/qget/downloadmanager.cpp b/tests/manual/qnetworkaccessmanager/qget/downloadmanager.cpp
index 4a7e03eea8..b9ad330e1e 100644
--- a/tests/manual/qnetworkaccessmanager/qget/downloadmanager.cpp
+++ b/tests/manual/qnetworkaccessmanager/qget/downloadmanager.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "qget.h"
#include <QAuthenticator>
diff --git a/tests/manual/qnetworkaccessmanager/qget/qget.cpp b/tests/manual/qnetworkaccessmanager/qget/qget.cpp
index d9874afec9..9577275b5b 100644
--- a/tests/manual/qnetworkaccessmanager/qget/qget.cpp
+++ b/tests/manual/qnetworkaccessmanager/qget/qget.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "qget.h"
diff --git a/tests/manual/qnetworkaccessmanager/qget/qget.h b/tests/manual/qnetworkaccessmanager/qget/qget.h
index 5123086961..20c1b97db4 100644
--- a/tests/manual/qnetworkaccessmanager/qget/qget.h
+++ b/tests/manual/qnetworkaccessmanager/qget/qget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QGET_H
#define QGET_H
diff --git a/tests/manual/qnetworkaccessmanager/qget/transferitem.cpp b/tests/manual/qnetworkaccessmanager/qget/transferitem.cpp
index 617cddf688..fc06b3d5b0 100644
--- a/tests/manual/qnetworkaccessmanager/qget/transferitem.cpp
+++ b/tests/manual/qnetworkaccessmanager/qget/transferitem.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qget.h"
#include <QDebug>
diff --git a/tests/manual/qnetworkinformation/mainwindow.h b/tests/manual/qnetworkinformation/mainwindow.h
index 6b615a7c90..822511a5f9 100644
--- a/tests/manual/qnetworkinformation/mainwindow.h
+++ b/tests/manual/qnetworkinformation/mainwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
diff --git a/tests/manual/qnetworkinformation/tst_qnetworkinformation.cpp b/tests/manual/qnetworkinformation/tst_qnetworkinformation.cpp
index 7c2daaadc4..5b7d55c7ce 100644
--- a/tests/manual/qnetworkinformation/tst_qnetworkinformation.cpp
+++ b/tests/manual/qnetworkinformation/tst_qnetworkinformation.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifdef MOBILE
# include "mainwindow.h"
diff --git a/tests/manual/qnetworkreply/main.cpp b/tests/manual/qnetworkreply/main.cpp
index 75f49b3662..aece9e1045 100644
--- a/tests/manual/qnetworkreply/main.cpp
+++ b/tests/manual/qnetworkreply/main.cpp
@@ -1,6 +1,6 @@
// Copyright (C) 2020 The Qt Company Ltd.
// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This file contains benchmarks for QNetworkReply functions.
#include <QDebug>
diff --git a/tests/manual/qopenglcontext/main.cpp b/tests/manual/qopenglcontext/main.cpp
index 851d885c7d..dee1321576 100644
--- a/tests/manual/qopenglcontext/main.cpp
+++ b/tests/manual/qopenglcontext/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui/QGuiApplication>
#include "qopenglcontextwindow.h"
diff --git a/tests/manual/qopenglcontext/qopenglcontextwindow.cpp b/tests/manual/qopenglcontext/qopenglcontextwindow.cpp
index b5f2707705..7640fb5ba7 100644
--- a/tests/manual/qopenglcontext/qopenglcontextwindow.cpp
+++ b/tests/manual/qopenglcontext/qopenglcontextwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qopenglcontextwindow.h"
#include <QtGui/QOpenGLFunctions>
diff --git a/tests/manual/qopenglcontext/qopenglcontextwindow.h b/tests/manual/qopenglcontext/qopenglcontextwindow.h
index 452967edfb..1822667070 100644
--- a/tests/manual/qopenglcontext/qopenglcontextwindow.h
+++ b/tests/manual/qopenglcontext/qopenglcontextwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QOPENGLCONTEXTWINDOW_H
#define QOPENGLCONTEXTWINDOW_H
diff --git a/tests/manual/qopengltextureblitter/main.cpp b/tests/manual/qopengltextureblitter/main.cpp
index 57104a487e..b213994898 100644
--- a/tests/manual/qopengltextureblitter/main.cpp
+++ b/tests/manual/qopengltextureblitter/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qopengltextureblitwindow.h"
#include <QtGui/QGuiApplication>
diff --git a/tests/manual/qopengltextureblitter/qopengltextureblitwindow.cpp b/tests/manual/qopengltextureblitter/qopengltextureblitwindow.cpp
index b0bb92eaba..90557c7dc4 100644
--- a/tests/manual/qopengltextureblitter/qopengltextureblitwindow.cpp
+++ b/tests/manual/qopengltextureblitter/qopengltextureblitwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qopengltextureblitwindow.h"
diff --git a/tests/manual/qopengltextureblitter/qopengltextureblitwindow.h b/tests/manual/qopengltextureblitter/qopengltextureblitwindow.h
index ff1d1665c5..0978d8cc2d 100644
--- a/tests/manual/qopengltextureblitter/qopengltextureblitwindow.h
+++ b/tests/manual/qopengltextureblitter/qopengltextureblitwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QOPENGLTEXTUREBLITWINDOW_H
#define QOPENGLTEXTUREBLITWINDOW_H
diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/fshader.glsl b/tests/manual/qopenglwidget/dockedopenglwidget/fshader.glsl
index 8e5a4de8fc..ddc6247574 100644
--- a/tests/manual/qopenglwidget/dockedopenglwidget/fshader.glsl
+++ b/tests/manual/qopenglwidget/dockedopenglwidget/fshader.glsl
@@ -1,3 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
uniform sampler2D texture;
varying vec2 v_texcoord;
diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.cpp b/tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.cpp
index bf5448d8e9..d7b9b8d05a 100644
--- a/tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.cpp
+++ b/tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "geometryengine.h"
diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.h b/tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.h
index ca24a21410..646d6570c8 100644
--- a/tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.h
+++ b/tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef GEOMETRYENGINE_H
#define GEOMETRYENGINE_H
diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/main.cpp b/tests/manual/qopenglwidget/dockedopenglwidget/main.cpp
index d72d148f3e..e21a976ca7 100644
--- a/tests/manual/qopenglwidget/dockedopenglwidget/main.cpp
+++ b/tests/manual/qopenglwidget/dockedopenglwidget/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
#include <QApplication>
diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.cpp b/tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.cpp
index 699e240b41..b9dc4c5fe2 100644
--- a/tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.cpp
+++ b/tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwidget.h"
#include <QMouseEvent>
diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.h b/tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.h
index b6fb85480c..b21131d485 100644
--- a/tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.h
+++ b/tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.cpp b/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.cpp
index 97a9ee1545..9a530bc4f7 100644
--- a/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.cpp
+++ b/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
#include "ui_mainwindow.h"
diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.h b/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.h
index e7ae438c53..bea5cc41cc 100644
--- a/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.h
+++ b/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/vshader.glsl b/tests/manual/qopenglwidget/dockedopenglwidget/vshader.glsl
index 098eda946e..10f3646aa6 100644
--- a/tests/manual/qopenglwidget/dockedopenglwidget/vshader.glsl
+++ b/tests/manual/qopenglwidget/dockedopenglwidget/vshader.glsl
@@ -1,3 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
uniform mat4 mvp_matrix;
attribute vec4 a_position;
diff --git a/tests/manual/qopenglwidget/openglwidget/CMakeLists.txt b/tests/manual/qopenglwidget/openglwidget/CMakeLists.txt
index ca04a82199..967feda558 100644
--- a/tests/manual/qopenglwidget/openglwidget/CMakeLists.txt
+++ b/tests/manual/qopenglwidget/openglwidget/CMakeLists.txt
@@ -10,6 +10,8 @@ qt_internal_add_manual_test(openglwidget
SOURCES
main.cpp
openglwidget.cpp openglwidget.h
+ NO_PCH_SOURCES
+ main.cpp # undef QT_NO_FOREACH
LIBRARIES
Qt::CorePrivate
Qt::Gui
diff --git a/tests/manual/qopenglwidget/openglwidget/main.cpp b/tests/manual/qopenglwidget/openglwidget/main.cpp
index 7ea5a4e7d7..3a0e019b06 100644
--- a/tests/manual/qopenglwidget/openglwidget/main.cpp
+++ b/tests/manual/qopenglwidget/openglwidget/main.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "openglwidget.h"
#include <QApplication>
@@ -31,7 +33,6 @@ public:
private slots:
void turnNative();
void hideShowAllGL();
- void dumpCompositingStatus();
signals:
void aboutToShowGLWidgets();
@@ -83,12 +84,6 @@ void Tools::dumpWidget(QWidget *w, int indent)
}
}
-void Tools::dumpCompositingStatus()
-{
- QWindow *w = m_root->window()->windowHandle();
- qDebug() << "Compositing status for" << w << m_root->window() << "is" << QWindowPrivate::get(w)->compositing;
-}
-
class TabWidgetResetter : public QObject
{
Q_OBJECT
@@ -193,10 +188,6 @@ int main(int argc, char *argv[])
toolsMenu->addAction("&Turn widgets (or some parent) into native", &t, SLOT(turnNative()));
toolsMenu->addAction("&Hide/show all OpenGL widgets", &t, SLOT(hideShowAllGL()));
- QTimer compStatusDumpTimer;
- QObject::connect(&compStatusDumpTimer, SIGNAL(timeout()), &t, SLOT(dumpCompositingStatus()));
- compStatusDumpTimer.start(5000);
-
wnd.show();
if (glw->isValid())
diff --git a/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp b/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp
index 7d165e4de0..7e39945639 100644
--- a/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp
+++ b/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#define GL_GLEXT_PROTOTYPES
diff --git a/tests/manual/qopenglwidget/openglwidget/openglwidget.h b/tests/manual/qopenglwidget/openglwidget/openglwidget.h
index 83b3b4c0e5..8366d17a11 100644
--- a/tests/manual/qopenglwidget/openglwidget/openglwidget.h
+++ b/tests/manual/qopenglwidget/openglwidget/openglwidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef OPENGLWIDGET_H
#define OPENGLWIDGET_H
diff --git a/tests/manual/qopenglwindow/multiwindow/main.cpp b/tests/manual/qopenglwindow/multiwindow/main.cpp
index 044efc7309..0b351d5796 100644
--- a/tests/manual/qopenglwindow/multiwindow/main.cpp
+++ b/tests/manual/qopenglwindow/multiwindow/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui>
diff --git a/tests/manual/qprintdevice_dump/main.cpp b/tests/manual/qprintdevice_dump/main.cpp
index b7c7ee2194..9d1de96221 100644
--- a/tests/manual/qprintdevice_dump/main.cpp
+++ b/tests/manual/qprintdevice_dump/main.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2014 John Layt <jlayt@kde.org>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include <qpa/qplatformprintplugin.h>
#include <qpa/qplatformprintersupport.h>
diff --git a/tests/manual/qscreen/CMakeLists.txt b/tests/manual/qscreen/CMakeLists.txt
index 7880cf2b21..4149310df4 100644
--- a/tests/manual/qscreen/CMakeLists.txt
+++ b/tests/manual/qscreen/CMakeLists.txt
@@ -10,6 +10,8 @@ qt_internal_add_manual_test(qscreen
main.cpp
propertyfield.cpp propertyfield.h
propertywatcher.cpp propertywatcher.h
+ NO_PCH_SOURCES
+ main.cpp # undef QT_NO_FOREACH
LIBRARIES
Qt::Gui
Qt::Widgets
diff --git a/tests/manual/qscreen/main.cpp b/tests/manual/qscreen/main.cpp
index f6c22e88a8..f85271923c 100644
--- a/tests/manual/qscreen/main.cpp
+++ b/tests/manual/qscreen/main.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "propertywatcher.h"
#include <QApplication>
diff --git a/tests/manual/qscreen/propertyfield.cpp b/tests/manual/qscreen/propertyfield.cpp
index a184ea3785..1a637e302f 100644
--- a/tests/manual/qscreen/propertyfield.cpp
+++ b/tests/manual/qscreen/propertyfield.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "propertyfield.h"
#include <QDebug>
diff --git a/tests/manual/qscreen/propertyfield.h b/tests/manual/qscreen/propertyfield.h
index b5ff55947e..62e9f9e99c 100644
--- a/tests/manual/qscreen/propertyfield.h
+++ b/tests/manual/qscreen/propertyfield.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef PROPERTYFIELD_H
#define PROPERTYFIELD_H
diff --git a/tests/manual/qscreen/propertywatcher.cpp b/tests/manual/qscreen/propertywatcher.cpp
index 5eec9f275b..6ba6f5c351 100644
--- a/tests/manual/qscreen/propertywatcher.cpp
+++ b/tests/manual/qscreen/propertywatcher.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "propertywatcher.h"
#include <QMetaProperty>
@@ -85,8 +85,8 @@ void PropertyWatcher::setSubject(QObject *s, const QString &annotation)
void PropertyWatcher::updateAllFields()
{
- QList<PropertyField *> fields = findChildren<PropertyField*>();
- foreach (PropertyField *field, fields)
+ const QList<PropertyField *> fields = findChildren<PropertyField*>();
+ for (PropertyField *field : fields)
field->propertyChanged();
emit updatedAllFields(this);
}
diff --git a/tests/manual/qscreen/propertywatcher.h b/tests/manual/qscreen/propertywatcher.h
index eaefd60beb..061d94964e 100644
--- a/tests/manual/qscreen/propertywatcher.h
+++ b/tests/manual/qscreen/propertywatcher.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef PROPERTY_WATCHER_H
#define PROPERTY_WATCHER_H
diff --git a/tests/manual/qscreen_xrandr/tst_qscreen_xrandr.cpp b/tests/manual/qscreen_xrandr/tst_qscreen_xrandr.cpp
index b851817831..542214792c 100644
--- a/tests/manual/qscreen_xrandr/tst_qscreen_xrandr.cpp
+++ b/tests/manual/qscreen_xrandr/tst_qscreen_xrandr.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qpainter.h>
#include <qrasterwindow.h>
diff --git a/tests/manual/qssloptions/main.cpp b/tests/manual/qssloptions/main.cpp
index a6d172f879..36d3ae57d3 100644
--- a/tests/manual/qssloptions/main.cpp
+++ b/tests/manual/qssloptions/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtNetwork/qsslconfiguration.h>
#include <QtCore/QCoreApplication>
diff --git a/tests/manual/qsslsocket/main.cpp b/tests/manual/qsslsocket/main.cpp
index 2894b3c1fa..cc3cdb92d6 100644
--- a/tests/manual/qsslsocket/main.cpp
+++ b/tests/manual/qsslsocket/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtNetwork/qsslconfiguration.h>
@@ -30,14 +30,15 @@ void tst_QSslSocket::nextProtocolNegotiation_data()
QTest::addColumn<QByteArray>("expectedProtocol");
QTest::addColumn<QSslConfiguration::NextProtocolNegotiationStatus>("expectedStatus");
- QList<QString> hosts = QList<QString>()
- << QStringLiteral("www.google.com")
- << QStringLiteral("www.facebook.com")
- << QStringLiteral("www.twitter.com")
- << QStringLiteral("graph.facebook.com")
- << QStringLiteral("api.twitter.com");
+ const QString hosts[] = {
+ QStringLiteral("www.google.com"),
+ QStringLiteral("www.facebook.com"),
+ QStringLiteral("www.twitter.com"),
+ QStringLiteral("graph.facebook.com"),
+ QStringLiteral("api.twitter.com"),
+ };
- foreach (QString host, hosts) {
+ for (const QString &host : hosts) {
QByteArray tag = host.toLocal8Bit();
tag.append("-none");
QTest::newRow(tag)
diff --git a/tests/manual/qstorageinfo/main.cpp b/tests/manual/qstorageinfo/main.cpp
index a2fd95129c..f6890b0c3b 100644
--- a/tests/manual/qstorageinfo/main.cpp
+++ b/tests/manual/qstorageinfo/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Intel Corporation
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QCoreApplication>
@@ -15,7 +15,7 @@ int main(int argc, char *argv[])
QStringList args = a.arguments();
args.takeFirst(); // skip application name
- foreach (const QString &path, args) {
+ for (const QString &path : std::as_const(args)) {
QStorageInfo info(path);
if (!info.isValid()) {
// no error string...
diff --git a/tests/manual/qstorageinfo/printvolumes.cpp b/tests/manual/qstorageinfo/printvolumes.cpp
index 5e4fdd4863..d6bf9dab52 100644
--- a/tests/manual/qstorageinfo/printvolumes.cpp
+++ b/tests/manual/qstorageinfo/printvolumes.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Intel Corporation
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QStorageInfo>
@@ -12,21 +12,31 @@ void printVolumes(const QList<QStorageInfo> &volumes, int (*printer)(const char
// 214958080 39088272 4096 /
// /dev/disk1s2 (hfs) RW 488050672 419909696 4096 Macintosh HD2 /Volumes/Macintosh HD2
- printer("Filesystem (Type) Size Available BSize Label Mounted on\n");
- foreach (const QStorageInfo &info, volumes) {
+ int fsColumnWidth = 25;
+ int labelColumnWidth = 20;
+ for (const QStorageInfo &info : volumes) {
+ int len = 3 + info.device().size() + info.fileSystemType().size();
+ fsColumnWidth = qMax(fsColumnWidth, len);
+ if (QString subvol = info.subvolume(); !subvol.isEmpty())
+ labelColumnWidth = qMax(labelColumnWidth, int(subvol.size() + strlen("subvol=")));
+ else
+ labelColumnWidth = qMax(labelColumnWidth, int(info.name().size()));
+ }
+
+ printer("%*s Size Available BSize %*s Mounted on\n",
+ -fsColumnWidth, "Filesystem (Type)",
+ -labelColumnWidth, "Label");
+ for (const QStorageInfo &info : volumes) {
QByteArray fsAndType = info.device();
if (info.fileSystemType() != fsAndType)
fsAndType += " (" + info.fileSystemType() + ')';
- printer("%-19s R%c ", fsAndType.constData(), info.isReadOnly() ? 'O' : 'W');
- if (fsAndType.size() > 19)
- printer("\n%23s", "");
-
+ printer("%*s R%c ", -fsColumnWidth, fsAndType.constData(), info.isReadOnly() ? 'O' : 'W');
printer("%10llu %10llu %5u ", info.bytesTotal() / 1024, info.bytesFree() / 1024, info.blockSize());
if (!info.subvolume().isEmpty())
- printer("subvol=%-18s ", qPrintable(info.subvolume()));
+ printer("subvol=%*s ", -labelColumnWidth + int(strlen("subvol=")), qPrintable(info.subvolume()));
else
- printer("%-25s ", qPrintable(info.name()));
+ printer("%*s ", -labelColumnWidth, qPrintable(info.name()));
printer("%s\n", qPrintable(info.rootPath()));
}
}
diff --git a/tests/manual/qsysinfo/main.cpp b/tests/manual/qsysinfo/main.cpp
index c870ea30de..247553d624 100644
--- a/tests/manual/qsysinfo/main.cpp
+++ b/tests/manual/qsysinfo/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 Intel Corporation.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QCoreApplication>
#include <QOperatingSystemVersion>
diff --git a/tests/manual/qt_poll/tst_qt_poll.cpp b/tests/manual/qt_poll/tst_qt_poll.cpp
index 2ed7687777..068120666e 100644
--- a/tests/manual/qt_poll/tst_qt_poll.cpp
+++ b/tests/manual/qt_poll/tst_qt_poll.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QT_NO_NATIVE_POLL
#define QT_NO_NATIVE_POLL
diff --git a/tests/manual/qtabbar/main.cpp b/tests/manual/qtabbar/main.cpp
index 33ec4b2b1b..02d4229973 100644
--- a/tests/manual/qtabbar/main.cpp
+++ b/tests/manual/qtabbar/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QWidget>
diff --git a/tests/manual/qtabbar/tabbarform.cpp b/tests/manual/qtabbar/tabbarform.cpp
index 51271f7373..8e09e182d8 100644
--- a/tests/manual/qtabbar/tabbarform.cpp
+++ b/tests/manual/qtabbar/tabbarform.cpp
@@ -1,3 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tabbarform.h"
TabBarForm::TabBarForm(QWidget *parent) :
diff --git a/tests/manual/qtabbar/tabbarform.h b/tests/manual/qtabbar/tabbarform.h
index ac101d6402..ae0ea290de 100644
--- a/tests/manual/qtabbar/tabbarform.h
+++ b/tests/manual/qtabbar/tabbarform.h
@@ -1,3 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TABBARFORM_H
#define TABBARFORM_H
diff --git a/tests/manual/qtabletevent/CMakeLists.txt b/tests/manual/qtabletevent/CMakeLists.txt
index d813c14df4..df4a66c8f1 100644
--- a/tests/manual/qtabletevent/CMakeLists.txt
+++ b/tests/manual/qtabletevent/CMakeLists.txt
@@ -1,6 +1,6 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-#add_subdirectory(device_information) # special case no member named 'staticQtMetaObject'
+#add_subdirectory(device_information) # TODO: no member named 'staticQtMetaObject'
add_subdirectory(event_compression)
add_subdirectory(regular_widgets)
diff --git a/tests/manual/qtabletevent/device_information/CMakeLists.txt b/tests/manual/qtabletevent/device_information/CMakeLists.txt
index d712429119..a0583b1f93 100644
--- a/tests/manual/qtabletevent/device_information/CMakeLists.txt
+++ b/tests/manual/qtabletevent/device_information/CMakeLists.txt
@@ -1,16 +1,20 @@
-# Copyright (C) 2022 The Qt Company Ltd.
+# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-#####################################################################
-## device_information Binary:
-#####################################################################
+project(tablet_device_info)
+cmake_minimum_required(VERSION 3.19)
-qt_internal_add_manual_test(device_information
- GUI
- SOURCES
- main.cpp
- tabletwidget.cpp tabletwidget.h
- LIBRARIES
- Qt::Gui
- Qt::Widgets
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_add_executable(tablet_device_info
+ main.cpp
+ tabletwidget.h tabletwidget.cpp
+)
+
+set_target_properties(tablet_device_info PROPERTIES
+ AUTOMOC TRUE
+)
+
+target_link_libraries(tablet_device_info PUBLIC
+ Qt::Widgets
)
diff --git a/tests/manual/qtabletevent/device_information/main.cpp b/tests/manual/qtabletevent/device_information/main.cpp
index 5d9b4d83fc..cb18e79eac 100644
--- a/tests/manual/qtabletevent/device_information/main.cpp
+++ b/tests/manual/qtabletevent/device_information/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QDebug>
diff --git a/tests/manual/qtabletevent/device_information/tabletwidget.cpp b/tests/manual/qtabletevent/device_information/tabletwidget.cpp
index 78c9636652..747c807e02 100644
--- a/tests/manual/qtabletevent/device_information/tabletwidget.cpp
+++ b/tests/manual/qtabletevent/device_information/tabletwidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tabletwidget.h"
#include <QPainter>
@@ -57,6 +57,8 @@ bool TabletWidget::eventFilter(QObject *, QEvent *ev)
mPos = event->pos();
mGPos = event->globalPosition();
mTimestamp = event->timestamp();
+ if (isVisible())
+ update();
}
break;
case QEvent::Wheel:
diff --git a/tests/manual/qtabletevent/device_information/tabletwidget.h b/tests/manual/qtabletevent/device_information/tabletwidget.h
index 9eee4bf639..477ae55b1b 100644
--- a/tests/manual/qtabletevent/device_information/tabletwidget.h
+++ b/tests/manual/qtabletevent/device_information/tabletwidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TABLETWIDGET_H
#define TABLETWIDGET_H
diff --git a/tests/manual/qtabletevent/event_compression/main.cpp b/tests/manual/qtabletevent/event_compression/main.cpp
index fe805cab4a..13edf8e704 100644
--- a/tests/manual/qtabletevent/event_compression/main.cpp
+++ b/tests/manual/qtabletevent/event_compression/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mousestatwidget.h"
diff --git a/tests/manual/qtabletevent/event_compression/mousestatwidget.cpp b/tests/manual/qtabletevent/event_compression/mousestatwidget.cpp
index 63ae1e0e81..483ce0019d 100644
--- a/tests/manual/qtabletevent/event_compression/mousestatwidget.cpp
+++ b/tests/manual/qtabletevent/event_compression/mousestatwidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mousestatwidget.h"
diff --git a/tests/manual/qtabletevent/event_compression/mousestatwidget.h b/tests/manual/qtabletevent/event_compression/mousestatwidget.h
index 1130ea1688..f204a753a3 100644
--- a/tests/manual/qtabletevent/event_compression/mousestatwidget.h
+++ b/tests/manual/qtabletevent/event_compression/mousestatwidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MOUSESTATWIDGET_H
#define MOUSESTATWIDGET_H
diff --git a/tests/manual/qtabletevent/regular_widgets/main.cpp b/tests/manual/qtabletevent/regular_widgets/main.cpp
index f6dab99976..3aee2bcdba 100644
--- a/tests/manual/qtabletevent/regular_widgets/main.cpp
+++ b/tests/manual/qtabletevent/regular_widgets/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QAction>
#include <QApplication>
diff --git a/tests/manual/qtbug-52641/main.cpp b/tests/manual/qtbug-52641/main.cpp
index 27323f1979..71cfcb4075 100644
--- a/tests/manual/qtbug-52641/main.cpp
+++ b/tests/manual/qtbug-52641/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Kai Pastor
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QFileDialog>
diff --git a/tests/manual/qtbug-8933/main.cpp b/tests/manual/qtbug-8933/main.cpp
index 30add62d6b..d586e84d20 100644
--- a/tests/manual/qtbug-8933/main.cpp
+++ b/tests/manual/qtbug-8933/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include "widget.h"
diff --git a/tests/manual/qtbug-8933/widget.cpp b/tests/manual/qtbug-8933/widget.cpp
index dd775f25c9..77ec831d65 100644
--- a/tests/manual/qtbug-8933/widget.cpp
+++ b/tests/manual/qtbug-8933/widget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "widget.h"
#include "ui_widget.h"
diff --git a/tests/manual/qtbug-8933/widget.h b/tests/manual/qtbug-8933/widget.h
index 5dc9c5019f..e3d3dee0d0 100644
--- a/tests/manual/qtbug-8933/widget.h
+++ b/tests/manual/qtbug-8933/widget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WIDGET_H
#define WIDGET_H
diff --git a/tests/manual/qtextcursorinsert/CMakeLists.txt b/tests/manual/qtextcursorinsert/CMakeLists.txt
new file mode 100644
index 0000000000..064b636bdd
--- /dev/null
+++ b/tests/manual/qtextcursorinsert/CMakeLists.txt
@@ -0,0 +1,14 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_manual_test(qtextcursorinsert
+ GUI
+ SOURCES
+ main.cpp
+ widget.cpp widget.h widget.ui
+ LIBRARIES
+ Qt::Gui
+ Qt::Widgets
+ ENABLE_AUTOGEN_TOOLS
+ uic
+)
diff --git a/tests/manual/qtextcursorinsert/main.cpp b/tests/manual/qtextcursorinsert/main.cpp
new file mode 100644
index 0000000000..f4fa2f705f
--- /dev/null
+++ b/tests/manual/qtextcursorinsert/main.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "widget.h"
+
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ Widget w;
+ w.show();
+ return a.exec();
+}
diff --git a/tests/manual/qtextcursorinsert/widget.cpp b/tests/manual/qtextcursorinsert/widget.cpp
new file mode 100644
index 0000000000..14440b7b7a
--- /dev/null
+++ b/tests/manual/qtextcursorinsert/widget.cpp
@@ -0,0 +1,188 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "widget.h"
+#include "./ui_widget.h"
+
+#include <QBuffer>
+#include <QShortcut>
+#include <QTextBlock>
+#include <QTextDocumentWriter>
+#include <QTextList>
+
+using namespace Qt::StringLiterals;
+
+Widget::Widget(QWidget *parent)
+ : QWidget(parent)
+ , ui(new Ui::Widget)
+{
+ ui->setupUi(this);
+
+ m_texts.insert(u"0-numbered html list"_s, u"<ol start=\"0\">\n<li>eggs</li>\n<li>maple syrup</li>\n</ol>"_s);
+ m_texts.insert(u"0-numbered markdown list"_s, u"0) eggs\n1) maple syrup\n"_s);
+ m_texts.insert(u"lorem ipsum markdown"_s,
+ u"Lorem ipsum dolor sit amet quod scimus quomodo legere et scribere Markdown, non solum applicationes interrete."_s);
+ m_texts.insert(u"markdown checkboxes"_s,
+ u"- [ ] kürbis-kernöl\n- [ ] mon cheri (große schachtel)\n- [ ] bergkäse\n- [ ] mannerwaffln\n"_s);
+ m_texts.insert(u"markdown checkboxes (3 hidden list styles)"_s,
+ u"- [ ] kürbis-kernöl\n- [ ] mon cheri (große schachtel)\n+ [ ] bergkäse\n* [ ] mannerwaffln\n"_s);
+ m_texts.insert(u"numbered html list"_s, u"<ol>\n<li>eggs</li>\n<li>maple syrup</li>\n</ol>"_s);
+ m_texts.insert(u"numbered markdown list"_s, u"1. bread\n1. milk\n"_s);
+
+ for (auto it = m_texts.constBegin(); it != m_texts.constEnd(); ++it) {
+ ui->richTextCB->addItem(it.key(), it.value());
+ ui->plainTextCB->addItem(it.key(), it.value());
+ }
+
+ ui->richTextCB->setCurrentIndex(m_texts.count() - 1);
+ ui->plainTextCB->setCurrentIndex(1);
+
+ m_fileDialog.setWindowTitle(tr("Save rich text"));
+ m_fileDialog.setAcceptMode(QFileDialog::AcceptSave);
+ m_fileDialog.setMimeTypeFilters({"text/markdown", "text/html", "text/plain",
+ "application/vnd.oasis.opendocument.text"});
+ connect(&m_fileDialog, &QFileDialog::fileSelected, this, &Widget::onSave);
+
+ connect(new QShortcut(QKeySequence::Save, this), &QShortcut::activated, [this]() { m_fileDialog.open(); });
+ connect(new QShortcut(QKeySequence::Quit, this), &QShortcut::activated, [this]() { qApp->quit(); });
+}
+
+Widget::~Widget()
+{
+ delete ui;
+}
+
+void Widget::on_insertMarkdownButton_clicked()
+{
+ if (ui->newBlockBeforeCB->isChecked()) {
+ if (ui->moveToBeginningRB->isChecked())
+ ui->textEdit->moveCursor(QTextCursor::StartOfBlock);
+ if (ui->moveToEndRB->isChecked())
+ ui->textEdit->moveCursor(QTextCursor::EndOfBlock);
+ if (ui->defaultBlockFormatCB->isChecked())
+ ui->textEdit->textCursor().insertBlock(QTextBlockFormat());
+ else
+ ui->textEdit->textCursor().insertBlock();
+ }
+ ui->textEdit->textCursor().insertMarkdown(ui->plainTextEdit->toPlainText());
+ if (ui->newBlockBeforeCB->isChecked()) {
+ if (ui->defaultBlockFormatCB->isChecked())
+ ui->textEdit->textCursor().insertBlock(QTextBlockFormat());
+ else
+ ui->textEdit->textCursor().insertBlock();
+ }
+}
+
+void Widget::on_insertHtmlButton_clicked()
+{
+ if (ui->newBlockBeforeCB->isChecked()) {
+ if (ui->moveToBeginningRB->isChecked())
+ ui->textEdit->moveCursor(QTextCursor::StartOfBlock);
+ if (ui->moveToEndRB->isChecked())
+ ui->textEdit->moveCursor(QTextCursor::EndOfBlock);
+ if (ui->defaultBlockFormatCB->isChecked())
+ ui->textEdit->textCursor().insertBlock(QTextBlockFormat());
+ else
+ ui->textEdit->textCursor().insertBlock();
+ }
+ ui->textEdit->insertHtml(ui->plainTextEdit->toPlainText());
+ if (ui->newBlockBeforeCB->isChecked()) {
+ if (ui->defaultBlockFormatCB->isChecked())
+ ui->textEdit->textCursor().insertBlock(QTextBlockFormat());
+ else
+ ui->textEdit->textCursor().insertBlock();
+ }
+}
+
+void Widget::on_insertPlainButton_clicked()
+{
+ if (ui->newBlockBeforeCB->isChecked()) {
+ if (ui->moveToBeginningRB->isChecked())
+ ui->textEdit->moveCursor(QTextCursor::StartOfBlock);
+ if (ui->moveToEndRB->isChecked())
+ ui->textEdit->moveCursor(QTextCursor::EndOfBlock);
+ if (ui->defaultBlockFormatCB->isChecked())
+ ui->textEdit->textCursor().insertBlock(QTextBlockFormat());
+ else
+ ui->textEdit->textCursor().insertBlock();
+ }
+ ui->textEdit->insertPlainText(ui->plainTextEdit->toPlainText());
+ if (ui->newBlockBeforeCB->isChecked()) {
+ if (ui->defaultBlockFormatCB->isChecked())
+ ui->textEdit->textCursor().insertBlock(QTextBlockFormat());
+ else
+ ui->textEdit->textCursor().insertBlock();
+ }
+}
+
+void Widget::on_plainTextCB_activated(int index)
+{
+ if (index < m_texts.size()) {
+ auto it = m_texts.constBegin();
+ std::advance(it, index);
+ ui->plainTextEdit->setPlainText(it.value());
+ }
+}
+
+void Widget::on_richTextCB_activated(int index)
+{
+ if (index < m_texts.size()) {
+ auto it = m_texts.constBegin();
+ std::advance(it, index);
+ if (it.key().contains(u"markdown", Qt::CaseInsensitive))
+ ui->textEdit->setMarkdown(it.value());
+ else if (it.key().contains(u"html", Qt::CaseInsensitive))
+ ui->textEdit->setHtml(it.value());
+ else
+ ui->textEdit->setPlainText(it.value());
+ }
+}
+
+void Widget::on_textEdit_cursorPositionChanged()
+{
+ QTextBlock block = ui->textEdit->textCursor().block();
+ ui->blockDesc->setText(u"%1 of %2"_s
+ .arg(block.blockNumber())
+ .arg(ui->textEdit->document()->blockCount()));
+ QTextList *list = block.textList();
+ if (list) {
+ QTextBlock first = list->item(0);
+ ui->listDesc->setText(u"index %1: \"%2\" is in list\nwith style %3 and %4 items starting with %5: %6 %7"_s
+ .arg(list->itemNumber(block))
+ .arg(block.text())
+ .arg(list->format().style())
+ .arg(list->count())
+ .arg(list->format().start())
+ .arg(list->format().style() > QTextListFormat::ListDecimal ? u""_s : list->itemText(first))
+ .arg(first.text()) );
+ } else {
+ ui->listDesc->setText(u"not in a list"_s);
+ }
+}
+
+void Widget::on_saveButton_clicked()
+{
+ m_fileDialog.open();
+}
+
+void Widget::onSave(const QString &file)
+{
+ QFile f(file);
+ if (f.open(QFile::WriteOnly)) {
+ if (m_fileDialog.selectedMimeTypeFilter() == u"text/markdown")
+ f.write(ui->textEdit->toMarkdown().toUtf8());
+ else if (m_fileDialog.selectedMimeTypeFilter() == u"text/html")
+ f.write(ui->textEdit->toHtml().toUtf8());
+ else if (m_fileDialog.selectedMimeTypeFilter() == u"text/plain")
+ f.write(ui->textEdit->toPlainText().toUtf8());
+ else if (m_fileDialog.selectedMimeTypeFilter() == u"application/vnd.oasis.opendocument.text") {
+ QBuffer buffer;
+ QTextDocumentWriter writer(&buffer, "ODF");
+ writer.write(ui->textEdit->document());
+ buffer.close();
+ f.write(buffer.data());
+ }
+ f.close();
+ }
+}
+
diff --git a/tests/manual/qtextcursorinsert/widget.h b/tests/manual/qtextcursorinsert/widget.h
new file mode 100644
index 0000000000..9185da975c
--- /dev/null
+++ b/tests/manual/qtextcursorinsert/widget.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef WIDGET_H
+#define WIDGET_H
+
+#include <QFileDialog>
+#include <QMap>
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+namespace Ui { class Widget; }
+QT_END_NAMESPACE
+
+class Widget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ Widget(QWidget *parent = nullptr);
+ ~Widget();
+
+private slots:
+ void on_insertMarkdownButton_clicked();
+ void on_insertHtmlButton_clicked();
+ void on_insertPlainButton_clicked();
+ void on_plainTextCB_activated(int index);
+ void on_richTextCB_activated(int index);
+ void on_textEdit_cursorPositionChanged();
+ void on_saveButton_clicked();
+ void onSave(const QString &file);
+
+private:
+ Ui::Widget *ui;
+ QMap<QString, QString> m_texts;
+ QFileDialog m_fileDialog;
+};
+#endif // WIDGET_H
diff --git a/tests/manual/qtextcursorinsert/widget.ui b/tests/manual/qtextcursorinsert/widget.ui
new file mode 100644
index 0000000000..42a4a60acb
--- /dev/null
+++ b/tests/manual/qtextcursorinsert/widget.ui
@@ -0,0 +1,222 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Widget</class>
+ <widget class="QWidget" name="Widget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>1200</width>
+ <height>600</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Try inserting (or dragging) text on right into text on left</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QTextEdit" name="textEdit">
+ <property name="html">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+hr { height: 1px; border-width: 0; }
+li.unchecked::marker { content: &quot;\2610&quot;; }
+li.checked::marker { content: &quot;\2612&quot;; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;ol style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;
+&lt;li style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;bread&lt;/li&gt;
+&lt;li style=&quot; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;milk&lt;/li&gt;&lt;/ol&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPlainTextEdit" name="plainTextEdit">
+ <property name="plainText">
+ <string>0) eggs
+1) maple syrup</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <layout class="QFormLayout" name="formLayout">
+ <item row="2" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>List</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="listDesc">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="richTextCB"/>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Change to</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Block</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="blockDesc">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QPushButton" name="saveButton">
+ <property name="text">
+ <string>Save</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QComboBox" name="plainTextCB"/>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Before</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="4" column="0">
+ <widget class="QCheckBox" name="newBlockBeforeCB">
+ <property name="text">
+ <string>new block</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QRadioButton" name="moveToEndRB">
+ <property name="text">
+ <string>move to end of block</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QRadioButton" name="moveToBeginningRB">
+ <property name="text">
+ <string>move to beginning of block</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QRadioButton" name="radioButton">
+ <property name="text">
+ <string>nothing</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QCheckBox" name="defaultBlockFormatCB">
+ <property name="text">
+ <string>default block format</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Insert</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="insertMarkdownButton">
+ <property name="text">
+ <string>Markdown</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="insertHtmlButton">
+ <property name="text">
+ <string>HTML</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="insertPlainButton">
+ <property name="text">
+ <string>Plain</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="title">
+ <string>After</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QCheckBox" name="newBlockAfterCB">
+ <property name="text">
+ <string>new block</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tests/manual/qtexteditlist/main.cpp b/tests/manual/qtexteditlist/main.cpp
index 973d9b0845..916855bb89 100644
--- a/tests/manual/qtexteditlist/main.cpp
+++ b/tests/manual/qtexteditlist/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "widget.h"
#include <QApplication>
diff --git a/tests/manual/qtexteditlist/widget.cpp b/tests/manual/qtexteditlist/widget.cpp
index 45ff000a67..671328d804 100644
--- a/tests/manual/qtexteditlist/widget.cpp
+++ b/tests/manual/qtexteditlist/widget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "widget.h"
#include "ui_widget.h"
diff --git a/tests/manual/qtexteditlist/widget.h b/tests/manual/qtexteditlist/widget.h
index 934529c5ac..a376b358d7 100644
--- a/tests/manual/qtexteditlist/widget.h
+++ b/tests/manual/qtexteditlist/widget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WIDGET_H
#define WIDGET_H
diff --git a/tests/manual/qtexttableborders/main.cpp b/tests/manual/qtexttableborders/main.cpp
index eccbce20d6..b45e853938 100644
--- a/tests/manual/qtexttableborders/main.cpp
+++ b/tests/manual/qtexttableborders/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "widget.h"
#include <QApplication>
diff --git a/tests/manual/qtexttableborders/widget.cpp b/tests/manual/qtexttableborders/widget.cpp
index a5af034562..80479a3d99 100644
--- a/tests/manual/qtexttableborders/widget.cpp
+++ b/tests/manual/qtexttableborders/widget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "widget.h"
#include "ui_widget.h"
diff --git a/tests/manual/qtexttableborders/widget.h b/tests/manual/qtexttableborders/widget.h
index 4d35264e02..d604861144 100644
--- a/tests/manual/qtexttableborders/widget.h
+++ b/tests/manual/qtexttableborders/widget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WIDGET_H
#define WIDGET_H
diff --git a/tests/manual/qtouchevent/main.cpp b/tests/manual/qtouchevent/main.cpp
index 43dc10d3ca..25b63fafb3 100644
--- a/tests/manual/qtouchevent/main.cpp
+++ b/tests/manual/qtouchevent/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QTest>
diff --git a/tests/manual/qtouchevent/touchwidget.cpp b/tests/manual/qtouchevent/touchwidget.cpp
index af12303dbd..7a8a0098e0 100644
--- a/tests/manual/qtouchevent/touchwidget.cpp
+++ b/tests/manual/qtouchevent/touchwidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "touchwidget.h"
diff --git a/tests/manual/qtouchevent/touchwidget.h b/tests/manual/qtouchevent/touchwidget.h
index f9532ec473..d4128634f3 100644
--- a/tests/manual/qtouchevent/touchwidget.h
+++ b/tests/manual/qtouchevent/touchwidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TOUCHWIDGET_H
#define TOUCHWIDGET_H
diff --git a/tests/manual/qvulkaninstance/main.cpp b/tests/manual/qvulkaninstance/main.cpp
index 1c2d771504..48825b34f6 100644
--- a/tests/manual/qvulkaninstance/main.cpp
+++ b/tests/manual/qvulkaninstance/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QVulkanInstance>
diff --git a/tests/manual/qwidget_zorder/main.cpp b/tests/manual/qwidget_zorder/main.cpp
index eb8d027c41..45285f9b8e 100644
--- a/tests/manual/qwidget_zorder/main.cpp
+++ b/tests/manual/qwidget_zorder/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QPushButton>
diff --git a/tests/manual/repaint/mainwindow/main.cpp b/tests/manual/repaint/mainwindow/main.cpp
index dd60547344..ea024d22a5 100644
--- a/tests/manual/repaint/mainwindow/main.cpp
+++ b/tests/manual/repaint/mainwindow/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QMainWindow>
@@ -26,9 +26,9 @@ int main(int argc, char **argv)
QToolBar *toolBar = new QToolBar();
- toolBar->addWidget(new StaticWidget())->setVisible(true);;
+ toolBar->addWidget(new StaticWidget())->setVisible(true);
- toolBar->addWidget(new QSpinBox())->setVisible(true);;
+ toolBar->addWidget(new QSpinBox())->setVisible(true);
mainWindow.addToolBar(toolBar);
mainWindow.resize(600, 400);
diff --git a/tests/manual/repaint/scrollarea/main.cpp b/tests/manual/repaint/scrollarea/main.cpp
index f520de78b2..b45a85c353 100644
--- a/tests/manual/repaint/scrollarea/main.cpp
+++ b/tests/manual/repaint/scrollarea/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QMainWindow>
diff --git a/tests/manual/repaint/shared/shared.h b/tests/manual/repaint/shared/shared.h
index 57ce56cd85..0a69011b6c 100644
--- a/tests/manual/repaint/shared/shared.h
+++ b/tests/manual/repaint/shared/shared.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QWidget>
#include <QPainter>
@@ -28,20 +28,20 @@ public:
const int rectSize = 10;
QRect rect(pos.x() - rectSize, pos.y() - rectSize, rectSize *2, rectSize * 2);
- QList<QRect> updateRects;
- updateRects.append(rect.translated(rectSize * 2, rectSize * 2));
- updateRects.append(rect.translated(rectSize * 2, -rectSize * 2));
- updateRects.append(rect.translated(-rectSize * 2, rectSize * 2));
- updateRects.append(rect.translated(-rectSize * 2, -rectSize * 2));
-
+ const QRect updateRects[] = {
+ rect.translated(rectSize * 2, rectSize * 2),
+ rect.translated(rectSize * 2, -rectSize * 2),
+ rect.translated(-rectSize * 2, rectSize * 2),
+ rect.translated(-rectSize * 2, -rectSize * 2),
+ };
bool useRegion = false;
if (useRegion) {
QRegion region;
- region.setRects(updateRects.data(), 4);
+ region.setRects(updateRects, 4);
update(region);
} else {
- foreach (QRect rect, updateRects)
+ for (QRect rect : updateRects)
update(rect);
}
}
diff --git a/tests/manual/repaint/splitter/main.cpp b/tests/manual/repaint/splitter/main.cpp
index e26698c413..62f9f74d5b 100644
--- a/tests/manual/repaint/splitter/main.cpp
+++ b/tests/manual/repaint/splitter/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/shared.h"
diff --git a/tests/manual/repaint/tableview/main.cpp b/tests/manual/repaint/tableview/main.cpp
index 9ee2e95d96..f7d04c6a60 100644
--- a/tests/manual/repaint/tableview/main.cpp
+++ b/tests/manual/repaint/tableview/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/shared.h"
diff --git a/tests/manual/repaint/task141091/main.cpp b/tests/manual/repaint/task141091/main.cpp
index 78c280aa32..2fceea0148 100644
--- a/tests/manual/repaint/task141091/main.cpp
+++ b/tests/manual/repaint/task141091/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QMainWindow>
diff --git a/tests/manual/repaint/toplevel/main.cpp b/tests/manual/repaint/toplevel/main.cpp
index b7d680a284..b93bf2fec8 100644
--- a/tests/manual/repaint/toplevel/main.cpp
+++ b/tests/manual/repaint/toplevel/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/shared.h"
diff --git a/tests/manual/repaint/widget/main.cpp b/tests/manual/repaint/widget/main.cpp
index c69de5369f..b4400d5170 100644
--- a/tests/manual/repaint/widget/main.cpp
+++ b/tests/manual/repaint/widget/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/shared.h"
diff --git a/tests/manual/rhi/CMakeLists.txt b/tests/manual/rhi/CMakeLists.txt
index ab4093e9a6..8f48bf219d 100644
--- a/tests/manual/rhi/CMakeLists.txt
+++ b/tests/manual/rhi/CMakeLists.txt
@@ -32,6 +32,8 @@ add_subdirectory(stereo)
add_subdirectory(tex1d)
add_subdirectory(displacement)
add_subdirectory(imguirenderer)
+add_subdirectory(multiview)
+add_subdirectory(msaatextureresolve)
if(QT_FEATURE_widgets)
- add_subdirectory(rhiwidget)
+ add_subdirectory(rhiwidgetproto)
endif()
diff --git a/tests/manual/rhi/compressedtexture_bc1/compressedtexture_bc1.cpp b/tests/manual/rhi/compressedtexture_bc1/compressedtexture_bc1.cpp
index 146a414924..80eb63783e 100644
--- a/tests/manual/rhi/compressedtexture_bc1/compressedtexture_bc1.cpp
+++ b/tests/manual/rhi/compressedtexture_bc1/compressedtexture_bc1.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
#include "../shared/cube.h"
diff --git a/tests/manual/rhi/compressedtexture_bc1_subupload/compressedtexture_bc1_subupload.cpp b/tests/manual/rhi/compressedtexture_bc1_subupload/compressedtexture_bc1_subupload.cpp
index a916cd7742..a98744afc0 100644
--- a/tests/manual/rhi/compressedtexture_bc1_subupload/compressedtexture_bc1_subupload.cpp
+++ b/tests/manual/rhi/compressedtexture_bc1_subupload/compressedtexture_bc1_subupload.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
#include "../shared/cube.h"
diff --git a/tests/manual/rhi/computebuffer/buildshaders.bat b/tests/manual/rhi/computebuffer/buildshaders.bat
index 2768273b70..07a602e18b 100755
--- a/tests/manual/rhi/computebuffer/buildshaders.bat
+++ b/tests/manual/rhi/computebuffer/buildshaders.bat
@@ -1,3 +1,5 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl "310 es,430" --hlsl 50 --msl 12 buffer.comp -o buffer.comp.qsb
qsb --glsl "310 es,430" --hlsl 50 --msl 12 main.vert -o main.vert.qsb
qsb --glsl "310 es,430" --hlsl 50 --msl 12 main.frag -o main.frag.qsb
diff --git a/tests/manual/rhi/computebuffer/computebuffer.cpp b/tests/manual/rhi/computebuffer/computebuffer.cpp
index bb5149bdb9..a54c0817b4 100644
--- a/tests/manual/rhi/computebuffer/computebuffer.cpp
+++ b/tests/manual/rhi/computebuffer/computebuffer.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
#include <QRandomGenerator>
diff --git a/tests/manual/rhi/computeimage/buildshaders.bat b/tests/manual/rhi/computeimage/buildshaders.bat
index 41a324d2b2..253d05b625 100755
--- a/tests/manual/rhi/computeimage/buildshaders.bat
+++ b/tests/manual/rhi/computeimage/buildshaders.bat
@@ -1 +1,3 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
qsb --glsl "310 es,430" --hlsl 50 --msl 12 image.comp -o image.comp.qsb
diff --git a/tests/manual/rhi/computeimage/computeimage.cpp b/tests/manual/rhi/computeimage/computeimage.cpp
index c7414a0236..5da68e4a18 100644
--- a/tests/manual/rhi/computeimage/computeimage.cpp
+++ b/tests/manual/rhi/computeimage/computeimage.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
diff --git a/tests/manual/rhi/cubemap/buildshaders.bat b/tests/manual/rhi/cubemap/buildshaders.bat
index ebf673875d..314559490f 100644
--- a/tests/manual/rhi/cubemap/buildshaders.bat
+++ b/tests/manual/rhi/cubemap/buildshaders.bat
@@ -1,2 +1,4 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c cubemap.vert -o cubemap.vert.qsb
qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c cubemap.frag -o cubemap.frag.qsb
diff --git a/tests/manual/rhi/cubemap/cubemap.cpp b/tests/manual/rhi/cubemap/cubemap.cpp
index 579da73633..bf027b49de 100644
--- a/tests/manual/rhi/cubemap/cubemap.cpp
+++ b/tests/manual/rhi/cubemap/cubemap.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
#include "../shared/cube.h"
diff --git a/tests/manual/rhi/cubemap_render/buildshaders.bat b/tests/manual/rhi/cubemap_render/buildshaders.bat
index 3886c138d8..4bee01d8cd 100644
--- a/tests/manual/rhi/cubemap_render/buildshaders.bat
+++ b/tests/manual/rhi/cubemap_render/buildshaders.bat
@@ -1,3 +1,5 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_oneface.vert -o cubemap_oneface.vert.qsb
qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_oneface.frag -o cubemap_oneface.frag.qsb
qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_mrt.vert -o cubemap_mrt.vert.qsb
diff --git a/tests/manual/rhi/cubemap_render/cubemap_render.cpp b/tests/manual/rhi/cubemap_render/cubemap_render.cpp
index ed01404e86..3eb013937f 100644
--- a/tests/manual/rhi/cubemap_render/cubemap_render.cpp
+++ b/tests/manual/rhi/cubemap_render/cubemap_render.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Demonstrates rendering to two cubemaps in two different ways:
// - one by one, to each face,
diff --git a/tests/manual/rhi/cubemap_scissor/cubemap_scissor.cpp b/tests/manual/rhi/cubemap_scissor/cubemap_scissor.cpp
index 8e69b48d40..78933830a1 100644
--- a/tests/manual/rhi/cubemap_scissor/cubemap_scissor.cpp
+++ b/tests/manual/rhi/cubemap_scissor/cubemap_scissor.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This is a test for scissoring. Based on the cubemap test (because there the
// rendering covers the entire viewport which is what we need here). The
diff --git a/tests/manual/rhi/displacement/buildshaders.bat b/tests/manual/rhi/displacement/buildshaders.bat
index 552277491d..95a51c0e91 100644
--- a/tests/manual/rhi/displacement/buildshaders.bat
+++ b/tests/manual/rhi/displacement/buildshaders.bat
@@ -1,3 +1,5 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl 320es,410 --hlsl 50 --msl 12 --msltess material.vert -o material.vert.qsb
qsb --glsl 320es,410 --hlsl 50 --msl 12 material.frag -o material.frag.qsb
diff --git a/tests/manual/rhi/displacement/displacement.cpp b/tests/manual/rhi/displacement/displacement.cpp
index 7a25133faf..680ffa0628 100644
--- a/tests/manual/rhi/displacement/displacement.cpp
+++ b/tests/manual/rhi/displacement/displacement.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#define EXAMPLEFW_IMGUI
#include "../shared/examplefw.h"
diff --git a/tests/manual/rhi/float16texture_with_compute/buildshaders.bat b/tests/manual/rhi/float16texture_with_compute/buildshaders.bat
index d1ffecf49d..15f39256c7 100644
--- a/tests/manual/rhi/float16texture_with_compute/buildshaders.bat
+++ b/tests/manual/rhi/float16texture_with_compute/buildshaders.bat
@@ -1,2 +1,4 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl "430,310 es" --hlsl 50 --msl 12 load.comp -o load.comp.qsb
qsb --glsl "430,310 es" --hlsl 50 --msl 12 prefilter.comp -o prefilter.comp.qsb
diff --git a/tests/manual/rhi/float16texture_with_compute/float16texture_with_compute.cpp b/tests/manual/rhi/float16texture_with_compute/float16texture_with_compute.cpp
index 44ec3c742e..6bf3d222b4 100644
--- a/tests/manual/rhi/float16texture_with_compute/float16texture_with_compute.cpp
+++ b/tests/manual/rhi/float16texture_with_compute/float16texture_with_compute.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// An advanced version of floattexture. Instead of RGBA32F, we use RGBA16F, and
// also generate the floating point data from rgba with compute. Then there's a
diff --git a/tests/manual/rhi/floattexture/floattexture.cpp b/tests/manual/rhi/floattexture/floattexture.cpp
index d580f933d9..8cd41bcc58 100644
--- a/tests/manual/rhi/floattexture/floattexture.cpp
+++ b/tests/manual/rhi/floattexture/floattexture.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
#include <qmath.h>
diff --git a/tests/manual/rhi/geometryshader/buildshaders.bat b/tests/manual/rhi/geometryshader/buildshaders.bat
index 6da26a6a2a..a3b7296e16 100755
--- a/tests/manual/rhi/geometryshader/buildshaders.bat
+++ b/tests/manual/rhi/geometryshader/buildshaders.bat
@@ -1,3 +1,5 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl 320es,410 --hlsl 50 test.vert -o test.vert.qsb
qsb --glsl 320es,410 test.geom -o test.geom.qsb
qsb -r hlsl,50,test_geom.hlsl test.geom.qsb
diff --git a/tests/manual/rhi/geometryshader/geometryshader.cpp b/tests/manual/rhi/geometryshader/geometryshader.cpp
index 1025ee1eb4..e03f247e9f 100644
--- a/tests/manual/rhi/geometryshader/geometryshader.cpp
+++ b/tests/manual/rhi/geometryshader/geometryshader.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
diff --git a/tests/manual/rhi/hdr/CMakeLists.txt b/tests/manual/rhi/hdr/CMakeLists.txt
new file mode 100644
index 0000000000..dc7bcb906c
--- /dev/null
+++ b/tests/manual/rhi/hdr/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+qt_internal_add_manual_test(hdr
+ GUI
+ SOURCES
+ hdr.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::GuiPrivate
+)
+
+qt_internal_add_resource(hdr "hdr"
+ PREFIX
+ "/"
+ FILES
+ "hdrtexture.vert.qsb"
+ "hdrtexture.frag.qsb"
+)
+
+set(imgui_base ../shared/imgui)
+set(imgui_target hdr)
+include(${imgui_base}/imgui.cmakeinc)
diff --git a/tests/manual/rhi/hdr/buildshaders.bat b/tests/manual/rhi/hdr/buildshaders.bat
new file mode 100644
index 0000000000..fdae5d0eb7
--- /dev/null
+++ b/tests/manual/rhi/hdr/buildshaders.bat
@@ -0,0 +1,4 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+qsb --qt6 hdrtexture.vert -o hdrtexture.vert.qsb
+qsb --qt6 hdrtexture.frag -o hdrtexture.frag.qsb
diff --git a/tests/manual/rhi/hdr/hdr.cpp b/tests/manual/rhi/hdr/hdr.cpp
new file mode 100644
index 0000000000..1ea3b276de
--- /dev/null
+++ b/tests/manual/rhi/hdr/hdr.cpp
@@ -0,0 +1,457 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+// Test application for HDR with scRGB.
+// Launch with the argument "scrgb" or "sdr", perhaps side-by-side even.
+
+#define EXAMPLEFW_PREINIT
+#define EXAMPLEFW_IMGUI
+#include "../shared/examplefw.h"
+
+#include "../shared/cube.h"
+
+QByteArray loadHdr(const QString &fn, QSize *size)
+{
+ QFile f(fn);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning("Failed to open %s", qPrintable(fn));
+ return QByteArray();
+ }
+
+ char sig[256];
+ f.read(sig, 11);
+ if (strncmp(sig, "#?RADIANCE\n", 11))
+ return QByteArray();
+
+ QByteArray buf = f.readAll();
+ const char *p = buf.constData();
+ const char *pEnd = p + buf.size();
+
+ // Process lines until the empty one.
+ QByteArray line;
+ while (p < pEnd) {
+ char c = *p++;
+ if (c == '\n') {
+ if (line.isEmpty())
+ break;
+ if (line.startsWith(QByteArrayLiteral("FORMAT="))) {
+ const QByteArray format = line.mid(7).trimmed();
+ if (format != QByteArrayLiteral("32-bit_rle_rgbe")) {
+ qWarning("HDR format '%s' is not supported", format.constData());
+ return QByteArray();
+ }
+ }
+ line.clear();
+ } else {
+ line.append(c);
+ }
+ }
+ if (p == pEnd) {
+ qWarning("Malformed HDR image data at property strings");
+ return QByteArray();
+ }
+
+ // Get the resolution string.
+ while (p < pEnd) {
+ char c = *p++;
+ if (c == '\n')
+ break;
+ line.append(c);
+ }
+ if (p == pEnd) {
+ qWarning("Malformed HDR image data at resolution string");
+ return QByteArray();
+ }
+
+ int w = 0, h = 0;
+ // We only care about the standard orientation.
+ if (!sscanf(line.constData(), "-Y %d +X %d", &h, &w)) {
+ qWarning("Unsupported HDR resolution string '%s'", line.constData());
+ return QByteArray();
+ }
+ if (w <= 0 || h <= 0) {
+ qWarning("Invalid HDR resolution");
+ return QByteArray();
+ }
+
+ // output is RGBA32F
+ const int blockSize = 4 * sizeof(float);
+ QByteArray data;
+ data.resize(w * h * blockSize);
+
+ typedef unsigned char RGBE[4];
+ RGBE *scanline = new RGBE[w];
+
+ for (int y = 0; y < h; ++y) {
+ if (pEnd - p < 4) {
+ qWarning("Unexpected end of HDR data");
+ delete[] scanline;
+ return QByteArray();
+ }
+
+ scanline[0][0] = *p++;
+ scanline[0][1] = *p++;
+ scanline[0][2] = *p++;
+ scanline[0][3] = *p++;
+
+ if (scanline[0][0] == 2 && scanline[0][1] == 2 && scanline[0][2] < 128) {
+ // new rle, the first pixel was a dummy
+ for (int channel = 0; channel < 4; ++channel) {
+ for (int x = 0; x < w && p < pEnd; ) {
+ unsigned char c = *p++;
+ if (c > 128) { // run
+ if (p < pEnd) {
+ int repCount = c & 127;
+ c = *p++;
+ while (repCount--)
+ scanline[x++][channel] = c;
+ }
+ } else { // not a run
+ while (c-- && p < pEnd)
+ scanline[x++][channel] = *p++;
+ }
+ }
+ }
+ } else {
+ // old rle
+ scanline[0][0] = 2;
+ int bitshift = 0;
+ int x = 1;
+ while (x < w && pEnd - p >= 4) {
+ scanline[x][0] = *p++;
+ scanline[x][1] = *p++;
+ scanline[x][2] = *p++;
+ scanline[x][3] = *p++;
+
+ if (scanline[x][0] == 1 && scanline[x][1] == 1 && scanline[x][2] == 1) { // run
+ int repCount = scanline[x][3] << bitshift;
+ while (repCount--) {
+ memcpy(scanline[x], scanline[x - 1], 4);
+ ++x;
+ }
+ bitshift += 8;
+ } else { // not a run
+ ++x;
+ bitshift = 0;
+ }
+ }
+ }
+
+ // adjust for -Y orientation
+ float *fp = reinterpret_cast<float *>(data.data() + (h - 1 - y) * blockSize * w);
+ for (int x = 0; x < w; ++x) {
+ float d = qPow(2.0f, float(scanline[x][3]) - 128.0f);
+ float r = scanline[x][0] / 256.0f * d;
+ float g = scanline[x][1] / 256.0f * d;
+ float b = scanline[x][2] / 256.0f * d;
+ float a = 1.0f;
+ *fp++ = r;
+ *fp++ = g;
+ *fp++ = b;
+ *fp++ = a;
+ }
+ }
+
+ delete[] scanline;
+
+ *size = QSize(w, h);
+
+ return data;
+}
+
+struct {
+ QMatrix4x4 winProj;
+ QList<QRhiResource *> releasePool;
+ QRhiResourceUpdateBatch *initialUpdates = nullptr;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiTexture *tex = nullptr;
+ QRhiSampler *sampler = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ QRhiGraphicsPipeline *ps = nullptr;
+ bool showDemoWindow = true;
+ QVector3D rotation;
+
+ bool usingHDRWindow;
+ bool adjustSDR = false;
+ float SDRWhiteLevelInNits = 200.0f;
+ bool tonemapHDR = false;
+ float tonemapInMax = 2.5f;
+ float tonemapOutMax = 0.0f;
+
+ QString imageFile;
+} d;
+
+void preInit()
+{
+ QStringList args = QCoreApplication::arguments();
+ if (args.contains("scrgb")) {
+ d.usingHDRWindow = true;
+ swapchainFormat = QRhiSwapChain::HDRExtendedSrgbLinear;
+ } else if (args.contains("p3")) {
+ d.usingHDRWindow = true;
+ swapchainFormat = QRhiSwapChain::HDRExtendedDisplayP3Linear;
+ } else if (args.contains("sdr")) {
+ d.usingHDRWindow = false;
+ swapchainFormat = QRhiSwapChain::SDR;
+ } else {
+ qFatal("Missing command line argument, specify scrgb or sdr");
+ }
+
+ if (args.contains("file")) {
+ d.imageFile = args[args.indexOf("file") + 1];
+ qDebug("Using HDR image file %s", qPrintable(d.imageFile));
+ } else {
+ qFatal("Missing command line argument, specify 'file' followed by a .hdr file. "
+ "Download for example the original .exr from https://viewer.openhdr.org/i/5fcb9a595812624a99d24c62/linear "
+ "and use ImageMagick's 'convert' to convert from .exr to .hdr");
+ }
+}
+
+void Window::customInit()
+{
+ if (!m_r->isTextureFormatSupported(QRhiTexture::RGBA32F))
+ qWarning("RGBA32F texture format is not supported");
+
+ d.initialUpdates = m_r->nextResourceUpdateBatch();
+
+ d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube));
+ d.vbuf->create();
+ d.releasePool << d.vbuf;
+
+ d.initialUpdates->uploadStaticBuffer(d.vbuf, cube);
+
+ d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 4 * 4);
+ d.ubuf->create();
+ d.releasePool << d.ubuf;
+
+ qint32 flip = 1;
+ d.initialUpdates->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
+
+ qint32 doLinearToSRGBInShader = !d.usingHDRWindow;
+ d.initialUpdates->updateDynamicBuffer(d.ubuf, 68, 4, &doLinearToSRGBInShader);
+
+ QSize size;
+ QByteArray floatData = loadHdr(d.imageFile, &size);
+
+ d.tex = m_r->newTexture(QRhiTexture::RGBA32F, size);
+ d.releasePool << d.tex;
+ d.tex->create();
+ QRhiTextureUploadDescription desc({ 0, 0, { floatData.constData(), quint32(floatData.size()) } });
+ d.initialUpdates->uploadTexture(d.tex, desc);
+
+ d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
+ QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
+ d.releasePool << d.sampler;
+ d.sampler->create();
+
+ d.srb = m_r->newShaderResourceBindings();
+ d.releasePool << d.srb;
+ d.srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
+ });
+ d.srb->create();
+
+ d.ps = m_r->newGraphicsPipeline();
+ d.releasePool << d.ps;
+ d.ps->setCullMode(QRhiGraphicsPipeline::Back);
+ const QRhiShaderStage stages[] = {
+ { QRhiShaderStage::Vertex, getShader(QLatin1String(":/hdrtexture.vert.qsb")) },
+ { QRhiShaderStage::Fragment, getShader(QLatin1String(":/hdrtexture.frag.qsb")) }
+ };
+ d.ps->setShaderStages(stages, stages + 2);
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 3 * sizeof(float) },
+ { 2 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
+ { 1, 1, QRhiVertexInputAttribute::Float2, 0 }
+ });
+ d.ps->setVertexInputLayout(inputLayout);
+ d.ps->setShaderResourceBindings(d.srb);
+ d.ps->setRenderPassDescriptor(m_rp);
+ d.ps->create();
+}
+
+void Window::customRelease()
+{
+ qDeleteAll(d.releasePool);
+ d.releasePool.clear();
+}
+
+void Window::customRender()
+{
+ if (d.tonemapOutMax == 0.0f) {
+ QRhiSwapChainHdrInfo info = m_sc->hdrInfo();
+ switch (info.limitsType) {
+ case QRhiSwapChainHdrInfo::LuminanceInNits:
+ d.tonemapOutMax = info.limits.luminanceInNits.maxLuminance / 80.0f;
+ break;
+ case QRhiSwapChainHdrInfo::ColorComponentValue:
+ // because on macOS it changes dynamically when starting up, so retry in next frame if it's still just 1.0
+ if (info.limits.colorComponentValue.maxColorComponentValue > 1.0f)
+ d.tonemapOutMax = info.limits.colorComponentValue.maxColorComponentValue;
+ break;
+ }
+ }
+
+ QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
+ QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
+ if (d.initialUpdates) {
+ u->merge(d.initialUpdates);
+ d.initialUpdates->release();
+ d.initialUpdates = nullptr;
+ }
+
+ QMatrix4x4 mvp = m_proj;
+ mvp.rotate(d.rotation.x(), 1, 0, 0);
+ mvp.rotate(d.rotation.y(), 0, 1, 0);
+ mvp.rotate(d.rotation.z(), 0, 0, 1);
+ u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
+
+ if (d.usingHDRWindow && d.tonemapHDR) {
+ u->updateDynamicBuffer(d.ubuf, 72, 4, &d.tonemapInMax);
+ u->updateDynamicBuffer(d.ubuf, 76, 4, &d.tonemapOutMax);
+ } else {
+ float zero[2] = {};
+ u->updateDynamicBuffer(d.ubuf, 72, 8, zero);
+ }
+
+ QColor clearColor = Qt::green; // sRGB
+ if (d.usingHDRWindow && d.adjustSDR) {
+ float sdrMultiplier = d.SDRWhiteLevelInNits / 80.0f; // scRGB 1.0 = 80 nits (and linear gamma)
+ clearColor = QColor::fromRgbF(clearColor.redF() * sdrMultiplier,
+ clearColor.greenF() * sdrMultiplier,
+ clearColor.blueF() * sdrMultiplier,
+ 1.0f);
+ }
+
+ const QSize outputSizeInPixels = m_sc->currentPixelSize();
+ cb->beginPass(m_sc->currentFrameRenderTarget(), clearColor, { 1.0f, 0 }, u);
+
+ cb->setGraphicsPipeline(d.ps);
+ cb->setViewport(QRhiViewport(0, 0, outputSizeInPixels.width(), outputSizeInPixels.height()));
+ cb->setShaderResources();
+ const QRhiCommandBuffer::VertexInput vbufBindings[] = {
+ { d.vbuf, 0 },
+ { d.vbuf, quint32(36 * 3 * sizeof(float)) }
+ };
+ cb->setVertexInput(0, 2, vbufBindings);
+ cb->draw(36);
+
+ m_imguiRenderer->render();
+
+ cb->endPass();
+}
+
+static void addTip(const char *s)
+{
+ ImGui::SameLine();
+ ImGui::TextDisabled("(?)");
+ if (ImGui::IsItemHovered()) {
+ ImGui::BeginTooltip();
+ ImGui::PushTextWrapPos(300);
+ ImGui::TextUnformatted(s);
+ ImGui::PopTextWrapPos();
+ ImGui::EndTooltip();
+ }
+}
+
+void Window::customGui()
+{
+ ImGui::SetNextWindowBgAlpha(1.0f);
+ ImGui::SetNextWindowPos(ImVec2(10, 420), ImGuiCond_FirstUseEver);
+ ImGui::SetNextWindowSize(ImVec2(800, 300), ImGuiCond_FirstUseEver);
+ ImGui::Begin("HDR test");
+
+ if (d.usingHDRWindow) {
+ if (swapchainFormat == QRhiSwapChain::HDRExtendedDisplayP3Linear) {
+ ImGui::Text("The window is now Extended Linear Display P3 + FP16 color buffer,\n"
+ "the ImGui UI and the green background are considered SDR content,\n"
+ "the cube is using a HDR texture.");
+ } else {
+ ImGui::Text("The window is now scRGB (Extended Linear sRGB) + FP16 color buffer,\n"
+ "the ImGui UI and the green background are considered SDR content,\n"
+ "the cube is using a HDR texture.");
+ }
+ ImGui::Checkbox("Adjust SDR content", &d.adjustSDR);
+ addTip("Multiplies fragment colors for non-HDR content with sdr_white_level / 80. "
+ "Not relevant with macOS (due to EDR being display-referred).");
+ if (d.adjustSDR) {
+ ImGui::SliderFloat("SDR white level (nits)", &d.SDRWhiteLevelInNits, 0.0f, 1000.0f);
+ imguiHDRMultiplier = d.SDRWhiteLevelInNits / 80.0f; // scRGB 1.0 = 80 nits (and linear gamma)
+ } else {
+ imguiHDRMultiplier = 1.0f; // 0 would mean linear to sRGB; don't want that with HDR
+ }
+ ImGui::Separator();
+ ImGui::Checkbox("Tonemap HDR content", &d.tonemapHDR);
+ addTip("Perform some most basic Reinhard tonemapping (changed to suit HDR) on the 3D content (the cube). "
+ "Display max luminance is set to the max color component value (macOS) or max luminance in nits / 80 (Windows) by default.");
+ if (d.tonemapHDR) {
+ ImGui::SliderFloat("Content max luminance\n(color component value)", &d.tonemapInMax, 0.0f, 20.0f);
+ ImGui::SliderFloat("Display max luminance\n(color component value)", &d.tonemapOutMax, 0.0f, 20.0f);
+ }
+ } else {
+ ImGui::Text("The window is standard dynamic range (no HDR, so non-linear sRGB effectively).\n"
+ "Here we just do linear -> sRGB for everything (UI, textured cube)\n"
+ "at the end of the pipeline, while the Qt::green background is already sRGB.");
+ }
+
+ ImGui::End();
+
+ ImGui::SetNextWindowPos(ImVec2(850, 560), ImGuiCond_FirstUseEver);
+ ImGui::SetNextWindowSize(ImVec2(420, 140), ImGuiCond_FirstUseEver);
+ ImGui::Begin("Misc");
+ float *rot = reinterpret_cast<float *>(&d.rotation);
+ ImGui::SliderFloat("Rotation X", &rot[0], 0.0f, 360.0f);
+ ImGui::SliderFloat("Rotation Y", &rot[1], 0.0f, 360.0f);
+ ImGui::SliderFloat("Rotation Z", &rot[2], 0.0f, 360.0f);
+ ImGui::End();
+
+ if (d.usingHDRWindow) {
+ ImGui::SetNextWindowPos(ImVec2(850, 10), ImGuiCond_FirstUseEver);
+ ImGui::SetNextWindowSize(ImVec2(420, 180), ImGuiCond_FirstUseEver);
+ ImGui::Begin("Actual platform info");
+ QRhiSwapChainHdrInfo info = m_sc->hdrInfo();
+ switch (info.limitsType) {
+ case QRhiSwapChainHdrInfo::LuminanceInNits:
+ ImGui::Text("Platform provides luminance in nits");
+ addTip("Windows/D3D: On laptops this will be screen brightness dependent. Increasing brightness implies the max luminance decreases. "
+ "It also seems to be affected by HDR Content Brightness in the Settings, if there is one (e.g. on laptops; not to be confused with SDR Content Brightess). "
+ "(note that the DXGI query does not seem to return changed values if there are runtime changes unless restarting the app).");
+ ImGui::Text("Min luminance: %.4f\nMax luminance: %.4f",
+ info.limits.luminanceInNits.minLuminance,
+ info.limits.luminanceInNits.maxLuminance);
+ break;
+ case QRhiSwapChainHdrInfo::ColorComponentValue:
+ ImGui::Text("Platform provides color component values");
+ addTip("macOS/Metal: On laptops this will be screen brightness dependent. Increasing brightness decreases the max color value. "
+ "Max brightness may bring it down to 1.0.");
+ ImGui::Text("maxColorComponentValue: %.4f\nmaxPotentialColorComponentValue: %.4f",
+ info.limits.colorComponentValue.maxColorComponentValue,
+ info.limits.colorComponentValue.maxPotentialColorComponentValue);
+ break;
+ }
+ ImGui::Separator();
+ switch (info.luminanceBehavior) {
+ case QRhiSwapChainHdrInfo::SceneReferred:
+ ImGui::Text("Luminance behavior is scene-referred");
+ break;
+ case QRhiSwapChainHdrInfo::DisplayReferred:
+ ImGui::Text("Luminance behavior is display-referred");
+ break;
+ }
+ addTip("Windows (DWM) HDR is scene-referred: 1.0 = 80 nits.\n\n"
+ "Apple EDR is display-referred: the value of 1.0 refers to whatever the system's current SDR white level is (100, 200, ... nits depending on the brightness).");
+ if (info.luminanceBehavior == QRhiSwapChainHdrInfo::SceneReferred) {
+ ImGui::Text("SDR white level: %.4f nits", info.sdrWhiteLevel);
+ addTip("On Windows this is queried from DISPLAYCONFIG_SDR_WHITE_LEVEL. "
+ "Affected by the slider in the Windows Settings (System/Display/HDR/[S|H]DR Content Brightness). "
+ "With max screen brightness (laptops) the value will likely be the same as the max luminance.");
+ }
+ ImGui::End();
+ }
+}
diff --git a/tests/manual/rhi/hdr/hdrtexture.frag b/tests/manual/rhi/hdr/hdrtexture.frag
new file mode 100644
index 0000000000..9d5e12005a
--- /dev/null
+++ b/tests/manual/rhi/hdr/hdrtexture.frag
@@ -0,0 +1,44 @@
+#version 440
+
+layout(location = 0) in vec2 v_texcoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ int flip;
+ int sdr;
+ float tonemapInMax;
+ float tonemapOutMax;
+} ubuf;
+
+layout(binding = 1) uniform sampler2D tex;
+
+vec3 linearToSRGB(vec3 color)
+{
+ vec3 S1 = sqrt(color);
+ vec3 S2 = sqrt(S1);
+ vec3 S3 = sqrt(S2);
+ return 0.585122381 * S1 + 0.783140355 * S2 - 0.368262736 * S3;
+}
+
+// https://learn.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range
+vec3 simpleReinhardTonemapper(vec3 c, float inMax, float outMax)
+{
+ c /= vec3(inMax);
+ c.r = c.r / (1 + c.r);
+ c.g = c.g / (1 + c.g);
+ c.b = c.b / (1 + c.b);
+ c *= vec3(outMax);
+ return c;
+}
+
+void main()
+{
+ vec4 c = texture(tex, v_texcoord);
+ if (ubuf.tonemapInMax != 0)
+ c.rgb = simpleReinhardTonemapper(c.rgb, ubuf.tonemapInMax, ubuf.tonemapOutMax);
+ else if (ubuf.sdr != 0)
+ c.rgb = linearToSRGB(c.rgb);
+ fragColor = vec4(c.rgb * c.a, c.a);
+}
diff --git a/tests/manual/rhi/hdr/hdrtexture.frag.qsb b/tests/manual/rhi/hdr/hdrtexture.frag.qsb
new file mode 100644
index 0000000000..441b660f0b
--- /dev/null
+++ b/tests/manual/rhi/hdr/hdrtexture.frag.qsb
Binary files differ
diff --git a/tests/manual/rhi/hdr/hdrtexture.vert b/tests/manual/rhi/hdr/hdrtexture.vert
new file mode 100644
index 0000000000..336d5f8cfb
--- /dev/null
+++ b/tests/manual/rhi/hdr/hdrtexture.vert
@@ -0,0 +1,22 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoord;
+
+layout(location = 0) out vec2 v_texcoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ int flip;
+ int sdr;
+ float tonemapInMaxNits;
+ float tonemapOutMaxNits;
+};
+
+void main()
+{
+ v_texcoord = vec2(texcoord.x, texcoord.y);
+ if (flip != 0)
+ v_texcoord.y = 1.0 - v_texcoord.y;
+ gl_Position = mvp * position;
+}
diff --git a/tests/manual/rhi/hdr/hdrtexture.vert.qsb b/tests/manual/rhi/hdr/hdrtexture.vert.qsb
new file mode 100644
index 0000000000..bcfb0da273
--- /dev/null
+++ b/tests/manual/rhi/hdr/hdrtexture.vert.qsb
Binary files differ
diff --git a/tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.cpp b/tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.cpp
index 9cbcebb380..2fb7405fa7 100644
--- a/tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.cpp
+++ b/tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.cpp
@@ -1,9 +1,9 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "hellowindow.h"
#include <QFile>
-#include <QtGui/private/qshader_p.h>
+#include <rhi/qshader.h>
static float vertexData[] = {
// Y up (note clipSpaceCorrMatrix in m_proj), CCW
diff --git a/tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.h b/tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.h
index 41e433d8fa..f3a91aeb09 100644
--- a/tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.h
+++ b/tests/manual/rhi/hellominimalcrossgfxtriangle/hellowindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef HELLOWINDOW_H
#define HELLOWINDOW_H
diff --git a/tests/manual/rhi/hellominimalcrossgfxtriangle/main.cpp b/tests/manual/rhi/hellominimalcrossgfxtriangle/main.cpp
index 949ac585cd..89926c19cc 100644
--- a/tests/manual/rhi/hellominimalcrossgfxtriangle/main.cpp
+++ b/tests/manual/rhi/hellominimalcrossgfxtriangle/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This is a compact, minimal demo of deciding the backend at runtime while
// using the exact same shaders and rendering code without any branching
@@ -37,7 +37,7 @@ int main(int argc, char **argv)
QRhi::Implementation graphicsApi;
#if defined(Q_OS_WIN)
graphicsApi = QRhi::D3D11;
-#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#elif QT_CONFIG(metal)
graphicsApi = QRhi::Metal;
#elif QT_CONFIG(vulkan)
graphicsApi = QRhi::Vulkan;
diff --git a/tests/manual/rhi/hellominimalcrossgfxtriangle/window.cpp b/tests/manual/rhi/hellominimalcrossgfxtriangle/window.cpp
index 01f548d3c9..edbd048c63 100644
--- a/tests/manual/rhi/hellominimalcrossgfxtriangle/window.cpp
+++ b/tests/manual/rhi/hellominimalcrossgfxtriangle/window.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "window.h"
#include <QPlatformSurfaceEvent>
@@ -82,7 +82,7 @@ bool Window::event(QEvent *e)
void Window::init()
{
- QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers | QRhi::EnableProfiling;
+ QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers;
if (m_graphicsApi == QRhi::Null) {
QRhiNullInitParams params;
@@ -120,7 +120,7 @@ void Window::init()
}
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
if (m_graphicsApi == QRhi::Metal) {
QRhiMetalInitParams params;
m_rhi.reset(QRhi::create(QRhi::Metal, &params, rhiFlags));
diff --git a/tests/manual/rhi/hellominimalcrossgfxtriangle/window.h b/tests/manual/rhi/hellominimalcrossgfxtriangle/window.h
index b3223e8042..eb43809096 100644
--- a/tests/manual/rhi/hellominimalcrossgfxtriangle/window.h
+++ b/tests/manual/rhi/hellominimalcrossgfxtriangle/window.h
@@ -1,26 +1,12 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WINDOW_H
#define WINDOW_H
#include <QWindow>
-
-#include <QtGui/private/qrhinull_p.h>
-#if QT_CONFIG(opengl)
-#include <QtGui/private/qrhigles2_p.h>
#include <QOffscreenSurface>
-#endif
-#if QT_CONFIG(vulkan)
-#include <QtGui/private/qrhivulkan_p.h>
-#endif
-#ifdef Q_OS_WIN
-#include <QtGui/private/qrhid3d11_p.h>
-#include <QtGui/private/qrhid3d12_p.h>
-#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
-#include <QtGui/private/qrhimetal_p.h>
-#endif
+#include <rhi/qrhi.h>
class Window : public QWindow
{
diff --git a/tests/manual/rhi/imguirenderer/CMakeLists.txt b/tests/manual/rhi/imguirenderer/CMakeLists.txt
index 1b5bbaccb5..8c04918d06 100644
--- a/tests/manual/rhi/imguirenderer/CMakeLists.txt
+++ b/tests/manual/rhi/imguirenderer/CMakeLists.txt
@@ -1,8 +1,10 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_manual_test(imguirenderer
GUI
SOURCES
imguirenderer.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
diff --git a/tests/manual/rhi/imguirenderer/imguirenderer.cpp b/tests/manual/rhi/imguirenderer/imguirenderer.cpp
index 8c3438a724..ef675c2e5b 100644
--- a/tests/manual/rhi/imguirenderer/imguirenderer.cpp
+++ b/tests/manual/rhi/imguirenderer/imguirenderer.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#define EXAMPLEFW_IMGUI
#include "../shared/examplefw.h"
diff --git a/tests/manual/rhi/instancing/buildshaders.bat b/tests/manual/rhi/instancing/buildshaders.bat
index 9053a1c54a..af18c09602 100644
--- a/tests/manual/rhi/instancing/buildshaders.bat
+++ b/tests/manual/rhi/instancing/buildshaders.bat
@@ -1,2 +1,4 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl "330,300 es" --hlsl 50 --msl 12 inst.vert -o inst.vert.qsb
qsb --glsl "330,300 es" --hlsl 50 --msl 12 inst.frag -o inst.frag.qsb
diff --git a/tests/manual/rhi/instancing/instancing.cpp b/tests/manual/rhi/instancing/instancing.cpp
index c6f89b593c..320cc2d6c7 100644
--- a/tests/manual/rhi/instancing/instancing.cpp
+++ b/tests/manual/rhi/instancing/instancing.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
#include "../shared/cube.h"
diff --git a/tests/manual/rhi/mrt/buildshaders.bat b/tests/manual/rhi/mrt/buildshaders.bat
index 75741448b1..b23e01d00e 100644
--- a/tests/manual/rhi/mrt/buildshaders.bat
+++ b/tests/manual/rhi/mrt/buildshaders.bat
@@ -1,2 +1,4 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c mrt.vert -o mrt.vert.qsb
qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c mrt.frag -o mrt.frag.qsb
diff --git a/tests/manual/rhi/mrt/mrt.cpp b/tests/manual/rhi/mrt/mrt.cpp
index d03b3db1cc..a548ed1592 100644
--- a/tests/manual/rhi/mrt/mrt.cpp
+++ b/tests/manual/rhi/mrt/mrt.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
diff --git a/tests/manual/rhi/msaarenderbuffer/msaarenderbuffer.cpp b/tests/manual/rhi/msaarenderbuffer/msaarenderbuffer.cpp
index 11274466f2..539a1b42e1 100644
--- a/tests/manual/rhi/msaarenderbuffer/msaarenderbuffer.cpp
+++ b/tests/manual/rhi/msaarenderbuffer/msaarenderbuffer.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
diff --git a/tests/manual/rhi/msaatexture/msaatexture.cpp b/tests/manual/rhi/msaatexture/msaatexture.cpp
index ef7044b6d5..9edc8f5714 100644
--- a/tests/manual/rhi/msaatexture/msaatexture.cpp
+++ b/tests/manual/rhi/msaatexture/msaatexture.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
diff --git a/tests/manual/rhi/msaatextureresolve/CMakeLists.txt b/tests/manual/rhi/msaatextureresolve/CMakeLists.txt
new file mode 100644
index 0000000000..fb14b119de
--- /dev/null
+++ b/tests/manual/rhi/msaatextureresolve/CMakeLists.txt
@@ -0,0 +1,41 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(msaatextureresolve LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_manual_test(msaatextureresolve
+ GUI
+ SOURCES
+ msaatextureresolve.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::GuiPrivate
+)
+
+# Resources:
+set_source_files_properties("../shared/color.frag.qsb"
+ PROPERTIES QT_RESOURCE_ALIAS "color.frag.qsb"
+)
+set_source_files_properties("../shared/color.vert.qsb"
+ PROPERTIES QT_RESOURCE_ALIAS "color.vert.qsb"
+)
+set_source_files_properties("../shared/texture.frag.qsb"
+ PROPERTIES QT_RESOURCE_ALIAS "texture.frag.qsb"
+)
+set_source_files_properties("../shared/texture.vert.qsb"
+ PROPERTIES QT_RESOURCE_ALIAS "texture.vert.qsb"
+)
+
+qt_internal_add_resource(msaatextureresolve "msaatextureresolve"
+ PREFIX
+ "/"
+ FILES
+ "../shared/color.frag.qsb"
+ "../shared/color.vert.qsb"
+ "../shared/texture.frag.qsb"
+ "../shared/texture.vert.qsb"
+)
diff --git a/tests/manual/rhi/msaatextureresolve/msaatextureresolve.cpp b/tests/manual/rhi/msaatextureresolve/msaatextureresolve.cpp
new file mode 100644
index 0000000000..128cecf707
--- /dev/null
+++ b/tests/manual/rhi/msaatextureresolve/msaatextureresolve.cpp
@@ -0,0 +1,235 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "../shared/examplefw.h"
+
+// Uses a multisample texture both for color and depth-stencil, renders into
+// those, and then resolves into non-multisample textures. Also the
+// depth-stencil, in order to exercise that rarely used path. If that is
+// functional, will not be visible on-screen. Frame captures with tools such as
+// RenderDoc can be used to verify that there is indeed a non-multisample depth
+// texture generated.
+
+static float vertexData[] =
+{ // Y up, CCW
+ -0.5f, 0.5f, 0.0f, 0.0f,
+ -0.5f, -0.5f, 0.0f, 1.0f,
+ 0.5f, -0.5f, 1.0f, 1.0f,
+ 0.5f, 0.5f, 1.0f, 0.0f
+};
+
+static quint16 indexData[] =
+{
+ 0, 1, 2, 0, 2, 3
+};
+
+static float triangleData[] =
+{ // Y up, CCW
+ 0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
+ -0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
+ 0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
+};
+
+struct {
+ QList<QRhiResource *> releasePool;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ibuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiTexture *msaaColorTexture = nullptr;
+ QRhiTexture *msaaDepthTexture = nullptr;
+ QRhiTextureRenderTarget *rt = nullptr;
+ QRhiRenderPassDescriptor *rtRp = nullptr;
+ QRhiTexture *tex = nullptr;
+ QRhiTexture *depthTex = nullptr;
+ QRhiSampler *sampler = nullptr;
+ QRhiBuffer *triUbuf = nullptr;
+ QRhiShaderResourceBindings *triSrb = nullptr;
+ QRhiGraphicsPipeline *triPs = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ QRhiGraphicsPipeline *ps = nullptr;
+ QRhiResourceUpdateBatch *initialUpdates = nullptr;
+ QMatrix4x4 triBaseMvp;
+ float triRot = 0;
+ QMatrix4x4 winProj;
+} d;
+
+void Window::customInit()
+{
+ if (!m_r->isFeatureSupported(QRhi::MultisampleTexture))
+ qFatal("Multisample textures not supported by this backend");
+
+ // Skip the check for ResolveDepthStencil, and let it run regardless. When
+ // not supported, it won't be functional, but should be handled somewhat
+ // gracefully.
+
+ d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData) + sizeof(triangleData));
+ d.vbuf->create();
+ d.releasePool << d.vbuf;
+
+ d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indexData));
+ d.ibuf->create();
+ d.releasePool << d.ibuf;
+
+ d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
+ d.ubuf->create();
+ d.releasePool << d.ubuf;
+
+ d.msaaColorTexture = m_r->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 4, QRhiTexture::RenderTarget); // 4x MSAA
+ d.msaaColorTexture->create();
+ d.releasePool << d.msaaColorTexture;
+
+ d.msaaDepthTexture = m_r->newTexture(QRhiTexture::D24S8, QSize(512, 512), 4, QRhiTexture::RenderTarget); // 4x MSAA
+ d.msaaDepthTexture->create();
+ d.releasePool << d.msaaDepthTexture;
+
+ // the non-msaa texture that will be the destination in the resolve
+ d.tex = m_r->newTexture(QRhiTexture::RGBA8, d.msaaColorTexture->pixelSize(), 1, QRhiTexture::RenderTarget);
+ d.releasePool << d.tex;
+ d.tex->create();
+
+ d.depthTex = m_r->newTexture(QRhiTexture::D24S8, d.msaaDepthTexture->pixelSize(), 1, QRhiTexture::RenderTarget);
+ d.releasePool << d.depthTex;
+ d.depthTex->create();
+
+ QRhiTextureRenderTargetDescription rtDesc;
+ QRhiColorAttachment rtAtt(d.msaaColorTexture);
+ rtAtt.setResolveTexture(d.tex);
+ rtDesc.setColorAttachments({ rtAtt });
+ rtDesc.setDepthTexture(d.msaaDepthTexture);
+ rtDesc.setDepthResolveTexture(d.depthTex);
+
+ d.rt = m_r->newTextureRenderTarget(rtDesc);
+ d.releasePool << d.rt;
+ d.rtRp = d.rt->newCompatibleRenderPassDescriptor();
+ d.releasePool << d.rtRp;
+ d.rt->setRenderPassDescriptor(d.rtRp);
+ d.rt->create();
+
+ d.triUbuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
+ d.releasePool << d.triUbuf;
+ d.triUbuf->create();
+
+ d.triSrb = m_r->newShaderResourceBindings();
+ d.releasePool << d.triSrb;
+ d.triSrb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.triUbuf)
+ });
+ d.triSrb->create();
+
+ d.triPs = m_r->newGraphicsPipeline();
+ d.releasePool << d.triPs;
+ d.triPs->setDepthTest(true);
+ d.triPs->setDepthWrite(true);
+ d.triPs->setSampleCount(4); // must match the render target
+ d.triPs->setShaderStages({
+ { QRhiShaderStage::Vertex, getShader(QLatin1String(":/color.vert.qsb")) },
+ { QRhiShaderStage::Fragment, getShader(QLatin1String(":/color.frag.qsb")) }
+ });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 5 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float3, quint32(2 * sizeof(float)) }
+ });
+ d.triPs->setVertexInputLayout(inputLayout);
+ d.triPs->setShaderResourceBindings(d.triSrb);
+ d.triPs->setRenderPassDescriptor(d.rtRp);
+ d.triPs->create();
+
+ d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
+ QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
+ d.releasePool << d.sampler;
+ d.sampler->create();
+
+ d.srb = m_r->newShaderResourceBindings();
+ d.releasePool << d.srb;
+ d.srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
+ });
+ d.srb->create();
+
+ d.ps = m_r->newGraphicsPipeline();
+ d.releasePool << d.ps;
+ d.ps->setShaderStages({
+ { QRhiShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
+ { QRhiShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
+ });
+ inputLayout.setBindings({
+ { 4 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float2, quint32(2 * sizeof(float)) }
+ });
+ d.ps->setVertexInputLayout(inputLayout);
+ d.ps->setShaderResourceBindings(d.srb);
+ d.ps->setRenderPassDescriptor(m_rp);
+ d.ps->create();
+
+ d.initialUpdates = m_r->nextResourceUpdateBatch();
+ d.initialUpdates->uploadStaticBuffer(d.vbuf, 0, sizeof(vertexData), vertexData);
+ d.initialUpdates->uploadStaticBuffer(d.vbuf, sizeof(vertexData), sizeof(triangleData), triangleData);
+ d.initialUpdates->uploadStaticBuffer(d.ibuf, indexData);
+
+ d.triBaseMvp = m_r->clipSpaceCorrMatrix();
+ d.triBaseMvp.perspective(45.0f, d.msaaColorTexture->pixelSize().width() / float(d.msaaColorTexture->pixelSize().height()), 0.01f, 1000.0f);
+ d.triBaseMvp.translate(0, 0, -2);
+ float opacity = 1.0f;
+ d.initialUpdates->updateDynamicBuffer(d.triUbuf, 64, 4, &opacity);
+
+ qint32 flip = m_r->isYUpInFramebuffer() ? 1 : 0;
+ d.initialUpdates->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
+}
+
+void Window::customRelease()
+{
+ qDeleteAll(d.releasePool);
+ d.releasePool.clear();
+}
+
+void Window::customRender()
+{
+ QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
+ QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
+ if (d.initialUpdates) {
+ u->merge(d.initialUpdates);
+ d.initialUpdates->release();
+ d.initialUpdates = nullptr;
+ }
+
+ QMatrix4x4 triMvp = d.triBaseMvp;
+ triMvp.rotate(d.triRot, 0, 1, 0);
+ d.triRot += 1;
+ u->updateDynamicBuffer(d.triUbuf, 0, 64, triMvp.constData());
+
+ if (d.winProj != m_proj) {
+ d.winProj = m_proj;
+ QMatrix4x4 mvp = m_proj;
+ mvp.scale(2.5f);
+ u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
+ }
+
+ // offscreen (triangle, msaa)
+ cb->beginPass(d.rt, QColor::fromRgbF(0.5f, 0.2f, 0.0f, 1.0f), { 1.0f, 0 }, u);
+ cb->setGraphicsPipeline(d.triPs);
+ cb->setViewport({ 0, 0, float(d.msaaColorTexture->pixelSize().width()), float(d.msaaColorTexture->pixelSize().height()) });
+ cb->setShaderResources();
+ QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, quint32(sizeof(vertexData)));
+ cb->setVertexInput(0, 1, &vbufBinding);
+ cb->draw(3);
+ cb->endPass();
+
+ // onscreen (quad)
+ const QSize outputSizeInPixels = m_sc->currentPixelSize();
+ cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 });
+ cb->setGraphicsPipeline(d.ps);
+ cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
+ cb->setShaderResources();
+ vbufBinding.second = 0;
+ cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
+ cb->drawIndexed(6);
+ cb->endPass();
+}
diff --git a/tests/manual/rhi/multiview/CMakeLists.txt b/tests/manual/rhi/multiview/CMakeLists.txt
new file mode 100644
index 0000000000..1e2efa02cb
--- /dev/null
+++ b/tests/manual/rhi/multiview/CMakeLists.txt
@@ -0,0 +1,21 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_manual_test(multiview
+ GUI
+ SOURCES
+ multiview.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::GuiPrivate
+)
+
+qt_internal_add_resource(multiview "multiview"
+ PREFIX
+ "/"
+ FILES
+ "multiview.vert.qsb"
+ "multiview.frag.qsb"
+ "texture.vert.qsb"
+ "texture.frag.qsb"
+)
diff --git a/tests/manual/rhi/multiview/buildshaders.bat b/tests/manual/rhi/multiview/buildshaders.bat
new file mode 100644
index 0000000000..d9d2825218
--- /dev/null
+++ b/tests/manual/rhi/multiview/buildshaders.bat
@@ -0,0 +1,6 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+qsb --view-count 2 --glsl "300 es,330" --hlsl 61 -c --msl 12 multiview.vert -o multiview.vert.qsb
+qsb --glsl "300 es,330" --hlsl 61 -c --msl 12 multiview.frag -o multiview.frag.qsb
+qsb --glsl "300 es,330" --hlsl 61 -c --msl 12 texture.vert -o texture.vert.qsb
+qsb --glsl "300 es,330" --hlsl 61 -c --msl 12 texture.frag -o texture.frag.qsb
diff --git a/tests/manual/rhi/multiview/multiview.cpp b/tests/manual/rhi/multiview/multiview.cpp
new file mode 100644
index 0000000000..a89821eb8e
--- /dev/null
+++ b/tests/manual/rhi/multiview/multiview.cpp
@@ -0,0 +1,305 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "../shared/examplefw.h"
+
+// Multiview rendering. Renders the same geometry (a triangle) with two
+// different transforms into two layers of a texture array object in a *single*
+// draw call. (NB under the hood it is at the hardware/driver's discretion what
+// happens; it may very well map to some simple looping and still drawing
+// twice, whereas with modern hardware it can be expected to be implemented
+// more efficiently, but that's hidden from us)
+
+// Toggle this to exercise 4x MSAA for the texture array that is the render
+// target of the multiview render pass. The elements written by the multiview
+// render pass get resolved to a non-multisample texture array at the end of
+// the pass.
+static bool MSAA = false;
+
+static float quadVertexData[] =
+{ // Y up, CCW
+ -0.5f, 0.5f, 0.0f, 0.0f,
+ -0.5f, -0.5f, 0.0f, 1.0f,
+ 0.5f, -0.5f, 1.0f, 1.0f,
+ 0.5f, 0.5f, 1.0f, 0.0f
+};
+
+static quint16 quadIndexData[] =
+{
+ 0, 1, 2, 0, 2, 3
+};
+
+static float triangleData[] =
+{ // Y up, CCW
+ 0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
+ -0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
+ 0.5f, -0.5f, 0.0f, 0.0f, 1.0f
+};
+
+static const int INSTANCE_COUNT = 5;
+
+static float instanceData[INSTANCE_COUNT * 3] =
+{
+ 0.4f, 0.0f, 0.0f,
+ 0.2f, 0.0f, 0.1f,
+ 0.0f, 0.0f, 0.2f,
+ -0.2f, 0.0f, 0.3f,
+ -0.4f, 0.0f, 0.4f
+};
+
+struct {
+ QList<QRhiResource *> releasePool;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *instanceBuf = nullptr;
+ QRhiBuffer *ibuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiTextureRenderTarget *rt = nullptr;
+ QRhiRenderPassDescriptor *rtRp = nullptr;
+ QRhiSampler *sampler = nullptr;
+ QRhiGraphicsPipeline *ps = nullptr;
+ QRhiResourceUpdateBatch *initialUpdates = nullptr;
+ QMatrix4x4 winProj;
+ QRhiTexture *tex = nullptr;
+ QRhiTexture *resolveTex = nullptr; // only if MSAA is true
+ QRhiTexture *ds = nullptr;
+ QRhiShaderResourceBindings *srb[2] = {};
+
+ QRhiBuffer *triUbuf = nullptr;
+ QRhiShaderResourceBindings *triSrb = nullptr;
+ QRhiGraphicsPipeline *triPs = nullptr;
+ QMatrix4x4 triBaseMvp;
+} d;
+
+void Window::customInit()
+{
+ if (!m_r->isFeatureSupported(QRhi::MultiView))
+ qFatal("Multiview is not supported");
+
+ int sampleCount = 1;
+ if (MSAA) {
+ qDebug("Using 4x MSAA for the multiview render pass");
+ sampleCount = 4;
+ }
+
+ // texture array with 2 elements, e.g. 0 is left eye, 1 is right
+ d.tex = m_r->newTextureArray(QRhiTexture::RGBA8, 2, QSize(512, 512), sampleCount, QRhiTexture::RenderTarget);
+ d.releasePool << d.tex;
+ d.tex->create();
+
+ if (MSAA) {
+ d.resolveTex = m_r->newTextureArray(QRhiTexture::RGBA8, 2, QSize(512, 512), 1, QRhiTexture::RenderTarget);
+ d.releasePool << d.resolveTex;
+ d.resolveTex->create();
+ }
+
+ // Have a depth-stencil buffer, just to exercise it, the triangles will be
+ // rendered with depth test/write enabled. The catch here is that we must
+ // use a texture array for depth/stencil as well, so QRhiRenderBuffer is
+ // not an option anymore.
+ d.ds = m_r->newTextureArray(QRhiTexture::D24S8, 2, QSize(512, 512), sampleCount, QRhiTexture::RenderTarget);
+ d.releasePool << d.ds;
+ d.ds->create();
+
+ // set up the multiview render target
+ QRhiColorAttachment multiViewAtt(d.tex);
+ // using array elements 0 and 1
+ multiViewAtt.setLayer(0);
+ multiViewAtt.setMultiViewCount(2); // the view count must be set both on the render target and the pipeline
+
+ // On-screen we work with a non-MSAA texture array, so the fragment shader
+ // does not need to deal with sampler2DMSArray, but can use sampler2DArray
+ // regardless of using multisampling or not. This means using an extra
+ // non-MSAA 2D texture array into which both array elements get resolved at
+ // the end of the multiview render pass.
+ QRhiTexture *textureForOnscreenView = d.tex;
+ if (MSAA) {
+ multiViewAtt.setResolveTexture(d.resolveTex);
+ textureForOnscreenView = d.resolveTex;
+ }
+
+ QRhiTextureRenderTargetDescription rtDesc(multiViewAtt);
+ rtDesc.setDepthTexture(d.ds);
+
+ d.rt = m_r->newTextureRenderTarget(rtDesc);
+ d.releasePool << d.rt;
+ d.rtRp = d.rt->newCompatibleRenderPassDescriptor();
+ d.releasePool << d.rtRp;
+ d.rt->setRenderPassDescriptor(d.rtRp);
+ d.rt->create();
+
+ // vertex buffer used by both passes
+ d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(quadVertexData) + sizeof(triangleData));
+ d.vbuf->create();
+ d.releasePool << d.vbuf;
+
+ // data for the instanced translation attribute
+ d.instanceBuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(instanceData));
+ d.instanceBuf->create();
+ d.releasePool << d.instanceBuf;
+
+ // resources for the on-screen visualizer
+ d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(quadIndexData));
+ d.ibuf->create();
+ d.releasePool << d.ibuf;
+
+ const int oneRoundedUniformBlockSize = m_r->ubufAligned(72);
+ d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, oneRoundedUniformBlockSize * 2);
+ d.ubuf->create();
+ d.releasePool << d.ubuf;
+
+ d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
+ QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
+ d.releasePool << d.sampler;
+ d.sampler->create();
+
+ // two srbs, just for the quad positioning on-screen
+ for (int i = 0; i < 2; ++i) {
+ QRhiShaderResourceBindings *srb = m_r->newShaderResourceBindings();
+ d.releasePool << srb;
+ srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
+ d.ubuf, i * oneRoundedUniformBlockSize, 72),
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage,
+ textureForOnscreenView, d.sampler)
+ });
+ srb->create();
+ d.srb[i] = srb;
+ }
+
+ d.ps = m_r->newGraphicsPipeline();
+ d.releasePool << d.ps;
+ d.ps->setShaderStages({
+ { QRhiShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
+ { QRhiShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
+ });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 4 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float2, quint32(2 * sizeof(float)) }
+ });
+ d.ps->setVertexInputLayout(inputLayout);
+ d.ps->setShaderResourceBindings(d.srb[0]); // all of them are layout-compatible
+ d.ps->setRenderPassDescriptor(m_rp);
+ d.ps->create();
+
+ d.initialUpdates = m_r->nextResourceUpdateBatch();
+ d.initialUpdates->uploadStaticBuffer(d.vbuf, 0, sizeof(quadVertexData), quadVertexData);
+ d.initialUpdates->uploadStaticBuffer(d.vbuf, sizeof(quadVertexData), sizeof(triangleData), triangleData);
+ d.initialUpdates->uploadStaticBuffer(d.instanceBuf, instanceData);
+ d.initialUpdates->uploadStaticBuffer(d.ibuf, quadIndexData);
+
+ qint32 flip = m_r->isYUpInFramebuffer() ? 1 : 0;
+ for (int i = 0; i < 2; ++i) {
+ d.initialUpdates->updateDynamicBuffer(d.ubuf, i * oneRoundedUniformBlockSize + 64, 4, &flip);
+ float layer = i;
+ d.initialUpdates->updateDynamicBuffer(d.ubuf, i * oneRoundedUniformBlockSize + 68, 4, &layer);
+ }
+
+ // create resources for the multiview render pass
+ d.triUbuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 128); // mat4 mvp[2]
+ d.releasePool << d.triUbuf;
+ d.triUbuf->create();
+
+ d.triSrb = m_r->newShaderResourceBindings();
+ d.releasePool << d.triSrb;
+ d.triSrb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.triUbuf)
+ });
+ d.triSrb->create();
+
+ d.triPs = m_r->newGraphicsPipeline();
+ d.releasePool << d.triPs;
+ d.triPs->setShaderStages({
+ { QRhiShaderStage::Vertex, getShader(QLatin1String(":/multiview.vert.qsb")) },
+ { QRhiShaderStage::Fragment, getShader(QLatin1String(":/multiview.frag.qsb")) }
+ });
+ d.triPs->setMultiViewCount(2); // the view count must be set both on the render target and the pipeline
+ inputLayout.setBindings({
+ { 5 * sizeof(float) },
+ { 3 * sizeof(float), QRhiVertexInputBinding::PerInstance }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float3, quint32(2 * sizeof(float)) },
+ { 1, 2, QRhiVertexInputAttribute::Float3, 0 }
+ });
+ d.triPs->setDepthTest(true);
+ d.triPs->setDepthWrite(true);
+ d.triPs->setSampleCount(sampleCount);
+ d.triPs->setVertexInputLayout(inputLayout);
+ d.triPs->setShaderResourceBindings(d.triSrb);
+ d.triPs->setRenderPassDescriptor(d.rtRp);
+ d.triPs->create();
+
+ d.triBaseMvp = m_r->clipSpaceCorrMatrix();
+ d.triBaseMvp.perspective(45.0f, d.rt->pixelSize().width() / float(d.rt->pixelSize().height()), 0.01f, 1000.0f);
+ d.triBaseMvp.translate(0, 0, -2);
+}
+
+void Window::customRelease()
+{
+ qDeleteAll(d.releasePool);
+ d.releasePool.clear();
+}
+
+void Window::customRender()
+{
+ QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
+ QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
+ if (d.initialUpdates) {
+ u->merge(d.initialUpdates);
+ d.initialUpdates->release();
+ d.initialUpdates = nullptr;
+ }
+
+ QMatrix4x4 triMvp = d.triBaseMvp;
+ // let's say this is the left eye, make the triangle point left for now
+ triMvp.rotate(90, 0, 0, 1);
+ u->updateDynamicBuffer(d.triUbuf, 0, 64, triMvp.constData());
+ triMvp = d.triBaseMvp;
+ // right for the right eye
+ triMvp.rotate(270, 0, 0, 1);
+ u->updateDynamicBuffer(d.triUbuf, 64, 64, triMvp.constData());
+
+ cb->beginPass(d.rt, QColor::fromRgbF(0.5f, 0.2f, 0.0f, 1.0f), { 1.0f, 0 }, u);
+ cb->setGraphicsPipeline(d.triPs);
+ cb->setViewport({ 0, 0, float(d.rt->pixelSize().width()), float(d.rt->pixelSize().height()) });
+ cb->setShaderResources();
+ const QRhiCommandBuffer::VertexInput multiViewPassVbufBindings[] = {
+ { d.vbuf, quint32(sizeof(quadVertexData)) },
+ { d.instanceBuf, 0 }
+ };
+ cb->setVertexInput(0, 2, multiViewPassVbufBindings);
+ cb->draw(3, INSTANCE_COUNT);
+ cb->endPass();
+
+ // "blit" the two texture layers on-screen just to visualize the contents
+ u = m_r->nextResourceUpdateBatch();
+ if (d.winProj != m_proj) {
+ d.winProj = m_proj;
+ const int oneRoundedUniformBlockSize = m_r->ubufAligned(72);
+ for (int i = 0; i < 2; ++i) {
+ QMatrix4x4 mvp = m_proj;
+ mvp.translate(0, 0, 1);
+ if (i == 0)
+ mvp.translate(-1.0f, 0, 0);
+ else
+ mvp.translate(1.0f, 0, 0);
+ u->updateDynamicBuffer(d.ubuf, i * oneRoundedUniformBlockSize, 64, mvp.constData());
+ }
+ }
+ const QSize outputSizeInPixels = m_sc->currentPixelSize();
+ cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u);
+ cb->setGraphicsPipeline(d.ps);
+ cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
+ const QRhiCommandBuffer::VertexInput quadPassVBufBindings[] = { { d.vbuf, 0 } };
+ cb->setVertexInput(0, 1, quadPassVBufBindings, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
+ for (int i = 0; i < 2; ++i) {
+ cb->setShaderResources(d.srb[i]);
+ cb->drawIndexed(6);
+ }
+ cb->endPass();
+}
diff --git a/tests/manual/rhi/multiview/multiview.frag b/tests/manual/rhi/multiview/multiview.frag
new file mode 100644
index 0000000000..afcbff3504
--- /dev/null
+++ b/tests/manual/rhi/multiview/multiview.frag
@@ -0,0 +1,11 @@
+#version 440
+
+layout(location = 0) in vec3 v_color;
+layout(location = 1) in flat uint v_viewIndex;
+
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ fragColor = vec4(v_color + v_viewIndex * 0.5, 1.0);
+}
diff --git a/tests/manual/rhi/multiview/multiview.frag.qsb b/tests/manual/rhi/multiview/multiview.frag.qsb
new file mode 100644
index 0000000000..c02a3cd4b9
--- /dev/null
+++ b/tests/manual/rhi/multiview/multiview.frag.qsb
Binary files differ
diff --git a/tests/manual/rhi/multiview/multiview.pro b/tests/manual/rhi/multiview/multiview.pro
new file mode 100644
index 0000000000..a9606f8898
--- /dev/null
+++ b/tests/manual/rhi/multiview/multiview.pro
@@ -0,0 +1,8 @@
+TEMPLATE = app
+
+QT += gui-private
+
+SOURCES = \
+ multiview.cpp
+
+RESOURCES = multiview.qrc
diff --git a/tests/manual/rhi/multiview/multiview.qrc b/tests/manual/rhi/multiview/multiview.qrc
new file mode 100644
index 0000000000..e931dd8c11
--- /dev/null
+++ b/tests/manual/rhi/multiview/multiview.qrc
@@ -0,0 +1,8 @@
+<!DOCTYPE RCC><RCC version="1.0">
+ <qresource>
+ <file>multiview.vert.qsb</file>
+ <file>multiview.frag.qsb</file>
+ <file>texture.vert.qsb</file>
+ <file>texture.frag.qsb</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/rhi/multiview/multiview.vert b/tests/manual/rhi/multiview/multiview.vert
new file mode 100644
index 0000000000..3dd17578d2
--- /dev/null
+++ b/tests/manual/rhi/multiview/multiview.vert
@@ -0,0 +1,21 @@
+#version 440
+#extension GL_EXT_multiview : require
+
+layout(location = 0) in vec4 pos;
+layout(location = 1) in vec3 color;
+layout(location = 2) in vec3 translation;
+
+layout(location = 0) out vec3 v_color;
+layout(location = 1) out flat uint v_viewIndex;
+
+layout(std140, binding = 0) uniform buf
+{
+ mat4 mvp[2];
+};
+
+void main()
+{
+ v_color = color;
+ gl_Position = mvp[gl_ViewIndex] * (pos + vec4(translation, 0.0));
+ v_viewIndex = gl_ViewIndex;
+}
diff --git a/tests/manual/rhi/multiview/multiview.vert.qsb b/tests/manual/rhi/multiview/multiview.vert.qsb
new file mode 100644
index 0000000000..4903c54103
--- /dev/null
+++ b/tests/manual/rhi/multiview/multiview.vert.qsb
Binary files differ
diff --git a/tests/manual/rhi/multiview/texture.frag b/tests/manual/rhi/multiview/texture.frag
new file mode 100644
index 0000000000..148f4113c8
--- /dev/null
+++ b/tests/manual/rhi/multiview/texture.frag
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec2 v_texcoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2DArray tex;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ int flip;
+ float layer;
+};
+
+void main()
+{
+ vec4 c = texture(tex, vec3(v_texcoord, layer));
+ fragColor = vec4(c.rgb * c.a, c.a);
+}
diff --git a/tests/manual/rhi/multiview/texture.frag.qsb b/tests/manual/rhi/multiview/texture.frag.qsb
new file mode 100644
index 0000000000..5d0de425d1
--- /dev/null
+++ b/tests/manual/rhi/multiview/texture.frag.qsb
Binary files differ
diff --git a/tests/manual/rhi/multiview/texture.vert b/tests/manual/rhi/multiview/texture.vert
new file mode 100644
index 0000000000..156dca0db6
--- /dev/null
+++ b/tests/manual/rhi/multiview/texture.vert
@@ -0,0 +1,20 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoord;
+
+layout(location = 0) out vec2 v_texcoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ int flip;
+ float layer;
+};
+
+void main()
+{
+ v_texcoord = vec2(texcoord.x, texcoord.y);
+ if (flip != 0)
+ v_texcoord.y = 1.0 - v_texcoord.y;
+ gl_Position = mvp * position;
+}
diff --git a/tests/manual/rhi/multiview/texture.vert.qsb b/tests/manual/rhi/multiview/texture.vert.qsb
new file mode 100644
index 0000000000..0d1078f394
--- /dev/null
+++ b/tests/manual/rhi/multiview/texture.vert.qsb
Binary files differ
diff --git a/tests/manual/rhi/multiwindow/multiwindow.cpp b/tests/manual/rhi/multiwindow/multiwindow.cpp
index f25a68c621..bea4af2d48 100644
--- a/tests/manual/rhi/multiwindow/multiwindow.cpp
+++ b/tests/manual/rhi/multiwindow/multiwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QWidget>
@@ -13,33 +13,17 @@
#include <QWindow>
#include <QPlatformSurfaceEvent>
#include <QElapsedTimer>
-
-#include <QtGui/private/qshader_p.h>
#include <QFile>
-
-#ifndef QT_NO_OPENGL
-#include <QtGui/private/qrhigles2_p.h>
-#include <QOffscreenSurface>
-#endif
-
-#if QT_CONFIG(vulkan)
#include <QLoggingCategory>
-#include <QtGui/private/qrhivulkan_p.h>
-#endif
-
-#ifdef Q_OS_WIN
-#include <QtGui/private/qrhid3d11_p.h>
-#endif
-
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
-#include <QtGui/private/qrhimetal_p.h>
-#endif
+#include <QOffscreenSurface>
+#include <rhi/qrhi.h>
enum GraphicsApi
{
OpenGL,
Vulkan,
D3D11,
+ D3D12,
Metal
};
@@ -54,6 +38,8 @@ static QString graphicsApiName()
return QLatin1String("Vulkan");
case D3D11:
return QLatin1String("Direct3D 11");
+ case D3D12:
+ return QLatin1String("Direct3D 12");
case Metal:
return QLatin1String("Metal");
default:
@@ -98,10 +84,14 @@ void createRhi()
QRhiD3D11InitParams params;
params.enableDebugLayer = true;
r.r = QRhi::create(QRhi::D3D11, &params);
+ } else if (graphicsApi == D3D12) {
+ QRhiD3D12InitParams params;
+ params.enableDebugLayer = true;
+ r.r = QRhi::create(QRhi::D3D12, &params);
}
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
if (graphicsApi == Metal) {
QRhiMetalInitParams params;
r.r = QRhi::create(QRhi::Metal, &params);
@@ -281,6 +271,7 @@ Window::Window(const QString &title, const QColor &bgColor, int axis, bool noVSy
#endif
break;
case D3D11:
+ case D3D12:
setSurfaceType(Direct3DSurface);
break;
case Metal:
@@ -478,7 +469,7 @@ int main(int argc, char **argv)
#if defined(Q_OS_WIN)
graphicsApi = D3D11;
-#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#elif QT_CONFIG(metal)
graphicsApi = Metal;
#elif QT_CONFIG(vulkan)
graphicsApi = Vulkan;
@@ -494,6 +485,8 @@ int main(int argc, char **argv)
cmdLineParser.addOption(vkOption);
QCommandLineOption d3dOption({ "d", "d3d11" }, QLatin1String("Direct3D 11"));
cmdLineParser.addOption(d3dOption);
+ QCommandLineOption d3d12Option({ "D", "d3d12" }, QLatin1String("Direct3D 12"));
+ cmdLineParser.addOption(d3d12Option);
QCommandLineOption mtlOption({ "m", "metal" }, QLatin1String("Metal"));
cmdLineParser.addOption(mtlOption);
cmdLineParser.process(app);
@@ -503,6 +496,8 @@ int main(int argc, char **argv)
graphicsApi = Vulkan;
if (cmdLineParser.isSet(d3dOption))
graphicsApi = D3D11;
+ if (cmdLineParser.isSet(d3d12Option))
+ graphicsApi = D3D12;
if (cmdLineParser.isSet(mtlOption))
graphicsApi = Metal;
@@ -517,6 +512,7 @@ int main(int argc, char **argv)
r.instance = new QVulkanInstance;
if (graphicsApi == Vulkan) {
r.instance->setLayers({ "VK_LAYER_KHRONOS_validation" });
+ r.instance->setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
if (!r.instance->create()) {
qWarning("Failed to create Vulkan instance, switching to OpenGL");
graphicsApi = OpenGL;
diff --git a/tests/manual/rhi/multiwindow_threaded/multiwindow_threaded.cpp b/tests/manual/rhi/multiwindow_threaded/multiwindow_threaded.cpp
index d817c90c69..634caebd8d 100644
--- a/tests/manual/rhi/multiwindow_threaded/multiwindow_threaded.cpp
+++ b/tests/manual/rhi/multiwindow_threaded/multiwindow_threaded.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QWidget>
@@ -16,27 +16,10 @@
#include <QEvent>
#include <QCommandLineParser>
#include <QElapsedTimer>
-
-#include <QtGui/private/qshader_p.h>
#include <QFile>
-
-#ifndef QT_NO_OPENGL
-#include <QtGui/private/qrhigles2_p.h>
-#include <QOffscreenSurface>
-#endif
-
-#if QT_CONFIG(vulkan)
#include <QLoggingCategory>
-#include <QtGui/private/qrhivulkan_p.h>
-#endif
-
-#ifdef Q_OS_WIN
-#include <QtGui/private/qrhid3d11_p.h>
-#endif
-
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
-#include <QtGui/private/qrhimetal_p.h>
-#endif
+#include <QOffscreenSurface>
+#include <rhi/qrhi.h>
#ifdef Q_OS_DARWIN
#include <QtCore/private/qcore_mac_p.h>
@@ -66,6 +49,8 @@ static QString graphicsApiName()
return QLatin1String("Vulkan");
case D3D11:
return QLatin1String("Direct3D 11");
+ case D3D12:
+ return QLatin1String("Direct3D 12");
case Metal:
return QLatin1String("Metal");
default:
@@ -329,10 +314,14 @@ void Renderer::createRhi()
QRhiD3D11InitParams params;
params.enableDebugLayer = true;
r = QRhi::create(QRhi::D3D11, &params, rhiFlags);
+ } else if (graphicsApi == D3D12) {
+ QRhiD3D12InitParams params;
+ params.enableDebugLayer = true;
+ r = QRhi::create(QRhi::D3D12, &params, rhiFlags);
}
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
if (graphicsApi == Metal) {
QRhiMetalInitParams params;
r = QRhi::create(QRhi::Metal, &params, rhiFlags);
@@ -634,7 +623,7 @@ void createWindow()
static QColor colors[] = { Qt::red, Qt::green, Qt::blue, Qt::yellow, Qt::cyan, Qt::gray };
const int n = windows.count();
Window *w = new Window(QString::asprintf("Window+Thread #%d (%s)", n, qPrintable(graphicsApiName())), graphicsApi);
- Renderer *renderer = new Renderer(w, colors[n % 6], n % 3);;
+ Renderer *renderer = new Renderer(w, colors[n % 6], n % 3);
QObject::connect(w, &Window::initRequested, w, [renderer] {
renderer->sendInit();
});
@@ -665,7 +654,7 @@ int main(int argc, char **argv)
#if defined(Q_OS_WIN)
graphicsApi = D3D11;
-#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#elif QT_CONFIG(metal)
graphicsApi = Metal;
#elif QT_CONFIG(vulkan)
graphicsApi = Vulkan;
@@ -681,6 +670,8 @@ int main(int argc, char **argv)
cmdLineParser.addOption(vkOption);
QCommandLineOption d3dOption({ "d", "d3d11" }, QLatin1String("Direct3D 11"));
cmdLineParser.addOption(d3dOption);
+ QCommandLineOption d3d12Option({ "D", "d3d12" }, QLatin1String("Direct3D 12"));
+ cmdLineParser.addOption(d3d12Option);
QCommandLineOption mtlOption({ "m", "metal" }, QLatin1String("Metal"));
cmdLineParser.addOption(mtlOption);
cmdLineParser.process(app);
@@ -690,6 +681,8 @@ int main(int argc, char **argv)
graphicsApi = Vulkan;
if (cmdLineParser.isSet(d3dOption))
graphicsApi = D3D11;
+ if (cmdLineParser.isSet(d3d12Option))
+ graphicsApi = D3D12;
if (cmdLineParser.isSet(mtlOption))
graphicsApi = Metal;
diff --git a/tests/manual/rhi/multiwindow_threaded/window.cpp b/tests/manual/rhi/multiwindow_threaded/window.cpp
index ea098b7f37..d9c7340237 100644
--- a/tests/manual/rhi/multiwindow_threaded/window.cpp
+++ b/tests/manual/rhi/multiwindow_threaded/window.cpp
@@ -1,13 +1,9 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "window.h"
#include <QPlatformSurfaceEvent>
-#ifndef QT_NO_OPENGL
-#include <QtGui/private/qrhigles2_p.h>
-#endif
-
#if QT_CONFIG(vulkan)
extern QVulkanInstance *instance;
#endif
@@ -25,6 +21,7 @@ Window::Window(const QString &title, GraphicsApi api)
#endif
break;
case D3D11:
+ case D3D12:
setSurfaceType(Direct3DSurface);
break;
case Metal:
diff --git a/tests/manual/rhi/multiwindow_threaded/window.h b/tests/manual/rhi/multiwindow_threaded/window.h
index 3e0212f772..e55e51de7d 100644
--- a/tests/manual/rhi/multiwindow_threaded/window.h
+++ b/tests/manual/rhi/multiwindow_threaded/window.h
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WINDOW_H
#define WINDOW_H
@@ -11,6 +11,7 @@ enum GraphicsApi
OpenGL,
Vulkan,
D3D11,
+ D3D12,
Metal
};
diff --git a/tests/manual/rhi/noninstanced/buildshaders.bat b/tests/manual/rhi/noninstanced/buildshaders.bat
index fc274eeec2..c1e0fb4722 100644
--- a/tests/manual/rhi/noninstanced/buildshaders.bat
+++ b/tests/manual/rhi/noninstanced/buildshaders.bat
@@ -1,2 +1,4 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 material.vert -o material.vert.qsb
qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 material.frag -o material.frag.qsb
diff --git a/tests/manual/rhi/noninstanced/noninstanced.cpp b/tests/manual/rhi/noninstanced/noninstanced.cpp
index 59850e2616..830dd4c84b 100644
--- a/tests/manual/rhi/noninstanced/noninstanced.cpp
+++ b/tests/manual/rhi/noninstanced/noninstanced.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#define EXAMPLEFW_PREINIT
#include "../shared/examplefw.h"
diff --git a/tests/manual/rhi/offscreen/offscreen.cpp b/tests/manual/rhi/offscreen/offscreen.cpp
index b36c3b3667..86bf5ad5a9 100644
--- a/tests/manual/rhi/offscreen/offscreen.cpp
+++ b/tests/manual/rhi/offscreen/offscreen.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QImage>
@@ -7,28 +7,10 @@
#include <QFile>
#include <QLoggingCategory>
#include <QCommandLineParser>
-#include <QtGui/private/qshader_p.h>
-
-#include <QtGui/private/qrhinull_p.h>
-
-#ifndef QT_NO_OPENGL
-#include <QtGui/private/qrhigles2_p.h>
-#include <QOffscreenSurface>
-#endif
-
-#if QT_CONFIG(vulkan)
#include <QLoggingCategory>
-#include <QtGui/private/qrhivulkan_p.h>
-#endif
-
-#ifdef Q_OS_WIN
-#include <QtGui/private/qrhid3d11_p.h>
-#include <QtGui/private/qrhid3d12_p.h>
-#endif
+#include <QOffscreenSurface>
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
-#include <QtGui/private/qrhimetal_p.h>
-#endif
+#include <rhi/qrhi.h>
//#define TEST_FINISH
@@ -86,7 +68,7 @@ int main(int argc, char **argv)
#if defined(Q_OS_WIN)
graphicsApi = D3D11;
-#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#elif QT_CONFIG(metal)
graphicsApi = Metal;
#elif QT_CONFIG(vulkan)
graphicsApi = Vulkan;
@@ -126,10 +108,11 @@ int main(int argc, char **argv)
qDebug("This is a multi-api example, use command line arguments to override:\n%s", qPrintable(cmdLineParser.helpText()));
QRhi *r = nullptr;
+ QRhi::Flags rhiFlags = QRhi::EnableTimestamps;
if (graphicsApi == Null) {
QRhiNullInitParams params;
- r = QRhi::create(QRhi::Null, &params);
+ r = QRhi::create(QRhi::Null, &params, rhiFlags);
}
#if QT_CONFIG(vulkan)
@@ -137,10 +120,11 @@ int main(int argc, char **argv)
if (graphicsApi == Vulkan) {
QLoggingCategory::setFilterRules(QStringLiteral("qt.vulkan=true"));
inst.setLayers({ "VK_LAYER_KHRONOS_validation" });
+ inst.setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
if (inst.create()) {
QRhiVulkanInitParams params;
params.inst = &inst;
- r = QRhi::create(QRhi::Vulkan, &params);
+ r = QRhi::create(QRhi::Vulkan, &params, rhiFlags);
} else {
qWarning("Failed to create Vulkan instance, switching to OpenGL");
graphicsApi = OpenGL;
@@ -154,7 +138,7 @@ int main(int argc, char **argv)
offscreenSurface.reset(QRhiGles2InitParams::newFallbackSurface());
QRhiGles2InitParams params;
params.fallbackSurface = offscreenSurface.data();
- r = QRhi::create(QRhi::OpenGLES2, &params);
+ r = QRhi::create(QRhi::OpenGLES2, &params, rhiFlags);
}
#endif
@@ -162,18 +146,18 @@ int main(int argc, char **argv)
if (graphicsApi == D3D11) {
QRhiD3D11InitParams params;
params.enableDebugLayer = true;
- r = QRhi::create(QRhi::D3D11, &params);
+ r = QRhi::create(QRhi::D3D11, &params, rhiFlags);
} else if (graphicsApi == D3D12) {
QRhiD3D12InitParams params;
params.enableDebugLayer = true;
- r = QRhi::create(QRhi::D3D12, &params);
+ r = QRhi::create(QRhi::D3D12, &params, rhiFlags);
}
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
if (graphicsApi == Metal) {
QRhiMetalInitParams params;
- r = QRhi::create(QRhi::Metal, &params);
+ r = QRhi::create(QRhi::Metal, &params, rhiFlags);
}
#endif
@@ -300,6 +284,8 @@ int main(int argc, char **argv)
#ifdef TEST_FINISH
r->endOffscreenFrame();
#endif
+ if (r->isFeatureSupported(QRhi::Timestamps))
+ qDebug() << "GPU time:" << cb->lastCompletedGpuTime() << "seconds (may refer to a previous frame)";
}
delete ps;
diff --git a/tests/manual/rhi/polygonmode/buildshaders.bat b/tests/manual/rhi/polygonmode/buildshaders.bat
index 3cd87ed7a2..d1c184109e 100755
--- a/tests/manual/rhi/polygonmode/buildshaders.bat
+++ b/tests/manual/rhi/polygonmode/buildshaders.bat
@@ -1,2 +1,4 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl 320es,410,120 test.vert --msl 12 --hlsl 50 -o test.vert.qsb
qsb --glsl 320es,410,120 test.frag --msl 12 --hlsl 50 -o test.frag.qsb
diff --git a/tests/manual/rhi/polygonmode/polygonmode.cpp b/tests/manual/rhi/polygonmode/polygonmode.cpp
index 88e9b47e39..466657ab03 100644
--- a/tests/manual/rhi/polygonmode/polygonmode.cpp
+++ b/tests/manual/rhi/polygonmode/polygonmode.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
diff --git a/tests/manual/rhi/rhi.pro b/tests/manual/rhi/rhi.pro
deleted file mode 100644
index 9238de6fc2..0000000000
--- a/tests/manual/rhi/rhi.pro
+++ /dev/null
@@ -1,30 +0,0 @@
-TEMPLATE = subdirs
-
-SUBDIRS += \
- hellominimalcrossgfxtriangle \
- compressedtexture_bc1 \
- compressedtexture_bc1_subupload \
- texuploads \
- msaatexture \
- msaarenderbuffer \
- cubemap \
- cubemap_scissor \
- cubemap_render \
- multiwindow \
- multiwindow_threaded \
- triquadcube \
- offscreen \
- floattexture \
- float16texture_with_compute \
- mrt \
- shadowmap \
- computebuffer \
- computeimage \
- instancing \
- noninstanced \
- tex3d
-
-qtConfig(widgets) {
- SUBDIRS += \
- qrhiprof
-}
diff --git a/tests/manual/rhi/rhiwidget/CMakeLists.txt b/tests/manual/rhi/rhiwidgetproto/CMakeLists.txt
index 97bfea5590..5b62ef557d 100644
--- a/tests/manual/rhi/rhiwidget/CMakeLists.txt
+++ b/tests/manual/rhi/rhiwidgetproto/CMakeLists.txt
@@ -1,7 +1,7 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-qt_internal_add_manual_test(rhiwidget
+qt_internal_add_manual_test(rhiwidgetproto
GUI
SOURCES
examplewidget.cpp examplewidget.h
@@ -20,14 +20,14 @@ set_source_files_properties("../shared/texture.vert.qsb"
set_source_files_properties("../shared/texture.frag.qsb"
PROPERTIES QT_RESOURCE_ALIAS "texture.frag.qsb"
)
-set(rhiwidget_resource_files
+set(rhiwidgetproto_resource_files
"../shared/texture.vert.qsb"
"../shared/texture.frag.qsb"
)
-qt_internal_add_resource(rhiwidget "rhiwidget"
+qt_internal_add_resource(rhiwidgetproto "rhiwidgetproto"
PREFIX
"/"
FILES
- ${rhiwidget_resource_files}
+ ${rhiwidgetproto_resource_files}
)
diff --git a/tests/manual/rhi/rhiwidget/examplewidget.cpp b/tests/manual/rhi/rhiwidgetproto/examplewidget.cpp
index 942766ac5c..6a6fd5b326 100644
--- a/tests/manual/rhi/rhiwidget/examplewidget.cpp
+++ b/tests/manual/rhi/rhiwidgetproto/examplewidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "examplewidget.h"
#include "../shared/cube.h"
diff --git a/tests/manual/rhi/rhiwidget/examplewidget.h b/tests/manual/rhi/rhiwidgetproto/examplewidget.h
index 1b58eedc20..a17fe7bce1 100644
--- a/tests/manual/rhi/rhiwidget/examplewidget.h
+++ b/tests/manual/rhi/rhiwidgetproto/examplewidget.h
@@ -1,11 +1,11 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef EXAMPLEWIDGET_H
#define EXAMPLEWIDGET_H
#include "rhiwidget.h"
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
class ExampleRhiWidget : public QRhiWidget
{
diff --git a/tests/manual/rhi/rhiwidget/main.cpp b/tests/manual/rhi/rhiwidgetproto/main.cpp
index 8af531cd3b..a3b1741855 100644
--- a/tests/manual/rhi/rhiwidget/main.cpp
+++ b/tests/manual/rhi/rhiwidgetproto/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QVBoxLayout>
diff --git a/tests/manual/rhi/rhiwidget/rhiwidget.cpp b/tests/manual/rhi/rhiwidgetproto/rhiwidget.cpp
index 2ba8721102..d535c655d0 100644
--- a/tests/manual/rhi/rhiwidget/rhiwidget.cpp
+++ b/tests/manual/rhi/rhiwidgetproto/rhiwidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "rhiwidget_p.h"
@@ -155,14 +155,7 @@ QPlatformBackingStoreRhiConfig QRhiWidgetPrivate::rhiConfig() const
void QRhiWidgetPrivate::ensureRhi()
{
Q_Q(QRhiWidget);
- // the QRhi and infrastructure belongs to the top-level widget, not to this widget
- QWidget *tlw = q->window();
- QWidgetPrivate *wd = get(tlw);
-
- QRhi *currentRhi = nullptr;
- if (QWidgetRepaintManager *repaintManager = wd->maybeRepaintManager())
- currentRhi = repaintManager->rhi();
-
+ QRhi *currentRhi = QWidgetPrivate::rhi();
if (currentRhi && currentRhi->backend() != QBackingStoreRhiSupport::apiToRhiBackend(config.api())) {
qWarning("The top-level window is already using another graphics API for composition, "
"'%s' is not compatible with this widget",
diff --git a/tests/manual/rhi/rhiwidget/rhiwidget.h b/tests/manual/rhi/rhiwidgetproto/rhiwidget.h
index 12ca7db413..0ac947b058 100644
--- a/tests/manual/rhi/rhiwidget/rhiwidget.h
+++ b/tests/manual/rhi/rhiwidgetproto/rhiwidget.h
@@ -1,11 +1,11 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef RHIWIDGET_H
#define RHIWIDGET_H
#include <QWidget>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
class QRhiWidgetPrivate;
diff --git a/tests/manual/rhi/rhiwidget/rhiwidget_p.h b/tests/manual/rhi/rhiwidgetproto/rhiwidget_p.h
index b5cd058a94..da8ebc5c8f 100644
--- a/tests/manual/rhi/rhiwidget/rhiwidget_p.h
+++ b/tests/manual/rhi/rhiwidgetproto/rhiwidget_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef RHIWIDGET_P_H
#define RHIWIDGET_P_H
diff --git a/tests/manual/rhi/shadowmap/buildshaders.bat b/tests/manual/rhi/shadowmap/buildshaders.bat
index 7b84cc0952..3e514a90b9 100644
--- a/tests/manual/rhi/shadowmap/buildshaders.bat
+++ b/tests/manual/rhi/shadowmap/buildshaders.bat
@@ -1,3 +1,5 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl "120,300 es" --hlsl 50 --msl 12 -c shadowmap.vert -o shadowmap.vert.qsb
qsb --glsl "120,300 es" --hlsl 50 --msl 12 -c shadowmap.frag -o shadowmap.frag.qsb
qsb --glsl "120,300 es" --hlsl 50 --msl 12 -c main.vert -o main.vert.qsb
diff --git a/tests/manual/rhi/shadowmap/shadowmap.cpp b/tests/manual/rhi/shadowmap/shadowmap.cpp
index a37543b9fd..626d7d9c49 100644
--- a/tests/manual/rhi/shadowmap/shadowmap.cpp
+++ b/tests/manual/rhi/shadowmap/shadowmap.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
#include "../shared/cube.h"
diff --git a/tests/manual/rhi/shared/buildshaders.bat b/tests/manual/rhi/shared/buildshaders.bat
index d0212bc7a7..faa253c8bc 100644
--- a/tests/manual/rhi/shared/buildshaders.bat
+++ b/tests/manual/rhi/shared/buildshaders.bat
@@ -1,7 +1,9 @@
-qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c color.vert -o color.vert.qsb
-qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c color.frag -o color.frag.qsb
-qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c texture.vert -o texture.vert.qsb
-qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c texture.frag -o texture.frag.qsb
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -c color.vert -o color.vert.qsb
+qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -c color.frag -o color.frag.qsb
+qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -c texture.vert -o texture.vert.qsb
+qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -c texture.frag -o texture.frag.qsb
qsb --glsl "310 es,150" --hlsl 50 --msl 12 -c texture_ms4.frag -o texture_ms4.frag.qsb
qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -c texture_arr.vert -o texture_arr.vert.qsb
qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -c texture_arr.frag -o texture_arr.frag.qsb
diff --git a/tests/manual/rhi/shared/color.frag.qsb b/tests/manual/rhi/shared/color.frag.qsb
index 0c3815b8be..753acda690 100644
--- a/tests/manual/rhi/shared/color.frag.qsb
+++ b/tests/manual/rhi/shared/color.frag.qsb
Binary files differ
diff --git a/tests/manual/rhi/shared/color.vert.qsb b/tests/manual/rhi/shared/color.vert.qsb
index dfa577f668..ef7545b621 100644
--- a/tests/manual/rhi/shared/color.vert.qsb
+++ b/tests/manual/rhi/shared/color.vert.qsb
Binary files differ
diff --git a/tests/manual/rhi/shared/dds_bc1.h b/tests/manual/rhi/shared/dds_bc1.h
index aebf7fdac8..6d7236d068 100644
--- a/tests/manual/rhi/shared/dds_bc1.h
+++ b/tests/manual/rhi/shared/dds_bc1.h
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
static const quint32 DDS_MAGIC = 0x20534444; // 'DDS '
static const quint32 DDS_FOURCC = 4;
diff --git a/tests/manual/rhi/shared/examplefw.h b/tests/manual/rhi/shared/examplefw.h
index 9879e8c5ba..f2ca57341c 100644
--- a/tests/manual/rhi/shared/examplefw.h
+++ b/tests/manual/rhi/shared/examplefw.h
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Adapted from hellominimalcrossgfxtriangle with the frame rendering stripped out.
// Include this file and implement Window::customInit, release and render.
@@ -13,28 +13,9 @@
#include <QTimer>
#include <QLoggingCategory>
#include <QColorSpace>
-
-#include <QtGui/private/qshader_p.h>
#include <QFile>
-#include <QtGui/private/qrhinull_p.h>
-
-#ifndef QT_NO_OPENGL
-#include <QtGui/private/qrhigles2_p.h>
#include <QOffscreenSurface>
-#endif
-
-#if QT_CONFIG(vulkan)
-#include <QtGui/private/qrhivulkan_p.h>
-#endif
-
-#ifdef Q_OS_WIN
-#include <QtGui/private/qrhid3d11_p.h>
-#include <QtGui/private/qrhid3d12_p.h>
-#endif
-
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
-#include <QtGui/private/qrhimetal_p.h>
-#endif
+#include <rhi/qrhi.h>
#ifdef EXAMPLEFW_IMGUI
#include "qrhiimgui_p.h"
@@ -92,13 +73,15 @@ QString graphicsApiName()
return QString();
}
-QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers;
+QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers | QRhi::EnableTimestamps;
int sampleCount = 1;
QRhiSwapChain::Flags scFlags;
QRhi::BeginFrameFlags beginFrameFlags;
QRhi::EndFrameFlags endFrameFlags;
bool transparentBackground = false;
bool debugLayer = true;
+QRhiSwapChain::Format swapchainFormat = QRhiSwapChain::SDR;
+float imguiHDRMultiplier = 0.0f;
class Window : public QWindow
{
@@ -278,7 +261,7 @@ void Window::init()
}
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
if (graphicsApi == Metal) {
QRhiMetalInitParams params;
m_r = QRhi::create(QRhi::Metal, &params, rhiFlags);
@@ -299,6 +282,10 @@ void Window::init()
m_sc->setDepthStencil(m_ds);
m_sc->setSampleCount(sampleCount);
m_sc->setFlags(scFlags);
+ if (!m_sc->isFormatSupported(swapchainFormat))
+ qWarning("Swapchain reports that requested format %d is not supported", int(swapchainFormat));
+ else
+ m_sc->setFormat(swapchainFormat);
m_rp = m_sc->newCompatibleRenderPassDescriptor();
m_sc->setRenderPassDescriptor(m_rp);
@@ -417,7 +404,7 @@ void Window::render()
QMatrix4x4 guiMvp = m_r->clipSpaceCorrMatrix();
guiMvp.ortho(0, outputSizeInPixels.width() / dpr, outputSizeInPixels.height() / dpr, 0, 1, -1);
- m_imguiRenderer->prepare(m_r, rt, cb, guiMvp, 1.0f);
+ m_imguiRenderer->prepare(m_r, rt, cb, guiMvp, 1.0f, imguiHDRMultiplier);
#endif
customRender();
@@ -439,7 +426,7 @@ int main(int argc, char **argv)
// Defaults.
#if defined(Q_OS_WIN)
graphicsApi = D3D11;
-#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#elif QT_CONFIG(metal)
graphicsApi = Metal;
#elif QT_CONFIG(vulkan)
graphicsApi = Vulkan;
diff --git a/tests/manual/rhi/shared/imgui/buildshaders.bat b/tests/manual/rhi/shared/imgui/buildshaders.bat
index eec4e3a070..560c634134 100644
--- a/tests/manual/rhi/shared/imgui/buildshaders.bat
+++ b/tests/manual/rhi/shared/imgui/buildshaders.bat
@@ -1,2 +1,4 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -c imgui.vert -o imgui.vert.qsb
qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -c imgui.frag -o imgui.frag.qsb
diff --git a/tests/manual/rhi/shared/imgui/imgui.frag b/tests/manual/rhi/shared/imgui/imgui.frag
index 51bd615deb..6169dd639f 100644
--- a/tests/manual/rhi/shared/imgui/imgui.frag
+++ b/tests/manual/rhi/shared/imgui/imgui.frag
@@ -8,14 +8,30 @@ layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
float opacity;
+ // Windows HDR: set to SDR_white_level_in_nits / 80
+ // macOS/iOS EDR: set to 1.0
+ // No HDR: set to 0.0, will do linear to sRGB at the end then.
+ float hdrWhiteLevelMult;
};
layout(binding = 1) uniform sampler2D tex;
+vec3 linearToSRGB(vec3 color)
+{
+ vec3 S1 = sqrt(color);
+ vec3 S2 = sqrt(S1);
+ vec3 S3 = sqrt(S2);
+ return 0.585122381 * S1 + 0.783140355 * S2 - 0.368262736 * S3;
+}
+
void main()
{
vec4 c = v_color * texture(tex, v_texcoord);
c.a *= opacity;
- c.rgb *= c.a;
+ if (hdrWhiteLevelMult > 0.0)
+ c.rgb *= hdrWhiteLevelMult;
+ else
+ c.rgb = linearToSRGB(c.rgb);
+ c.rgb *= c.a; // premultiplied alpha
fragColor = c;
}
diff --git a/tests/manual/rhi/shared/imgui/imgui.frag.qsb b/tests/manual/rhi/shared/imgui/imgui.frag.qsb
index 2abf236f56..09b1e44697 100644
--- a/tests/manual/rhi/shared/imgui/imgui.frag.qsb
+++ b/tests/manual/rhi/shared/imgui/imgui.frag.qsb
Binary files differ
diff --git a/tests/manual/rhi/shared/imgui/imgui.vert b/tests/manual/rhi/shared/imgui/imgui.vert
index bb24a22c13..45510ea0fe 100644
--- a/tests/manual/rhi/shared/imgui/imgui.vert
+++ b/tests/manual/rhi/shared/imgui/imgui.vert
@@ -10,6 +10,7 @@ layout(location = 1) out vec4 v_color;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
float opacity;
+ float sdrMult;
};
void main()
diff --git a/tests/manual/rhi/shared/imgui/imgui.vert.qsb b/tests/manual/rhi/shared/imgui/imgui.vert.qsb
index 0f2300f676..3ee5a7b716 100644
--- a/tests/manual/rhi/shared/imgui/imgui.vert.qsb
+++ b/tests/manual/rhi/shared/imgui/imgui.vert.qsb
Binary files differ
diff --git a/tests/manual/rhi/shared/imgui/qrhiimgui.cpp b/tests/manual/rhi/shared/imgui/qrhiimgui.cpp
index d4330238b9..88b0a5d897 100644
--- a/tests/manual/rhi/shared/imgui/qrhiimgui.cpp
+++ b/tests/manual/rhi/shared/imgui/qrhiimgui.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qrhiimgui_p.h"
#include <QtCore/qfile.h>
@@ -48,7 +48,12 @@ void QRhiImguiRenderer::releaseResources()
m_rhi = nullptr;
}
-void QRhiImguiRenderer::prepare(QRhi *rhi, QRhiRenderTarget *rt, QRhiCommandBuffer *cb, const QMatrix4x4 &mvp, float opacity)
+void QRhiImguiRenderer::prepare(QRhi *rhi,
+ QRhiRenderTarget *rt,
+ QRhiCommandBuffer *cb,
+ const QMatrix4x4 &mvp,
+ float opacity,
+ float hdrWhiteLevelMultiplierOrZeroForSDRsRGB)
{
if (!m_rhi) {
m_rhi = rhi;
@@ -89,7 +94,7 @@ void QRhiImguiRenderer::prepare(QRhi *rhi, QRhiRenderTarget *rt, QRhiCommandBuff
}
if (!m_ubuf) {
- m_ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 4));
+ m_ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 4 + 4));
m_ubuf->setName(QByteArrayLiteral("imgui uniform buffer"));
if (!m_ubuf->create())
return;
@@ -201,6 +206,7 @@ void QRhiImguiRenderer::prepare(QRhi *rhi, QRhiRenderTarget *rt, QRhiCommandBuff
u->updateDynamicBuffer(m_ubuf.get(), 0, 64, mvp.constData());
u->updateDynamicBuffer(m_ubuf.get(), 64, 4, &opacity);
+ u->updateDynamicBuffer(m_ubuf.get(), 68, 4, &hdrWhiteLevelMultiplierOrZeroForSDRsRGB);
for (int i = 0; i < texturesNeedUpdate.count(); ++i) {
Texture &t(m_textures[texturesNeedUpdate[i]]);
diff --git a/tests/manual/rhi/shared/imgui/qrhiimgui_p.h b/tests/manual/rhi/shared/imgui/qrhiimgui_p.h
index 1b4b4d67b6..ec86c2af7f 100644
--- a/tests/manual/rhi/shared/imgui/qrhiimgui_p.h
+++ b/tests/manual/rhi/shared/imgui/qrhiimgui_p.h
@@ -1,10 +1,10 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QRHIIMGUI_P_H
#define QRHIIMGUI_P_H
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
@@ -45,7 +45,12 @@ public:
StaticRenderData sf;
FrameRenderData f;
- void prepare(QRhi *rhi, QRhiRenderTarget *rt, QRhiCommandBuffer *cb, const QMatrix4x4 &mvp, float opacity);
+ void prepare(QRhi *rhi,
+ QRhiRenderTarget *rt,
+ QRhiCommandBuffer *cb,
+ const QMatrix4x4 &mvp,
+ float opacity = 1.0f,
+ float hdrWhiteLevelMultiplierOrZeroForSDRsRGB = 0.0f);
void render();
void releaseResources();
diff --git a/tests/manual/rhi/shared/texture.frag.qsb b/tests/manual/rhi/shared/texture.frag.qsb
index b535db90a5..fc1896df76 100644
--- a/tests/manual/rhi/shared/texture.frag.qsb
+++ b/tests/manual/rhi/shared/texture.frag.qsb
Binary files differ
diff --git a/tests/manual/rhi/shared/texture.vert.qsb b/tests/manual/rhi/shared/texture.vert.qsb
index f09ed73ef8..40ac8a0691 100644
--- a/tests/manual/rhi/shared/texture.vert.qsb
+++ b/tests/manual/rhi/shared/texture.vert.qsb
Binary files differ
diff --git a/tests/manual/rhi/shared/texture_arr.frag.qsb b/tests/manual/rhi/shared/texture_arr.frag.qsb
index 3674fdad1d..7229c9f5fc 100644
--- a/tests/manual/rhi/shared/texture_arr.frag.qsb
+++ b/tests/manual/rhi/shared/texture_arr.frag.qsb
Binary files differ
diff --git a/tests/manual/rhi/shared/texture_arr.vert.qsb b/tests/manual/rhi/shared/texture_arr.vert.qsb
index fe46d2c385..fd34dcaf51 100644
--- a/tests/manual/rhi/shared/texture_arr.vert.qsb
+++ b/tests/manual/rhi/shared/texture_arr.vert.qsb
Binary files differ
diff --git a/tests/manual/rhi/shared/texture_ms4.frag.qsb b/tests/manual/rhi/shared/texture_ms4.frag.qsb
index 4080308652..896df41645 100644
--- a/tests/manual/rhi/shared/texture_ms4.frag.qsb
+++ b/tests/manual/rhi/shared/texture_ms4.frag.qsb
Binary files differ
diff --git a/tests/manual/rhi/stenciloutline/buildshaders.bat b/tests/manual/rhi/stenciloutline/buildshaders.bat
index fc274eeec2..c1e0fb4722 100644
--- a/tests/manual/rhi/stenciloutline/buildshaders.bat
+++ b/tests/manual/rhi/stenciloutline/buildshaders.bat
@@ -1,2 +1,4 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 material.vert -o material.vert.qsb
qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 material.frag -o material.frag.qsb
diff --git a/tests/manual/rhi/stenciloutline/stenciloutline.cpp b/tests/manual/rhi/stenciloutline/stenciloutline.cpp
index bdf7f217b1..dfa4cdbbff 100644
--- a/tests/manual/rhi/stenciloutline/stenciloutline.cpp
+++ b/tests/manual/rhi/stenciloutline/stenciloutline.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
#include "../shared/cube.h"
diff --git a/tests/manual/rhi/stereo/main.cpp b/tests/manual/rhi/stereo/main.cpp
index 7ab1448217..cae25c8d24 100644
--- a/tests/manual/rhi/stereo/main.cpp
+++ b/tests/manual/rhi/stereo/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This is a demo showing stereoscopic rendering.
// For now, the backend is hardcoded to be OpenGL, because that's the only
@@ -22,7 +22,7 @@ int main(int argc, char **argv)
QSurfaceFormat::setDefaultFormat(fmt);
- Window w;
+ Window w{QRhi::Vulkan};
w.resize(1280, 720);
w.setTitle(QCoreApplication::applicationName());
w.show();
diff --git a/tests/manual/rhi/stereo/window.cpp b/tests/manual/rhi/stereo/window.cpp
index 23ab726046..18e1ecc409 100644
--- a/tests/manual/rhi/stereo/window.cpp
+++ b/tests/manual/rhi/stereo/window.cpp
@@ -1,16 +1,32 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "window.h"
#include <QPlatformSurfaceEvent>
#include <QTimer>
#include <QFile>
-#include <QtGui/private/qshader_p.h>
+#include <rhi/qshader.h>
#include "../shared/cube.h"
-Window::Window()
+Window::Window(QRhi::Implementation graphicsApi)
+ :m_graphicsApi(graphicsApi)
{
- setSurfaceType(OpenGLSurface);
+ switch (graphicsApi) {
+ default:
+ case QRhi::OpenGLES2:
+ setSurfaceType(OpenGLSurface);
+ break;
+ case QRhi::Vulkan:
+ instance.setLayers({ "VK_LAYER_KHRONOS_validation" });
+ instance.create();
+ setVulkanInstance(&instance);
+ setSurfaceType(VulkanSurface);
+ break;
+ case QRhi::D3D11:
+ case QRhi::D3D12:
+ setSurfaceType(Direct3DSurface);
+ break;
+ }
}
void Window::exposeEvent(QExposeEvent *)
@@ -55,13 +71,46 @@ bool Window::event(QEvent *e)
void Window::init()
{
- QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers | QRhi::EnableProfiling;
-
- m_fallbackSurface.reset(QRhiGles2InitParams::newFallbackSurface());
- QRhiGles2InitParams params;
- params.fallbackSurface = m_fallbackSurface.get();
- params.window = this;
- m_rhi.reset(QRhi::create(QRhi::OpenGLES2, &params, rhiFlags));
+ QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers;
+
+ switch (m_graphicsApi) {
+ case QRhi::Vulkan:
+ {
+ QRhiVulkanInitParams params;
+ params.window = this;
+ params.inst = vulkanInstance();
+ m_rhi.reset(QRhi::create(QRhi::Vulkan, &params, rhiFlags));
+ break;
+ }
+ case QRhi::Null:
+ case QRhi::Metal:
+ case QRhi::OpenGLES2:
+ {
+ m_fallbackSurface.reset(QRhiGles2InitParams::newFallbackSurface());
+ QRhiGles2InitParams params;
+ params.fallbackSurface = m_fallbackSurface.get();
+ params.window = this;
+ m_rhi.reset(QRhi::create(QRhi::OpenGLES2, &params, rhiFlags));
+ break;
+ }
+#ifdef Q_OS_WIN
+ case QRhi::D3D11:
+ {
+ QRhiD3D11InitParams params;
+ m_rhi.reset(QRhi::create(QRhi::D3D11, &params, rhiFlags));
+ break;
+ }
+ case QRhi::D3D12:
+ {
+ QRhiD3D12InitParams params;
+ params.enableDebugLayer = true;
+ m_rhi.reset(QRhi::create(QRhi::D3D12, &params, rhiFlags));
+ break;
+ }
+#endif
+ default:
+ break;
+ }
m_sc.reset(m_rhi->newSwapChain());
m_ds.reset(m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
diff --git a/tests/manual/rhi/stereo/window.h b/tests/manual/rhi/stereo/window.h
index 28e25dfdb4..0a175e31a0 100644
--- a/tests/manual/rhi/stereo/window.h
+++ b/tests/manual/rhi/stereo/window.h
@@ -1,27 +1,29 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WINDOW_H
#define WINDOW_H
#include <QWindow>
-#include <QtGui/private/qrhigles2_p.h>
#include <QOffscreenSurface>
+#include <rhi/qrhi.h>
class Window : public QWindow
{
public:
- Window();
+ Window(QRhi::Implementation graphicsApi);
void releaseSwapChain();
protected:
+ QVulkanInstance instance;
std::unique_ptr<QOffscreenSurface> m_fallbackSurface;
std::unique_ptr<QRhi> m_rhi;
std::unique_ptr<QRhiSwapChain> m_sc;
std::unique_ptr<QRhiRenderBuffer> m_ds;
std::unique_ptr<QRhiRenderPassDescriptor> m_rp;
+ QRhi::Implementation m_graphicsApi;
bool m_hasSwapChain = false;
QMatrix4x4 m_proj;
diff --git a/tests/manual/rhi/tessellation/buildshaders.bat b/tests/manual/rhi/tessellation/buildshaders.bat
index bc992dc28c..61345d1906 100644
--- a/tests/manual/rhi/tessellation/buildshaders.bat
+++ b/tests/manual/rhi/tessellation/buildshaders.bat
@@ -1,3 +1,5 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl 320es,410 --hlsl 50 --msl 12 --msltess test.vert -o test.vert.qsb
qsb --glsl 320es,410 --msl 12 --tess-mode triangles test.tesc -o test.tesc.qsb
qsb -r hlsl,50,test_hull.hlsl test.tesc.qsb
diff --git a/tests/manual/rhi/tessellation/tessellation.cpp b/tests/manual/rhi/tessellation/tessellation.cpp
index c746e6f83b..a50ddeeeb2 100644
--- a/tests/manual/rhi/tessellation/tessellation.cpp
+++ b/tests/manual/rhi/tessellation/tessellation.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
diff --git a/tests/manual/rhi/tex1d/CMakeLists.txt b/tests/manual/rhi/tex1d/CMakeLists.txt
index b27627a13a..f456b35d56 100644
--- a/tests/manual/rhi/tex1d/CMakeLists.txt
+++ b/tests/manual/rhi/tex1d/CMakeLists.txt
@@ -5,7 +5,7 @@ qt_internal_add_manual_test(tex1d
GUI
SOURCES
tex1d.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
diff --git a/tests/manual/rhi/tex1d/buildshaders.bat b/tests/manual/rhi/tex1d/buildshaders.bat
index 37934cf362..27bb8fc0e6 100644
--- a/tests/manual/rhi/tex1d/buildshaders.bat
+++ b/tests/manual/rhi/tex1d/buildshaders.bat
@@ -1,2 +1,4 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl "120,150,300 es" --hlsl 50 --msl 12 -c texture1d.vert -o texture1d.vert.qsb
qsb --glsl "120,150,300 es" --hlsl 50 --msl 12 -c texture1d.frag -o texture1d.frag.qsb
diff --git a/tests/manual/rhi/tex1d/tex1d.cpp b/tests/manual/rhi/tex1d/tex1d.cpp
index 66e0186b0f..7791c840ca 100644
--- a/tests/manual/rhi/tex1d/tex1d.cpp
+++ b/tests/manual/rhi/tex1d/tex1d.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
#include <QImage>
diff --git a/tests/manual/rhi/tex3d/buildshaders.bat b/tests/manual/rhi/tex3d/buildshaders.bat
index 041c554688..699d2b112b 100644
--- a/tests/manual/rhi/tex3d/buildshaders.bat
+++ b/tests/manual/rhi/tex3d/buildshaders.bat
@@ -1,2 +1,4 @@
+:: Copyright (C) 2024 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb --glsl "300 es,150" --hlsl 50 --msl 12 -c texture3d.vert -o texture3d.vert.qsb
qsb --glsl "300 es,150" --hlsl 50 --msl 12 -c texture3d.frag -o texture3d.frag.qsb
diff --git a/tests/manual/rhi/tex3d/tex3d.cpp b/tests/manual/rhi/tex3d/tex3d.cpp
index d98ae2fe45..33c4cab8d1 100644
--- a/tests/manual/rhi/tex3d/tex3d.cpp
+++ b/tests/manual/rhi/tex3d/tex3d.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
#include <QImage>
diff --git a/tests/manual/rhi/texturearray/texturearray.cpp b/tests/manual/rhi/texturearray/texturearray.cpp
index 0a354dd53a..241f440e01 100644
--- a/tests/manual/rhi/texturearray/texturearray.cpp
+++ b/tests/manual/rhi/texturearray/texturearray.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
#include <QElapsedTimer>
diff --git a/tests/manual/rhi/texuploads/texuploads.cpp b/tests/manual/rhi/texuploads/texuploads.cpp
index 566bc48474..5723d19be9 100644
--- a/tests/manual/rhi/texuploads/texuploads.cpp
+++ b/tests/manual/rhi/texuploads/texuploads.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
#include "../shared/cube.h"
@@ -192,7 +192,7 @@ void Window::customRender()
if (d.testStage == 6) {
const QRhiTexture::NativeTexture nativeTexture = d.tex->nativeTexture();
if (nativeTexture.object) {
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
if (graphicsApi == Metal) {
qDebug() << "Metal texture: " << nativeTexture.object;
// Now could cast to id<MTLTexture> and do something with
diff --git a/tests/manual/rhi/triquadcube/quadrenderer.cpp b/tests/manual/rhi/triquadcube/quadrenderer.cpp
index 4536412309..67392dc583 100644
--- a/tests/manual/rhi/triquadcube/quadrenderer.cpp
+++ b/tests/manual/rhi/triquadcube/quadrenderer.cpp
@@ -1,9 +1,9 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "quadrenderer.h"
#include <QFile>
-#include <QtGui/private/qshader_p.h>
+#include <rhi/qshader.h>
// Renders a quad using indexed drawing. No QRhiGraphicsPipeline is created, it
// expects to reuse the one created by TriangleRenderer. A separate
diff --git a/tests/manual/rhi/triquadcube/quadrenderer.h b/tests/manual/rhi/triquadcube/quadrenderer.h
index a9f882fe54..8e5b556e06 100644
--- a/tests/manual/rhi/triquadcube/quadrenderer.h
+++ b/tests/manual/rhi/triquadcube/quadrenderer.h
@@ -1,10 +1,10 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QUADRENDERER_H
#define QUADRENDERER_H
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
class QuadRenderer
{
diff --git a/tests/manual/rhi/triquadcube/texturedcuberenderer.cpp b/tests/manual/rhi/triquadcube/texturedcuberenderer.cpp
index 23b3c5c7c5..452c593e16 100644
--- a/tests/manual/rhi/triquadcube/texturedcuberenderer.cpp
+++ b/tests/manual/rhi/triquadcube/texturedcuberenderer.cpp
@@ -1,9 +1,9 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "texturedcuberenderer.h"
#include <QFile>
-#include <QtGui/private/qshader_p.h>
+#include <rhi/qshader.h>
#include "../shared/cube.h"
diff --git a/tests/manual/rhi/triquadcube/texturedcuberenderer.h b/tests/manual/rhi/triquadcube/texturedcuberenderer.h
index 59517b2fef..53fc9a30d1 100644
--- a/tests/manual/rhi/triquadcube/texturedcuberenderer.h
+++ b/tests/manual/rhi/triquadcube/texturedcuberenderer.h
@@ -1,10 +1,10 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TEXTUREDCUBERENDERER_H
#define TEXTUREDCUBERENDERER_H
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
class TexturedCubeRenderer
{
diff --git a/tests/manual/rhi/triquadcube/triangleoncuberenderer.cpp b/tests/manual/rhi/triquadcube/triangleoncuberenderer.cpp
index b66d869642..33dc0776d7 100644
--- a/tests/manual/rhi/triquadcube/triangleoncuberenderer.cpp
+++ b/tests/manual/rhi/triquadcube/triangleoncuberenderer.cpp
@@ -1,9 +1,9 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "triangleoncuberenderer.h"
#include <QFile>
-#include <QtGui/private/qshader_p.h>
+#include <rhi/qshader.h>
// toggle to test the preserved content (no clear) path
const bool IMAGE_UNDER_OFFSCREEN_RENDERING = false;
diff --git a/tests/manual/rhi/triquadcube/triangleoncuberenderer.h b/tests/manual/rhi/triquadcube/triangleoncuberenderer.h
index ae8134601e..9d77feb253 100644
--- a/tests/manual/rhi/triquadcube/triangleoncuberenderer.h
+++ b/tests/manual/rhi/triquadcube/triangleoncuberenderer.h
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TRIANGLEONCUBERENDERER_H
#define TRIANGLEONCUBERENDERER_H
diff --git a/tests/manual/rhi/triquadcube/trianglerenderer.cpp b/tests/manual/rhi/triquadcube/trianglerenderer.cpp
index 160a94ef46..21e3e791b4 100644
--- a/tests/manual/rhi/triquadcube/trianglerenderer.cpp
+++ b/tests/manual/rhi/triquadcube/trianglerenderer.cpp
@@ -1,9 +1,9 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "trianglerenderer.h"
#include <QFile>
-#include <QtGui/private/qshader_p.h>
+#include <rhi/qshader.h>
//#define VBUF_IS_DYNAMIC
diff --git a/tests/manual/rhi/triquadcube/trianglerenderer.h b/tests/manual/rhi/triquadcube/trianglerenderer.h
index ec591eb284..bad2198be5 100644
--- a/tests/manual/rhi/triquadcube/trianglerenderer.h
+++ b/tests/manual/rhi/triquadcube/trianglerenderer.h
@@ -1,10 +1,10 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TRIANGLERENDERER_H
#define TRIANGLERENDERER_H
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
class TriangleRenderer
{
diff --git a/tests/manual/rhi/triquadcube/triquadcube.cpp b/tests/manual/rhi/triquadcube/triquadcube.cpp
index 81e6247680..2ef0d567db 100644
--- a/tests/manual/rhi/triquadcube/triquadcube.cpp
+++ b/tests/manual/rhi/triquadcube/triquadcube.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// An example exercising more than a single feature. Enables profiling
// (resource logging to a file) and inserts debug markers and sets some
@@ -35,7 +35,6 @@ struct {
QSize lastOutputSize;
int frameCount = 0;
QFile profOut;
- QVarLengthArray<float, 64> gpuFrameTimes;
QElapsedTimer gpuFrameTimePrintTimer;
} d;
@@ -136,20 +135,8 @@ void Window::customInit()
// With Vulkan at least we should see some details from the memory allocator.
qDebug() << m_r->statistics();
- // Every two seconds try printing an average of the gpu frame times.
+ // Every two seconds try printing last known gpu frame time.
d.gpuFrameTimePrintTimer.start();
- m_r->addGpuFrameTimeCallback([](float elapsedMs) {
- d.gpuFrameTimes.append(elapsedMs);
- if (d.gpuFrameTimePrintTimer.elapsed() > 2000) {
- float at = 0.0f;
- for (float t : d.gpuFrameTimes)
- at += t;
- at /= d.gpuFrameTimes.count();
- qDebug() << "Average GPU frame time" << at;
- d.gpuFrameTimes.clear();
- d.gpuFrameTimePrintTimer.restart();
- }
- });
}
void Window::customRelease()
@@ -170,6 +157,11 @@ void Window::customRender()
const QSize outputSize = m_sc->currentPixelSize();
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
+ if (d.gpuFrameTimePrintTimer.elapsed() > 2000) {
+ qDebug() << "Last completed GPU frame time" << cb->lastCompletedGpuTime() << "seconds";
+ d.gpuFrameTimePrintTimer.restart();
+ }
+
if (outputSize != d.lastOutputSize) {
d.triRenderer.resize(outputSize);
if (!d.triangleOnly) {
diff --git a/tests/manual/shortcuts/main.cpp b/tests/manual/shortcuts/main.cpp
index 44be64d974..514d62a484 100644
--- a/tests/manual/shortcuts/main.cpp
+++ b/tests/manual/shortcuts/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QVBoxLayout>
diff --git a/tests/manual/socketengine/main.cpp b/tests/manual/socketengine/main.cpp
index d79f14f339..54457ed20c 100644
--- a/tests/manual/socketengine/main.cpp
+++ b/tests/manual/socketengine/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QDebug>
#include <qtest.h>
@@ -13,6 +13,8 @@
#include <cstdio>
#include <QCoreApplication>
+using namespace std::chrono_literals;
+
const int bufsize = 16*1024;
char buf[bufsize];
@@ -39,7 +41,7 @@ int main(int argc, char**argv)
int r = socketEngine->connectToHost(QHostAddress("74.125.77.99"), 80); // google
bool readyToRead = false;
bool readyToWrite = false;
- socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, true, true, 10*1000);
+ socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, true, true, 10s);
if (r <= 0) //timeout or error
exit(1);
if (readyToWrite) {
@@ -49,7 +51,7 @@ int main(int argc, char**argv)
if (ret == request.length()) {
// read the response in a loop
do {
- bool waitReadResult = socketEngine->waitForRead(10*1000);
+ bool waitReadResult = socketEngine->waitForRead(10s);
int available = socketEngine->bytesAvailable();
if (waitReadResult == true && available == 0) {
// disconnected
diff --git a/tests/manual/startsystemmove/main.cpp b/tests/manual/startsystemmove/main.cpp
index 875367e6b5..2889bdc4f7 100644
--- a/tests/manual/startsystemmove/main.cpp
+++ b/tests/manual/startsystemmove/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui>
diff --git a/tests/manual/stereographicsview/main.cpp b/tests/manual/stereographicsview/main.cpp
index 6564e8efc4..a085f782b3 100644
--- a/tests/manual/stereographicsview/main.cpp
+++ b/tests/manual/stereographicsview/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
diff --git a/tests/manual/stereographicsview/mainwindow.cpp b/tests/manual/stereographicsview/mainwindow.cpp
index 003b4da8c3..86269b096c 100644
--- a/tests/manual/stereographicsview/mainwindow.cpp
+++ b/tests/manual/stereographicsview/mainwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
#include "./ui_mainwindow.h"
diff --git a/tests/manual/stereographicsview/mainwindow.h b/tests/manual/stereographicsview/mainwindow.h
index 4a65dad68d..5219aae644 100644
--- a/tests/manual/stereographicsview/mainwindow.h
+++ b/tests/manual/stereographicsview/mainwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
diff --git a/tests/manual/stereographicsview/mygraphicsview.cpp b/tests/manual/stereographicsview/mygraphicsview.cpp
index dc6e39f4a0..933ee00835 100644
--- a/tests/manual/stereographicsview/mygraphicsview.cpp
+++ b/tests/manual/stereographicsview/mygraphicsview.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mygraphicsview.h"
#include <QResizeEvent>
diff --git a/tests/manual/stereographicsview/mygraphicsview.h b/tests/manual/stereographicsview/mygraphicsview.h
index 03204cb316..57cf902d5d 100644
--- a/tests/manual/stereographicsview/mygraphicsview.h
+++ b/tests/manual/stereographicsview/mygraphicsview.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MYGRAPHICSVIEW_H
#define MYGRAPHICSVIEW_H
diff --git a/tests/manual/textrendering/codeeditor/codeeditor.cpp b/tests/manual/textrendering/codeeditor/codeeditor.cpp
index 15f84622e4..3b7fd58d30 100644
--- a/tests/manual/textrendering/codeeditor/codeeditor.cpp
+++ b/tests/manual/textrendering/codeeditor/codeeditor.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "codeeditor.h"
diff --git a/tests/manual/textrendering/codeeditor/codeeditor.h b/tests/manual/textrendering/codeeditor/codeeditor.h
index e8e6a583f0..583bf3b4a0 100644
--- a/tests/manual/textrendering/codeeditor/codeeditor.h
+++ b/tests/manual/textrendering/codeeditor/codeeditor.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef CODEEDITOR_H
#define CODEEDITOR_H
diff --git a/tests/manual/textrendering/codeeditor/main.cpp b/tests/manual/textrendering/codeeditor/main.cpp
index 20b72b18ae..3e3d98ae31 100644
--- a/tests/manual/textrendering/codeeditor/main.cpp
+++ b/tests/manual/textrendering/codeeditor/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
diff --git a/tests/manual/textrendering/glyphshaping/main.cpp b/tests/manual/textrendering/glyphshaping/main.cpp
index b9f94d46b5..bbe36a8abf 100644
--- a/tests/manual/textrendering/glyphshaping/main.cpp
+++ b/tests/manual/textrendering/glyphshaping/main.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include <QApplication>
#include <QDir>
diff --git a/tests/manual/textrendering/nativetext/main.cpp b/tests/manual/textrendering/nativetext/main.cpp
index 265ebcac45..65a361eddb 100644
--- a/tests/manual/textrendering/nativetext/main.cpp
+++ b/tests/manual/textrendering/nativetext/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
diff --git a/tests/manual/textrendering/textperformance/main.cpp b/tests/manual/textrendering/textperformance/main.cpp
index fa3a5122ce..babeb2f810 100644
--- a/tests/manual/textrendering/textperformance/main.cpp
+++ b/tests/manual/textrendering/textperformance/main.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include <QApplication>
#include <QDialog>
diff --git a/tests/manual/touch/main.cpp b/tests/manual/touch/main.cpp
index 80155ddb57..dcc29799aa 100644
--- a/tests/manual/touch/main.cpp
+++ b/tests/manual/touch/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QAction>
diff --git a/tests/manual/touchGraphicsItem/main.cpp b/tests/manual/touchGraphicsItem/main.cpp
index 727e2b4cd2..7ec9272a86 100644
--- a/tests/manual/touchGraphicsItem/main.cpp
+++ b/tests/manual/touchGraphicsItem/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
diff --git a/tests/manual/transientwindow/main.cpp b/tests/manual/transientwindow/main.cpp
index 99136310a8..67b731dc95 100644
--- a/tests/manual/transientwindow/main.cpp
+++ b/tests/manual/transientwindow/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
#include <QApplication>
diff --git a/tests/manual/transientwindow/mainwindow.cpp b/tests/manual/transientwindow/mainwindow.cpp
index 273bd17edb..747304605d 100644
--- a/tests/manual/transientwindow/mainwindow.cpp
+++ b/tests/manual/transientwindow/mainwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
#include <QDebug>
diff --git a/tests/manual/transientwindow/mainwindow.h b/tests/manual/transientwindow/mainwindow.h
index 472c2df383..8659f122e9 100644
--- a/tests/manual/transientwindow/mainwindow.h
+++ b/tests/manual/transientwindow/mainwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
diff --git a/tests/manual/triangulator/main.cpp b/tests/manual/triangulator/main.cpp
index 9e6de3666e..3a384f5888 100644
--- a/tests/manual/triangulator/main.cpp
+++ b/tests/manual/triangulator/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QMainWindow>
diff --git a/tests/manual/triangulator/triviswidget.cpp b/tests/manual/triangulator/triviswidget.cpp
index aa47001521..5a14fc9ca1 100644
--- a/tests/manual/triangulator/triviswidget.cpp
+++ b/tests/manual/triangulator/triviswidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "triviswidget.h"
#include <QVBoxLayout>
diff --git a/tests/manual/triangulator/triviswidget.h b/tests/manual/triangulator/triviswidget.h
index 4d1028c2d0..d52d9c2942 100644
--- a/tests/manual/triangulator/triviswidget.h
+++ b/tests/manual/triangulator/triviswidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TRIVISWIDGET_H
#define TRIVISWIDGET_H
diff --git a/tests/manual/unc/main.cpp b/tests/manual/unc/main.cpp
index 4ba7105ee5..98b50e775c 100644
--- a/tests/manual/unc/main.cpp
+++ b/tests/manual/unc/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QDialog>
diff --git a/tests/manual/wasm/CMakeLists.txt b/tests/manual/wasm/CMakeLists.txt
index f625defb21..b13f6781b8 100644
--- a/tests/manual/wasm/CMakeLists.txt
+++ b/tests/manual/wasm/CMakeLists.txt
@@ -7,6 +7,7 @@ add_subdirectory(a11y)
if(QT_FEATURE_widgets)
add_subdirectory(cursors)
add_subdirectory(localfiles)
+add_subdirectory(localfonts)
add_subdirectory(qstdweb)
add_subdirectory(clipboard)
endif()
diff --git a/tests/manual/wasm/a11y/CMakeLists.txt b/tests/manual/wasm/a11y/CMakeLists.txt
index 5268d53c8b..dee39e1f5f 100644
--- a/tests/manual/wasm/a11y/CMakeLists.txt
+++ b/tests/manual/wasm/a11y/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
if(QT_FEATURE_widgets)
add_subdirectory(basic_widgets)
endif()
diff --git a/tests/manual/wasm/a11y/basic_widgets/CMakeLists.txt b/tests/manual/wasm/a11y/basic_widgets/CMakeLists.txt
index 11534bdf68..a028f96e1c 100644
--- a/tests/manual/wasm/a11y/basic_widgets/CMakeLists.txt
+++ b/tests/manual/wasm/a11y/basic_widgets/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_manual_test(a11y_basic_widgets
GUI
SOURCES
diff --git a/tests/manual/wasm/a11y/basic_widgets/basic_widgets.html b/tests/manual/wasm/a11y/basic_widgets/basic_widgets.html
index 899e72bb00..091809be5c 100644
--- a/tests/manual/wasm/a11y/basic_widgets/basic_widgets.html
+++ b/tests/manual/wasm/a11y/basic_widgets/basic_widgets.html
@@ -7,7 +7,7 @@
<script>
window.onload = async () => {
- let qt_instance = await createQtAppInstance({
+ let qt_instance = await a11y_basic_widgets_entry({
qtContainerElements: [document.getElementById("qt_container")],
});
}
diff --git a/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.cpp b/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.cpp
index eaf736160a..dc1688f5b9 100644
--- a/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.cpp
+++ b/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "basica11ywidget.h"
diff --git a/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.h b/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.h
index c8d9a4157b..b990d163e5 100644
--- a/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.h
+++ b/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
#include "tabswidget.h"
diff --git a/tests/manual/wasm/a11y/basic_widgets/main.cpp b/tests/manual/wasm/a11y/basic_widgets/main.cpp
index 5417195ecf..52d72428bb 100644
--- a/tests/manual/wasm/a11y/basic_widgets/main.cpp
+++ b/tests/manual/wasm/a11y/basic_widgets/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QtWidgets>
diff --git a/tests/manual/wasm/a11y/basic_widgets/tabswidget.cpp b/tests/manual/wasm/a11y/basic_widgets/tabswidget.cpp
index 19bd0cf71b..63428c965a 100644
--- a/tests/manual/wasm/a11y/basic_widgets/tabswidget.cpp
+++ b/tests/manual/wasm/a11y/basic_widgets/tabswidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tabswidget.h"
diff --git a/tests/manual/wasm/a11y/basic_widgets/tabswidget.h b/tests/manual/wasm/a11y/basic_widgets/tabswidget.h
index 0d7983cb42..6405c0dab7 100644
--- a/tests/manual/wasm/a11y/basic_widgets/tabswidget.h
+++ b/tests/manual/wasm/a11y/basic_widgets/tabswidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TABDIALOG_H
#define TABDIALOG_H
diff --git a/tests/manual/wasm/clipboard/CMakeLists.txt b/tests/manual/wasm/clipboard/CMakeLists.txt
index 80e049097b..40fb8ca308 100644
--- a/tests/manual/wasm/clipboard/CMakeLists.txt
+++ b/tests/manual/wasm/clipboard/CMakeLists.txt
@@ -10,6 +10,8 @@ qt_internal_add_manual_test(clipboard
SOURCES
main.cpp
mainwindow.cpp mainwindow.h mainwindow.ui
+ NO_PCH_SOURCES
+ main.cpp # undef QT_NO_FOREACH
LIBRARIES
Qt::Core
Qt::Gui
diff --git a/tests/manual/wasm/clipboard/main.cpp b/tests/manual/wasm/clipboard/main.cpp
index 7142125ff7..aa838f6670 100644
--- a/tests/manual/wasm/clipboard/main.cpp
+++ b/tests/manual/wasm/clipboard/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
diff --git a/tests/manual/wasm/clipboard/mainwindow.cpp b/tests/manual/wasm/clipboard/mainwindow.cpp
index 77d6582775..c67c795b05 100644
--- a/tests/manual/wasm/clipboard/mainwindow.cpp
+++ b/tests/manual/wasm/clipboard/mainwindow.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "mainwindow.h"
#include "ui_mainwindow.h"
@@ -12,6 +14,7 @@
#include <QKeyEvent>
#include <QMimeDatabase>
#include <QFileInfo>
+#include <QCryptographicHash>
#ifdef Q_OS_WASM
#include <emscripten.h>
@@ -262,20 +265,23 @@ void MainWindow::dropEvent(QDropEvent* e)
QString urlStr = url.toDisplayString();
int size = urlStr.length();
sizeStr.setNum(size);
+
+ QString fileName = url.toLocalFile();
+ QString sha1;
+ QFile file(fileName);
+ if (file.exists()) {
+ file.open(QFile::ReadOnly);
+ sha1 = QCryptographicHash::hash(file.readAll(), QCryptographicHash::Sha1).toHex();
+ }
+
ui->textEdit_2->insertPlainText(" Drop has url data length: " + sizeStr + "\n");
- ui->textEdit_2->insertPlainText(urlStr + "\n");
-
- QString fname = url.toLocalFile();
- QFileInfo info(fname);
- if (info.exists()) { // this is a file
- QMimeDatabase db;
- QMimeType mt = db.mimeTypeForFile(info);
- if (mt.name().contains("image")) {
- QImage image = QImage(fname);
- setImage(image);
- }
+ if (sha1.isEmpty()) {
+ ui->textEdit->insertPlainText(urlStr);
}
+ ui->textEdit_2->insertPlainText(" " + urlStr + (!sha1.isEmpty() ? " sha1 " + sha1.left(8) : "") + "\n");
+
}
+ ui->textEdit_2->insertPlainText("\n");
if (e->mimeData()->hasImage()) {
qsizetype imageSize = qvariant_cast<QImage>(e->mimeData()->imageData()).sizeInBytes();
@@ -292,14 +298,15 @@ void MainWindow::dropEvent(QDropEvent* e)
int size = e->mimeData()->html().length();
sizeStr.setNum(size);
ui->textEdit_2->insertPlainText(" Drop has html data length: " + sizeStr + "\n");
- ui->textEdit_2->insertPlainText(e->mimeData()->html()+"\n");
- ui->textEdit->insertHtml(e->mimeData()->html()+"<br>");
+ for (const auto &line : e->mimeData()->html().split('\n', Qt::SkipEmptyParts))
+ ui->textEdit_2->insertPlainText(" " + line + "\n");
}
if (e->mimeData()->hasText()) {
int size = e->mimeData()->text().length();
sizeStr.setNum(size);
ui->textEdit_2->insertPlainText(" Drop has text data length: " + sizeStr + "\n");
- ui->textEdit_2->insertPlainText(e->mimeData()->text());
+ for (const auto &line : e->mimeData()->text().split('\n', Qt::SkipEmptyParts))
+ ui->textEdit_2->insertPlainText(" " + line + "\n");
}
const QString message = tr(" Drop accepted, %1 ")
diff --git a/tests/manual/wasm/clipboard/mainwindow.h b/tests/manual/wasm/clipboard/mainwindow.h
index d06b213971..fe101ad494 100644
--- a/tests/manual/wasm/clipboard/mainwindow.h
+++ b/tests/manual/wasm/clipboard/mainwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
diff --git a/tests/manual/wasm/cursors/MainWindow.cpp b/tests/manual/wasm/cursors/MainWindow.cpp
index b62c6752aa..c6e4fbcca1 100644
--- a/tests/manual/wasm/cursors/MainWindow.cpp
+++ b/tests/manual/wasm/cursors/MainWindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "MainWindow.h"
#include "ui_MainWindow.h"
diff --git a/tests/manual/wasm/cursors/MainWindow.h b/tests/manual/wasm/cursors/MainWindow.h
index ed570a72c4..ebaeed9e5c 100644
--- a/tests/manual/wasm/cursors/MainWindow.h
+++ b/tests/manual/wasm/cursors/MainWindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#pragma once
#include <QMainWindow>
diff --git a/tests/manual/wasm/cursors/main.cpp b/tests/manual/wasm/cursors/main.cpp
index 99a1d41524..9a59cdd994 100644
--- a/tests/manual/wasm/cursors/main.cpp
+++ b/tests/manual/wasm/cursors/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "MainWindow.h"
#include <QApplication>
diff --git a/tests/manual/wasm/eventloop/asyncify_exec/main.cpp b/tests/manual/wasm/eventloop/asyncify_exec/main.cpp
index ab3018c12e..f09163184d 100644
--- a/tests/manual/wasm/eventloop/asyncify_exec/main.cpp
+++ b/tests/manual/wasm/eventloop/asyncify_exec/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore>
// This test shows how to use asyncify to enable blocking the main
diff --git a/tests/manual/wasm/eventloop/dialog_exec/main.cpp b/tests/manual/wasm/eventloop/dialog_exec/main.cpp
index adf8a02c37..f5b072fc0b 100644
--- a/tests/manual/wasm/eventloop/dialog_exec/main.cpp
+++ b/tests/manual/wasm/eventloop/dialog_exec/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui>
#include <QtWidgets>
diff --git a/tests/manual/wasm/eventloop/eventloop_auto/CMakeLists.txt b/tests/manual/wasm/eventloop/eventloop_auto/CMakeLists.txt
index 9bfa875be7..88ddfac4c8 100644
--- a/tests/manual/wasm/eventloop/eventloop_auto/CMakeLists.txt
+++ b/tests/manual/wasm/eventloop/eventloop_auto/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
include_directories(../../qtwasmtestlib/)
# default buid
diff --git a/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto.html b/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto.html
index 7ff9d8e7f2..e8e35abcbb 100644
--- a/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto.html
+++ b/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto.html
@@ -3,7 +3,7 @@
<script type="text/javascript" src="eventloop_auto.js"></script>
<script>
window.onload = () => {
- runTestCase(document.getElementById("log"));
+ runTestCase(eventloop_auto_entry, document.getElementById("log"));
};
</script>
<p>Running event dispatcher auto test.</p>
diff --git a/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto_asyncify.html b/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto_asyncify.html
index a277c9b9e0..f09b29d85b 100644
--- a/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto_asyncify.html
+++ b/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto_asyncify.html
@@ -3,7 +3,7 @@
<script type="text/javascript" src="eventloop_auto_asyncify.js"></script>
<script>
window.onload = () => {
- runTestCase(document.getElementById("log"));
+ runTestCase(eventloop_auto_asyncify_entry, document.getElementById("log"));
};
</script>
<p>Running event dispatcher auto test.</p>
diff --git a/tests/manual/wasm/eventloop/eventloop_auto/main.cpp b/tests/manual/wasm/eventloop/eventloop_auto/main.cpp
index b2ff6779fd..32af372b62 100644
--- a/tests/manual/wasm/eventloop/eventloop_auto/main.cpp
+++ b/tests/manual/wasm/eventloop/eventloop_auto/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QCoreApplication>
#include <QtCore/QEvent>
diff --git a/tests/manual/wasm/eventloop/main_exec/main.cpp b/tests/manual/wasm/eventloop/main_exec/main.cpp
index c981fd4c2c..17eccafe18 100644
--- a/tests/manual/wasm/eventloop/main_exec/main.cpp
+++ b/tests/manual/wasm/eventloop/main_exec/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui>
// This example demonstrates how the standard Qt main()
diff --git a/tests/manual/wasm/eventloop/main_noexec/main.cpp b/tests/manual/wasm/eventloop/main_noexec/main.cpp
index 18d0542137..6ddd88bd14 100644
--- a/tests/manual/wasm/eventloop/main_noexec/main.cpp
+++ b/tests/manual/wasm/eventloop/main_noexec/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui>
// This example demonstrates how to create QGuiApplication
diff --git a/tests/manual/wasm/eventloop/thread_exec/main.cpp b/tests/manual/wasm/eventloop/thread_exec/main.cpp
index b24be17e5b..589066b34d 100644
--- a/tests/manual/wasm/eventloop/thread_exec/main.cpp
+++ b/tests/manual/wasm/eventloop/thread_exec/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui>
class EventTarget : public QObject
diff --git a/tests/manual/wasm/localfiles/main.cpp b/tests/manual/wasm/localfiles/main.cpp
index 39d9f21901..862bff50a4 100644
--- a/tests/manual/wasm/localfiles/main.cpp
+++ b/tests/manual/wasm/localfiles/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets/QtWidgets>
#include <emscripten/val.h>
@@ -91,7 +91,8 @@ private Q_SLOTS:
{
QFileDialog::getOpenFileContent(
m_filterEdit->text(),
- std::bind(&AppWindow::onFileContentReady, this, std::placeholders::_1, std::placeholders::_2));
+ std::bind(&AppWindow::onFileContentReady, this, std::placeholders::_1, std::placeholders::_2),
+ &m_loadFileUi);
}
void onSaveClicked()
diff --git a/tests/manual/wasm/localfonts/CMakeLists.txt b/tests/manual/wasm/localfonts/CMakeLists.txt
new file mode 100644
index 0000000000..b5df4ad9fa
--- /dev/null
+++ b/tests/manual/wasm/localfonts/CMakeLists.txt
@@ -0,0 +1,4 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(fontloading)
diff --git a/tests/manual/wasm/localfonts/fontloading/CMakeLists.txt b/tests/manual/wasm/localfonts/fontloading/CMakeLists.txt
new file mode 100644
index 0000000000..c3dc37d27d
--- /dev/null
+++ b/tests/manual/wasm/localfonts/fontloading/CMakeLists.txt
@@ -0,0 +1,20 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_manual_test(fontloading
+ GUI
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::Widgets
+)
+
+add_custom_command(
+ TARGET fontloading POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/fontloading.html
+ ${CMAKE_CURRENT_BINARY_DIR}/fontloading.html)
+#add_custom_target(html DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/fontloading.html)
+#add_dependencies(fontloading html)
diff --git a/tests/manual/wasm/localfonts/fontloading/fontloading.html b/tests/manual/wasm/localfonts/fontloading/fontloading.html
new file mode 100644
index 0000000000..619217205b
--- /dev/null
+++ b/tests/manual/wasm/localfonts/fontloading/fontloading.html
@@ -0,0 +1,167 @@
+<!doctype html>
+
+<script src="qtloader.js"></script>
+<script src="fontloading.js"></script>
+
+<style>
+ body {
+ padding: 5px;
+ }
+
+ .container {
+ display: flex;
+ }
+
+ .column {
+ flex: 1;
+ }
+</style>
+
+
+<script>
+
+ // UI
+ let familyCount;
+ let families;
+
+ // Data
+ let fontFamilies = new Set();
+ let loadStartTime;
+
+ // App
+ let instance;
+
+ async function updatePermissionStatus() {
+ let permissonStatusElement = document.getElementById("permissonStatus");
+ let permissionStatus = await navigator.permissions.query({ name: "local-fonts" })
+ permissonStatusElement.innerHTML = permissionStatus.state;
+ }
+
+ window.onload = async () => {
+ let supported = document.getElementById("supported");
+ let permissonStatus = document.getElementById("permissonStatus");
+ let permission = document.getElementById("permission");
+ let defaultfonts = document.getElementById("defaultfonts");
+ let allfonts = document.getElementById("allfonts");
+ let start = document.getElementById("start");
+ let loadFonts = document.getElementById("loadFonts");
+ startupTime = document.getElementById("startupTime");
+ familyCount = document.getElementById("familyCount");
+ families = document.getElementById("families");
+
+ fontFamilies.clear();
+
+ let localFontsAccessSupported = window.queryLocalFonts ? true : false
+ if (localFontsAccessSupported) {
+ supported.innerHTML = "True"
+ } else {
+ supported.innerHTML = "False"
+ return;
+ }
+
+ updatePermissionStatus();
+
+ const module = WebAssembly.compileStreaming(fetch('fontloading.wasm'));
+
+ start.onclick = async () => {
+
+ // Delete any previous instance.
+ if (instance) {
+ instance.deleteapp(); // see main.cpp
+ instance = null;
+ }
+
+ loadStartTime = performance.now();
+ startupTime.innerHTML = 0;
+ familyCount.innerHTML = 0;
+ let localFontFamilyLoadCollection = "NoFontFamilies"
+ if (defaultfonts.checked)
+ localFontFamilyLoadCollection = "DefaultFontFamilies"
+ else if (allfonts.checked)
+ localFontFamilyLoadCollection = "AllFontFamilies"
+
+ let qtcontainer = document.getElementById("qtcontainer");
+ qtcontainer.innerHTML = ""; // clear children
+ qtcontainer.style.visibility = "hidden";
+
+ let extraFonts = document.getElementById("extrafonts").value.split(",");
+
+ let config = {
+ qt: {
+ module: module,
+ containerElements: [qtcontainer],
+ onLoaded: () => {
+ console.log("JS: onLoaded")
+ qtcontainer.style.visibility = "visible";
+ },
+ entryFunction: window.fontloading_entry,
+ localFonts: {
+ requestPermission: permission.checked,
+ familiesCollection: localFontFamilyLoadCollection,
+ extraFamilies: extraFonts,
+ }
+ }
+ }
+ instance = await qtLoad(config);
+
+ updatePermissionStatus();
+ }
+
+ loadFonts.onclick = async () => {
+ loadStartTime = null; // disable timer
+ let fontsFamilies = document.getElementById("extraRuntimeFontFamilies").value.split(",");
+ console.log("extraRuntimeFontFamilies: " + fontsFamilies);
+ instance.qtLoadLocalFontFamilies(fontsFamilies);
+ }
+ };
+
+ function fontFamiliesLoaded(count) {
+ familyCount.innerHTML = count;
+ if (loadStartTime) {
+ elapsed = performance.now() - loadStartTime;
+ startupTime.innerHTML = Math.round(elapsed + 1);
+ }
+ }
+
+ function fontFamilyLoaded(family) {
+ fontFamilies.add(family);
+ }
+
+</script>
+
+<h2>Local Font Loading Test</h2>
+<p>Click "Load" button below to load the Qt test app with the specified settings. This test provides additional logs on the JavaScript console.</p>
+
+<div class="container">
+ <div class="column">
+ <span>Browser supports the Local Font Access API: </span><span id="supported" style="font-weight: bold;"></span><br>
+ <span>Local Font Access permission status: </span><span id="permissonStatus" style="font-weight: bold;"></span><br>
+ <br>
+ <input type="checkbox" id="permission"><label for="permission">Ask for Local Font access permission on startup</label><br>
+ <input type="radio" id="nofonts" name="fontset"></input><label for="nofonts">No local fonts</label><br>
+ <input type="radio" id="defaultfonts" name="fontset" checked></input><label for="defaultfonts">Default local fonts (web-safe fonts)</label><br>
+ <input type="radio" id="allfonts" name="fontset"></input><label for="allfonts">All local fonts (warning: extremely slow)</label><br>
+ <br>
+ <label for="extrafonts">Extra Font Families (comma separated) </label><input type="text" id="extrafonts" value=""></input><br>
+ <br>
+ <input type="checkbox" id="permission"><label for="permission">Enable 'Fonts' Logging Category</label><br>
+ <input type="checkbox" id="permission"><label for="permission">Enable Font Streaming</label><br>
+ <br>
+ <button type="button" id="start">Start Application</button><br>
+ <br>
+
+ <span>Startup time: </span><span id="startupTime"></span><br>
+ <span>Font family count: </span><span id="familyCount"></span><br>
+ <span>Font families: </span><span id="families"></span><br>
+ <br>
+
+ <button type="button" id="loadFonts">Load Extra Fonts</button>
+ <input type="text" id="extraRuntimeFontFamilies" value=""></input><br>
+ </div>
+
+ <div class="column">
+ <div id="qtcontainer" style="width: 100%; height: 300px; visibility: hidden;"></div>
+ </div>
+</div>
+
+
diff --git a/tests/manual/wasm/localfonts/fontloading/main.cpp b/tests/manual/wasm/localfonts/fontloading/main.cpp
new file mode 100644
index 0000000000..3824c8b871
--- /dev/null
+++ b/tests/manual/wasm/localfonts/fontloading/main.cpp
@@ -0,0 +1,78 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtGui>
+#include <QtWidgets>
+
+#include <emscripten/bind.h>
+#include <emscripten/val.h>
+
+using namespace emscripten;
+
+class FontViewer : public QWidget
+{
+public:
+ FontViewer() {
+ QTextEdit *edit = new QTextEdit;
+ edit->setPlainText("The quick brown fox jumps over the lazy dog\nHow quickly daft jumping zebras vex\nPack my box with five dozen liquor jugs");
+
+ QComboBox *combo = new QComboBox;
+ combo->addItems(QFontDatabase::families());
+
+ connect(combo, &QComboBox::currentTextChanged, [=](const QString &family) {
+ QFont font(family);
+ edit->setFont(font);
+ });
+
+ QObject::connect(qApp, &QGuiApplication::fontDatabaseChanged, [=]() {
+ QStringList families = QFontDatabase::families();
+ combo->clear();
+ combo->addItems(families);
+ });
+
+ QLayout *layout = new QVBoxLayout;
+ layout->addWidget(edit);
+ layout->addWidget(combo);
+ setLayout(layout);
+ }
+};
+
+FontViewer *g_viewer = nullptr;
+QApplication *g_app = nullptr;
+
+void deleteapp() {
+ delete g_viewer;
+ delete g_app;
+};
+
+EMSCRIPTEN_BINDINGS(fonloading) {
+ function("deleteapp", &deleteapp);
+}
+
+int main(int argc, char **argv)
+{
+ qDebug() << "C++ main: Creating application";
+ g_app = new QApplication(argc, argv);
+
+ // Make sure there is one call to fontFamiliesLoaded at startup,
+ // even if no further fonts are loaded.
+ QTimer::singleShot(0, [=]() {
+ emscripten::val window = emscripten::val::global("window");
+ window.call<void>("fontFamiliesLoaded", QFontDatabase::families().count());
+ });
+
+ g_viewer = new FontViewer();
+ g_viewer->show();
+
+ QObject::connect(g_app, &QGuiApplication::fontDatabaseChanged, [=]() {
+ QStringList families = QFontDatabase::families();
+
+ emscripten::val window = emscripten::val::global("window");
+
+ window.call<void>("fontFamiliesLoaded", families.count());
+ for (int i = 0; i < families.count(); ++i) {
+ window.call<void>("fontFamilyLoaded", families[i].toStdString());
+ }
+ });
+}
+
diff --git a/tests/manual/wasm/network/echo_client_mainthread/CMakeLists.txt b/tests/manual/wasm/network/echo_client_mainthread/CMakeLists.txt
index 05416c0b66..4b81661c79 100644
--- a/tests/manual/wasm/network/echo_client_mainthread/CMakeLists.txt
+++ b/tests/manual/wasm/network/echo_client_mainthread/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_manual_test(echo_client_mainthread
GUI
SOURCES
diff --git a/tests/manual/wasm/network/echo_client_mainthread/main.cpp b/tests/manual/wasm/network/echo_client_mainthread/main.cpp
index 9a0ac1a60a..ef696e5978 100644
--- a/tests/manual/wasm/network/echo_client_mainthread/main.cpp
+++ b/tests/manual/wasm/network/echo_client_mainthread/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore>
#include <QtNetwork>
diff --git a/tests/manual/wasm/network/echo_client_secondarythread/CMakeLists.txt b/tests/manual/wasm/network/echo_client_secondarythread/CMakeLists.txt
index a1f2bef254..d240e9e70b 100644
--- a/tests/manual/wasm/network/echo_client_secondarythread/CMakeLists.txt
+++ b/tests/manual/wasm/network/echo_client_secondarythread/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_manual_test(echo_client_secondarythread
GUI
SOURCES
diff --git a/tests/manual/wasm/network/echo_client_secondarythread/main.cpp b/tests/manual/wasm/network/echo_client_secondarythread/main.cpp
index 615706727f..52cea93495 100644
--- a/tests/manual/wasm/network/echo_client_secondarythread/main.cpp
+++ b/tests/manual/wasm/network/echo_client_secondarythread/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore>
#include <QtNetwork>
diff --git a/tests/manual/wasm/network/echo_server/CMakeLists.txt b/tests/manual/wasm/network/echo_server/CMakeLists.txt
index cf98163fb8..72ec413a0e 100644
--- a/tests/manual/wasm/network/echo_server/CMakeLists.txt
+++ b/tests/manual/wasm/network/echo_server/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
project(echo_server)
cmake_minimum_required(VERSION 3.19)
diff --git a/tests/manual/wasm/network/echo_server/main.cpp b/tests/manual/wasm/network/echo_server/main.cpp
index 9ea15b7caa..3a67cabc79 100644
--- a/tests/manual/wasm/network/echo_server/main.cpp
+++ b/tests/manual/wasm/network/echo_server/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore>
#include <QtNetwork>
diff --git a/tests/manual/wasm/network/sockify_sockets_auto/CMakeLists.txt b/tests/manual/wasm/network/sockify_sockets_auto/CMakeLists.txt
index f69bd80259..68a3993778 100644
--- a/tests/manual/wasm/network/sockify_sockets_auto/CMakeLists.txt
+++ b/tests/manual/wasm/network/sockify_sockets_auto/CMakeLists.txt
@@ -1,8 +1,10 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_manual_test(sockify_sockets_auto
SOURCES
main.cpp
../../qtwasmtestlib/qtwasmtestlib.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Core
Qt::Network
)
diff --git a/tests/manual/wasm/network/sockify_sockets_auto/main.cpp b/tests/manual/wasm/network/sockify_sockets_auto/main.cpp
index 313ac716b6..b6aa232b4a 100644
--- a/tests/manual/wasm/network/sockify_sockets_auto/main.cpp
+++ b/tests/manual/wasm/network/sockify_sockets_auto/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtwasmtestlib.h>
#include <QtCore>
diff --git a/tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html b/tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html
index 5376249a71..080ada94e7 100644
--- a/tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html
+++ b/tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html
@@ -3,7 +3,7 @@
<script type="text/javascript" src="sockify_sockets_auto.js"></script>
<script>
window.onload = async () => {
- runTestCase(document.getElementById("log"));
+ runTestCase(sockify_sockets_auto_entry, document.getElementById("log"));
};
</script>
<p> Sockify tunneled sockets auto test.
diff --git a/tests/manual/wasm/qstdweb/CMakeLists.txt b/tests/manual/wasm/qstdweb/CMakeLists.txt
index 234b12bab9..39039c3910 100644
--- a/tests/manual/wasm/qstdweb/CMakeLists.txt
+++ b/tests/manual/wasm/qstdweb/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_manual_test(promise_auto
SOURCES
promise_main.cpp
@@ -25,7 +27,7 @@ qt_internal_add_manual_test(files_auto
SOURCES
files_main.cpp
../qtwasmtestlib/qtwasmtestlib.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Core
Qt::CorePrivate
Qt::GuiPrivate
@@ -49,7 +51,7 @@ qt_internal_add_manual_test(qwasmcompositor_auto
SOURCES
qwasmcompositor_main.cpp
../qtwasmtestlib/qtwasmtestlib.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Core
Qt::CorePrivate
Qt::GuiPrivate
@@ -70,3 +72,27 @@ add_custom_command(
${CMAKE_CURRENT_BINARY_DIR}/qtwasmtestlib.js)
target_link_options(qwasmcompositor_auto PRIVATE -sASYNCIFY -Os)
+
+qt_internal_add_manual_test(iodevices_auto
+ SOURCES
+ iodevices_main.cpp
+ ../qtwasmtestlib/qtwasmtestlib.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::CorePrivate
+ Qt::GuiPrivate
+)
+
+add_custom_command(
+ TARGET iodevices_auto POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/iodevices_auto.html
+ ${CMAKE_CURRENT_BINARY_DIR}/iodevices_auto.html)
+
+add_custom_command(
+ TARGET iodevices_auto POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/../qtwasmtestlib/qtwasmtestlib.js
+ ${CMAKE_CURRENT_BINARY_DIR}/qtwasmtestlib.js)
+
+target_link_options(iodevices_auto PRIVATE -sASYNCIFY -Os)
diff --git a/tests/manual/wasm/qstdweb/files_auto.html b/tests/manual/wasm/qstdweb/files_auto.html
index 71e8088dfb..9027fdc660 100644
--- a/tests/manual/wasm/qstdweb/files_auto.html
+++ b/tests/manual/wasm/qstdweb/files_auto.html
@@ -6,7 +6,7 @@
<script type="text/javascript" src="files_auto.js"></script>
<script>
window.onload = () => {
- runTestCase(document.getElementById("log"));
+ runTestCase(files_auto_entry, document.getElementById("log"));
};
</script>
<p>Running files auto test.</p>
diff --git a/tests/manual/wasm/qstdweb/files_main.cpp b/tests/manual/wasm/qstdweb/files_main.cpp
index 5c0e0e8292..45939feb10 100644
--- a/tests/manual/wasm/qstdweb/files_main.cpp
+++ b/tests/manual/wasm/qstdweb/files_main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QCoreApplication>
#include <QtCore/QEvent>
diff --git a/tests/manual/wasm/qstdweb/iodevices_auto.html b/tests/manual/wasm/qstdweb/iodevices_auto.html
new file mode 100644
index 0000000000..7937b8a483
--- /dev/null
+++ b/tests/manual/wasm/qstdweb/iodevices_auto.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<script type="text/javascript" src="qtwasmtestlib.js"></script>
+<script type="text/javascript" src="iodevices_auto.js"></script>
+<script>
+ window.onload = () => {
+ runTestCase(iodevices_auto_entry, document.getElementById("log"));
+ };
+</script>
+<p>Running qstdweb iodevices auto test.</p>
+<div id="log"></div>
diff --git a/tests/manual/wasm/qstdweb/iodevices_main.cpp b/tests/manual/wasm/qstdweb/iodevices_main.cpp
new file mode 100644
index 0000000000..0dbdd0084e
--- /dev/null
+++ b/tests/manual/wasm/qstdweb/iodevices_main.cpp
@@ -0,0 +1,103 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtCore/QtCore>
+#include <QtCore/private/qstdweb_p.h>
+
+#include <qtwasmtestlib.h>
+
+#include "emscripten.h"
+
+using qstdweb::ArrayBuffer;
+using qstdweb::Uint8Array;
+using qstdweb::Blob;
+using qstdweb::BlobIODevice;
+using qstdweb::Uint8ArrayIODevice;
+
+class WasmIoDevicesTest: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void blobIODevice();
+ void uint8ArrayIODevice();
+};
+
+// Creates a test arraybuffer with byte values [0..size] % 256 * 2
+char testByteValue(int i) { return (i % 256) * 2; }
+ArrayBuffer createTestArrayBuffer(int size)
+{
+ ArrayBuffer buffer(size);
+ Uint8Array array(buffer);
+ for (int i = 0; i < size; ++i)
+ array.val().set(i, testByteValue(i));
+ return buffer;
+}
+
+void WasmIoDevicesTest::blobIODevice()
+{
+ if (!qstdweb::canBlockCallingThread()) {
+ QtWasmTest::completeTestFunction(QtWasmTest::TestResult::Skip, "requires asyncify");
+ return;
+ }
+
+ // Create test buffer and BlobIODevice
+ const int bufferSize = 16;
+ BlobIODevice blobDevice(Blob::fromArrayBuffer(createTestArrayBuffer(bufferSize)));
+
+ // Read back byte for byte from the device
+ QWASMVERIFY(blobDevice.open(QIODevice::ReadOnly));
+ for (int i = 0; i < bufferSize; ++i) {
+ char byte;
+ blobDevice.seek(i);
+ blobDevice.read(&byte, 1);
+ QWASMCOMPARE(byte, testByteValue(i));
+ }
+
+ blobDevice.close();
+ QWASMVERIFY(!blobDevice.open(QIODevice::WriteOnly));
+ QWASMSUCCESS();
+}
+
+void WasmIoDevicesTest::uint8ArrayIODevice()
+{
+ // Create test buffer and Uint8ArrayIODevice
+ const int bufferSize = 1024;
+ Uint8Array array(createTestArrayBuffer(bufferSize));
+ Uint8ArrayIODevice arrayDevice(array);
+
+ // Read back byte for byte from the device
+ QWASMVERIFY(arrayDevice.open(QIODevice::ReadWrite));
+ for (int i = 0; i < bufferSize; ++i) {
+ char byte;
+ arrayDevice.seek(i);
+ arrayDevice.read(&byte, 1);
+ QWASMCOMPARE(byte, testByteValue(i));
+ }
+
+ // Write a different set of bytes
+ QWASMCOMPARE(arrayDevice.seek(0), true);
+ for (int i = 0; i < bufferSize; ++i) {
+ char byte = testByteValue(i + 1);
+ arrayDevice.seek(i);
+ QWASMCOMPARE(arrayDevice.write(&byte, 1), 1);
+ }
+
+ // Verify that the original array was updated
+ QByteArray copy = QByteArray::fromEcmaUint8Array(array.val());
+ for (int i = 0; i < bufferSize; ++i)
+ QWASMCOMPARE(copy.at(i), testByteValue(i + 1));
+
+ arrayDevice.close();
+ QWASMSUCCESS();
+}
+
+int main(int argc, char **argv)
+{
+ auto testObject = std::make_shared<WasmIoDevicesTest>();
+ QtWasmTest::initTestCase<QCoreApplication>(argc, argv, testObject);
+ return 0;
+}
+
+#include "iodevices_main.moc"
+
diff --git a/tests/manual/wasm/qstdweb/promise_auto.html b/tests/manual/wasm/qstdweb/promise_auto.html
index 786145419f..94a8dbb88a 100644
--- a/tests/manual/wasm/qstdweb/promise_auto.html
+++ b/tests/manual/wasm/qstdweb/promise_auto.html
@@ -3,7 +3,7 @@
<script type="text/javascript" src="promise_auto.js"></script>
<script>
window.onload = () => {
- runTestCase(document.getElementById("log"));
+ runTestCase(promise_auto_entry, document.getElementById("log"));
};
</script>
<p>Running promise auto test.</p>
diff --git a/tests/manual/wasm/qstdweb/promise_main.cpp b/tests/manual/wasm/qstdweb/promise_main.cpp
index 351f06c91d..c5f6f7f412 100644
--- a/tests/manual/wasm/qstdweb/promise_main.cpp
+++ b/tests/manual/wasm/qstdweb/promise_main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QCoreApplication>
#include <QtCore/QEvent>
diff --git a/tests/manual/wasm/qstdweb/qwasmcompositor_auto.html b/tests/manual/wasm/qstdweb/qwasmcompositor_auto.html
index 26daecdf41..f33aab0b9c 100644
--- a/tests/manual/wasm/qstdweb/qwasmcompositor_auto.html
+++ b/tests/manual/wasm/qstdweb/qwasmcompositor_auto.html
@@ -3,7 +3,7 @@
<script type="text/javascript" src="qwasmcompositor_auto.js"></script>
<script>
window.onload = () => {
- runTestCase(document.getElementById("log"));
+ runTestCase(qwasmcompositor_auto_entry, document.getElementById("log"));
};
</script>
<p>Running files auto test.</p>
diff --git a/tests/manual/wasm/qstdweb/qwasmcompositor_main.cpp b/tests/manual/wasm/qstdweb/qwasmcompositor_main.cpp
index 8b5807a8dd..e1a9cf604d 100644
--- a/tests/manual/wasm/qstdweb/qwasmcompositor_main.cpp
+++ b/tests/manual/wasm/qstdweb/qwasmcompositor_main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QEvent>
#include <QtCore/QObject>
@@ -7,7 +7,7 @@
#include <QtGui/qguiapplication.h>
#include <QtGui/qoffscreensurface.h>
#include <QtGui/qpa/qwindowsysteminterface.h>
-#include <QtGui/private/qrhigles2_p.h>
+#include <QtGui/rhi/qrhi.h>
#include <qtwasmtestlib.h>
@@ -66,7 +66,7 @@ void Window::keyPressEvent(QKeyEvent *)
void Window::init()
{
- QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers | QRhi::EnableProfiling;
+ QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers;
m_fallbackSurface.reset(QRhiGles2InitParams::newFallbackSurface());
QRhiGles2InitParams params;
@@ -94,10 +94,8 @@ public:
QWasmCompositorTest() : m_window(val::global("window")), m_testSupport(val::object())
{
m_window.set("testSupport", m_testSupport);
- m_testSupport.set("qtAddContainerElement",
- emscripten::val::module_property("qtAddContainerElement"));
- m_testSupport.set("qtRemoveContainerElement",
- emscripten::val::module_property("qtRemoveContainerElement"));
+ m_testSupport.set("qtSetContainerElements",
+ emscripten::val::module_property("qtSetContainerElements"));
}
~QWasmCompositorTest() noexcept
@@ -118,12 +116,12 @@ private:
});
m_cleanup.emplace_back([]() mutable {
EM_ASM({
- testSupport.qtRemoveContainerElement(testSupport.screenElement);
+ testSupport.qtSetContainerElements([]);
testSupport.screenElement.parentElement.removeChild(testSupport.screenElement);
});
});
- EM_ASM({ testSupport.qtAddContainerElement(testSupport.screenElement); });
+ EM_ASM({ testSupport.qtSetContainerElements([testSupport.screenElement]); });
}
template<class T>
diff --git a/tests/manual/wasm/qtloader_integration/CMakeLists.txt b/tests/manual/wasm/qtloader_integration/CMakeLists.txt
new file mode 100644
index 0000000000..2603a05135
--- /dev/null
+++ b/tests/manual/wasm/qtloader_integration/CMakeLists.txt
@@ -0,0 +1,45 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_manual_test(tst_qtloader_integration
+ GUI
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::Widgets
+)
+
+set_target_properties(tst_qtloader_integration PROPERTIES QT_WASM_EXTRA_EXPORTED_METHODS "ENV")
+
+add_custom_command(
+ TARGET tst_qtloader_integration POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_qtloader_integration.html
+ ${CMAKE_CURRENT_BINARY_DIR}/tst_qtloader_integration.html)
+
+add_custom_command(
+ TARGET tst_qtloader_integration POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/plugins/platforms/wasm/qtloader.js
+ ${CMAKE_CURRENT_BINARY_DIR}/qtloader.js)
+
+add_custom_command(
+ TARGET tst_qtloader_integration POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/../shared/testrunner.js
+ ${CMAKE_CURRENT_BINARY_DIR}/testrunner.js)
+
+add_custom_command(
+ TARGET tst_qtloader_integration POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_body.js
+ ${CMAKE_CURRENT_BINARY_DIR}/test_body.js)
+
+add_custom_command(
+ TARGET tst_qtloader_integration POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/preload.json
+ ${CMAKE_CURRENT_BINARY_DIR}/preload.json)
diff --git a/tests/manual/wasm/qtloader_integration/main.cpp b/tests/manual/wasm/qtloader_integration/main.cpp
new file mode 100644
index 0000000000..4bb502b69c
--- /dev/null
+++ b/tests/manual/wasm/qtloader_integration/main.cpp
@@ -0,0 +1,183 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include <QtWidgets/QtWidgets>
+
+#include <iostream>
+#include <sstream>
+
+#include <emscripten/bind.h>
+#include <emscripten/val.h>
+#include <emscripten.h>
+
+#include <QtGui/qpa/qplatformscreen.h>
+
+namespace {
+constexpr int ExitValueImmediateReturn = 42;
+constexpr int ExitValueFromExitApp = 22;
+
+std::string screenInformation()
+{
+ auto screens = qGuiApp->screens();
+ std::ostringstream out;
+ out << "[";
+ const char *separator = "";
+ for (const auto &screen : screens) {
+ out << separator;
+ out << "[" << std::to_string(screen->geometry().x()) << ","
+ << std::to_string(screen->geometry().y()) << ","
+ << std::to_string(screen->geometry().width()) << ","
+ << std::to_string(screen->geometry().height()) << "]";
+ separator = ",";
+ }
+ out << "]";
+ return out.str();
+}
+
+std::string logicalDpi()
+{
+ auto screens = qGuiApp->screens();
+ std::ostringstream out;
+ out << "[";
+ const char *separator = "";
+ for (const auto &screen : screens) {
+ out << separator;
+ out << "[" << std::to_string(screen->handle()->logicalDpi().first) << ", "
+ << std::to_string(screen->handle()->logicalDpi().second) << "]";
+ separator = ",";
+ }
+ out << "]";
+ return out.str();
+}
+
+std::string preloadedFiles()
+{
+ QStringList files = QDir("/preload").entryList(QDir::Files);
+ std::ostringstream out;
+ out << "[";
+ const char *separator = "";
+ for (const auto &file : files) {
+ out << separator;
+ out << file.toStdString();
+ separator = ",";
+ }
+ out << "]";
+ return out.str();
+}
+
+void crash()
+{
+ std::abort();
+}
+
+void stackOverflow()
+{
+ stackOverflow(); // should eventually termniate with exception
+}
+
+void exitApp()
+{
+ emscripten_force_exit(ExitValueFromExitApp);
+}
+
+void produceOutput()
+{
+ fprintf(stdout, "Sample output!\n");
+}
+
+std::string retrieveArguments()
+{
+ auto arguments = QApplication::arguments();
+ std::ostringstream out;
+ out << "[";
+ const char *separator = "";
+ for (const auto &argument : arguments) {
+ out << separator;
+ out << "'" << argument.toStdString() << "'";
+ separator = ",";
+ }
+ out << "]";
+ return out.str();
+}
+
+std::string getEnvironmentVariable(std::string name) {
+ return QString::fromLatin1(qgetenv(name.c_str())).toStdString();
+}
+} // namespace
+
+class AppWindow : public QObject
+{
+ Q_OBJECT
+public:
+ AppWindow() : m_layout(new QVBoxLayout(&m_ui))
+ {
+ addWidget<QLabel>("Qt Loader integration tests");
+
+ m_ui.setLayout(m_layout);
+ }
+
+ void show() { m_ui.show(); }
+
+ ~AppWindow() = default;
+
+private:
+ template<class T, class... Args>
+ T *addWidget(Args... args)
+ {
+ T *widget = new T(std::forward<Args>(args)..., &m_ui);
+ m_layout->addWidget(widget);
+ return widget;
+ }
+
+ QWidget m_ui;
+ QVBoxLayout *m_layout;
+};
+
+int main(int argc, char **argv)
+{
+ QApplication application(argc, argv);
+ const auto arguments = application.arguments();
+ const bool exitImmediately =
+ std::find(arguments.begin(), arguments.end(), QStringLiteral("--exit-immediately"))
+ != arguments.end();
+ if (exitImmediately)
+ emscripten_force_exit(ExitValueImmediateReturn);
+
+ const bool crashImmediately =
+ std::find(arguments.begin(), arguments.end(), QStringLiteral("--crash-immediately"))
+ != arguments.end();
+ if (crashImmediately)
+ crash();
+
+ const bool stackOverflowImmediately =
+ std::find(arguments.begin(), arguments.end(), QStringLiteral("--stack-owerflow-immediately"))
+ != arguments.end();
+ if (stackOverflowImmediately)
+ stackOverflow();
+
+ const bool noGui = std::find(arguments.begin(), arguments.end(), QStringLiteral("--no-gui"))
+ != arguments.end();
+
+ if (!noGui) {
+ AppWindow window;
+ window.show();
+ return application.exec();
+ }
+ return application.exec();
+}
+
+EMSCRIPTEN_BINDINGS(qtLoaderIntegrationTest)
+{
+ emscripten::constant("EXIT_VALUE_IMMEDIATE_RETURN", ExitValueImmediateReturn);
+ emscripten::constant("EXIT_VALUE_FROM_EXIT_APP", ExitValueFromExitApp);
+
+ emscripten::function("screenInformation", &screenInformation);
+ emscripten::function("logicalDpi", &logicalDpi);
+ emscripten::function("preloadedFiles", &preloadedFiles);
+ emscripten::function("crash", &crash);
+ emscripten::function("exitApp", &exitApp);
+ emscripten::function("produceOutput", &produceOutput);
+ emscripten::function("retrieveArguments", &retrieveArguments);
+ emscripten::function("getEnvironmentVariable", &getEnvironmentVariable);
+}
+
+#include "main.moc"
diff --git a/tests/manual/wasm/qtloader_integration/preload.json b/tests/manual/wasm/qtloader_integration/preload.json
new file mode 100644
index 0000000000..d7e09911ff
--- /dev/null
+++ b/tests/manual/wasm/qtloader_integration/preload.json
@@ -0,0 +1,10 @@
+[
+ {
+ "source": "qtloader.js",
+ "destination": "/preload/qtloader.js"
+ },
+ {
+ "source": "$QTDIR/qtlogo.svg",
+ "destination": "/preload/qtlogo.svg"
+ }
+]
diff --git a/tests/manual/wasm/qtloader_integration/test_body.js b/tests/manual/wasm/qtloader_integration/test_body.js
new file mode 100644
index 0000000000..4fb49c31aa
--- /dev/null
+++ b/tests/manual/wasm/qtloader_integration/test_body.js
@@ -0,0 +1,517 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import { Mock, assert, TestRunner } from './testrunner.js';
+
+export class QtLoaderIntegrationTests
+{
+ #testScreenContainers = []
+
+ async beforeEach()
+ {
+ this.#addScreenContainer('screen-container-0', { width: '200px', height: '300px' });
+ }
+
+ async afterEach()
+ {
+ this.#testScreenContainers.forEach(screenContainer =>
+ {
+ document.body.removeChild(screenContainer);
+ });
+ this.#testScreenContainers = [];
+ }
+
+ async missingConfig()
+ {
+ let caughtException;
+ try {
+ await qtLoad();
+ } catch (e) {
+ caughtException = e;
+ }
+
+ assert.isNotUndefined(caughtException);
+ assert.equal('config is required, expected an object', caughtException.message);
+ }
+
+ async missingQtSection()
+ {
+ let caughtException;
+ try {
+ await qtLoad({});
+ } catch (e) {
+ caughtException = e;
+ }
+
+ assert.isNotUndefined(caughtException);
+ assert.equal(
+ 'config.qt is required, expected an object', caughtException.message);
+ }
+
+ async missingEntryFunction()
+ {
+ let caughtException;
+ try {
+ await qtLoad({ qt: {}});
+ } catch (e) {
+ caughtException = e;
+ }
+
+ assert.isNotUndefined(caughtException);
+ assert.equal(
+ 'config.qt.entryFunction is required, expected a function', caughtException.message);
+ }
+
+ async badEntryFunction()
+ {
+ let caughtException;
+ try {
+ await qtLoad({ qt: { entryFunction: 'invalid' }});
+ } catch (e) {
+ caughtException = e;
+ }
+
+ assert.isNotUndefined(caughtException);
+ assert.equal(
+ 'config.qt.entryFunction is required, expected a function', caughtException.message);
+ }
+
+ async environmentVariables()
+ {
+ const instance = await qtLoad({
+ qt: {
+ environment: {
+ variable1: 'value1',
+ variable2: 'value2'
+ },
+ entryFunction: tst_qtloader_integration_entry,
+ containerElements: [this.#testScreenContainers[0]]
+ }
+ });
+ assert.isTrue(instance.getEnvironmentVariable('variable1') === 'value1');
+ assert.isTrue(instance.getEnvironmentVariable('variable2') === 'value2');
+ }
+
+ async screenContainerManipulations()
+ {
+ // ... (do other things), then call addContainerElement() to add a new container/screen.
+ // This can happen either before or after load() is called - loader will route the
+ // call to instance when it's ready.
+ this.#addScreenContainer('appcontainer1', { width: '100px', height: '100px' })
+
+ const instance = await qtLoad({
+ qt: {
+ entryFunction: tst_qtloader_integration_entry,
+ containerElements: this.#testScreenContainers
+ }
+ });
+ {
+ const screenInformation = this.#getScreenInformation(instance);
+
+ assert.equal(2, screenInformation.length);
+ assert.equal(200, screenInformation[0].width);
+ assert.equal(300, screenInformation[0].height);
+ assert.equal(100, screenInformation[1].width);
+ assert.equal(100, screenInformation[1].height);
+ }
+
+ this.#addScreenContainer('appcontainer2', { width: '234px', height: '99px' })
+ instance.qtSetContainerElements(this.#testScreenContainers);
+
+ {
+ const screenInformation = this.#getScreenInformation(instance);
+
+ assert.equal(3, screenInformation.length);
+ assert.equal(200, screenInformation[0].width);
+ assert.equal(300, screenInformation[0].height);
+ assert.equal(100, screenInformation[1].width);
+ assert.equal(100, screenInformation[1].height);
+ assert.equal(234, screenInformation[2].width);
+ assert.equal(99, screenInformation[2].height);
+ }
+
+ document.body.removeChild(this.#testScreenContainers.splice(2, 1)[0]);
+ instance.qtSetContainerElements(this.#testScreenContainers);
+ {
+ const screenInformation = this.#getScreenInformation(instance);
+
+ assert.equal(2, screenInformation.length);
+ assert.equal(200, screenInformation[0].width);
+ assert.equal(300, screenInformation[0].height);
+ assert.equal(100, screenInformation[1].width);
+ assert.equal(100, screenInformation[1].height);
+ }
+ }
+
+ async primaryScreenIsAlwaysFirst()
+ {
+ const instance = await qtLoad({
+ qt: {
+ entryFunction: tst_qtloader_integration_entry,
+ containerElements: this.#testScreenContainers,
+ }
+ });
+ this.#addScreenContainer(
+ 'appcontainer3', { width: '12px', height: '24px' },
+ container => this.#testScreenContainers.splice(0, 0, container));
+ this.#addScreenContainer(
+ 'appcontainer4', { width: '34px', height: '68px' },
+ container => this.#testScreenContainers.splice(1, 0, container));
+
+ instance.qtSetContainerElements(this.#testScreenContainers);
+ {
+ const screenInformation = this.#getScreenInformation(instance);
+
+ assert.equal(3, screenInformation.length);
+ // The primary screen (at position 0) is always at 0
+ assert.equal(12, screenInformation[0].width);
+ assert.equal(24, screenInformation[0].height);
+ // Other screens are pushed at the back
+ assert.equal(200, screenInformation[1].width);
+ assert.equal(300, screenInformation[1].height);
+ assert.equal(34, screenInformation[2].width);
+ assert.equal(68, screenInformation[2].height);
+ }
+
+ this.#testScreenContainers.forEach(screenContainer =>
+ {
+ document.body.removeChild(screenContainer);
+ });
+ this.#testScreenContainers = [
+ this.#addScreenContainer('appcontainer5', { width: '11px', height: '12px' }),
+ this.#addScreenContainer('appcontainer6', { width: '13px', height: '14px' }),
+ ];
+
+ instance.qtSetContainerElements(this.#testScreenContainers);
+ {
+ const screenInformation = this.#getScreenInformation(instance);
+
+ assert.equal(2, screenInformation.length);
+ assert.equal(11, screenInformation[0].width);
+ assert.equal(12, screenInformation[0].height);
+ assert.equal(13, screenInformation[1].width);
+ assert.equal(14, screenInformation[1].height);
+ }
+ }
+
+ async multipleInstances()
+ {
+ // Fetch/Compile the module once; reuse for each instance. This is also if the page wants to
+ // initiate the .wasm file download fetch as early as possible, before the browser has
+ // finished fetching and parsing testapp.js and qtloader.js
+ const module = WebAssembly.compileStreaming(fetch('tst_qtloader_integration.wasm'));
+
+ const instances = await Promise.all([1, 2, 3].map(i => qtLoad({
+ qt: {
+ entryFunction: tst_qtloader_integration_entry,
+ containerElements: [this.#addScreenContainer(`screen-container-${i}`, {
+ width: `${i * 10}px`,
+ height: `${i * 10}px`,
+ })],
+ module,
+ }
+ })));
+ // Confirm the identity of instances by querying their screen widths and heights
+ {
+ const screenInformation = this.#getScreenInformation(instances[0]);
+ console.log();
+ assert.equal(1, screenInformation.length);
+ assert.equal(10, screenInformation[0].width);
+ assert.equal(10, screenInformation[0].height);
+ }
+ {
+ const screenInformation = this.#getScreenInformation(instances[1]);
+ assert.equal(1, screenInformation.length);
+ assert.equal(20, screenInformation[0].width);
+ assert.equal(20, screenInformation[0].height);
+ }
+ {
+ const screenInformation = this.#getScreenInformation(instances[2]);
+ assert.equal(1, screenInformation.length);
+ assert.equal(30, screenInformation[0].width);
+ assert.equal(30, screenInformation[0].height);
+ }
+ }
+
+ async consoleMode()
+ {
+ // 'Console mode' for autotesting type scenarios
+ let accumulatedStdout = '';
+ const instance = await qtLoad({
+ arguments: ['--no-gui'],
+ print: output =>
+ {
+ accumulatedStdout += output;
+ },
+ qt: {
+ entryFunction: tst_qtloader_integration_entry,
+ }
+ });
+
+ this.#callTestInstanceApi(instance, 'produceOutput');
+ assert.equal('Sample output!', accumulatedStdout);
+ }
+
+ async modulePromiseProvided()
+ {
+ await qtLoad({
+ qt: {
+ entryFunction: createQtAppInstance,
+ containerElements: [this.#testScreenContainers[0]],
+ module: WebAssembly.compileStreaming(
+ fetch('tst_qtloader_integration.wasm'))
+ }
+ });
+ }
+
+ async moduleProvided()
+ {
+ await qtLoad({
+ qt: {
+ entryFunction: tst_qtloader_integration_entry,
+ containerElements: [this.#testScreenContainers[0]],
+ module: await WebAssembly.compileStreaming(
+ fetch('tst_qtloader_integration.wasm'))
+ }
+ });
+ }
+
+ async arguments()
+ {
+ const instance = await qtLoad({
+ arguments: ['--no-gui', 'arg1', 'other', 'yetanotherarg'],
+ qt: {
+ entryFunction: tst_qtloader_integration_entry,
+ }
+ });
+ const args = this.#callTestInstanceApi(instance, 'retrieveArguments');
+ assert.equal(5, args.length);
+ assert.isTrue('arg1' === args[2]);
+ assert.equal('other', args[3]);
+ assert.equal('yetanotherarg', args[4]);
+ }
+
+ async moduleProvided_exceptionThrownInFactory()
+ {
+ let caughtException;
+ try {
+ await qtLoad({
+ qt: {
+ entryFunction: tst_qtloader_integration_entry,
+ containerElements: [this.#testScreenContainers[0]],
+ module: Promise.reject(new Error('Failed to load')),
+ }
+ });
+ } catch (e) {
+ caughtException = e;
+ }
+ assert.isTrue(caughtException !== undefined);
+ assert.equal('Failed to load', caughtException.message);
+ }
+
+ async abort()
+ {
+ const onExitMock = new Mock();
+ const instance = await qtLoad({
+ arguments: ['--no-gui'],
+ qt: {
+ onExit: onExitMock,
+ entryFunction: tst_qtloader_integration_entry,
+ }
+ });
+ try {
+ instance.crash();
+ } catch { }
+ assert.equal(1, onExitMock.calls.length);
+ const exitStatus = onExitMock.calls[0][0];
+ assert.isTrue(exitStatus.crashed);
+ assert.isUndefined(exitStatus.code);
+ assert.isNotUndefined(exitStatus.text);
+ }
+
+ async abortImmediately()
+ {
+ const onExitMock = new Mock();
+ let caughtException;
+ try {
+ await qtLoad({
+ arguments: ['--no-gui', '--crash-immediately'],
+ qt: {
+ onExit: onExitMock,
+ entryFunction: tst_qtloader_integration_entry,
+ }
+ });
+ } catch (e) {
+ caughtException = e;
+ }
+
+ assert.isTrue(caughtException !== undefined);
+ assert.equal(1, onExitMock.calls.length);
+ const exitStatus = onExitMock.calls[0][0];
+ assert.isTrue(exitStatus.crashed);
+ assert.isUndefined(exitStatus.code);
+ assert.isNotUndefined(exitStatus.text);
+ }
+
+ async stackOwerflowImmediately()
+ {
+ const onExitMock = new Mock();
+ let caughtException;
+ try {
+ await qtLoad({
+ arguments: ['--no-gui', '--stack-owerflow-immediately'],
+ qt: {
+ onExit: onExitMock,
+ entryFunction: tst_qtloader_integration_entry,
+ }
+ });
+ } catch (e) {
+ caughtException = e;
+ }
+
+ assert.isTrue(caughtException !== undefined);
+ assert.equal(1, onExitMock.calls.length);
+ const exitStatus = onExitMock.calls[0][0];
+ assert.isTrue(exitStatus.crashed);
+ assert.isUndefined(exitStatus.code);
+ // text should be "RangeError: Maximum call stack
+ // size exceeded", or similar.
+ assert.isNotUndefined(exitStatus.text);
+ }
+
+ async userAbortCallbackCalled()
+ {
+ const onAbortMock = new Mock();
+ let instance = await qtLoad({
+ arguments: ['--no-gui'],
+ onAbort: onAbortMock,
+ qt: {
+ entryFunction: tst_qtloader_integration_entry,
+ }
+ });
+ try {
+ instance.crash();
+ } catch (e) {
+ // emscripten throws an 'Aborted' error here, which we ignore for the sake of the test
+ }
+ assert.equal(1, onAbortMock.calls.length);
+ }
+
+ async exit()
+ {
+ const onExitMock = new Mock();
+ let instance = await qtLoad({
+ arguments: ['--no-gui'],
+ qt: {
+ onExit: onExitMock,
+ entryFunction: tst_qtloader_integration_entry,
+ }
+ });
+ // The module is running. onExit should not have been called.
+ assert.equal(0, onExitMock.calls.length);
+ try {
+ instance.exitApp();
+ } catch (e) {
+ // emscripten throws a 'Runtime error: unreachable' error here. We ignore it for the
+ // sake of the test.
+ }
+ assert.equal(1, onExitMock.calls.length);
+ const exitStatus = onExitMock.calls[0][0];
+ assert.isFalse(exitStatus.crashed);
+ assert.equal(instance.EXIT_VALUE_FROM_EXIT_APP, exitStatus.code);
+ assert.isUndefined(exitStatus.text);
+ }
+
+ async exitImmediately()
+ {
+ const onExitMock = new Mock();
+ const instance = await qtLoad({
+ arguments: ['--no-gui', '--exit-immediately'],
+ qt: {
+ onExit: onExitMock,
+ entryFunction: tst_qtloader_integration_entry,
+ }
+ });
+ assert.equal(1, onExitMock.calls.length);
+
+ const exitStatusFromOnExit = onExitMock.calls[0][0];
+
+ assert.isFalse(exitStatusFromOnExit.crashed);
+ assert.equal(instance.EXIT_VALUE_IMMEDIATE_RETURN, exitStatusFromOnExit.code);
+ assert.isUndefined(exitStatusFromOnExit.text);
+ }
+
+ async userQuitCallbackCalled()
+ {
+ const quitMock = new Mock();
+ let instance = await qtLoad({
+ arguments: ['--no-gui'],
+ quit: quitMock,
+ qt: {
+ entryFunction: tst_qtloader_integration_entry,
+ }
+ });
+ try {
+ instance.exitApp();
+ } catch (e) {
+ // emscripten throws a 'Runtime error: unreachable' error here. We ignore it for the
+ // sake of the test.
+ }
+ assert.equal(1, quitMock.calls.length);
+ const [exitCode, exception] = quitMock.calls[0];
+ assert.equal(instance.EXIT_VALUE_FROM_EXIT_APP, exitCode);
+ assert.equal('ExitStatus', exception.name);
+ }
+
+ async preloadFiles()
+ {
+ const instance = await qtLoad({
+ arguments: ["--no-gui"],
+ qt: {
+ preload: ['preload.json'],
+ qtdir: '.',
+ }
+ });
+ const preloadedFiles = instance.preloadedFiles();
+ // Verify that preloaded file list matches files specified in preload.json
+ assert.equal("[qtloader.js,qtlogo.svg]", preloadedFiles);
+ }
+
+ #callTestInstanceApi(instance, apiName)
+ {
+ return eval(instance[apiName]());
+ }
+
+ #getScreenInformation(instance)
+ {
+ return this.#callTestInstanceApi(instance, 'screenInformation').map(elem => ({
+ x: elem[0],
+ y: elem[1],
+ width: elem[2],
+ height: elem[3],
+ }));
+ }
+
+ #addScreenContainer(id, style, inserter)
+ {
+ const container = (() =>
+ {
+ const container = document.createElement('div');
+ container.id = id;
+ container.style.width = style.width;
+ container.style.height = style.height;
+ document.body.appendChild(container);
+ return container;
+ })();
+ inserter ? inserter(container) : this.#testScreenContainers.push(container);
+ return container;
+ }
+}
+
+(async () =>
+{
+ const runner = new TestRunner(new QtLoaderIntegrationTests(), {
+ timeoutSeconds: 10
+ });
+ await runner.runAll();
+})();
diff --git a/tests/manual/wasm/qtloader_integration/tst_qtloader_integration.html b/tests/manual/wasm/qtloader_integration/tst_qtloader_integration.html
new file mode 100644
index 0000000000..7aa7528a1d
--- /dev/null
+++ b/tests/manual/wasm/qtloader_integration/tst_qtloader_integration.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html lang="en-us">
+
+<head>
+ <title>tst_qtloader_integration</title>
+ <script src='tst_qtloader_integration.js'></script>
+ <script src="qtloader.js" defer></script>
+ <script type="module" src="test_body.js" defer></script>
+</head>
+
+<body></body>
+
+</html>
diff --git a/tests/manual/wasm/qtwasmtestlib/README.md b/tests/manual/wasm/qtwasmtestlib/README.md
index 515c33ae6a..6de81fe8b4 100644
--- a/tests/manual/wasm/qtwasmtestlib/README.md
+++ b/tests/manual/wasm/qtwasmtestlib/README.md
@@ -48,7 +48,7 @@ Finally provide an html file which hosts the test runner and calls runTestCase()
<script type="text/javascript" src="test_case.js"></script>
<script>
window.onload = async () => {
- runTestCase(document.getElementById("log"));
+ runTestCase(entryFunction, document.getElementById("log"));
};
</script>
<p>Running Foo auto test.</p>
@@ -67,7 +67,7 @@ html file provides container elements which becomes QScreens for the test code.
window.onload = async () => {
let log = document.getElementById("log")
let containers = [document.getElementById("container")];
- runTestCase(log, containers);
+ runTestCase(entryFunction, log, containers);
};
</script>
<p>Running Foo auto test.</p>
diff --git a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp
index d86b8ee2c0..ec03c7209a 100644
--- a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp
+++ b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qtwasmtestlib.h"
diff --git a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.h b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.h
index 5dcc41419e..2307ed1ccd 100644
--- a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.h
+++ b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QT_WASM_TESTRUNNER_H
#define QT_WASM_TESTRUNNER_H
diff --git a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.js b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.js
index eaf518d1ef..d4f815b887 100644
--- a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.js
+++ b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.js
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// A minimal async test runner for Qt async auto tests.
//
@@ -77,12 +77,13 @@ async function runTestFunction(instance, name) {
}
}
-async function runTestCaseImpl(testFunctionStarted, testFunctionCompleted, qtContainers) {
+async function runTestCaseImpl(entryFunction, testFunctionStarted, testFunctionCompleted, qtContainers)
+{
// Create test case instance
const config = {
qtContainerElements: qtContainers || []
}
- const instance = await createQtAppInstance(config);
+ const instance = await entryFunction(config);
// Run all test functions
const functionsString = instance.getTestFunctions();
@@ -124,10 +125,11 @@ function testFunctionCompleted(status) {
g_htmlLogElement.innerHTML += line;
}
-async function runTestCase(htmlLogElement, qtContainers) {
+async function runTestCase(entryFunction, htmlLogElement, qtContainers)
+{
g_htmlLogElement = htmlLogElement;
try {
- await runTestCaseImpl(testFunctionStarted, testFunctionCompleted, qtContainers);
+ await runTestCaseImpl(entryFunction, testFunctionStarted, testFunctionCompleted, qtContainers);
g_htmlLogElement.innerHTML += "<br> DONE"
} catch (err) {
g_htmlLogElement.innerHTML += err
diff --git a/tests/manual/wasm/qwasmwindow/CMakeLists.txt b/tests/manual/wasm/qwasmwindow/CMakeLists.txt
deleted file mode 100644
index 73e267331e..0000000000
--- a/tests/manual/wasm/qwasmwindow/CMakeLists.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-qt_internal_add_manual_test(qwasmwindow_harness
- SOURCES
- qwasmwindow_harness.cpp
- PUBLIC_LIBRARIES
- Qt::Core
- Qt::CorePrivate
- Qt::GuiPrivate
-)
-
-add_custom_command(
- TARGET qwasmwindow_harness POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy
- ${CMAKE_CURRENT_SOURCE_DIR}/qwasmwindow_harness.html
- ${CMAKE_CURRENT_BINARY_DIR}/qwasmwindow_harness.html
-)
-
-add_custom_command(
- TARGET qwasmwindow_harness POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy
- ${CMAKE_CURRENT_SOURCE_DIR}/qwasmwindow.py
- ${CMAKE_CURRENT_BINARY_DIR}/qwasmwindow.py
-)
-
-add_custom_command(
- TARGET qwasmwindow_harness POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy
- ${CMAKE_CURRENT_SOURCE_DIR}/run.sh
- ${CMAKE_CURRENT_BINARY_DIR}/run.sh
-)
-
-add_custom_command(
- TARGET qwasmwindow_harness POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy
- ${CMAKE_CURRENT_SOURCE_DIR}/../../../../util/wasm/qtwasmserver/qtwasmserver.py
- ${CMAKE_CURRENT_BINARY_DIR}/qtwasmserver.py
-)
diff --git a/tests/manual/wasm/qwasmwindow/qwasmwindow.py b/tests/manual/wasm/qwasmwindow/qwasmwindow.py
deleted file mode 100644
index a80e394cc2..0000000000
--- a/tests/manual/wasm/qwasmwindow/qwasmwindow.py
+++ /dev/null
@@ -1,445 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-from selenium.webdriver import Chrome
-from selenium.webdriver.common.actions.action_builder import ActionBuilder
-from selenium.webdriver.common.actions.pointer_actions import PointerActions
-from selenium.webdriver.common.actions.interaction import POINTER_TOUCH
-from selenium.webdriver.common.actions.pointer_input import PointerInput
-from selenium.webdriver.common.by import By
-from selenium.webdriver.support.expected_conditions import presence_of_element_located
-from selenium.webdriver.support.ui import WebDriverWait
-from selenium.webdriver.common.action_chains import ActionChains
-
-import unittest
-from enum import Enum, auto
-
-class WidgetTestCase(unittest.TestCase):
- def setUp(self):
- self._driver = Chrome()
- self._driver.get(
- 'http://localhost:8001/qwasmwindow_harness.html')
- self._test_sandbox_element = WebDriverWait(self._driver, 30).until(
- presence_of_element_located((By.ID, 'test-sandbox'))
- )
- self.addTypeEqualityFunc(Rect, assert_rects_equal)
-
- def test_window_resizing(self):
- defaultWindowMinSize = 100
- screen = Screen(self._driver, ScreenPosition.FIXED,
- x=0, y=0, width=600, height=600)
- window = Window(screen, x=100, y=100, width=200, height=200)
- self.assertEqual(window.rect, Rect(x=100, y=100, width=200, height=200))
-
- window.drag(Handle.TOP_LEFT, direction=UP(10) + LEFT(10))
- self.assertEqual(window.rect, Rect(x=90, y=90, width=210, height=210))
-
- window.drag(Handle.TOP, direction=DOWN(10) + LEFT(100))
- self.assertEqual(window.rect, Rect(x=90, y=100, width=210, height=200))
-
- window.drag(Handle.TOP_RIGHT, direction=UP(5) + LEFT(5))
- self.assertEqual(window.rect, Rect(x=90, y=95, width=205, height=205))
-
- window.drag(Handle.RIGHT, direction=DOWN(100) + RIGHT(5))
- self.assertEqual(window.rect, Rect(x=90, y=95, width=210, height=205))
-
- window.drag(Handle.BOTTOM_RIGHT, direction=UP(5) + LEFT(10))
- self.assertEqual(window.rect, Rect(x=90, y=95, width=200, height=200))
-
- window.drag(Handle.BOTTOM, direction=DOWN(20) + LEFT(100))
- self.assertEqual(window.rect, Rect(x=90, y=95, width=200, height=220))
-
- window.drag(Handle.BOTTOM_LEFT, direction=DOWN(10) + LEFT(10))
- self.assertEqual(window.rect, Rect(x=80, y=95, width=210, height=230))
-
- window.drag(Handle.LEFT, direction=DOWN(343) + LEFT(5))
- self.assertEqual(window.rect, Rect(x=75, y=95, width=215, height=230))
-
- window.drag(Handle.BOTTOM_RIGHT, direction=UP(150) + LEFT(150))
- self.assertEqual(window.rect, Rect(x=75, y=95, width=defaultWindowMinSize, height=defaultWindowMinSize))
-
- def test_cannot_resize_over_screen_top_edge(self):
- screen = Screen(self._driver, ScreenPosition.FIXED,
- x=200, y=200, width=300, height=300)
- window = Window(screen, x=300, y=300, width=100, height=100)
- self.assertEqual(window.rect, Rect(x=300, y=300, width=100, height=100))
- frame_rect_before_resize = window.frame_rect
-
- window.drag(Handle.TOP, direction=UP(200))
- self.assertEqual(window.rect.x, 300)
- self.assertEqual(window.frame_rect.y, screen.rect.y)
- self.assertEqual(window.rect.width, 100)
- self.assertEqual(window.frame_rect.y + window.frame_rect.height,
- frame_rect_before_resize.y + frame_rect_before_resize.height)
-
- def test_window_move(self):
- screen = Screen(self._driver, ScreenPosition.FIXED,
- x=200, y=200, width=300, height=300)
- window = Window(screen, x=300, y=300, width=100, height=100)
- self.assertEqual(window.rect, Rect(x=300, y=300, width=100, height=100))
-
- window.drag(Handle.TOP_WINDOW_BAR, direction=UP(30))
- self.assertEqual(window.rect, Rect(x=300, y=270, width=100, height=100))
-
- window.drag(Handle.TOP_WINDOW_BAR, direction=RIGHT(50))
- self.assertEqual(window.rect, Rect(x=350, y=270, width=100, height=100))
-
- window.drag(Handle.TOP_WINDOW_BAR, direction=DOWN(30) + LEFT(70))
- self.assertEqual(window.rect, Rect(x=280, y=300, width=100, height=100))
-
- def test_screen_limits_window_moves(self):
- screen = Screen(self._driver, ScreenPosition.RELATIVE,
- x=200, y=200, width=300, height=300)
- window = Window(screen, x=300, y=300, width=100, height=100)
- self.assertEqual(window.rect, Rect(x=300, y=300, width=100, height=100))
-
- window.drag(Handle.TOP_WINDOW_BAR, direction=LEFT(300))
- self.assertEqual(window.frame_rect.x, screen.rect.x - window.frame_rect.width / 2)
-
- def test_screen_in_scroll_container_limits_window_moves(self):
- screen = Screen(self._driver, ScreenPosition.IN_SCROLL_CONTAINER,
- x=200, y=2000, width=300, height=300,
- container_width=500, container_height=7000)
- screen.scroll_to()
- window = Window(screen, x=300, y=2100, width=100, height=100)
- self.assertEqual(window.rect, Rect(x=300, y=2100, width=100, height=100))
-
- window.drag(Handle.TOP_WINDOW_BAR, direction=LEFT(300))
- self.assertEqual(window.frame_rect.x, screen.rect.x - window.frame_rect.width / 2)
-
- def test_maximize(self):
- screen = Screen(self._driver, ScreenPosition.RELATIVE,
- x=200, y=200, width=300, height=300)
- window = Window(screen, x=300, y=300, width=100, height=100, title='Maximize')
- self.assertEqual(window.rect, Rect(x=300, y=300, width=100, height=100))
-
- window.maximize()
- self.assertEqual(window.frame_rect, Rect(x=200, y=200, width=300, height=300))
-
- def test_multitouch_window_move(self):
- screen = Screen(self._driver, ScreenPosition.FIXED,
- x=0, y=0, width=800, height=800)
- windows = [Window(screen, x=50, y=50, width=100, height=100, title='First'),
- Window(screen, x=400, y=400, width=100, height=100, title='Second'),
- Window(screen, x=50, y=400, width=100, height=100, title='Third')]
- self.assertEqual(windows[0].rect, Rect(x=50, y=50, width=100, height=100))
- self.assertEqual(windows[1].rect, Rect(x=400, y=400, width=100, height=100))
- self.assertEqual(windows[2].rect, Rect(x=50, y=400, width=100, height=100))
-
- actions = [TouchDragAction(origin=windows[0].at(Handle.TOP_WINDOW_BAR), direction=DOWN(20) + RIGHT(20)),
- TouchDragAction(origin=windows[1].at(Handle.TOP_WINDOW_BAR), direction=DOWN(20) + LEFT(20)),
- TouchDragAction(origin=windows[2].at(Handle.TOP_WINDOW_BAR), direction=UP(20) + RIGHT(20))]
- perform_touch_drag_actions(actions)
- self.assertEqual(windows[0].rect, Rect(x=70, y=70, width=100, height=100))
- self.assertEqual(windows[1].rect, Rect(x=380, y=420, width=100, height=100))
- self.assertEqual(windows[2].rect, Rect(x=70, y=380, width=100, height=100))
-
- def test_multitouch_window_resize(self):
- screen = Screen(self._driver, ScreenPosition.FIXED,
- x=0, y=0, width=800, height=800)
- windows = [Window(screen, x=50, y=50, width=150, height=150, title='First'),
- Window(screen, x=400, y=400, width=150, height=150, title='Second'),
- Window(screen, x=50, y=400, width=150, height=150, title='Third')]
- self.assertEqual(windows[0].rect, Rect(x=50, y=50, width=150, height=150))
- self.assertEqual(windows[1].rect, Rect(x=400, y=400, width=150, height=150))
- self.assertEqual(windows[2].rect, Rect(x=50, y=400, width=150, height=150))
-
- actions = [TouchDragAction(origin=windows[0].at(Handle.TOP_LEFT), direction=DOWN(20) + RIGHT(20)),
- TouchDragAction(origin=windows[1].at(Handle.TOP), direction=DOWN(20) + LEFT(20)),
- TouchDragAction(origin=windows[2].at(Handle.BOTTOM_RIGHT), direction=UP(20) + RIGHT(20))]
- perform_touch_drag_actions(actions)
- self.assertEqual(windows[0].rect, Rect(x=70, y=70, width=130, height=130))
- self.assertEqual(windows[1].rect, Rect(x=400, y=420, width=150, height=130))
- self.assertEqual(windows[2].rect, Rect(x=50, y=400, width=170, height=130))
-
- def tearDown(self):
- self._driver.quit()
-
-
-class ScreenPosition(Enum):
- FIXED = auto()
- RELATIVE = auto()
- IN_SCROLL_CONTAINER = auto()
-
-
-class Screen:
- def __init__(self, driver, positioning, x, y, width, height, container_width=0, container_height=0):
- self.driver = driver
- self.x = x
- self.y = y
- self.width = width
- self.height = height
- if positioning == ScreenPosition.FIXED:
- command = f'initializeScreenWithFixedPosition({self.x}, {self.y}, {self.width}, {self.height})'
- elif positioning == ScreenPosition.RELATIVE:
- command = f'initializeScreenWithRelativePosition({self.x}, {self.y}, {self.width}, {self.height})'
- elif positioning == ScreenPosition.IN_SCROLL_CONTAINER:
- command = f'initializeScreenInScrollContainer({container_width}, {container_height}, {self.x}, {self.y}, {self.width}, {self.height})'
- self.element = self.driver.execute_script(
- f'''
- return testSupport.{command};
- '''
- )
- if positioning == ScreenPosition.IN_SCROLL_CONTAINER:
- self.element = self.element[1]
-
- screen_information = call_instance_function(
- self.driver, 'screenInformation')
- if len(screen_information) != 1:
- raise AssertionError('Expecting exactly one screen_information!')
- self.screen_info = screen_information[0]
-
- @property
- def rect(self):
- self.screen_info = call_instance_function(
- self.driver, 'screenInformation')[0]
- geo = self.screen_info['geometry']
- return Rect(geo['x'], geo['y'], geo['width'], geo['height'])
-
- def scroll_to(self):
- ActionChains(self.driver).scroll_to_element(self.element).perform()
-
-
-class Window:
- def __init__(self, screen, x, y, width, height, title='title'):
- self.driver = screen.driver
- self.title = title
- self.driver.execute_script(
- f'''
- instance.createWindow({x}, {y}, {width}, {height}, '{screen.screen_info["name"]}', '{title}');
- '''
- )
- self._window_id = self.__window_information()['id']
- self.element = screen.element.shadow_root.find_element(
- By.CSS_SELECTOR, f'#qt-window-{self._window_id}')
-
- def __window_information(self):
- information = call_instance_function(self.driver, 'windowInformation')
- return next(filter(lambda e: e['title'] == self.title, information))
-
- @property
- def rect(self):
- geo = self.__window_information()["geometry"]
- return Rect(geo['x'], geo['y'], geo['width'], geo['height'])
-
- @property
- def frame_rect(self):
- geo = self.__window_information()["frameGeometry"]
- return Rect(geo['x'], geo['y'], geo['width'], geo['height'])
-
- def drag(self, handle, direction):
- ActionChains(self.driver) \
- .move_to_element_with_offset(self.element, *self.at(handle)['offset']) \
- .click_and_hold() \
- .move_by_offset(*translate_direction_to_offset(direction)) \
- .release().perform()
-
- def maximize(self):
- maximize_button = self.element.find_element(
- By.CSS_SELECTOR, f'.title-bar :nth-child(6)')
- maximize_button.click()
-
- def at(self, handle):
- """ Returns (window, offset) for given handle on window"""
- width = self.frame_rect.width
- height = self.frame_rect.height
-
- if handle == Handle.TOP_LEFT:
- offset = (-width/2, -height/2)
- elif handle == Handle.TOP:
- offset = (0, -height/2)
- elif handle == Handle.TOP_RIGHT:
- offset = (width/2, -height/2)
- elif handle == Handle.LEFT:
- offset = (-width/2, 0)
- elif handle == Handle.RIGHT:
- offset = (width/2, 0)
- elif handle == Handle.BOTTOM_LEFT:
- offset = (-width/2, height/2)
- elif handle == Handle.BOTTOM:
- offset = (0, height/2)
- elif handle == Handle.BOTTOM_RIGHT:
- offset = (width/2, height/2)
- elif handle == Handle.TOP_WINDOW_BAR:
- frame_top = self.frame_rect.y
- client_area_top = self.rect.y
- top_frame_bar_width = client_area_top - frame_top
- offset = (0, -height/2 + top_frame_bar_width/2)
- return {'window': self, 'offset': offset}
-
-
-class TouchDragAction:
- def __init__(self, origin, direction):
- self.origin = origin
- self.direction = direction
- self.step = 2
-
-
-def perform_touch_drag_actions(actions):
- driver = actions[0].origin['window'].driver
- touch_action_builder = ActionBuilder(driver)
- pointers = [PointerActions(source=touch_action_builder.add_pointer_input(
- POINTER_TOUCH, f'touch_input_{i}')) for i in range(len(actions))]
-
- for action, pointer in zip(actions, pointers):
- pointer.move_to(
- action.origin['window'].element, *action.origin['offset'])
- pointer.pointer_down(width=10, height=10, pressure=1)
- moves = [translate_direction_to_offset(a.direction) for a in actions]
-
- def movement_finished():
- for move in moves:
- if move != (0, 0):
- return False
- return True
-
- def sign(num):
- if num > 0:
- return 1
- elif num < 0:
- return -1
- return 0
-
- while not movement_finished():
- for i in range(len(actions)):
- pointer = pointers[i]
- move = moves[i]
- step = actions[i].step
-
- current_move = (
- min(abs(move[0]), step) * sign(move[0]), min(abs(move[1]), step) * sign(move[1]))
- moves[i] = (move[0] - current_move[0], move[1] - current_move[1])
- pointer.move_by(current_move[0],
- current_move[1], width=10, height=10)
- for pointer in pointers:
- pointer.pointer_up()
-
- touch_action_builder.perform()
-
-
-class TouchDragAction:
- def __init__(self, origin, direction):
- self.origin = origin
- self.direction = direction
- self.step = 2
-
-
-def perform_touch_drag_actions(actions):
- driver = actions[0].origin['window'].driver
- touch_action_builder = ActionBuilder(driver)
- pointers = [PointerActions(source=touch_action_builder.add_pointer_input(
- POINTER_TOUCH, f'touch_input_{i}')) for i in range(len(actions))]
-
- for action, pointer in zip(actions, pointers):
- pointer.move_to(
- action.origin['window'].element, *action.origin['offset'])
- pointer.pointer_down(width=10, height=10, pressure=1)
-
- moves = [translate_direction_to_offset(a.direction) for a in actions]
-
- def movement_finished():
- for move in moves:
- if move != (0, 0):
- return False
- return True
-
- def sign(num):
- if num > 0:
- return 1
- elif num < 0:
- return -1
- return 0
-
- while not movement_finished():
- for i in range(len(actions)):
- pointer = pointers[i]
- move = moves[i]
- step = actions[i].step
-
- current_move = (
- min(abs(move[0]), step) * sign(move[0]), min(abs(move[1]), step) * sign(move[1]))
- moves[i] = (move[0] - current_move[0], move[1] - current_move[1])
- pointer.move_by(current_move[0],
- current_move[1], width=10, height=10)
-
- for pointer in pointers:
- pointer.pointer_up()
-
- touch_action_builder.perform()
-
-
-def translate_direction_to_offset(direction):
- return (direction.val[1] - direction.val[3], direction.val[2] - direction.val[0])
-
-
-def call_instance_function(driver, name):
- return driver.execute_script(
- f'''let result;
- window.{name}Callback = data => result = data;
- instance.{name}();
- return eval(result);''')
-
-
-class Direction:
- def __init__(self):
- self.val = (0, 0, 0, 0)
-
- def __init__(self, north, east, south, west):
- self.val = (north, east, south, west)
-
- def __add__(self, other):
- return Direction(self.val[0] + other.val[0],
- self.val[1] + other.val[1],
- self.val[2] + other.val[2],
- self.val[3] + other.val[3])
-
-
-class UP(Direction):
- def __init__(self, step=1):
- self.val = (step, 0, 0, 0)
-
-
-class RIGHT(Direction):
- def __init__(self, step=1):
- self.val = (0, step, 0, 0)
-
-
-class DOWN(Direction):
- def __init__(self, step=1):
- self.val = (0, 0, step, 0)
-
-
-class LEFT(Direction):
- def __init__(self, step=1):
- self.val = (0, 0, 0, step)
-
-
-class Handle(Enum):
- TOP_LEFT = auto()
- TOP = auto()
- TOP_RIGHT = auto()
- LEFT = auto()
- RIGHT = auto()
- BOTTOM_LEFT = auto()
- BOTTOM = auto()
- BOTTOM_RIGHT = auto()
- TOP_WINDOW_BAR = auto()
-
-
-class Rect:
- def __init__(self, x, y, width, height) -> None:
- self.x = x
- self.y = y
- self.width = width
- self.height = height
-
- def __str__(self):
- return f'(x: {self.x}, y: {self.y}, width: {self.width}, height: {self.height})'
-
-
-def assert_rects_equal(geo1, geo2, msg=None):
- if geo1.x != geo2.x or geo1.y != geo2.y or geo1.width != geo2.width or geo1.height != geo2.height:
- raise AssertionError(f'Rectangles not equal: \n{geo1} \nvs \n{geo2}')
-
-
-unittest.main()
diff --git a/tests/manual/wasm/qwasmwindow/qwasmwindow_harness.cpp b/tests/manual/wasm/qwasmwindow/qwasmwindow_harness.cpp
deleted file mode 100644
index 3a8f0693df..0000000000
--- a/tests/manual/wasm/qwasmwindow/qwasmwindow_harness.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include <QtCore/QEvent>
-#include <QtCore/qobject.h>
-#include <QtCore/qregularexpression.h>
-#include <QtGui/qscreen.h>
-#include <QtGui/qwindow.h>
-#include <QtGui/qguiapplication.h>
-
-#include <emscripten.h>
-#include <emscripten/bind.h>
-#include <emscripten/val.h>
-
-#include <memory>
-#include <sstream>
-#include <vector>
-
-class DeleteOnCloseWindow : public QWindow
-{
- Q_OBJECT
-
-private:
- void closeEvent(QCloseEvent *ev) override
- {
- Q_UNUSED(ev);
- delete this;
- }
-};
-
-using namespace emscripten;
-
-std::string toJSArray(const std::vector<std::string> &elements)
-{
- std::ostringstream out;
- out << "[";
- bool comma = false;
- for (const auto &element : elements) {
- out << (comma ? "," : "");
- out << element;
- comma = true;
- }
- out << "]";
- return out.str();
-}
-
-std::string toJSString(const QString &qstring)
-{
- Q_ASSERT_X(([qstring]() {
- static QRegularExpression unescapedQuoteRegex(R"re((?:^|[^\\])')re");
- return qstring.indexOf(unescapedQuoteRegex) == -1;
- })(),
- Q_FUNC_INFO, "Unescaped single quotes found");
- return "'" + qstring.toStdString() + "'";
-}
-
-std::string rectToJSObject(const QRect &rect)
-{
- std::ostringstream out;
- out << "{"
- << " x: " << std::to_string(rect.x()) << ","
- << " y: " << std::to_string(rect.y()) << ","
- << " width: " << std::to_string(rect.width()) << ","
- << " height: " << std::to_string(rect.height()) << "}";
- return out.str();
-}
-
-std::string screenToJSObject(const QScreen &screen)
-{
- std::ostringstream out;
- out << "{"
- << " name: " << toJSString(screen.name()) << ","
- << " geometry: " << rectToJSObject(screen.geometry()) << "}";
- return out.str();
-}
-
-std::string windowToJSObject(const QWindow &window)
-{
- std::ostringstream out;
- out << "{"
- << " id: " << std::to_string(window.winId()) << ","
- << " geometry: " << rectToJSObject(window.geometry()) << ","
- << " frameGeometry: " << rectToJSObject(window.frameGeometry()) << ","
- << " title: '" << window.title().toStdString() << "' }";
- return out.str();
-}
-
-void windowInformation()
-{
- auto windows = qGuiApp->allWindows();
-
- std::vector<std::string> windowsAsJsObjects;
- windowsAsJsObjects.reserve(windows.size());
- std::transform(windows.begin(), windows.end(), std::back_inserter(windowsAsJsObjects),
- [](const QWindow *window) { return windowToJSObject(*window); });
-
- emscripten::val::global("window").call<void>("windowInformationCallback",
- emscripten::val(toJSArray(windowsAsJsObjects)));
-}
-
-void screenInformation()
-{
- auto screens = qGuiApp->screens();
-
- std::vector<std::string> screensAsJsObjects;
- screensAsJsObjects.reserve(screens.size());
- std::transform(screens.begin(), screens.end(), std::back_inserter(screensAsJsObjects),
- [](const QScreen *screen) { return screenToJSObject(*screen); });
- emscripten::val::global("window").call<void>("screenInformationCallback",
- emscripten::val(toJSArray(screensAsJsObjects)));
-}
-
-void createWindow(int x, int y, int w, int h, std::string screenId, std::string title)
-{
- auto screens = qGuiApp->screens();
- auto screen_it = std::find_if(screens.begin(), screens.end(), [&screenId](QScreen *screen) {
- return screen->name() == QString::fromLatin1(screenId);
- });
- if (screen_it == screens.end()) {
- qWarning() << "No such screen: " << screenId;
- return;
- }
-
- auto *window = new DeleteOnCloseWindow;
-
- window->setFlag(Qt::WindowTitleHint);
- window->setFlag(Qt::WindowMaximizeButtonHint);
- window->setTitle(QString::fromLatin1(title));
- window->setGeometry(x, y, w, h);
- window->setScreen(*screen_it);
- window->showNormal();
-}
-
-EMSCRIPTEN_BINDINGS(qwasmwindow)
-{
- emscripten::function("screenInformation", &screenInformation);
- emscripten::function("windowInformation", &windowInformation);
- emscripten::function("createWindow", &createWindow);
-}
-
-int main(int argc, char **argv)
-{
- QGuiApplication app(argc, argv);
-
- app.exec();
- return 0;
-}
-
-#include "qwasmwindow_harness.moc"
diff --git a/tests/manual/wasm/qwasmwindow/qwasmwindow_harness.html b/tests/manual/wasm/qwasmwindow/qwasmwindow_harness.html
deleted file mode 100644
index 9eb82618b9..0000000000
--- a/tests/manual/wasm/qwasmwindow/qwasmwindow_harness.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<!doctype html>
-
-<head>
- <script type="text/javascript" src="qwasmwindow_harness.js"></script>
- <script>
- (async () => {
- const instance = await createQtAppInstance({});
- window.instance = instance;
-
- const testSandbox = document.createElement('div');
- testSandbox.id = 'test-sandbox';
- document.body.appendChild(testSandbox);
-
- const makeSizedDiv = (left, top, width, height) => {
- const screenDiv = document.createElement('div');
-
- screenDiv.style.left = `${left}px`;
- screenDiv.style.top = `${top}px`;
- screenDiv.style.width = `${width}px`;
- screenDiv.style.height = `${height}px`;
- screenDiv.style.backgroundColor = 'lightblue';
-
- return screenDiv;
- };
-
- window.testSupport = {
- initializeScreenWithFixedPosition: (left, top, width, height) => {
- const screenDiv = makeSizedDiv(left, top, width, height);
- testSandbox.appendChild(screenDiv);
-
- screenDiv.style.position = 'fixed';
- instance.qtAddContainerElement(screenDiv);
-
- return screenDiv;
- },
- initializeScreenWithRelativePosition: (left, top, width, height) => {
- const screenDiv = makeSizedDiv(left, top, width, height);
- testSandbox.appendChild(screenDiv);
-
- screenDiv.style.position = 'relative';
- instance.qtAddContainerElement(screenDiv);
-
- return screenDiv;
- },
- initializeScreenInScrollContainer:
- (scrollWidth, scrollHeight, left, top, width, height) => {
- const scrollContainer = document.createElement('div');
- scrollContainer.style.height = `${scrollHeight}px`;
- scrollContainer.style.width = `${scrollWidth}px`;
- testSandbox.appendChild(scrollContainer);
-
- const screenDiv = makeSizedDiv(left, top, width, height);
- scrollContainer.appendChild(screenDiv);
- screenDiv.style.position = 'relative';
-
- instance.qtAddContainerElement(screenDiv);
-
- return [scrollContainer, screenDiv];
- }
- };
- })();
- </script>
-</head>
-
-<body>
-</body>
diff --git a/tests/manual/wasm/qwasmwindow/run.sh b/tests/manual/wasm/qwasmwindow/run.sh
deleted file mode 100755
index f6271d6131..0000000000
--- a/tests/manual/wasm/qwasmwindow/run.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#! /bin/bash
-
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-
-set -m
-
-function removeServer()
-{
- [ -z "$cleanupPid" ] || kill $cleanupPid
-}
-
-trap removeServer EXIT
-
-script_dir=`dirname ${BASH_SOURCE[0]}`
-cd "$script_dir"
-python3 qtwasmserver.py -p 8001 > /dev/null 2>&1 &
-cleanupPid=$!
-
-python3 qwasmwindow.py $@
-
-echo 'Press any key to continue...' >&2
-read -n 1
diff --git a/tests/manual/wasm/rasterwindow/main.cpp b/tests/manual/wasm/rasterwindow/main.cpp
index 38921c8c30..576b73112b 100644
--- a/tests/manual/wasm/rasterwindow/main.cpp
+++ b/tests/manual/wasm/rasterwindow/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui>
#include "rasterwindow.h"
diff --git a/tests/manual/wasm/rasterwindow/rasterwindow.cpp b/tests/manual/wasm/rasterwindow/rasterwindow.cpp
index b8da476d46..8fd036c274 100644
--- a/tests/manual/wasm/rasterwindow/rasterwindow.cpp
+++ b/tests/manual/wasm/rasterwindow/rasterwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "rasterwindow.h"
RasterWindow::RasterWindow()
diff --git a/tests/manual/wasm/rasterwindow/rasterwindow.h b/tests/manual/wasm/rasterwindow/rasterwindow.h
index b01efb9ddb..e488420440 100644
--- a/tests/manual/wasm/rasterwindow/rasterwindow.h
+++ b/tests/manual/wasm/rasterwindow/rasterwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef RASTERWINDOW_H
#define RASTERWINDOW_H
diff --git a/tests/manual/wasm/shared/run.sh b/tests/manual/wasm/shared/run.sh
index a8af94e957..f04e45278c 100755
--- a/tests/manual/wasm/shared/run.sh
+++ b/tests/manual/wasm/shared/run.sh
@@ -1,7 +1,7 @@
#! /bin/bash
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
set -m
diff --git a/tests/manual/wasm/shared/testrunner.js b/tests/manual/wasm/shared/testrunner.js
index da87026b03..197e3bfa6d 100644
--- a/tests/manual/wasm/shared/testrunner.js
+++ b/tests/manual/wasm/shared/testrunner.js
@@ -1,6 +1,84 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+function parseQuery()
+{
+ const trimmed = window.location.search.substring(1);
+ return new Map(
+ trimmed.length === 0 ?
+ [] :
+ trimmed.split('&').map(paramNameAndValue =>
+ {
+ const [name, value] = paramNameAndValue.split('=');
+ return [decodeURIComponent(name), value ? decodeURIComponent(value) : ''];
+ }));
+}
+
+export class assert
+{
+ static isFalse(value)
+ {
+ if (value !== false)
+ throw new Error(`Assertion failed, expected to be false, was ${value}`);
+ }
+
+ static isTrue(value)
+ {
+ if (value !== true)
+ throw new Error(`Assertion failed, expected to be true, was ${value}`);
+ }
+
+ static isUndefined(value)
+ {
+ if (typeof value !== 'undefined')
+ throw new Error(`Assertion failed, expected to be undefined, was ${value}`);
+ }
+
+ static isNotUndefined(value)
+ {
+ if (typeof value === 'undefined')
+ throw new Error(`Assertion failed, expected not to be undefined, was ${value}`);
+ }
+
+ static equal(expected, actual)
+ {
+ if (expected !== actual)
+ throw new Error(`Assertion failed, expected to be ${expected}, was ${actual}`);
+ }
+
+ static notEqual(expected, actual)
+ {
+ if (expected === actual)
+ throw new Error(`Assertion failed, expected not to be ${expected}`);
+ }
+}
+
+export class Mock extends Function
+{
+ #calls = [];
+
+ constructor()
+ {
+ super()
+ const proxy = new Proxy(this, {
+ apply: (target, _, args) => target.onCall(...args)
+ });
+ proxy.thisMock = this;
+
+ return proxy;
+ }
+
+ get calls()
+ {
+ return this.thisMock.#calls;
+ }
+
+ onCall(...args)
+ {
+ this.#calls.push(args);
+ }
+}
+
function output(message)
{
const outputLine = document.createElement('div');
@@ -15,10 +93,12 @@ function output(message)
export class TestRunner
{
#testClassInstance
+ #timeoutSeconds
- constructor(testClassInstance)
+ constructor(testClassInstance, config)
{
this.#testClassInstance = testClassInstance;
+ this.#timeoutSeconds = config?.timeoutSeconds ?? 2;
}
async run(testCase)
@@ -39,8 +119,8 @@ export class TestRunner
const timeout = window.setTimeout(() =>
{
rejected = true;
- reject(new Error('Timeout after 2 seconds'));
- }, 2000);
+ reject(new Error(`Timeout after ${this.#timeoutSeconds} seconds`));
+ }, this.#timeoutSeconds * 1000);
prototype[testCase].apply(this.#testClassInstance).then(() =>
{
if (!rejected) {
@@ -61,12 +141,15 @@ export class TestRunner
async runAll()
{
+ const query = parseQuery();
+ const testFilter = query.has('testfilter') ? new RegExp(query.get('testfilter')) : undefined;
+
const SPECIAL_FUNCTIONS =
['beforeEach', 'afterEach', 'beforeAll', 'afterAll', 'constructor'];
const prototype = Object.getPrototypeOf(this.#testClassInstance);
const testFunctions =
Object.getOwnPropertyNames(prototype).filter(
- entry => SPECIAL_FUNCTIONS.indexOf(entry) === -1);
+ entry => SPECIAL_FUNCTIONS.indexOf(entry) === -1 && (!testFilter || entry.match(testFilter)));
if (prototype.beforeAll)
await prototype.beforeAll.apply(this.#testClassInstance);
diff --git a/tests/manual/widgetgrab/main.cpp b/tests/manual/widgetgrab/main.cpp
index ecf28f63b0..d454b238fd 100644
--- a/tests/manual/widgetgrab/main.cpp
+++ b/tests/manual/widgetgrab/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QDebug>
diff --git a/tests/manual/widgets/itemviews/autoResizePrecision/tablehorz/testtable1.cpp b/tests/manual/widgets/itemviews/autoResizePrecision/tablehorz/testtable1.cpp
index 1f194e3f7f..c3573a7852 100644
--- a/tests/manual/widgets/itemviews/autoResizePrecision/tablehorz/testtable1.cpp
+++ b/tests/manual/widgets/itemviews/autoResizePrecision/tablehorz/testtable1.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2012 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets/QtWidgets>
diff --git a/tests/manual/widgets/itemviews/autoResizePrecision/tablevert/testtable2.cpp b/tests/manual/widgets/itemviews/autoResizePrecision/tablevert/testtable2.cpp
index 1f4070e589..4157dab9c3 100644
--- a/tests/manual/widgets/itemviews/autoResizePrecision/tablevert/testtable2.cpp
+++ b/tests/manual/widgets/itemviews/autoResizePrecision/tablevert/testtable2.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2012 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets/QtWidgets>
diff --git a/tests/manual/widgets/itemviews/autoResizePrecision/treeview/testtree.cpp b/tests/manual/widgets/itemviews/autoResizePrecision/treeview/testtree.cpp
index 42d987e493..37c6df7002 100644
--- a/tests/manual/widgets/itemviews/autoResizePrecision/treeview/testtree.cpp
+++ b/tests/manual/widgets/itemviews/autoResizePrecision/treeview/testtree.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2012 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets/QtWidgets>
diff --git a/tests/manual/widgets/itemviews/delegate/example.cpp b/tests/manual/widgets/itemviews/delegate/example.cpp
index 3421b7790b..7dbbf445df 100644
--- a/tests/manual/widgets/itemviews/delegate/example.cpp
+++ b/tests/manual/widgets/itemviews/delegate/example.cpp
@@ -1,11 +1,11 @@
// Copyright (C) 2012 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QLineEdit>
#include <QApplication>
#include <QTableView>
#include <QStandardItemModel>
-#include <QItemDelegate>
+#include <QStyledItemDelegate>
#include <QDebug>
#include <QComboBox>
@@ -16,10 +16,10 @@ public:
~ExampleEditor() { QApplication::instance()->quit(); }
};
-class ExampleDelegate : public QItemDelegate
+class ExampleDelegate : public QStyledItemDelegate
{
public:
- ExampleDelegate() : QItemDelegate()
+ ExampleDelegate() : QStyledItemDelegate()
{
m_editor = new ExampleEditor(0);
m_combobox = new QComboBox(0);
diff --git a/tests/manual/widgets/itemviews/qconcatenatetablesproxymodel/main.cpp b/tests/manual/widgets/itemviews/qconcatenatetablesproxymodel/main.cpp
index d723464f4e..ce99a93d1f 100644
--- a/tests/manual/widgets/itemviews/qconcatenatetablesproxymodel/main.cpp
+++ b/tests/manual/widgets/itemviews/qconcatenatetablesproxymodel/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QConcatenateTablesProxyModel>
diff --git a/tests/manual/widgets/itemviews/qheaderview/qheaderviewtest1.cpp b/tests/manual/widgets/itemviews/qheaderview/qheaderviewtest1.cpp
index f6ea484bed..437dbb6126 100644
--- a/tests/manual/widgets/itemviews/qheaderview/qheaderviewtest1.cpp
+++ b/tests/manual/widgets/itemviews/qheaderview/qheaderviewtest1.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2012 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets/QtWidgets>
diff --git a/tests/manual/widgets/itemviews/qtreeview/main.cpp b/tests/manual/widgets/itemviews/qtreeview/main.cpp
index 6389faf290..327d8ecb84 100644
--- a/tests/manual/widgets/itemviews/qtreeview/main.cpp
+++ b/tests/manual/widgets/itemviews/qtreeview/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
diff --git a/tests/manual/widgets/itemviews/qtreewidget/main.cpp b/tests/manual/widgets/itemviews/qtreewidget/main.cpp
index 3b67c0c4fe..1b292c1c3b 100644
--- a/tests/manual/widgets/itemviews/qtreewidget/main.cpp
+++ b/tests/manual/widgets/itemviews/qtreewidget/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2012 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QVBoxLayout>
#include <QTreeWidget>
diff --git a/tests/manual/widgets/itemviews/tableview-span-navigation/main.cpp b/tests/manual/widgets/itemviews/tableview-span-navigation/main.cpp
index 419ed0c73e..1e053eeb05 100644
--- a/tests/manual/widgets/itemviews/tableview-span-navigation/main.cpp
+++ b/tests/manual/widgets/itemviews/tableview-span-navigation/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QStandardItemModel>
diff --git a/tests/manual/widgets/kernel/CMakeLists.txt b/tests/manual/widgets/kernel/CMakeLists.txt
index 3ad46154c0..c394151047 100644
--- a/tests/manual/widgets/kernel/CMakeLists.txt
+++ b/tests/manual/widgets/kernel/CMakeLists.txt
@@ -1,7 +1,7 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# add_subdirectory(qtooltip) # special case broken in dev
+# add_subdirectory(qtooltip) # TODO: broken in dev
add_subdirectory(sizeonhide)
add_subdirectory(layoutreplace)
add_subdirectory(setscreen)
diff --git a/tests/manual/widgets/kernel/layoutreplace/main.cpp b/tests/manual/widgets/kernel/layoutreplace/main.cpp
index e01b40a8b3..7d80efff8e 100644
--- a/tests/manual/widgets/kernel/layoutreplace/main.cpp
+++ b/tests/manual/widgets/kernel/layoutreplace/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2013 Thorbjørn Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGridLayout>
#include <QFormLayout>
diff --git a/tests/manual/widgets/kernel/qtooltip/main.cpp b/tests/manual/widgets/kernel/qtooltip/main.cpp
index f3600e0a1f..245738fb58 100644
--- a/tests/manual/widgets/kernel/qtooltip/main.cpp
+++ b/tests/manual/widgets/kernel/qtooltip/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2013 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QDialog>
diff --git a/tests/manual/widgets/kernel/setscreen/main.cpp b/tests/manual/widgets/kernel/setscreen/main.cpp
index 8c847d2b92..3610c9ff35 100644
--- a/tests/manual/widgets/kernel/setscreen/main.cpp
+++ b/tests/manual/widgets/kernel/setscreen/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
diff --git a/tests/manual/widgets/kernel/sizeonhide/main.cpp b/tests/manual/widgets/kernel/sizeonhide/main.cpp
index 671da034e3..a20e6d3827 100644
--- a/tests/manual/widgets/kernel/sizeonhide/main.cpp
+++ b/tests/manual/widgets/kernel/sizeonhide/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2013 Thorbjørn Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
diff --git a/tests/manual/widgets/qgraphicsview/rubberband/rubberbandtest.cpp b/tests/manual/widgets/qgraphicsview/rubberband/rubberbandtest.cpp
index 727c6d4c88..8a4c57e63d 100644
--- a/tests/manual/widgets/qgraphicsview/rubberband/rubberbandtest.cpp
+++ b/tests/manual/widgets/qgraphicsview/rubberband/rubberbandtest.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2012 Thorbjørn Lund Martsum - tmartsum[at]gmail.com
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
diff --git a/tests/manual/widgets/styles/main.cpp b/tests/manual/widgets/styles/main.cpp
index 25802eb5b2..89050b90e1 100644
--- a/tests/manual/widgets/styles/main.cpp
+++ b/tests/manual/widgets/styles/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QAction>
#include <QApplication>
diff --git a/tests/manual/widgets/widgets/bigmenucreator/main.cpp b/tests/manual/widgets/widgets/bigmenucreator/main.cpp
index 26c823c16e..4afe0e018b 100644
--- a/tests/manual/widgets/widgets/bigmenucreator/main.cpp
+++ b/tests/manual/widgets/widgets/bigmenucreator/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
#include <QApplication>
diff --git a/tests/manual/widgets/widgets/bigmenucreator/mainwindow.cpp b/tests/manual/widgets/widgets/bigmenucreator/mainwindow.cpp
index 116983aa90..a2d6c79311 100644
--- a/tests/manual/widgets/widgets/bigmenucreator/mainwindow.cpp
+++ b/tests/manual/widgets/widgets/bigmenucreator/mainwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
#include "ui_mainwindow.h"
diff --git a/tests/manual/widgets/widgets/bigmenucreator/mainwindow.h b/tests/manual/widgets/widgets/bigmenucreator/mainwindow.h
index fff499c1bb..008820275d 100644
--- a/tests/manual/widgets/widgets/bigmenucreator/mainwindow.h
+++ b/tests/manual/widgets/widgets/bigmenucreator/mainwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
diff --git a/tests/manual/widgets/widgets/defaultUpMenuBar/main.cpp b/tests/manual/widgets/widgets/defaultUpMenuBar/main.cpp
index 7eeccf1286..c1d7c032d0 100644
--- a/tests/manual/widgets/widgets/defaultUpMenuBar/main.cpp
+++ b/tests/manual/widgets/widgets/defaultUpMenuBar/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This test is to check that the menus in a menubar are displayed correctly in both the top and
// bottom cases. Especially when using multiple screens. If possible relayout the screens in order
diff --git a/tests/manual/widgets/widgets/multiscreen-menus/main.cpp b/tests/manual/widgets/widgets/multiscreen-menus/main.cpp
index e99017338e..7f9628e374 100644
--- a/tests/manual/widgets/widgets/multiscreen-menus/main.cpp
+++ b/tests/manual/widgets/widgets/multiscreen-menus/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
#include <QApplication>
diff --git a/tests/manual/widgets/widgets/multiscreen-menus/mainwindow.cpp b/tests/manual/widgets/widgets/multiscreen-menus/mainwindow.cpp
index ec3b4085a3..a3af329783 100644
--- a/tests/manual/widgets/widgets/multiscreen-menus/mainwindow.cpp
+++ b/tests/manual/widgets/widgets/multiscreen-menus/mainwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mainwindow.h"
#include "ui_mainwindow.h"
diff --git a/tests/manual/widgets/widgets/multiscreen-menus/mainwindow.h b/tests/manual/widgets/widgets/multiscreen-menus/mainwindow.h
index 9189339a1e..7a70b44657 100644
--- a/tests/manual/widgets/widgets/multiscreen-menus/mainwindow.h
+++ b/tests/manual/widgets/widgets/multiscreen-menus/mainwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
diff --git a/tests/manual/widgets/widgets/qmainwindow/saveStateSize/main.cpp b/tests/manual/widgets/widgets/qmainwindow/saveStateSize/main.cpp
index c7cf256415..6576bcf8ab 100644
--- a/tests/manual/widgets/widgets/qmainwindow/saveStateSize/main.cpp
+++ b/tests/manual/widgets/widgets/qmainwindow/saveStateSize/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Test that the size of the saved state bytearray does not change due to moving a
// toolbar from one area to another. It should stay the same size as the first time
diff --git a/tests/manual/widgets/widgets/qtabbar/stylesheet/main.cpp b/tests/manual/widgets/widgets/qtabbar/stylesheet/main.cpp
index 09a64b4437..7d886d1ebc 100644
--- a/tests/manual/widgets/widgets/qtabbar/stylesheet/main.cpp
+++ b/tests/manual/widgets/widgets/qtabbar/stylesheet/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This test is for checking that when there is padding set on the stylesheet and the elide mode is
// set that it is correctly shown as elided and not clipped.
diff --git a/tests/manual/widgets/widgets/qtoolbutton/menuOnMultiScreens/main.cpp b/tests/manual/widgets/widgets/qtoolbutton/menuOnMultiScreens/main.cpp
index f0e1b7ce59..3a575d8fe6 100644
--- a/tests/manual/widgets/widgets/qtoolbutton/menuOnMultiScreens/main.cpp
+++ b/tests/manual/widgets/widgets/qtoolbutton/menuOnMultiScreens/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This tests that when in a multiple screen setup, that screens that have a top-left of 0x0 or
// a top left of being above/below the other screen then showing the toolbutton menu will be
diff --git a/tests/manual/windowactivation/main.cpp b/tests/manual/windowactivation/main.cpp
index 91ac1049f8..f171075089 100644
--- a/tests/manual/windowactivation/main.cpp
+++ b/tests/manual/windowactivation/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
diff --git a/tests/manual/windowchildgeometry/controllerwidget.cpp b/tests/manual/windowchildgeometry/controllerwidget.cpp
index ad4983accf..8478ba5411 100644
--- a/tests/manual/windowchildgeometry/controllerwidget.cpp
+++ b/tests/manual/windowchildgeometry/controllerwidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "controllerwidget.h"
#include <controls.h>
diff --git a/tests/manual/windowchildgeometry/controllerwidget.h b/tests/manual/windowchildgeometry/controllerwidget.h
index 2d0adacf48..c2ebb8c07d 100644
--- a/tests/manual/windowchildgeometry/controllerwidget.h
+++ b/tests/manual/windowchildgeometry/controllerwidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef CONTROLLERWIDGET_H
#define CONTROLLERWIDGET_H
diff --git a/tests/manual/windowchildgeometry/main.cpp b/tests/manual/windowchildgeometry/main.cpp
index 58b2c305c1..8c2b6df4d3 100644
--- a/tests/manual/windowchildgeometry/main.cpp
+++ b/tests/manual/windowchildgeometry/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include "controllerwidget.h"
diff --git a/tests/manual/windowflags/CMakeLists.txt b/tests/manual/windowflags/CMakeLists.txt
index 3e34d4452f..27e4fd5ba6 100644
--- a/tests/manual/windowflags/CMakeLists.txt
+++ b/tests/manual/windowflags/CMakeLists.txt
@@ -12,6 +12,8 @@ qt_internal_add_manual_test(tst_manual_windowflags
controls.cpp controls.h
main.cpp
previewwindow.cpp previewwindow.h
+ NO_PCH_SOURCES
+ controls.cpp # undef QT_NO_FOREACH
LIBRARIES
Qt::Gui
Qt::Widgets
diff --git a/tests/manual/windowflags/controllerwindow.cpp b/tests/manual/windowflags/controllerwindow.cpp
index c2c36025d5..32979c1cd3 100644
--- a/tests/manual/windowflags/controllerwindow.cpp
+++ b/tests/manual/windowflags/controllerwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "controllerwindow.h"
#include "controls.h"
diff --git a/tests/manual/windowflags/controllerwindow.h b/tests/manual/windowflags/controllerwindow.h
index 933bbe749a..c51ed45a2e 100644
--- a/tests/manual/windowflags/controllerwindow.h
+++ b/tests/manual/windowflags/controllerwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef CONTROLLERWINDOW_H
#define CONTROLLERWINDOW_H
diff --git a/tests/manual/windowflags/controls.cpp b/tests/manual/windowflags/controls.cpp
index 43d8fac4c1..97bedd0331 100644
--- a/tests/manual/windowflags/controls.cpp
+++ b/tests/manual/windowflags/controls.cpp
@@ -1,5 +1,7 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include "controls.h"
diff --git a/tests/manual/windowflags/controls.h b/tests/manual/windowflags/controls.h
index a2b4c61228..7918468be8 100644
--- a/tests/manual/windowflags/controls.h
+++ b/tests/manual/windowflags/controls.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef CONTROLS_H
#define CONTROLS_H
diff --git a/tests/manual/windowflags/main.cpp b/tests/manual/windowflags/main.cpp
index b5368b0ef8..ad99b92d13 100644
--- a/tests/manual/windowflags/main.cpp
+++ b/tests/manual/windowflags/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QStringList>
diff --git a/tests/manual/windowflags/previewwindow.cpp b/tests/manual/windowflags/previewwindow.cpp
index a4b3a1a08f..5a048ef5cf 100644
--- a/tests/manual/windowflags/previewwindow.cpp
+++ b/tests/manual/windowflags/previewwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QPlainTextEdit>
#include <QPushButton>
diff --git a/tests/manual/windowflags/previewwindow.h b/tests/manual/windowflags/previewwindow.h
index 500b0ffac5..3bfae9c0e9 100644
--- a/tests/manual/windowflags/previewwindow.h
+++ b/tests/manual/windowflags/previewwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef PREVIEWWINDOW_H
#define PREVIEWWINDOW_H
diff --git a/tests/manual/windowgeometry/controllerwidget.cpp b/tests/manual/windowgeometry/controllerwidget.cpp
index 5b8e8e58c1..8328b58e7c 100644
--- a/tests/manual/windowgeometry/controllerwidget.cpp
+++ b/tests/manual/windowgeometry/controllerwidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "controllerwidget.h"
#include <controls.h>
diff --git a/tests/manual/windowgeometry/controllerwidget.h b/tests/manual/windowgeometry/controllerwidget.h
index a2591f4225..6358311be9 100644
--- a/tests/manual/windowgeometry/controllerwidget.h
+++ b/tests/manual/windowgeometry/controllerwidget.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef CONTROLLERWIDGET_H
#define CONTROLLERWIDGET_H
diff --git a/tests/manual/windowgeometry/main.cpp b/tests/manual/windowgeometry/main.cpp
index 58b2c305c1..8c2b6df4d3 100644
--- a/tests/manual/windowgeometry/main.cpp
+++ b/tests/manual/windowgeometry/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include "controllerwidget.h"
diff --git a/tests/manual/windowmask/CMakeLists.txt b/tests/manual/windowmask/CMakeLists.txt
new file mode 100644
index 0000000000..e1417bedbe
--- /dev/null
+++ b/tests/manual/windowmask/CMakeLists.txt
@@ -0,0 +1,14 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## windowmask Binary:
+#####################################################################
+
+qt_internal_add_manual_test(windowmask
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Widgets
+)
diff --git a/tests/manual/windowmask/main.cpp b/tests/manual/windowmask/main.cpp
new file mode 100644
index 0000000000..3055e18a42
--- /dev/null
+++ b/tests/manual/windowmask/main.cpp
@@ -0,0 +1,124 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtWidgets>
+
+template<typename Paintable>
+class Circle : public Paintable
+{
+public:
+ using Paintable::setMinimumSize;
+ using Paintable::setMaximumSize;
+ using Paintable::width;
+ using Paintable::height;
+ using Paintable::screen;
+ using Paintable::devicePixelRatio;
+ using Paintable::metaObject;
+ using Paintable::setMask;
+ using Paintable::frameGeometry;
+ using Paintable::setPosition;
+ using Paintable::startSystemMove;
+ using Paintable::setFlags;
+ using Paintable::requestUpdate;
+
+ Circle()
+ {
+ setMinimumSize({200, 200});
+ setMaximumSize({200, 200});
+ setFlags(Qt::Window | Qt::FramelessWindowHint);
+ }
+
+protected:
+ void paintEvent(QPaintEvent *) override
+ {
+ qWarning() << "Painting into a" << this << "with DPR" << devicePixelRatio()
+ << "on a screen with DPR" << screen()->devicePixelRatio();
+ QPainter painter(static_cast<Paintable *>(this));
+ painter.fillRect(0, 0, width(), height(), devicePixelRatio() == 1 ? Qt::red : Qt::green);
+ painter.setPen(QPen(Qt::black, 5));
+ painter.drawRect(10, 10, width() - 20, height() - 20);
+ painter.drawText(0, 0, width(), height(), Qt::AlignHCenter | Qt::AlignVCenter, metaObject()->className());
+ }
+ void mousePressEvent(QMouseEvent *event) override
+ {
+ if (event->button() == Qt::LeftButton) {
+ if (event->modifiers() & Qt::ControlModifier)
+ requestUpdate();
+ else if (event->modifiers() & Qt::AltModifier)
+ updateMask();
+ else if (event->modifiers() & Qt::ShiftModifier && startSystemMove())
+ dragPosition = {};
+ else
+ dragPosition = event->globalPosition() - frameGeometry().topLeft();
+ }
+ }
+ void mouseMoveEvent(QMouseEvent *event) override
+ {
+ if (event->buttons() & Qt::LeftButton && !dragPosition.isNull())
+ setPosition((event->globalPosition() - dragPosition).toPoint());
+ }
+ void resizeEvent(QResizeEvent *) override
+ {
+ updateMask();
+ }
+
+ void updateMask()
+ {
+ int side = qMin(width(), height());
+ QRegion maskedRegion(width() / 2 - side / 2, height() / 2 - side / 2, side,
+ side, QRegion::Ellipse);
+ qDebug() << "Updating mask for" << this << "to" << maskedRegion.boundingRect();
+ setMask(maskedRegion);
+ }
+
+ QPointF dragPosition;
+};
+
+class WindowLikeWidget : public QWidget
+{
+public:
+ void setPosition(const QPoint &point) { QWidget::move(point); }
+ bool startSystemMove() { return windowHandle()->startSystemMove(); }
+ void setFlags(Qt::WindowFlags flags) { setWindowFlags(flags); }
+ void requestUpdate() { update(); }
+};
+
+class Widget : public Circle<WindowLikeWidget>
+{
+public:
+ Widget()
+ {
+ setAttribute(Qt::WA_TranslucentBackground);
+ }
+};
+
+class Window : public Circle<QRasterWindow>
+{
+};
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ Widget widget;
+ widget.show();
+
+ Window window;
+ window.show();
+
+ auto dumpScreenData = [](QScreen *screen){
+ qDebug() << "- name:" << screen->name();
+ qDebug() << "- dpr :" << screen->devicePixelRatio();
+ };
+
+ QObject::connect(widget.windowHandle(), &QWindow::screenChanged, &widget, [&]{
+ qDebug() << "Screen changed for" << &widget;
+ dumpScreenData(widget.screen());
+ });
+ QObject::connect(&window, &QWindow::screenChanged, &window, [&]{
+ qDebug() << "Screen changed for" << &window;
+ dumpScreenData(window.screen());
+ });
+
+ return app.exec();
+}
diff --git a/tests/manual/windowmodality/main.cpp b/tests/manual/windowmodality/main.cpp
index 7c15a44793..6a11f6c42d 100644
--- a/tests/manual/windowmodality/main.cpp
+++ b/tests/manual/windowmodality/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "ui_dialog.h"
#include "ui_widget.h"
diff --git a/tests/manual/windowtransparency/windowtransparency.cpp b/tests/manual/windowtransparency/windowtransparency.cpp
index 2878308bef..10ee6960c3 100644
--- a/tests/manual/windowtransparency/windowtransparency.cpp
+++ b/tests/manual/windowtransparency/windowtransparency.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui>
#include <QtWidgets>
diff --git a/tests/manual/xcb_gl_integration/main.cpp b/tests/manual/xcb_gl_integration/main.cpp
index 8c469bf901..a19505900c 100644
--- a/tests/manual/xcb_gl_integration/main.cpp
+++ b/tests/manual/xcb_gl_integration/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui/QGuiApplication>
#include <QtGui/QWindow>
diff --git a/tests/manual/xembed/CMakeLists.txt b/tests/manual/xembed/CMakeLists.txt
index f486f202a5..44bb2ea818 100644
--- a/tests/manual/xembed/CMakeLists.txt
+++ b/tests/manual/xembed/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
if(QT_FEATURE_gtk3)
add_subdirectory(gtk-container)
diff --git a/tests/manual/xembed/gtk-container/CMakeLists.txt b/tests/manual/xembed/gtk-container/CMakeLists.txt
index c109bbc95b..34a802c142 100644
--- a/tests/manual/xembed/gtk-container/CMakeLists.txt
+++ b/tests/manual/xembed/gtk-container/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
qt_find_package(GTK3)
qt_find_package(X11)
diff --git a/tests/manual/xembed/gtk-container/gtk-container.cpp b/tests/manual/xembed/gtk-container/gtk-container.cpp
index 41d88e8d51..2ecbe10545 100644
--- a/tests/manual/xembed/gtk-container/gtk-container.cpp
+++ b/tests/manual/xembed/gtk-container/gtk-container.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <gtk/gtk.h>
#include <gtk/gtkx.h>
diff --git a/tests/manual/xembed/qt-client-raster/main.cpp b/tests/manual/xembed/qt-client-raster/main.cpp
index f522e1401f..628a9f652c 100644
--- a/tests/manual/xembed/qt-client-raster/main.cpp
+++ b/tests/manual/xembed/qt-client-raster/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "rasterwindow.h"
#include <QDebug>
diff --git a/tests/manual/xembed/qt-client-raster/rasterwindow.cpp b/tests/manual/xembed/qt-client-raster/rasterwindow.cpp
index b99ca4b254..89d765f707 100644
--- a/tests/manual/xembed/qt-client-raster/rasterwindow.cpp
+++ b/tests/manual/xembed/qt-client-raster/rasterwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "rasterwindow.h"
diff --git a/tests/manual/xembed/qt-client-raster/rasterwindow.h b/tests/manual/xembed/qt-client-raster/rasterwindow.h
index 251237dc0b..2d41cd8e5d 100644
--- a/tests/manual/xembed/qt-client-raster/rasterwindow.h
+++ b/tests/manual/xembed/qt-client-raster/rasterwindow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef RASTERWINDOW_H
#define RASTERWINDOW_H
diff --git a/tests/manual/xembed/qt-client-widget/main.cpp b/tests/manual/xembed/qt-client-widget/main.cpp
index 7451d7bb1e..bd8b05b054 100644
--- a/tests/manual/xembed/qt-client-widget/main.cpp
+++ b/tests/manual/xembed/qt-client-widget/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QDebug>
diff --git a/tests/manual/xembed/qt-client-widget/window.cpp b/tests/manual/xembed/qt-client-widget/window.cpp
index 756e956bb6..92feca7506 100644
--- a/tests/manual/xembed/qt-client-widget/window.cpp
+++ b/tests/manual/xembed/qt-client-widget/window.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtWidgets>
diff --git a/tests/manual/xembed/qt-client-widget/window.h b/tests/manual/xembed/qt-client-widget/window.h
index b749c04009..2b069f1ff0 100644
--- a/tests/manual/xembed/qt-client-widget/window.h
+++ b/tests/manual/xembed/qt-client-widget/window.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WINDOW_H
#define WINDOW_H
diff --git a/tests/manual/xmlstreamlint/main.cpp b/tests/manual/xmlstreamlint/main.cpp
index 5b445f87bd..b825d1e7f5 100644
--- a/tests/manual/xmlstreamlint/main.cpp
+++ b/tests/manual/xmlstreamlint/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QCoreApplication>
#include <QFile>