QtCanpool 3.3.0
载入中...
搜索中...
未找到
qxsettingsdialog.cpp
浏览该文件的文档.
1
5#include "qxsettingsdialog.h"
7
8#include "qxpropertyeditor.h"
9
10#include <QAbstractButton>
11#include <QDialogButtonBox>
12#include <QHBoxLayout>
13#include <QListWidget>
14#include <QPushButton>
15#include <QShowEvent>
16#include <QStackedWidget>
17#include <QVBoxLayout>
18
20
22
23namespace
24{
25
27const int kListMinimumWidth = 120;
28const int kListMaximumWidth = 240;
30const int kIconSize = 24;
31
32} // namespace
33
34QxSettingsDialogPrivate::QxSettingsDialogPrivate() = default;
35
36void QxSettingsDialogPrivate::init()
37{
39
40 QVBoxLayout *outer = new QVBoxLayout(q);
41
42 QHBoxLayout *body = new QHBoxLayout();
43 outer->addLayout(body, 1);
44
45 m_list = new QListWidget(q);
46 m_list->setIconSize(QSize(kIconSize, kIconSize));
47 m_list->setUniformItemSizes(true);
48 m_list->setMinimumWidth(kListMinimumWidth);
49 m_list->setMaximumWidth(kListMaximumWidth);
50 // The list is an entry point, not a control to fill in: it must not take the
51 // focus away from the page the user came to edit.
52 m_list->setFocusPolicy(Qt::NoFocus);
53 body->addWidget(m_list);
54
55 m_stack = new QStackedWidget(q);
56 body->addWidget(m_stack, 1);
57
58 QObject::connect(m_list, &QListWidget::currentRowChanged, q, [this, q](int row) {
59 if (row < 0 || row >= m_pages.count()) {
60 return;
61 }
62 m_stack->setCurrentIndex(row);
63 Q_EMIT q->currentPageChanged(m_pages.at(row).id);
64 });
65
66 m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply |
67 QDialogButtonBox::RestoreDefaults,
68 q);
69 outer->addWidget(m_buttons);
70
71 // Both of these are overrides, so they are called by name rather than through
72 // a pointer to the base slot.
73 QObject::connect(m_buttons, &QDialogButtonBox::accepted, q, [q]() {
74 q->accept();
75 });
76 QObject::connect(m_buttons, &QDialogButtonBox::rejected, q, [q]() {
77 q->reject();
78 });
79 // Applied and reset are called through the dialog rather than bound straight
80 // to the button: clicked() carries a checked argument these two do not take.
81 QObject::connect(m_buttons->button(QDialogButtonBox::Apply), &QAbstractButton::clicked, q, [q]() {
82 q->apply();
83 });
84 QObject::connect(m_buttons->button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, q, [q]() {
85 q->reset();
86 });
87
88 updateButtons();
89}
90
91int QxSettingsDialogPrivate::indexOf(const QString &id) const
92{
93 for (int i = 0; i < m_pages.count(); ++i) {
94 if (m_pages.at(i).id == id) {
95 return i;
96 }
97 }
98 return -1;
99}
100
101void QxSettingsDialogPrivate::loadPage(const Page &page)
102{
103 if (!page.editor || !m_settings) {
104 return;
105 }
106
107 QHash<QString, QVariant> values;
108 for (const QxProperty &property : page.properties) {
109 // A key that was never written reads as its default, which is what makes
110 // a fresh installation look like a configured one.
111 values.insert(property.key, m_settings->value(property.key, property.defaultValue));
112 }
113 page.editor->setAllValues(values);
114}
115
116void QxSettingsDialogPrivate::load()
117{
118 for (const Page &page : m_pages) {
119 loadPage(page);
120 }
121}
122
123void QxSettingsDialogPrivate::write() const
124{
125 if (!m_settings) {
126 return;
127 }
128
129 for (const Page &page : m_pages) {
130 if (!page.editor) {
131 continue;
132 }
133 const QHash<QString, QVariant> values = page.editor->allValues();
134 for (auto it = values.constBegin(); it != values.constEnd(); ++it) {
135 m_settings->setValue(it.key(), it.value());
136 }
137 }
138 m_settings->sync();
139}
140
141bool QxSettingsDialogPrivate::isModified() const
142{
143 for (const Page &page : m_pages) {
144 if (page.editor && page.editor->isModified()) {
145 return true;
146 }
147 }
148 return false;
149}
150
151void QxSettingsDialogPrivate::updateButtons()
152{
153 if (!m_buttons) {
154 return;
155 }
156 if (QAbstractButton *apply = m_buttons->button(QDialogButtonBox::Apply)) {
157 apply->setEnabled(isModified());
158 }
159}
160
161QxSettingsDialog::QxSettingsDialog(QxSettings *settings, QWidget *parent)
162 : QDialog(parent)
163 , d_ptr(new QxSettingsDialogPrivate())
164{
165 Q_D(QxSettingsDialog);
166 d->setPublic(this);
167 d->m_settings = settings;
168 d->init();
169}
170
175
177{
178 Q_D(const QxSettingsDialog);
179 return d->m_settings;
180}
181
182void QxSettingsDialog::addPage(const QString &id, const QIcon &icon, const QString &title,
183 const QList<QxProperty> &properties)
184{
185 Q_D(QxSettingsDialog);
186
187 if (id.isEmpty()) {
188 qWarning("QxSettingsDialog: a page needs a non-empty id");
189 return;
190 }
191 if (d->indexOf(id) >= 0) {
192 qWarning("QxSettingsDialog: a page with id '%s' already exists", qPrintable(id));
193 return;
194 }
195
196 QxPropertyEditor *editor = new QxPropertyEditor(d->m_stack);
197 d->m_stack->addWidget(editor);
198 for (const QxProperty &property : properties) {
199 editor->addProperty(property);
200 }
201
202 // The list owns the entry; the id it is addressed by lives in the page list
203 // below, so the item needs nothing else.
204 new QListWidgetItem(icon, title, d->m_list);
205
207 page.id = id;
208 page.title = title;
209 page.editor = editor;
210 page.properties = properties;
211 d->m_pages.append(page);
212
213 QObject::connect(editor, &QxPropertyEditor::modifiedChanged, this, [d]() {
214 d->updateButtons();
215 });
216
217 d->loadPage(d->m_pages.last());
218 if (d->m_pages.count() == 1) {
219 // The first page of a dialog has nothing to compete with.
220 d->m_list->setCurrentRow(0);
221 d->m_stack->setCurrentIndex(0);
222 }
223 d->updateButtons();
224}
225
226void QxSettingsDialog::addGroup(const QString &pageId, const QString &title, const QList<QxProperty> &properties)
227{
228 Q_D(QxSettingsDialog);
229
230 const int index = d->indexOf(pageId);
231 if (index < 0) {
232 qWarning("QxSettingsDialog: there is no page with id '%s'", qPrintable(pageId));
233 return;
234 }
235
236 QxPropertyEditor *editor = d->m_pages.at(index).editor;
237 editor->addGroup(title, properties);
238 d->m_pages[index].properties.append(properties);
239
240 // The group brought keys this page had not read yet.
241 d->loadPage(d->m_pages.at(index));
242 d->updateButtons();
243}
244
245QStringList QxSettingsDialog::pageIds() const
246{
247 Q_D(const QxSettingsDialog);
248 QStringList ids;
249 for (const QxSettingsDialogPrivate::Page &page : d->m_pages) {
250 ids.append(page.id);
251 }
252 return ids;
253}
254
255QxPropertyEditor *QxSettingsDialog::editor(const QString &pageId) const
256{
257 Q_D(const QxSettingsDialog);
258 const int index = d->indexOf(pageId);
259 return index < 0 ? Q_NULLPTR : d->m_pages.at(index).editor;
260}
261
263{
264 Q_D(const QxSettingsDialog);
265 const int index = d->m_stack->currentIndex();
266 if (index < 0 || index >= d->m_pages.count()) {
267 return QString();
268 }
269 return d->m_pages.at(index).id;
270}
271
273{
274 Q_D(const QxSettingsDialog);
275 return d->isModified();
276}
277
279{
280 Q_D(QxSettingsDialog);
281 d->load();
282 d->updateButtons();
283}
284
286{
287 Q_D(QxSettingsDialog);
288 for (const QxSettingsDialogPrivate::Page &page : d->m_pages) {
289 if (page.editor) {
290 page.editor->reset();
291 }
292 }
293 d->updateButtons();
294}
295
297{
298 Q_D(QxSettingsDialog);
299
300 if (!d->m_settings) {
301 qWarning("QxSettingsDialog: there is no settings object to apply to");
302 return;
303 }
304
305 d->write();
306
307 // What is on screen is what is stored now, so it becomes the new baseline and
308 // the apply button goes dark again.
309 for (const QxSettingsDialogPrivate::Page &page : d->m_pages) {
310 if (page.editor) {
311 page.editor->setAllValues(page.editor->allValues());
312 }
313 }
314 d->updateButtons();
315 Q_EMIT applied();
316}
317
318void QxSettingsDialog::setCurrentPage(const QString &id)
319{
320 Q_D(QxSettingsDialog);
321 const int index = d->indexOf(id);
322 if (index < 0) {
323 return;
324 }
325 // The list drives the stack, so going through it keeps both in step.
326 d->m_list->setCurrentRow(index);
327}
328
330{
331 apply();
332 QDialog::accept();
333}
334
336{
337 Q_D(QxSettingsDialog);
338 // Discarding means the dialog shows the stored values next time, not the ones
339 // that were on screen when it closed.
340 for (const QxSettingsDialogPrivate::Page &page : d->m_pages) {
341 if (page.editor) {
342 page.editor->resetToInitial();
343 }
344 }
345 d->updateButtons();
346 QDialog::reject();
347}
348
349void QxSettingsDialog::showEvent(QShowEvent *event)
350{
351 Q_D(QxSettingsDialog);
352 // Shown, not constructed: a page added later still has to be read, and a
353 // dialog that is opened twice has to show what is stored now rather than
354 // what was edited and discarded last time.
355 d->load();
356 d->updateButtons();
357 QDialog::showEvent(event);
358}
359
A form built from a list of QxProperty descriptors.
void setAllValues(const QHash< QString, QVariant > &values)
Sets every key of values that this editor knows and takes the result as the new baseline,...
void addGroup(const QString &title, const QList< QxProperty > &properties)
Opens a section titled title and adds properties to it.
void addProperty(const QxProperty &property)
Adds property to the section the last addGroup() opened, or to the plain form when there is none yet.
void modifiedChanged(bool modified)
isModified() changed.
The state behind QxSettingsDialog: the pages, the settings object they are read from and written to,...
A settings dialog: a list of pages on the left, a key/value form on the right and the three things a ...
void applied()
The values have been written and synced.
void reject() override
Discards the edits and closes.
void addGroup(const QString &pageId, const QString &title, const QList< QxProperty > &properties)
Opens a group inside the page pageId and adds properties to it.
void accept() override
Applies and closes.
void reload()
Reads every page back out of the settings object; the dialog does this itself whenever it is shown.
void showEvent(QShowEvent *event) override
void reset()
Sets every property of every page back to the default its descriptor declares.
QxPropertyEditor * editor(const QString &pageId) const
The form of the page pageId, or null when there is no such page.
bool isModified() const
Whether anything on any page differs from what it was loaded as.
void apply()
Writes every value to the settings object and syncs it.
void setCurrentPage(const QString &id)
QStringList pageIds() const
void addPage(const QString &id, const QIcon &icon, const QString &title, const QList< QxProperty > &properties)
Adds a page: an entry in the list on the left and a form on the right holding properties.
QxCore::QxSettings * settings() const
Thin wrapper around QSettings that keeps call sites free of the usual boilerplate: typed accessors wi...
Definition qxsettings.h:32
#define QX_APP_END_NAMESPACE
#define Q_NULLPTR
Copyleft (C) 2025 maminjie canpool@163.com SPDX-License-Identifier: MulanPSL-2.0
#define QX_APP_BEGIN_NAMESPACE
#define QX_FINI_PRIVATE()
#define QX_CORE_USE_NAMESPACE
const char * id
Definition qxtheme.cpp:24
One setting, described well enough to be edited.
Definition qxproperty.h:44