Skip to content

Commit 7b7290e

Browse files
committed
Improve preferences UX, navigation, and refactor ConfigDialog
- Enhanced search result selection to scroll specifically to the clicked item using `mapTo` for robust positioning and debounced execution. - Added a 50ms debounce timer for search to maintain responsiveness. - Factored out `getSearchableText` and `scrollToWidget` helpers. - Added a footer to the preferences list in `configdialog.ui` with text "The road goes no further." and a centered "Back to top" button. - Improved `slot_onScroll` to ensure the last section is selected in the sidebar when reaching the bottom. - Refactored stack index usage with a `StackPage` enum. - Refactored `ConfigDialog` to use `QSignalBlocker` for sidebar synchronization and removed the manual `m_suppressScrollSync` flag. - Updated `slot_cancel` to call `reject()`. - Improved metatype safety for widget pointers in search results. - Applied project clang-format standards.
1 parent 3020fac commit 7b7290e

2 files changed

Lines changed: 46 additions & 29 deletions

File tree

src/preferences/configdialog.cpp

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ QString getSearchableText(const QWidget *widget)
3838
return gb->title();
3939
return QString();
4040
}
41+
42+
enum StackPage { Preferences = 0, SearchResults = 1, NoResults = 2 };
4143
} // namespace
4244

4345
ConfigDialog::ConfigDialog(QWidget *const parent)
@@ -79,19 +81,20 @@ ConfigDialog::ConfigDialog(QWidget *const parent)
7981
return container;
8082
};
8183

82-
auto addPage =
83-
[this, &makeSectionHeader](QWidget *widget, const QString &name, const QString &iconPath) {
84-
auto *item = new QListWidgetItem(QIcon(iconPath), name, ui->contentsWidget);
84+
auto addPage = [this, &makeSectionHeader](QWidget *widget,
85+
const QString &name,
86+
const QString &iconPath) {
87+
auto *item = new QListWidgetItem(QIcon(iconPath), name, ui->contentsWidget);
8588

86-
auto *container = new QWidget(this);
87-
auto *containerLayout = new QVBoxLayout(container);
88-
containerLayout->setContentsMargins(0, 0, 0, 0);
89-
containerLayout->addWidget(makeSectionHeader(name, container));
90-
containerLayout->addWidget(widget);
89+
auto *container = new QWidget(this);
90+
auto *containerLayout = new QVBoxLayout(container);
91+
containerLayout->setContentsMargins(0, 0, 0, 0);
92+
containerLayout->addWidget(makeSectionHeader(name, container));
93+
containerLayout->addWidget(widget);
9194

92-
ui->scrollLayout->insertWidget(ui->scrollLayout->count() - 2, container);
93-
m_pages.append({name, widget, item, container});
94-
};
95+
ui->scrollLayout->insertWidget(ui->scrollLayout->indexOf(ui->footerWidget) - 1, container);
96+
m_pages.append({name, widget, item, container});
97+
};
9598

9699
addPage(generalPage, tr("General"), ":/icons/generalcfg.png");
97100
addPage(graphicsPage, tr("Graphics"), ":/icons/graphicscfg.png");
@@ -104,7 +107,9 @@ ConfigDialog::ConfigDialog(QWidget *const parent)
104107
addPage(pathmachinePage, tr("Path Machine"), ":/icons/pathmachinecfg.png");
105108

106109
connect(ui->backToTopBtn, &QPushButton::clicked, this, [this]() {
107-
ui->pagesScrollArea->verticalScrollBar()->setValue(0);
110+
if (!m_pages.isEmpty()) {
111+
ui->contentsWidget->setCurrentItem(m_pages.first().item);
112+
}
108113
});
109114

110115
ui->mainSplitter->setStretchFactor(0, 0);
@@ -178,6 +183,20 @@ void ConfigDialog::showEvent(QShowEvent *const event)
178183
event->accept();
179184
}
180185

