From 42434314d65cdc29402c7adcb08c2fa5113f7ca2 Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Wed, 2 Aug 2023 01:56:58 +0200 Subject: Implement find dialog This dialog consolidates options for searching through textual elements in lists which simplifies the logic present in the MainWindow class. --- src/finddialog.cpp | 134 ++++++++++++++++++++++++++++++++++++++++ src/finddialog.h | 49 +++++++++++++++ src/finddialog.ui | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/mainwindow.cpp | 39 ++++++------ src/mainwindow.h | 11 ++++ 5 files changed, 390 insertions(+), 19 deletions(-) create mode 100644 src/finddialog.cpp create mode 100644 src/finddialog.h create mode 100644 src/finddialog.ui (limited to 'src') diff --git a/src/finddialog.cpp b/src/finddialog.cpp new file mode 100644 index 0000000..4de17d0 --- /dev/null +++ b/src/finddialog.cpp @@ -0,0 +1,134 @@ +#include "finddialog.h" +#include "./ui_finddialog.h" + +#include +#include +#include +#include +#include + +FindDialog::FindDialog(QWidget* parent) + : QDialog(parent, Qt::Tool), ui(new Ui::FindDialog) +{ + ui->setupUi(this); +} + +FindDialog::~FindDialog() { + delete ui; +} + +void FindDialog::on_buttonSearchPrev_clicked() { + if (--selected_match < 0) { + selected_match = total_matches - 1; + ui->statusLabel->setText(tr("Reached top, continued from bottom.")); + } else update_status(); + select_match(); +} + +void FindDialog::on_buttonSearchNext_clicked() { + if (++selected_match >= total_matches) { + selected_match = 0; + ui->statusLabel->setText(tr("Reached bottom, continued from top.")); + } else update_status(); + select_match(); +} + +// Update the textbox in the main window and search after 400 ms of no typing +void FindDialog::on_textInputSearch_textEdited(const QString &text) { + emit search_text_changed(text); + static QTimer* differed_search = new QTimer(this); + if (static bool timer_init_done = false; not timer_init_done) { + differed_search->setTimerType(Qt::CoarseTimer); + differed_search->setSingleShot(true); + differed_search->setInterval(400); + connect(differed_search, &QTimer::timeout, this, &FindDialog::run_search); + timer_init_done = true; + } + differed_search->start(); +} + +void FindDialog::set_search_text(const QString &text) { + ui->textInputSearch->setText(text); +} + +inline void FindDialog::run_search() { + init_search(); +} + +void FindDialog::on_hSlider_valueChanged(int value) { + selected_match = value; + update_status(); + select_match(); +} + +void FindDialog::on_spinBox_valueChanged(int value) { + // Avoids repeatedly changing the selected_match as the valueChanged signal is a notifier for the spin box's value + if (not value_changed_processed) { + selected_match = value - 1; + update_status(); + select_match(); + } + value_changed_processed = false; +} + +void FindDialog::set_qlist_widget(QListWidget* widget) { + list_widget = widget; +} + +// Start a new search only if the search text input has changed +void FindDialog::init_search() { + QString current_search = ui->textInputSearch->text(); + if (*last_search == current_search or not list_widget) return; + + matches = list_widget->findItems(current_search, Qt::MatchContains); + + *last_search = current_search; + selected_match = 0; + total_matches = matches.size(); + + if (total_matches > 0) { + ui->hSlider->setEnabled(true); + ui->spinBox->setEnabled(true); + ui->buttonSearchPrev->setEnabled(true); + ui->buttonSearchNext->setEnabled(true); + } else { + ui->spinBox->setEnabled(false); + ui->hSlider->setEnabled(false); + ui->buttonSearchPrev->setEnabled(false); + ui->buttonSearchNext->setEnabled(false); + } + + // Set the minimum as when there are no matches, the widget would return out of range index values to having an out of range minimum. + ui->hSlider->setMinimum(0); + ui->spinBox->setMinimum(1); + ui->hSlider->setMaximum(total_matches - 1); + ui->spinBox->setMaximum(total_matches); + + select_match(); + update_status(); +} + +// Activate the match requested by the user +void FindDialog::select_match() { + if (list_widget and total_matches != 0 and selected_match >= 0) { + list_widget->scrollToItem(matches[selected_match], QAbstractItemView::EnsureVisible); + list_widget->setCurrentItem(matches[selected_match]); + emit item_selected(matches[selected_match]); + } + + // Best place to update these sliders as select_match is always called by each input + ui->hSlider->setValue(selected_match); + ui->spinBox->setValue(selected_match + 1); + value_changed_processed = true; +} + +// Update the status line with current search information +void FindDialog::update_status() { + if (total_matches > 0) { + ui->statusLabel->setText(tr("Search result %1 out of %2.") + .arg(selected_match+1) + .arg(total_matches)); + } else ui->statusLabel->setText(tr("No search results.")); +} + + diff --git a/src/finddialog.h b/src/finddialog.h new file mode 100644 index 0000000..c6907f8 --- /dev/null +++ b/src/finddialog.h @@ -0,0 +1,49 @@ +#pragma once +#include +#include +#include "./ui_finddialog.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class FindDialog; } +QT_END_NAMESPACE + +class FindDialog : public QDialog { + Q_OBJECT + +public: + FindDialog(QWidget *parent = nullptr); + ~FindDialog(); + +public slots: + void set_search_text(const QString &text); + void set_qlist_widget(QListWidget* widget); + inline void run_search(); + +signals: + void item_selected(QListWidgetItem* item); + void search_text_changed(const QString &text); + +private slots: + void on_textInputSearch_textEdited(const QString &text); + void on_buttonSearchPrev_clicked(); + void on_buttonSearchNext_clicked(); + void on_hSlider_valueChanged(int value); + void on_spinBox_valueChanged(int value); + +private: + Ui::FindDialog* ui; + + QListWidget* list_widget = nullptr; + QString* last_search = new QString; + QList matches; + int selected_match = 0; + int total_matches = 0; + + // This is used with the spin box to avoid having repeated value changes due to the on_spinBox_valueChanged slot + bool value_changed_processed = false; + + void init_search(); + void update_status(); + void select_match(); + bool has_search_changed(); +}; diff --git a/src/finddialog.ui b/src/finddialog.ui new file mode 100644 index 0000000..0ee159c --- /dev/null +++ b/src/finddialog.ui @@ -0,0 +1,176 @@ + + + FindDialog + + + + 0 + 0 + 336 + 135 + + + + + 0 + 0 + + + + + 0 + 135 + + + + + 16777215 + 135 + + + + Qt::WheelFocus + + + Find + + + + + + 0 + + + + + + + Search in list + + + + + + + false + + + + 48 + 16777215 + + + + Previous + + + + + + + + + + false + + + + 48 + 16777215 + + + + Next + + + + + + + + + + + + + + false + + + Qt::Horizontal + + + + + + + false + + + QAbstractSpinBox::NoButtons + + + 1 + + + + + + + + + + + Ready. + + + + + + + QDialogButtonBox::Close + + + + + + + + + + + + + buttonBox + accepted() + FindDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FindDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 363f2b1..2b8a0df 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,6 +1,7 @@ #include "mainwindow.h" #include "./ui_mainwindow.h" #include "src/archive_parser.h" +#include "src/finddialog.h" #include "src/list_item.h" #include "src/command_line.h" @@ -100,29 +101,29 @@ void MainWindow::on_actionCopy_status_triggered(bool checked) { } void MainWindow::on_buttonSearch_clicked() { - static QString* last_search = new QString; - static int selected_match = 0; - - QString current_search = ui->textInputSearch->text(); + if (!find_dialog) { + find_dialog = new FindDialog(this); + find_dialog->set_qlist_widget(ui->listWidget); + connect(find_dialog, &FindDialog::item_selected, this, &MainWindow::select_list_item); + connect(this, &MainWindow::search_text_changed, find_dialog, &FindDialog::set_search_text); + connect(find_dialog, &FindDialog::search_text_changed, this, &MainWindow::set_search_text); + } - QList matches = ui->listWidget->findItems(current_search, Qt::MatchContains); + find_dialog->show(); + find_dialog->raise(); + find_dialog->activateWindow(); +} - if (not (*last_search == current_search)) { - *last_search = current_search; - selected_match = 0; - } else if (++selected_match >= matches.size()) selected_match = 0; +void MainWindow::select_list_item(QListWidgetItem* item) { + on_listWidget_itemActivated(item); +} - if (not matches.isEmpty()) { - ui->listWidget->scrollToItem(matches[selected_match], QAbstractItemView::EnsureVisible); - ui->listWidget->setCurrentItem(matches[selected_match]); - on_listWidget_itemActivated(matches[selected_match]); - } +void MainWindow::on_textInputSearch_textEdited(const QString &text) { + emit search_text_changed(text); +} - if (matches.size() > 0) - ui->statusbar->showMessage(tr("Search result %1 out of %2") - .arg(selected_match+1) - .arg(matches.size())); - else ui->statusbar->showMessage(tr("No search results.")); +void MainWindow::set_search_text(const QString &text) { + ui->textInputSearch->setText(text); } void MainWindow::relist_statuses() { diff --git a/src/mainwindow.h b/src/mainwindow.h index abf9acc..7b87880 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -9,6 +9,7 @@ #include "archive_parser.h" #include "types.h" #include "command_line.h" +#include "finddialog.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } @@ -24,6 +25,13 @@ public: void act_command_line(CommandLineParsedOptions &options, QCommandLineParser &parser); +public slots: + void select_list_item(QListWidgetItem* item); + void set_search_text(const QString &text); + +signals: + void search_text_changed(const QString &text); + private slots: void on_actionOpen_triggered(bool checked); void on_actionQuit_triggered(bool checked); @@ -39,6 +47,7 @@ private slots: void on_actionRandom_status_triggered(bool checked); void on_actionCopy_status_triggered(bool checked); + void on_textInputSearch_textEdited(const QString &text); void on_buttonSearch_clicked(); void on_menuView_aboutToHide(); @@ -54,6 +63,8 @@ private: void finish_listWidget_itemActivated(const QString& status_info); void finish_open_file(const Archive::InitError& parse_error); + FindDialog* find_dialog = nullptr; + QString open_file_filename; Ui::MainWindow *ui; -- cgit v1.2.3-54-g00ecf