summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/citra_qt/configuration/config.cpp2
-rw-r--r--src/citra_qt/game_list.cpp233
-rw-r--r--src/citra_qt/game_list.h47
-rw-r--r--src/citra_qt/main.cpp23
-rw-r--r--src/citra_qt/main.h3
-rw-r--r--src/citra_qt/main.ui9
-rw-r--r--src/citra_qt/ui_settings.h1
7 files changed, 299 insertions, 19 deletions
diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp
index 0b9b73f9e..2b99447ec 100644
--- a/src/citra_qt/configuration/config.cpp
+++ b/src/citra_qt/configuration/config.cpp
@@ -177,6 +177,7 @@ void Config::ReadValues() {
177 177
178 UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool(); 178 UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool();
179 UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool(); 179 UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool();
180 UISettings::values.show_filter_bar = qt_config->value("showFilterBar", true).toBool();
180 UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool(); 181 UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool();
181 UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool(); 182 UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool();
182 UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); 183 UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
@@ -295,6 +296,7 @@ void Config::SaveValues() {
295 296
296 qt_config->setValue("singleWindowMode", UISettings::values.single_window_mode); 297 qt_config->setValue("singleWindowMode", UISettings::values.single_window_mode);
297 qt_config->setValue("displayTitleBars", UISettings::values.display_titlebar); 298 qt_config->setValue("displayTitleBars", UISettings::values.display_titlebar);
299 qt_config->setValue("showFilterBar", UISettings::values.show_filter_bar);
298 qt_config->setValue("showStatusBar", UISettings::values.show_status_bar); 300 qt_config->setValue("showStatusBar", UISettings::values.show_status_bar);
299 qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing); 301 qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing);
300 qt_config->setValue("firstStart", UISettings::values.first_start); 302 qt_config->setValue("firstStart", UISettings::values.first_start);
diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp
index a9ec9e830..d6e26ed47 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -4,9 +4,9 @@
4 4
5#include <QFileInfo> 5#include <QFileInfo>
6#include <QHeaderView> 6#include <QHeaderView>
7#include <QKeyEvent>
7#include <QMenu> 8#include <QMenu>
8#include <QThreadPool> 9#include <QThreadPool>
9#include <QVBoxLayout>
10#include "common/common_paths.h" 10#include "common/common_paths.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/string_util.h" 12#include "common/string_util.h"
@@ -15,10 +15,189 @@
15#include "game_list_p.h" 15#include "game_list_p.h"
16#include "ui_settings.h" 16#include "ui_settings.h"
17 17
18GameList::GameList(QWidget* parent) : QWidget{parent} { 18GameList::SearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) {
19 QVBoxLayout* layout = new QVBoxLayout; 19 this->gamelist = gamelist;
20 edit_filter_text_old = "";
21}
22
23// EventFilter in order to process systemkeys while editing the searchfield
24bool GameList::SearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* event) {
25 // If it isn't a KeyRelease event then continue with standard event processing
26 if (event->type() != QEvent::KeyRelease)
27 return QObject::eventFilter(obj, event);
28
29 QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
30 int rowCount = gamelist->tree_view->model()->rowCount();
31 QString edit_filter_text = gamelist->search_field->edit_filter->text().toLower();
32
33 // If the searchfield's text hasn't changed special function keys get checked
34 // If no function key changes the searchfield's text the filter doesn't need to get reloaded
35 if (edit_filter_text == edit_filter_text_old) {
36 switch (keyEvent->key()) {
37 // Escape: Resets the searchfield
38 case Qt::Key_Escape: {
39 if (edit_filter_text_old.isEmpty()) {
40 return QObject::eventFilter(obj, event);
41 } else {
42 gamelist->search_field->edit_filter->clear();
43 edit_filter_text = "";
44 }
45 break;
46 }
47 // Return and Enter
48 // If the enter key gets pressed first checks how many and which entry is visable
49 // If there is only one result launch this game
50 case Qt::Key_Return:
51 case Qt::Key_Enter: {
52 QStandardItemModel* item_model = new QStandardItemModel(gamelist->tree_view);
53 QModelIndex root_index = item_model->invisibleRootItem()->index();
54 QStandardItem* child_file;
55 QString file_path;
56 int resultCount = 0;
57 for (int i = 0; i < rowCount; ++i) {
58 if (!gamelist->tree_view->isRowHidden(i, root_index)) {
59 ++resultCount;
60 child_file = gamelist->item_model->item(i, 0);
61 file_path = child_file->data(GameListItemPath::FullPathRole).toString();
62 }
63 }
64 if (resultCount == 1) {
65 // To avoid loading error dialog loops while confirming them using enter
66 // Also users usually want to run a diffrent game after closing one
67 gamelist->search_field->edit_filter->setText("");
68 edit_filter_text = "";
69 emit gamelist->GameChosen(file_path);
70 } else {
71 return QObject::eventFilter(obj, event);
72 }
73 break;
74 }
75 default:
76 return QObject::eventFilter(obj, event);
77 }
78 }
79 edit_filter_text_old = edit_filter_text;
80 return QObject::eventFilter(obj, event);
81}
82
83void GameList::SearchField::setFilterResult(int visable, int total) {
84 QString result_of_text = tr("of");
85 QString result_text;
86 if (total == 1) {
87 result_text = tr("result");
88 } else {
89 result_text = tr("results");
90 }
91 label_filter_result->setText(
92 QString("%1 %2 %3 %4").arg(visable).arg(result_of_text).arg(total).arg(result_text));
93}
94
95void GameList::SearchField::clear() {
96 edit_filter->setText("");
97}
20 98
99void GameList::SearchField::setFocus() {
100 if (edit_filter->isVisible()) {
101 edit_filter->setFocus();
102 }
103}
104
105GameList::SearchField::SearchField(GameList* parent) : QWidget{parent} {
106 KeyReleaseEater* keyReleaseEater = new KeyReleaseEater(parent);
107 layout_filter = new QHBoxLayout;
108 layout_filter->setMargin(8);
109 label_filter = new QLabel;
110 label_filter->setText(tr("Filter:"));
111 edit_filter = new QLineEdit;
112 edit_filter->setText("");
113 edit_filter->setPlaceholderText(tr("Enter pattern to filter"));
114 edit_filter->installEventFilter(keyReleaseEater);
115 edit_filter->setClearButtonEnabled(true);
116 connect(edit_filter, SIGNAL(textChanged(const QString&)), parent,
117 SLOT(onTextChanged(const QString&)));
118 label_filter_result = new QLabel;
119 button_filter_close = new QToolButton(this);
120 button_filter_close->setText("X");
121 button_filter_close->setCursor(Qt::ArrowCursor);
122 button_filter_close->setStyleSheet("QToolButton{ border: none; padding: 0px; color: "
123 "#000000; font-weight: bold; background: #F0F0F0; }"
124 "QToolButton:hover{ border: none; padding: 0px; color: "
125 "#EEEEEE; font-weight: bold; background: #E81123}");
126 connect(button_filter_close, SIGNAL(clicked()), parent, SLOT(onFilterCloseClicked()));
127 layout_filter->setSpacing(10);
128 layout_filter->addWidget(label_filter);
129 layout_filter->addWidget(edit_filter);
130 layout_filter->addWidget(label_filter_result);
131 layout_filter->addWidget(button_filter_close);
132 setLayout(layout_filter);
133}
134
135/**
136* Checks if all words separated by spaces are contained in another string
137* This offers a word order insensitive search function
138*
139* @param String that gets checked if it contains all words of the userinput string
140* @param String containing all words getting checked
141* @return true if the haystack contains all words of userinput
142*/
143bool GameList::containsAllWords(QString haystack, QString userinput) {
144 QStringList userinput_split = userinput.split(" ", QString::SplitBehavior::SkipEmptyParts);
145 return std::all_of(userinput_split.begin(), userinput_split.end(),
146 [haystack](QString s) { return haystack.contains(s); });
147}
148
149// Event in order to filter the gamelist after editing the searchfield
150void GameList::onTextChanged(const QString& newText) {
151 int rowCount = tree_view->model()->rowCount();
152 QString edit_filter_text = newText.toLower();
153
154 QModelIndex root_index = item_model->invisibleRootItem()->index();
155
156 // If the searchfield is empty every item is visible
157 // Otherwise the filter gets applied
158 if (edit_filter_text.isEmpty()) {
159 for (int i = 0; i < rowCount; ++i) {
160 tree_view->setRowHidden(i, root_index, false);
161 }
162 search_field->setFilterResult(rowCount, rowCount);
163 } else {
164 QStandardItem* child_file;
165 QString file_path, file_name, file_title, file_programmid;
166 int result_count = 0;
167 for (int i = 0; i < rowCount; ++i) {
168 child_file = item_model->item(i, 0);
169 file_path = child_file->data(GameListItemPath::FullPathRole).toString().toLower();
170 file_name = file_path.mid(file_path.lastIndexOf("/") + 1);
171 file_title = child_file->data(GameListItemPath::TitleRole).toString().toLower();
172 file_programmid =
173 child_file->data(GameListItemPath::ProgramIdRole).toString().toLower();
174
175 // Only items which filename in combination with its title contains all words
176 // that are in the searchfiel will be visible in the gamelist
177 // The search is case insensitive because of toLower()
178 // I decided not to use Qt::CaseInsensitive in containsAllWords to prevent
179 // multiple conversions of edit_filter_text for each game in the gamelist
180 if (containsAllWords(file_name.append(" ").append(file_title), edit_filter_text) ||
181 (file_programmid.count() == 16 && edit_filter_text.contains(file_programmid))) {
182 tree_view->setRowHidden(i, root_index, false);
183 ++result_count;
184 } else {
185 tree_view->setRowHidden(i, root_index, true);
186 }
187 search_field->setFilterResult(result_count, rowCount);
188 }
189 }
190}
191
192void GameList::onFilterCloseClicked() {
193 main_window->filterBarSetChecked(false);
194}
195
196GameList::GameList(GMainWindow* parent) : QWidget{parent} {
197 this->main_window = parent;
198 layout = new QVBoxLayout;
21 tree_view = new QTreeView; 199 tree_view = new QTreeView;
200 search_field = new SearchField(this);
22 item_model = new QStandardItemModel(tree_view); 201 item_model = new QStandardItemModel(tree_view);
23 tree_view->setModel(item_model); 202 tree_view->setModel(item_model);
24 203
@@ -46,7 +225,9 @@ GameList::GameList(QWidget* parent) : QWidget{parent} {
46 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); 225 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
47 226
48 layout->setContentsMargins(0, 0, 0, 0); 227 layout->setContentsMargins(0, 0, 0, 0);
228 layout->setSpacing(0);
49 layout->addWidget(tree_view); 229 layout->addWidget(tree_view);
230 layout->addWidget(search_field);
50 setLayout(layout); 231 setLayout(layout);
51} 232}
52 233
@@ -54,6 +235,18 @@ GameList::~GameList() {
54 emit ShouldCancelWorker(); 235 emit ShouldCancelWorker();
55} 236}
56 237
238void GameList::setFilterFocus() {
239 search_field->setFocus();
240}
241
242void GameList::setFilterVisible(bool visablility) {
243 search_field->setVisible(visablility);
244}
245
246void GameList::clearFilter() {
247 search_field->clear();
248}
249
57void GameList::AddEntry(const QList<QStandardItem*>& entry_items) { 250void GameList::AddEntry(const QList<QStandardItem*>& entry_items) {
58 item_model->invisibleRootItem()->appendRow(entry_items); 251 item_model->invisibleRootItem()->appendRow(entry_items);
59} 252}
@@ -69,11 +262,16 @@ void GameList::ValidateEntry(const QModelIndex& item) {
69 std::string std_file_path(file_path.toStdString()); 262 std::string std_file_path(file_path.toStdString());
70 if (!FileUtil::Exists(std_file_path) || FileUtil::IsDirectory(std_file_path)) 263 if (!FileUtil::Exists(std_file_path) || FileUtil::IsDirectory(std_file_path))
71 return; 264 return;
265 // Users usually want to run a diffrent game after closing one
266 search_field->clear();
72 emit GameChosen(file_path); 267 emit GameChosen(file_path);
73} 268}
74 269
75void GameList::DonePopulating() { 270void GameList::DonePopulating() {
76 tree_view->setEnabled(true); 271 tree_view->setEnabled(true);
272 int rowCount = tree_view->model()->rowCount();
273 search_field->setFilterResult(rowCount, rowCount);
274 search_field->setFocus();
77} 275}
78 276
79void GameList::PopupContextMenu(const QPoint& menu_location) { 277void GameList::PopupContextMenu(const QPoint& menu_location) {
@@ -151,25 +349,26 @@ static bool HasSupportedFileExtension(const std::string& file_name) {
151void GameList::RefreshGameDirectory() { 349void GameList::RefreshGameDirectory() {
152 if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { 350 if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) {
153 LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); 351 LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
352 search_field->clear();
154 PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 353 PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
155 } 354 }
156} 355}
157 356
158/** 357/**
159 * Adds the game list folder to the QFileSystemWatcher to check for updates. 358* Adds the game list folder to the QFileSystemWatcher to check for updates.
160 * 359*
161 * The file watcher will fire off an update to the game list when a change is detected in the game 360* The file watcher will fire off an update to the game list when a change is detected in the game
162 * list folder. 361* list folder.
163 * 362*
164 * Notice: This method is run on the UI thread because QFileSystemWatcher is not thread safe and 363* Notice: This method is run on the UI thread because QFileSystemWatcher is not thread safe and
165 * this function is fast enough to not stall the UI thread. If performance is an issue, it should 364* this function is fast enough to not stall the UI thread. If performance is an issue, it should
166 * be moved to another thread and properly locked to prevent concurrency issues. 365* be moved to another thread and properly locked to prevent concurrency issues.
167 * 366*
168 * @param dir folder to check for changes in 367* @param dir folder to check for changes in
169 * @param recursion 0 if recursion is disabled. Any positive number passed to this will add each 368* @param recursion 0 if recursion is disabled. Any positive number passed to this will add each
170 * directory recursively to the watcher and will update the file list if any of the folders 369* directory recursively to the watcher and will update the file list if any of the folders
171 * change. The number determines how deep the recursion should traverse. 370* change. The number determines how deep the recursion should traverse.
172 */ 371*/
173void GameList::UpdateWatcherList(const std::string& dir, unsigned int recursion) { 372void GameList::UpdateWatcherList(const std::string& dir, unsigned int recursion) {
174 const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory, 373 const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory,
175 const std::string& virtual_name) -> bool { 374 const std::string& virtual_name) -> bool {
diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h
index b141fa3a5..3c06cddc8 100644
--- a/src/citra_qt/game_list.h
+++ b/src/citra_qt/game_list.h
@@ -5,13 +5,19 @@
5#pragma once 5#pragma once
6 6
7#include <QFileSystemWatcher> 7#include <QFileSystemWatcher>
8#include <QHBoxLayout>
9#include <QLabel>
10#include <QLineEdit>
8#include <QModelIndex> 11#include <QModelIndex>
9#include <QSettings> 12#include <QSettings>
10#include <QStandardItem> 13#include <QStandardItem>
11#include <QStandardItemModel> 14#include <QStandardItemModel>
12#include <QString> 15#include <QString>
16#include <QToolButton>
13#include <QTreeView> 17#include <QTreeView>
18#include <QVBoxLayout>
14#include <QWidget> 19#include <QWidget>
20#include "main.h"
15 21
16class GameListWorker; 22class GameListWorker;
17 23
@@ -26,9 +32,40 @@ public:
26 COLUMN_COUNT, // Number of columns 32 COLUMN_COUNT, // Number of columns
27 }; 33 };
28 34
29 explicit GameList(QWidget* parent = nullptr); 35 class SearchField : public QWidget {
36 public:
37 void setFilterResult(int visable, int total);
38 void clear();
39 void setFocus();
40 explicit SearchField(GameList* parent = nullptr);
41
42 private:
43 class KeyReleaseEater : public QObject {
44 public:
45 explicit KeyReleaseEater(GameList* gamelist);
46
47 private:
48 GameList* gamelist = nullptr;
49 QString edit_filter_text_old;
50
51 protected:
52 bool eventFilter(QObject* obj, QEvent* event);
53 };
54 QHBoxLayout* layout_filter = nullptr;
55 QTreeView* tree_view = nullptr;
56 QLabel* label_filter = nullptr;
57 QLineEdit* edit_filter = nullptr;
58 QLabel* label_filter_result = nullptr;
59 QToolButton* button_filter_close = nullptr;
60 };
61
62 explicit GameList(GMainWindow* parent = nullptr);
30 ~GameList() override; 63 ~GameList() override;
31 64
65 void clearFilter();
66 void setFilterFocus();
67 void setFilterVisible(bool visablility);
68
32 void PopulateAsync(const QString& dir_path, bool deep_scan); 69 void PopulateAsync(const QString& dir_path, bool deep_scan);
33 70
34 void SaveInterfaceLayout(); 71 void SaveInterfaceLayout();
@@ -41,6 +78,10 @@ signals:
41 void ShouldCancelWorker(); 78 void ShouldCancelWorker();
42 void OpenSaveFolderRequested(u64 program_id); 79 void OpenSaveFolderRequested(u64 program_id);
43 80
81private slots:
82 void onTextChanged(const QString& newText);
83 void onFilterCloseClicked();
84
44private: 85private:
45 void AddEntry(const QList<QStandardItem*>& entry_items); 86 void AddEntry(const QList<QStandardItem*>& entry_items);
46 void ValidateEntry(const QModelIndex& item); 87 void ValidateEntry(const QModelIndex& item);
@@ -49,7 +90,11 @@ private:
49 void PopupContextMenu(const QPoint& menu_location); 90 void PopupContextMenu(const QPoint& menu_location);
50 void UpdateWatcherList(const std::string& path, unsigned int recursion); 91 void UpdateWatcherList(const std::string& path, unsigned int recursion);
51 void RefreshGameDirectory(); 92 void RefreshGameDirectory();
93 bool containsAllWords(QString haystack, QString userinput);
52 94
95 SearchField* search_field;
96 GMainWindow* main_window = nullptr;
97 QVBoxLayout* layout = nullptr;
53 QTreeView* tree_view = nullptr; 98 QTreeView* tree_view = nullptr;
54 QStandardItemModel* item_model = nullptr; 99 QStandardItemModel* item_model = nullptr;
55 GameListWorker* current_worker = nullptr; 100 GameListWorker* current_worker = nullptr;
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index b17ed6968..ea66cc425 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -93,7 +93,7 @@ void GMainWindow::InitializeWidgets() {
93 render_window = new GRenderWindow(this, emu_thread.get()); 93 render_window = new GRenderWindow(this, emu_thread.get());
94 render_window->hide(); 94 render_window->hide();
95 95
96 game_list = new GameList(); 96 game_list = new GameList(this);
97 ui.horizontalLayout->addWidget(game_list); 97 ui.horizontalLayout->addWidget(game_list);
98 98
99 // Create status bar 99 // Create status bar
@@ -247,6 +247,9 @@ void GMainWindow::RestoreUIState() {
247 ui.action_Display_Dock_Widget_Headers->setChecked(UISettings::values.display_titlebar); 247 ui.action_Display_Dock_Widget_Headers->setChecked(UISettings::values.display_titlebar);
248 OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked()); 248 OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked());
249 249
250 ui.action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar);
251 game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked());
252
250 ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar); 253 ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar);
251 statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked()); 254 statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked());
252} 255}
@@ -283,6 +286,8 @@ void GMainWindow::ConnectMenuEvents() {
283 &GMainWindow::ToggleWindowMode); 286 &GMainWindow::ToggleWindowMode);
284 connect(ui.action_Display_Dock_Widget_Headers, &QAction::triggered, this, 287 connect(ui.action_Display_Dock_Widget_Headers, &QAction::triggered, this,
285 &GMainWindow::OnDisplayTitleBars); 288 &GMainWindow::OnDisplayTitleBars);
289 ui.action_Show_Filter_Bar->setShortcut(tr("CTRL+F"));
290 connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar);
286 connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); 291 connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
287} 292}
288 293
@@ -444,6 +449,7 @@ void GMainWindow::ShutdownGame() {
444 ui.action_Stop->setEnabled(false); 449 ui.action_Stop->setEnabled(false);
445 render_window->hide(); 450 render_window->hide();
446 game_list->show(); 451 game_list->show();
452 game_list->setFilterFocus();
447 453
448 // Disable status bar updates 454 // Disable status bar updates
449 status_bar_update_timer.stop(); 455 status_bar_update_timer.stop();
@@ -617,6 +623,15 @@ void GMainWindow::OnConfigure() {
617 } 623 }
618} 624}
619 625
626void GMainWindow::OnToggleFilterBar() {
627 game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked());
628 if (ui.action_Show_Filter_Bar->isChecked()) {
629 game_list->setFilterFocus();
630 } else {
631 game_list->clearFilter();
632 }
633}
634
620void GMainWindow::OnSwapScreens() { 635void GMainWindow::OnSwapScreens() {
621 Settings::values.swap_screen = !Settings::values.swap_screen; 636 Settings::values.swap_screen = !Settings::values.swap_screen;
622 Settings::Apply(); 637 Settings::Apply();
@@ -671,6 +686,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
671#endif 686#endif
672 UISettings::values.single_window_mode = ui.action_Single_Window_Mode->isChecked(); 687 UISettings::values.single_window_mode = ui.action_Single_Window_Mode->isChecked();
673 UISettings::values.display_titlebar = ui.action_Display_Dock_Widget_Headers->isChecked(); 688 UISettings::values.display_titlebar = ui.action_Display_Dock_Widget_Headers->isChecked();
689 UISettings::values.show_filter_bar = ui.action_Show_Filter_Bar->isChecked();
674 UISettings::values.show_status_bar = ui.action_Show_Status_Bar->isChecked(); 690 UISettings::values.show_status_bar = ui.action_Show_Status_Bar->isChecked();
675 UISettings::values.first_start = false; 691 UISettings::values.first_start = false;
676 692
@@ -720,6 +736,11 @@ bool GMainWindow::ConfirmChangeGame() {
720 return answer != QMessageBox::No; 736 return answer != QMessageBox::No;
721} 737}
722 738
739void GMainWindow::filterBarSetChecked(bool state) {
740 ui.action_Show_Filter_Bar->setChecked(state);
741 emit(OnToggleFilterBar());
742}
743
723#ifdef main 744#ifdef main
724#undef main 745#undef main
725#endif 746#endif
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index ec841eaa5..2f398eb7b 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -7,6 +7,7 @@
7 7
8#include <memory> 8#include <memory>
9#include <QMainWindow> 9#include <QMainWindow>
10#include <QTimer>
10#include "ui_main.h" 11#include "ui_main.h"
11 12
12class CallstackWidget; 13class CallstackWidget;
@@ -41,6 +42,7 @@ class GMainWindow : public QMainWindow {
41 }; 42 };
42 43
43public: 44public:
45 void filterBarSetChecked(bool state);
44 GMainWindow(); 46 GMainWindow();
45 ~GMainWindow(); 47 ~GMainWindow();
46 48
@@ -122,6 +124,7 @@ private slots:
122 void OnMenuRecentFile(); 124 void OnMenuRecentFile();
123 void OnSwapScreens(); 125 void OnSwapScreens();
124 void OnConfigure(); 126 void OnConfigure();
127 void OnToggleFilterBar();
125 void OnDisplayTitleBars(bool); 128 void OnDisplayTitleBars(bool);
126 void ToggleWindowMode(); 129 void ToggleWindowMode();
127 void OnCreateGraphicsSurfaceViewer(); 130 void OnCreateGraphicsSurfaceViewer();
diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui
index 47dbb6ef7..f64b878f0 100644
--- a/src/citra_qt/main.ui
+++ b/src/citra_qt/main.ui
@@ -88,6 +88,7 @@
88 </widget> 88 </widget>
89 <addaction name="action_Single_Window_Mode"/> 89 <addaction name="action_Single_Window_Mode"/>
90 <addaction name="action_Display_Dock_Widget_Headers"/> 90 <addaction name="action_Display_Dock_Widget_Headers"/>
91 <addaction name="action_Show_Filter_Bar"/>
91 <addaction name="action_Show_Status_Bar"/> 92 <addaction name="action_Show_Status_Bar"/>
92 <addaction name="menu_View_Debugging"/> 93 <addaction name="menu_View_Debugging"/>
93 </widget> 94 </widget>
@@ -167,6 +168,14 @@
167 <string>Display Dock Widget Headers</string> 168 <string>Display Dock Widget Headers</string>
168 </property> 169 </property>
169 </action> 170 </action>
171 <action name="action_Show_Filter_Bar">
172 <property name="checkable">
173 <bool>true</bool>
174 </property>
175 <property name="text">
176 <string>Show Filter Bar</string>
177 </property>
178 </action>
170 <action name="action_Show_Status_Bar"> 179 <action name="action_Show_Status_Bar">
171 <property name="checkable"> 180 <property name="checkable">
172 <bool>true</bool> 181 <bool>true</bool>
diff --git a/src/citra_qt/ui_settings.h b/src/citra_qt/ui_settings.h
index 6408ece2b..bc37f81c5 100644
--- a/src/citra_qt/ui_settings.h
+++ b/src/citra_qt/ui_settings.h
@@ -27,6 +27,7 @@ struct Values {
27 27
28 bool single_window_mode; 28 bool single_window_mode;
29 bool display_titlebar; 29 bool display_titlebar;
30 bool show_filter_bar;
30 bool show_status_bar; 31 bool show_status_bar;
31 32
32 bool confirm_before_closing; 33 bool confirm_before_closing;