summaryrefslogtreecommitdiffstats
path: root/tests/manual
diff options
context:
space:
mode:
authorVolker Hilsheimer <volker.hilsheimer@qt.io>2023-12-11 16:17:09 +0100
committerVolker Hilsheimer <volker.hilsheimer@qt.io>2023-12-15 18:21:22 +0100
commitbddf27cd5a5412c8282fab43111e7319874ca44e (patch)
treecdead9ae9b2d1789bb5560015fe04e3c6a4c8956 /tests/manual
parent38fa1d55ee25bf78bb391fbc8433e9d2a3b74e6c (diff)
Move the settingseditor example into tests/manual
The example uses QTreeWidget when it should use a QTreeView with a dedicated item model, primarily shows how to use item views (and very little about QSettings), and is generally not useful to show how an application could or should use QSettings to store settings. Turn it into a manual test instead; it's useful for that as it supports ini and plist files, and settings in different scopes. Pick-to: 6.7 Fixes: QTBUG-119978 Change-Id: I7ce039f6391c41c679d126d90a251eee60327c39 Reviewed-by: Ed Cooke <ed.cooke@qt.io> Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
Diffstat (limited to 'tests/manual')
-rw-r--r--tests/manual/examples/widgets/tools/settingseditor/CMakeLists.txt14
-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
13 files changed, 1303 insertions, 0 deletions
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..c7f65b1563
--- /dev/null
+++ b/tests/manual/examples/widgets/tools/settingseditor/CMakeLists.txt
@@ -0,0 +1,14 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+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