From 48a338b1321d9e169c73b71c01cd2f9ba00f9c53 Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Sat, 1 Jun 2024 18:05:30 -0400 Subject: Implement Recent files list and menu --- src/mainwindow.cpp | 28 ++++++++++++------ src/mainwindow.h | 3 ++ src/recent_files.cpp | 70 +++++++++++++++++++++++++++++++++++++++++++++ src/recent_files.h | 54 ++++++++++++++++++++++++++++++++++ src/settings_interface.cpp | 2 ++ src/widgets/tab_welcome.cpp | 8 ++++-- src/widgets/tab_welcome.h | 9 +++--- 7 files changed, 158 insertions(+), 16 deletions(-) create mode 100644 src/recent_files.cpp create mode 100644 src/recent_files.h (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 89351ae..58fb991 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2,6 +2,7 @@ #include "./ui_mainwindow.h" #include "src/archive/base_archive.h" #include "src/command_line.h" +#include "src/recent_files.h" #include "src/settingsdialog.h" #include "src/aboutdialog.h" #include "src/widgets/tab_activity_list.h" @@ -17,6 +18,15 @@ MainWindow::MainWindow(QWidget *parent) , ui(new Ui::MainWindow) { ui->setupUi(this); + + recent_files_menu = recent_files.create_menu(tr("Recent files…")); + ui->menuFile->insertMenu(ui->actionSettings, recent_files_menu.get()); + connect(recent_files_menu.get(), &QMenu::triggered, this, [&](QAction* action) { + // Needed for interepreting a QAction as a RecentFiles::Action so that we can retrieve the file_path and open it + if (auto* ac = qobject_cast(action); ac) + this->open_file(ac->file_path); + }); + create_initial_tabs(); connect(&archive_thread_watcher, &QFutureWatcher::finished, this, &MainWindow::archive_thread_watcher_done); } @@ -27,11 +37,9 @@ MainWindow::~MainWindow() } void MainWindow::create_initial_tabs() { - welcome_tab = new TabWelcome(); + welcome_tab = new TabWelcome(recent_files_menu); ui->tabWidget->addTab(welcome_tab, tr("Welcome")); connect(welcome_tab, &TabWelcome::show_file_open_dialog, this, &MainWindow::on_actionOpen_triggered); - // TODO: recent files - //connect(welcome_tab, &TabWelcome::show_recent_files, this, &MainWindow::); activity_list_tab = new TabActivityList(&data_archive); ui->tabWidget->addTab(activity_list_tab, tr("Activity List")); @@ -101,12 +109,6 @@ void MainWindow::on_actionOpen_triggered() { if (fileDialog.exec()) { QStringList files = fileDialog.selectedFiles(); open_file(files[0]); - - // Close the welcome tab as it's not needed anymore - if (welcome_tab) { - welcome_tab->deleteLater(); - welcome_tab = nullptr; - } } } @@ -137,12 +139,20 @@ void MainWindow::on_actionAbout_triggered(bool checked) { } void MainWindow::open_file(const QString &filename) { + // Close the welcome tab as it's not needed anymore + // Done here as this method is directly called when opening a recent file + if (welcome_tab) { + welcome_tab->deleteLater(); + welcome_tab = nullptr; + } + if (data_archive) { delete data_archive; data_archive = nullptr; } open_file_filename = filename; + recent_files.add_file(filename); // BUG: the overwritten cursor seems to get "lost" and revert in moc_mainwindow.cpp (after the end of the function we're in) when opening a file through a file dialog. // The cursor behaves properly when opening a file throught the command line. diff --git a/src/mainwindow.h b/src/mainwindow.h index 03daaad..68ecf18 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -8,6 +8,7 @@ #include "archive/base_archive.h" #include "src/list_item.h" #include "src/settingsdialog.h" +#include "src/recent_files.h" #include "src/widgets/tab_activity_list.h" #include "src/widgets/tab_actor_info.h" #include "src/widgets/tab_welcome.h" @@ -58,6 +59,8 @@ private: TabActorInfo* actor_info_tab = nullptr; QString open_file_filename; + RecentFiles recent_files; + RecentFiles::QMenuPtr recent_files_menu; Ui::MainWindow *ui; diff --git a/src/recent_files.cpp b/src/recent_files.cpp new file mode 100644 index 0000000..2382f1a --- /dev/null +++ b/src/recent_files.cpp @@ -0,0 +1,70 @@ +#include "recent_files.h" +#include + +RecentFiles::RecentFiles(unsigned int size, bool force_empty) : size(size) { + if (not force_empty) + list = settings_interface.read_setting("ui/recent_files").value(); +} + +RecentFiles::~RecentFiles() { + if (has_list_changed) { + settings_interface.write_setting("ui/recent_files", list); + settings_interface.commit(); + } +} + +RecentFiles::QMenuPtr RecentFiles::create_menu(const QString& title, QWidget* parent) { + if (menu) return menu; + + menu = std::make_shared(title, parent); + generate_menu_actions(); + + return menu; +} + +void RecentFiles::generate_menu_actions() { + menu->clear(); + + if (not list.isEmpty()) { + int i = 1; + for (const QString& path : list) { + Action* action = new Action(QStringLiteral("&%1. %2").arg(i++).arg(QFileInfo(path).fileName())); + action->file_path = path; + menu->addAction(action); + } + } else { + Action* action = new Action(QWidget::tr("No recent files")); + action->setStatusTip(QWidget::tr("To view recent files, open a file")); + action->setEnabled(false); + menu->addAction(action); + } +} + +void RecentFiles::add_file(const QString& path) { + if (not list.contains(path)) { + list.prepend(path); + if (list.size() > size) +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) + list.resize(size); +#else + do list.removeLast(); + while (list.size() > size); +#endif + has_list_changed = true; + generate_menu_actions(); + } +} + +const QStringList& RecentFiles::get_files() { + return list; +} + +void RecentFiles::clear() { + list.empty(); +} + +RecentFiles::Action::Action(const QString &text, QObject *parent) : QAction(text, parent) { + connect(this, &QAction::triggered, this, [=](bool checked) { + emit this->chosen_file(file_path); + }); +} diff --git a/src/recent_files.h b/src/recent_files.h new file mode 100644 index 0000000..7edfb3d --- /dev/null +++ b/src/recent_files.h @@ -0,0 +1,54 @@ +#pragma once +#include "src/settings_interface.h" +#include +#include +#include + +class RecentFiles { +public: + // QAction extension to signal file path + class Action; + + // size: length of the list before removing older files + // force_empty: work from an empty list + RecentFiles(unsigned int size = 10, bool force_empty = false); + // Note: recent files list is committed on destruction + ~RecentFiles(); + + // Submit a file to the recent files list + // It will not be added if it's already in the list + void add_file(const QString& path); + + // Get the list of recent files + const QStringList& get_files(); + + // Clear the list of recent files + void clear(); + + // Create a QMenu with the list of recent files + // The caller is responsable of managing the pointer's lifetime. It works like any other QMnenu, but is already populated. + // title: the text shown + // parent: the QMenu's parent + typedef std::shared_ptr QMenuPtr; + QMenuPtr create_menu(const QString& title, QWidget* parent = nullptr); + +private: + SettingsInterface settings_interface; + QStringList list; + unsigned int size; + bool has_list_changed = false; + + QMenuPtr menu; + + void generate_menu_actions(); +}; + +class RecentFiles::Action : public QAction { + Q_OBJECT +public: + Action(const QString &text, QObject *parent = nullptr); + QString file_path; + +signals: + void chosen_file(const QString& file_path); +}; diff --git a/src/settings_interface.cpp b/src/settings_interface.cpp index e14da92..13e2066 100644 --- a/src/settings_interface.cpp +++ b/src/settings_interface.cpp @@ -115,6 +115,8 @@ const QVariant default_setting(const QString &key) { else if (key == "net/instance/address" or key == "net/instance/token") return ""; + else if (key == "ui/recent_files") + return QStringList(); else return -1; } diff --git a/src/widgets/tab_welcome.cpp b/src/widgets/tab_welcome.cpp index 2c8dc56..49b4f2a 100644 --- a/src/widgets/tab_welcome.cpp +++ b/src/widgets/tab_welcome.cpp @@ -1,7 +1,8 @@ #include "tab_welcome.h" +#include -TabWelcome::TabWelcome(QWidget* parent) - : QWidget(parent), ui(new Ui::TabWelcome) +TabWelcome::TabWelcome(RecentFiles::QMenuPtr recent_files_menu, QWidget* parent) + : recent_files_menu(recent_files_menu), QWidget(parent), ui(new Ui::TabWelcome) { ui->setupUi(this); ui->horizontalLayout->setContentsMargins(0, 0, 0, 0); @@ -20,5 +21,6 @@ void TabWelcome::on_openFileButton_clicked(bool checked) { } void TabWelcome::on_openRecentButton_clicked(bool checked) { - emit show_recent_files(); + if (recent_files_menu) + recent_files_menu->popup(ui->openRecentButton->mapToGlobal(QPoint(0,0))); } diff --git a/src/widgets/tab_welcome.h b/src/widgets/tab_welcome.h index effc84f..497aae2 100644 --- a/src/widgets/tab_welcome.h +++ b/src/widgets/tab_welcome.h @@ -1,6 +1,7 @@ #pragma once #include #include "./ui_tab_welcome.h" +#include "../recent_files.h" QT_BEGIN_NAMESPACE namespace Ui { class TabWelcome; } @@ -10,15 +11,14 @@ class TabWelcome : public QWidget { Q_OBJECT public: - TabWelcome(QWidget* parent = nullptr); + TabWelcome(RecentFiles::QMenuPtr recent_files_menu = nullptr, QWidget* parent = nullptr); ~TabWelcome(); signals: // Show Open file dialog void show_file_open_dialog(); - - // Show recents dropdown - void show_recent_files(); + // Open this file path + void open_file(const QString& path); private slots: void on_openFileButton_clicked(bool checked); @@ -26,4 +26,5 @@ private slots: private: Ui::TabWelcome* ui; + RecentFiles::QMenuPtr recent_files_menu; }; -- cgit v1.2.3-54-g00ecf