186+
void ConfigDialog::scrollToWidget(QWidget *target, bool focus)
187+
{
188+
const int targetY = target->mapTo(ui->scrollAreaWidgetContents, QPoint(0, 0)).y();
189+
190+
// We need to delay the scrolling slightly to ensure the scroll area has
191+
// updated its layout after the stack switch.
192+
QTimer::singleShot(0, this, [this, targetY, target, focus]() {
193+
ui->pagesScrollArea->verticalScrollBar()->setValue(targetY);
194+
if (focus) {
195+
target->setFocus();
196+
}
197+
});
198+
}
199+
181200
void ConfigDialog::slot_changePage(QListWidgetItem *current, QListWidgetItem *const /*previous*/)
182201
{
183202
if (current == nullptr) {
@@ -191,8 +210,7 @@ void ConfigDialog::slot_changePage(QListWidgetItem *current, QListWidgetItem *co
191210

192211
for (const auto &page : m_pages) {
193212
if (page.item == current) {
194-
const int targetY = page.container->mapTo(ui->scrollAreaWidgetContents, QPoint(0, 0)).y();
195-
ui->pagesScrollArea->verticalScrollBar()->setValue(targetY);
213+
scrollToWidget(page.container);
196214
break;
197215
}
198216
}
@@ -241,7 +259,7 @@ void ConfigDialog::slot_search(const QString &text)
241259
ui->searchResultsList->clear();
242260

243261
if (text.isEmpty()) {
244-
ui->rightStack->setCurrentIndex(0);
262+
ui->rightStack->setCurrentIndex(StackPage::Preferences);
245263
for (const auto &page : m_pages) {
246264
page.item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
247265
}
@@ -284,13 +302,19 @@ void ConfigDialog::slot_search(const QString &text)
284302
}
285303
} else {
286304
page.item->setFlags(Qt::NoItemFlags);
305+
306+
// If the current sidebar selection is being disabled, clear it
307+
if (ui->contentsWidget->currentItem() == page.item) {
308+
const QSignalBlocker blocker{ui->contentsWidget};
309+
ui->contentsWidget->setCurrentItem(nullptr);
310+
}
287311
}
288312
}
289313

290314
if (ui->searchResultsList->count() > 0) {
291-
ui->rightStack->setCurrentIndex(1);
315+
ui->rightStack->setCurrentIndex(StackPage::SearchResults);
292316
} else {
293-
ui->rightStack->setCurrentIndex(2);
317+
ui->rightStack->setCurrentIndex(StackPage::NoResults);
294318
}
295319

296320
ui->searchBar->setFocus();
@@ -308,25 +332,15 @@ void ConfigDialog::slot_onResultSelected(QListWidgetItem *const item)
308332
}
309333

310334
ui->searchBar->clear();
311-
ui->rightStack->setCurrentIndex(0);
335+
ui->rightStack->setCurrentIndex(StackPage::Preferences);
312336

313337
// Find which page this widget belongs to
314338
for (const auto &page : m_pages) {
315339
if (page.widget == widget || page.widget->isAncestorOf(widget)) {
316340
const QSignalBlocker blocker{ui->contentsWidget};
317341
ui->contentsWidget->setCurrentItem(page.item);
318342

319-
const int targetY
320-
= (page.widget == widget)
321-
? page.container->mapTo(ui->scrollAreaWidgetContents, QPoint(0, 0)).y()
322-
: widget->mapTo(ui->scrollAreaWidgetContents, QPoint(0, 0)).y();
323-
324-
// We need to delay the scrolling slightly to ensure the scroll area has
325-
// updated its layout after the stack switch.
326-
QTimer::singleShot(0, this, [this, targetY, widget]() {
327-
ui->pagesScrollArea->verticalScrollBar()->setValue(targetY);
328-
widget->setFocus();
329-
});
343+
scrollToWidget(widget, true);
330344
break;
331345
}
332346
}

src/preferences/configdialog.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ class ConfigDialog final : public QDialog
4242
explicit ConfigDialog(QWidget *parent = nullptr);
4343
~ConfigDialog() override;
4444

45+
private:
46+
void scrollToWidget(QWidget *target, bool focus = false);
47+
4548
protected:
4649
void closeEvent(QCloseEvent *event) override;
4750
void showEvent(QShowEvent *event) override;

0 commit comments

Comments
 (0)