Qt Utilities  6.4.1
Common Qt related C++ classes and routines used by my applications such as dialogs, widgets and models
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 
18 namespace 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(dialogStyle());
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 
86 OptionCategory *SettingsDialog::category(int categoryIndex) const
87 {
88  return m_categoryModel->category(categoryIndex);
89 }
90 
98 OptionPage *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 
111 void 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 
132 void 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 
183 void SettingsDialog::updateTabWidget()
184 {
185  if (!m_currentCategory) {
186  m_ui->pagesTabWidget->clear();
187  return;
188  }
189  m_ui->pagesTabWidget->setUpdatesEnabled(false);
190  const QString searchKeyWord = m_ui->filterLineEdit->text();
191  int index = 0, pageIndex = 0;
192  for (OptionPage *const page : m_currentCategory->pages()) {
193  if (page->matches(searchKeyWord)) {
194  QScrollArea *scrollArea;
195  if (index < m_ui->pagesTabWidget->count()) {
196  scrollArea = qobject_cast<QScrollArea *>(m_ui->pagesTabWidget->widget(index));
197  scrollArea->takeWidget();
198  m_ui->pagesTabWidget->setTabText(index, page->widget()->windowTitle());
199  m_ui->pagesTabWidget->setTabIcon(index, page->widget()->windowIcon());
200  } else {
201  scrollArea = new QScrollArea(m_ui->pagesTabWidget);
202  scrollArea->setFrameStyle(QFrame::NoFrame);
203  scrollArea->setBackgroundRole(QPalette::Base);
204  scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
205  scrollArea->setWidgetResizable(true);
206  m_ui->pagesTabWidget->addTab(scrollArea, page->widget()->windowTitle());
207  m_ui->pagesTabWidget->setTabIcon(index, page->widget()->windowIcon());
208  }
209  if (page->widget()->layout()) {
210  page->widget()->layout()->setAlignment(Qt::AlignTop | Qt::AlignLeft);
211  }
212  scrollArea->setWidget(page->widget());
213  ++index;
214  }
215  if (pageIndex == m_currentCategory->currentIndex()) {
216  m_ui->pagesTabWidget->setCurrentIndex(pageIndex);
217  }
218  ++pageIndex;
219  }
220  while (index < m_ui->pagesTabWidget->count()) {
221  QScrollArea *const scrollArea = qobject_cast<QScrollArea *>(m_ui->pagesTabWidget->widget(index));
222  scrollArea->takeWidget();
223  m_ui->pagesTabWidget->removeTab(index);
224  delete scrollArea;
225  }
226  m_ui->pagesTabWidget->tabBar()->setHidden(!m_tabBarAlwaysVisible && m_ui->pagesTabWidget->count() == 1);
227  m_ui->pagesTabWidget->setUpdatesEnabled(true);
228 }
229 
235 bool SettingsDialog::apply()
236 {
237  // apply each page in each category and gather error messages
238  QString errorMessage;
239  for (OptionCategory *const category : m_categoryModel->categories()) {
240  for (OptionPage *const page : category->pages()) {
241  if (!page->hasBeenShown() || page->apply()) {
242  // nothing to apply or no error
243  continue;
244  }
245 
246  // add error message
247  if (errorMessage.isEmpty()) {
248  errorMessage = tr("<p><b>Errors occurred when applying changes:</b></p><ul>");
249  }
250  QStringList &errors = const_cast<OptionPage *>(page)->errors();
251  if (errors.isEmpty()) {
252  errorMessage.append(QStringLiteral("<li><i>") % category->displayName() % QLatin1Char('/') % page->widget()->windowTitle()
253  % QStringLiteral("</i>: ") % tr("unknonw error") % QStringLiteral("</li>"));
254  } else {
255  for (const QString &error : errors) {
256  errorMessage.append(QStringLiteral("<li><i>") % category->displayName() % QLatin1Char('/') % page->widget()->windowTitle()
257  % QStringLiteral("</i>: ") % error % QStringLiteral("</li>"));
258  }
259  errors.clear();
260  }
261  }
262  }
263 
264  // show error messages (if errors occurred)
265  if (!errorMessage.isEmpty()) {
266  errorMessage.append(QStringLiteral("</ul>"));
267  QMessageBox::warning(this, windowTitle(), errorMessage);
268  }
269 
270  // return status
271  emit applied();
272  return errorMessage.isEmpty();
273 }
274 
279 void SettingsDialog::reset()
280 {
281  for (OptionCategory *const category : m_categoryModel->categories()) {
283  }
284  emit resetted();
285 }
286 } // 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:15
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:61
bool hasBeenShown() const
Returns an indication whether the option page has been shown yet.
Definition: optionpage.h:56
QWidget * widget()
Returns the widget for the option page.
Definition: optionpage.cpp:45
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.
OptionPage * page(int categoryIndex, int pageIndex) const
Returns the page for the specified categoryIndex and the specified pageIndex.
~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.