summaryrefslogtreecommitdiffstats
path: root/tests/manual/examples/widgets
diff options
context:
space:
mode:
Diffstat (limited to 'tests/manual/examples/widgets')
-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.txt1
-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.txt2
-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.txt38
-rw-r--r--tests/manual/examples/widgets/wiggly/dialog.cpp27
-rw-r--r--tests/manual/examples/widgets/wiggly/dialog.h19
-rw-r--r--tests/manual/examples/widgets/wiggly/main.cpp16
-rw-r--r--tests/manual/examples/widgets/wiggly/wiggly.pro11
-rw-r--r--tests/manual/examples/widgets/wiggly/wigglywidget.cpp65
-rw-r--r--tests/manual/examples/widgets/wiggly/wigglywidget.h32
-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
526 files changed, 27676 insertions, 0 deletions
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..ebae3362e9
--- /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 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 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..15f3c3cacf
--- /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 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 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..fb9ebc2848
--- /dev/null
+++ b/tests/manual/examples/widgets/scroller/CMakeLists.txt
@@ -0,0 +1 @@
+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..9a2d66d99e
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/plugandpaint/plugins/CMakeLists.txt
@@ -0,0 +1,2 @@
+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
new file mode 100644
index 0000000000..fe68396f92
--- /dev/null
+++ b/tests/manual/examples/widgets/wiggly/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(wiggly LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/widgets/wiggly")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(wiggly
+ dialog.cpp dialog.h
+ main.cpp
+ wigglywidget.cpp wigglywidget.h
+)
+
+set_target_properties(wiggly PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(wiggly PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS wiggly
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/widgets/wiggly/dialog.cpp b/tests/manual/examples/widgets/wiggly/dialog.cpp
new file mode 100644
index 0000000000..0f3aa84fd8
--- /dev/null
+++ b/tests/manual/examples/widgets/wiggly/dialog.cpp
@@ -0,0 +1,27 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "dialog.h"
+#include "wigglywidget.h"
+
+#include <QLineEdit>
+#include <QVBoxLayout>
+
+//! [0]
+Dialog::Dialog(QWidget *parent)
+ : QDialog(parent)
+{
+ WigglyWidget *wigglyWidget = new WigglyWidget;
+ QLineEdit *lineEdit = new QLineEdit;
+
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ layout->addWidget(wigglyWidget);
+ layout->addWidget(lineEdit);
+
+ connect(lineEdit, &QLineEdit::textChanged, wigglyWidget, &WigglyWidget::setText);
+ lineEdit->setText(u8"🖖 " + tr("Hello world!"));
+
+ setWindowTitle(tr("Wiggly"));
+ resize(360, 145);
+}
+//! [0]
diff --git a/tests/manual/examples/widgets/wiggly/dialog.h b/tests/manual/examples/widgets/wiggly/dialog.h
new file mode 100644
index 0000000000..1f27668f4a
--- /dev/null
+++ b/tests/manual/examples/widgets/wiggly/dialog.h
@@ -0,0 +1,19 @@
+// 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>
+
+//! [0]
+class Dialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit Dialog(QWidget *parent = nullptr);
+};
+//! [0]
+
+#endif
diff --git a/tests/manual/examples/widgets/wiggly/main.cpp b/tests/manual/examples/widgets/wiggly/main.cpp
new file mode 100644
index 0000000000..e7df19b6a0
--- /dev/null
+++ b/tests/manual/examples/widgets/wiggly/main.cpp
@@ -0,0 +1,16 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "dialog.h"
+
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ Dialog dialog;
+ dialog.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/examples/widgets/wiggly/wiggly.pro b/tests/manual/examples/widgets/wiggly/wiggly.pro
new file mode 100644
index 0000000000..0e39f18add
--- /dev/null
+++ b/tests/manual/examples/widgets/wiggly/wiggly.pro
@@ -0,0 +1,11 @@
+QT += widgets
+
+HEADERS = wigglywidget.h \
+ dialog.h
+SOURCES = wigglywidget.cpp \
+ dialog.cpp \
+ main.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/widgets/wiggly
+INSTALLS += target
diff --git a/tests/manual/examples/widgets/wiggly/wigglywidget.cpp b/tests/manual/examples/widgets/wiggly/wigglywidget.cpp
new file mode 100644
index 0000000000..3e686a67cf
--- /dev/null
+++ b/tests/manual/examples/widgets/wiggly/wigglywidget.cpp
@@ -0,0 +1,65 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "wigglywidget.h"
+
+#include <QFontMetrics>
+#include <QPainter>
+#include <QTimerEvent>
+
+//! [0]
+WigglyWidget::WigglyWidget(QWidget *parent)
+ : QWidget(parent), step(0)
+{
+ setBackgroundRole(QPalette::Midlight);
+ setAutoFillBackground(true);
+
+ QFont newFont = font();
+ newFont.setPointSize(newFont.pointSize() + 20);
+ setFont(newFont);
+
+ timer.start(60, this);
+}
+//! [0]
+
+//! [1]
+void WigglyWidget::paintEvent(QPaintEvent * /* event */)
+//! [1] //! [2]
+{
+ static constexpr int sineTable[16] = {
+ 0, 38, 71, 92, 100, 92, 71, 38, 0, -38, -71, -92, -100, -92, -71, -38
+ };
+
+ QFontMetrics metrics(font());
+ int x = (width() - metrics.horizontalAdvance(text)) / 2;
+ int y = (height() + metrics.ascent() - metrics.descent()) / 2;
+ QColor color;
+//! [2]
+
+//! [3]
+ QPainter painter(this);
+//! [3] //! [4]
+ int offset = 0;
+ for (char32_t codePoint : text.toUcs4()) {
+ int index = (step + offset++) % 16;
+ color.setHsv((15 - index) * 16, 255, 191);
+ painter.setPen(color);
+ QString symbol = QString::fromUcs4(&codePoint, 1);
+ painter.drawText(x, y - ((sineTable[index] * metrics.height()) / 400), symbol);
+ x += metrics.horizontalAdvance(symbol);
+ }
+}
+//! [4]
+
+//! [5]
+void WigglyWidget::timerEvent(QTimerEvent *event)
+//! [5] //! [6]
+{
+ if (event->timerId() == timer.timerId()) {
+ ++step;
+ update();
+ } else {
+ QWidget::timerEvent(event);
+ }
+//! [6]
+}
diff --git a/tests/manual/examples/widgets/wiggly/wigglywidget.h b/tests/manual/examples/widgets/wiggly/wigglywidget.h
new file mode 100644
index 0000000000..1c21e52234
--- /dev/null
+++ b/tests/manual/examples/widgets/wiggly/wigglywidget.h
@@ -0,0 +1,32 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef WIGGLYWIDGET_H
+#define WIGGLYWIDGET_H
+
+#include <QBasicTimer>
+#include <QWidget>
+
+//! [0]
+class WigglyWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ WigglyWidget(QWidget *parent = nullptr);
+
+public slots:
+ void setText(const QString &newText) { text = newText; }
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+ void timerEvent(QTimerEvent *event) override;
+
+private:
+ QBasicTimer timer;
+ QString text;
+ int step;
+};
+//! [0]
+
+#endif
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)