Qt Utilities 6.12.0
Common Qt related C++ classes and routines used by my applications such as dialogs, widgets and models
Loading...
Searching...
No Matches
settingsdialog.cpp
Go to the documentation of this file.
1#include "./settingsdialog.h"
2
3#include "./optioncategory.h"
6#include "./optionpage.h"
7
8#include "../misc/dialogutils.h"
9
10#include "ui_settingsdialog.h"
11
12#include <QItemSelectionModel>
13#include <QMessageBox>
14#include <QScrollArea>
15#include <QShowEvent>
16#include <QStringBuilder>
17
18namespace QtUtilities {
19
31 : QDialog(parent)
32 , m_ui(new Ui::SettingsDialog)
33 , m_categoryModel(new OptionCategoryModel(this))
34 , m_categoryFilterModel(new OptionCategoryFilterModel(this))
35 , m_currentCategory(nullptr)
36 , m_tabBarAlwaysVisible(true)
37{
38 m_ui->setupUi(this);
39 makeHeading(m_ui->headingLabel);
40 setStyleSheet(dialogStyleForPalette(palette()));
41
42 // setup models
43 m_categoryFilterModel->setSourceModel(m_categoryModel);
44 m_ui->categoriesListView->setModel(m_categoryFilterModel);
45
46 // connect signals and slots
47 // selection models
48 connect(m_ui->categoriesListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &SettingsDialog::currentCategoryChanged);
49 // buttons
50 connect(m_ui->abortPushButton, &QPushButton::clicked, this, &SettingsDialog::reject);
51 connect(m_ui->applyPushButton, &QPushButton::clicked, this, &SettingsDialog::apply);
52 connect(m_ui->okPushButton, &QPushButton::clicked, this, &SettingsDialog::accept);
53 // dialog
54 connect(this, &SettingsDialog::accepted, this, &SettingsDialog::apply);
55 connect(this, &SettingsDialog::rejected, this, &SettingsDialog::reset);
56 // misc
57 connect(m_ui->filterLineEdit, &QLineEdit::textChanged, m_categoryFilterModel, &OptionCategoryFilterModel::setFilterFixedString);
58 connect(m_ui->filterLineEdit, &QLineEdit::textChanged, this, &SettingsDialog::updateTabWidget);
59}
60
65{
66}
67
73{
74 m_tabBarAlwaysVisible = value;
75 if (m_currentCategory) {
76 m_ui->pagesTabWidget->tabBar()->setHidden(!value && m_currentCategory->pages().size() == 1);
77 }
78}
79
87{
88 return m_categoryModel->category(categoryIndex);
89}
90
98OptionPage *SettingsDialog::page(int categoryIndex, int pageIndex) const
99{
100 if (OptionCategory *const category = this->category(categoryIndex)) {
101 if (pageIndex < category->pages().length()) {
102 return category->pages()[pageIndex];
103 }
104 }
105 return nullptr;
106}
107
111void SettingsDialog::showEvent(QShowEvent *event)
112{
113 if (event->spontaneous()) {
114 return;
115 }
116 for (OptionCategory *const category : m_categoryModel->categories()) {
117 for (OptionPage *const page : category->pages()) {
118 if (page->hasBeenShown()) {
119 page->reset();
120 }
121 }
122 }
123}
124
132void SettingsDialog::currentCategoryChanged(const QModelIndex &index)
133{
134 showCategory(m_categoryModel->category(m_categoryFilterModel->mapToSource(index)));
135}
136
142{
143 if (m_currentCategory) {
144 m_currentCategory->setCurrentIndex(m_ui->pagesTabWidget->currentIndex());
145 }
146 if (category) {
147 if (m_currentCategory != category) {
148 m_currentCategory = category;
149 m_ui->headingLabel->setText(category->displayName());
150 }
151 } else {
152 m_currentCategory = nullptr;
153 m_ui->headingLabel->setText(tr("No category selected"));
154 }
155 updateTabWidget();
156}
157
168{
169 const bool hasSingleCategory = singleCategory != nullptr;
170 m_ui->filterLineEdit->setHidden(hasSingleCategory);
171 m_ui->categoriesListView->setHidden(hasSingleCategory);
172 m_ui->headingLabel->setHidden(hasSingleCategory);
173 if (hasSingleCategory) {
174 m_ui->filterLineEdit->clear();
175 categoryModel()->setCategories(QList<OptionCategory *>({ singleCategory }));
176 showCategory(singleCategory);
177 }
178}
179
183QWidget *SettingsDialog::cornerWidget(Qt::Corner corner) const
184{
185 return m_ui->pagesTabWidget->cornerWidget(corner);
186}
187
191void SettingsDialog::setCornerWidget(QWidget *widget, Qt::Corner corner)
192{
193 m_ui->pagesTabWidget->setCornerWidget(widget, corner);
194}
195
200{
201 m_ui->headingLayout->addWidget(widget);
202}
203
207void SettingsDialog::selectPage(int categoryIndex, int pageIndex)
208{
209 m_categoryFilterModel->setFilterFixedString(QString());
210 m_ui->filterLineEdit->clear();
211 showCategory(m_categoryModel->category(categoryIndex));
212 m_ui->categoriesListView->selectionModel()->select(
213 m_categoryFilterModel->mapFromSource(m_categoryModel->index(categoryIndex)), QItemSelectionModel::ClearAndSelect);
214 m_ui->pagesTabWidget->setCurrentIndex(pageIndex);
215}
216
220void SettingsDialog::updateTabWidget()
221{
222 if (!m_currentCategory) {
223 m_ui->pagesTabWidget->clear();
224 return;
225 }
226 m_ui->pagesTabWidget->setUpdatesEnabled(false);
227
228 const auto searchKeyWord = m_ui->filterLineEdit->text();
229 int index = 0, pageIndex = 0;
230 for (OptionPage *const page : m_currentCategory->pages()) {
231 if (page->matches(searchKeyWord)) {
232 // ensure the page's widget has no parent anymore; otherwise windowIcon() might return the parent's icon
233 auto *const widget = page->widget();
234 widget->setParent(nullptr);
235
236 // add the widget to the tab widget within a scroll area
237 QScrollArea *scrollArea;
238 if (index < m_ui->pagesTabWidget->count()) {
239 scrollArea = qobject_cast<QScrollArea *>(m_ui->pagesTabWidget->widget(index));
240 scrollArea->takeWidget();
241 m_ui->pagesTabWidget->setTabText(index, widget->windowTitle());
242 m_ui->pagesTabWidget->setTabIcon(index, widget->windowIcon());
243 } else {
244 scrollArea = new QScrollArea(m_ui->pagesTabWidget);
245 scrollArea->setFrameStyle(QFrame::NoFrame);
246 scrollArea->setBackgroundRole(QPalette::Base);
247 scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
248 scrollArea->setWidgetResizable(true);
249 m_ui->pagesTabWidget->addTab(scrollArea, widget->windowTitle());
250 m_ui->pagesTabWidget->setTabIcon(index, widget->windowIcon());
251 }
252 if (auto *const layout = widget->layout()) {
253 layout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
254 }
255 scrollArea->setWidget(widget);
256 ++index;
257 }
258 if (pageIndex == m_currentCategory->currentIndex()) {
259 m_ui->pagesTabWidget->setCurrentIndex(pageIndex);
260 }
261 ++pageIndex;
262 }
263
264 // remove surplus tabs
265 while (index < m_ui->pagesTabWidget->count()) {
266 auto *const scrollArea = qobject_cast<QScrollArea *>(m_ui->pagesTabWidget->widget(index));
267 scrollArea->takeWidget();
268 m_ui->pagesTabWidget->removeTab(index);
269 delete scrollArea;
270 }
271
272 m_ui->pagesTabWidget->tabBar()->setHidden(!m_tabBarAlwaysVisible && m_ui->pagesTabWidget->count() == 1);
273 m_ui->pagesTabWidget->setUpdatesEnabled(true);
274}
275
282{
283 // apply each page in each category and gather error messages
284 QString errorMessage;
285 for (OptionCategory *const category : m_categoryModel->categories()) {
286 for (OptionPage *const page : category->pages()) {
287 if (!page->hasBeenShown() || page->apply()) {
288 // nothing to apply or no error
289 continue;
290 }
291
292 // add error message
293 if (errorMessage.isEmpty()) {
294 errorMessage = tr("<p><b>Errors occurred when applying changes:</b></p><ul>");
295 }
296 QStringList &errors = const_cast<OptionPage *>(page)->errors();
297 if (errors.isEmpty()) {
298 errorMessage.append(QStringLiteral("<li><i>") % category->displayName() % QLatin1Char('/') % page->widget()->windowTitle()
299 % QStringLiteral("</i>: ") % tr("unknown error") % QStringLiteral("</li>"));
300 } else {
301 for (const QString &error : errors) {
302 errorMessage.append(QStringLiteral("<li><i>") % category->displayName() % QLatin1Char('/') % page->widget()->windowTitle()
303 % QStringLiteral("</i>: ") % error % QStringLiteral("</li>"));
304 }
305 errors.clear();
306 }
307 }
308 }
309
310 // show error messages (if errors occurred)
311 if (!errorMessage.isEmpty()) {
312 errorMessage.append(QStringLiteral("</ul>"));
313 QMessageBox::warning(this, windowTitle(), errorMessage);
314 }
315
316 // return status
317 emit applied();
318 return errorMessage.isEmpty();
319}
320
326{
327 for (OptionCategory *const category : m_categoryModel->categories()) {
329 }
330 emit resetted();
331}
332
333bool SettingsDialog::event(QEvent *event)
334{
335 const auto res = QDialog::event(event);
336 switch (event->type()) {
337 case QEvent::PaletteChange:
338 setStyleSheet(dialogStyleForPalette(palette()));
339 break;
340 default:;
341 }
342 return res;
343}
344
345} // namespace QtUtilities
The OptionCategoryFilterModel class is used by SettingsDialog to filter option categories.
The OptionCategoryModel class is used by SettingsDialog to store and display option categories.
OptionCategory * category(const QModelIndex &index) const
Returns the category for the specified model index.
QList< OptionCategory * > categories
void setCategories(const QList< OptionCategory * > &categories)
Sets the categories for the model.
The OptionCategory class wraps associated option pages.
void setCurrentIndex(int currentIndex)
Sets the current index.
int currentIndex() const
Returns the index of the currently shown page.
void resetAllPages()
Resets all pages.
QList< OptionPage * > pages
The OptionPage class is the base class for SettingsDialog pages.
Definition: optionpage.h:33
virtual bool apply()=0
Applies altered settings.
virtual void reset()=0
Discards altered settings and resets relevant widgets.
bool matches(const QString &searchKeyWord)
Returns whether the pages matches the specified searchKeyWord.
Definition: optionpage.cpp:64
bool hasBeenShown() const
Returns an indication whether the option page has been shown yet.
Definition: optionpage.h:74
QWidget * widget()
Returns the widget for the option page.
Definition: optionpage.cpp:48
The SettingsDialog class provides a framework for creating settings dialogs with different categories...
OptionCategoryModel * categoryModel()
Returns the category model used by the settings dialog to manage the categories.
void setTabBarAlwaysVisible(bool value)
Sets whether the tab bar is always visible.
OptionCategory * category(int categoryIndex) const
Returns the category for the specified categoryIndex.
SettingsDialog(QWidget *parent=nullptr)
Constructs a settings dialog.
void showCategory(OptionCategory *category)
Sets the current category to the specified category and updates the relevant widgets to show it.
bool event(QEvent *event) override
OptionPage * page(int categoryIndex, int pageIndex) const
Returns the page for the specified categoryIndex and the specified pageIndex.
void reset()
Resets all changes.
bool apply()
Applies all changes.
void addHeadingWidget(QWidget *widget)
Adds a widget next to the heading.
QWidget * cornerWidget(Qt::Corner corner=Qt::TopRightCorner) const
Returns the tab-widget's corner widget.
~SettingsDialog() override
Destroys the settings dialog.
void showEvent(QShowEvent *event) override
Resets all pages before the dialog is shown by the application.
void setSingleCategory(OptionCategory *singleCategory)
Enables single-category mode to show only the specified singleCategory.
void setCornerWidget(QWidget *widget, Qt::Corner corner=Qt::TopRightCorner)
Sets the tab-widget's corner widget.
void selectPage(int categoryIndex, int pageIndex)
Selects the specified page within the specified category.