|
|
|
@ -56,7 +56,7 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve
|
|
|
|
|
case Qt::Key_Return:
|
|
|
|
|
case Qt::Key_Enter: {
|
|
|
|
|
if (gamelist->search_field->visible == 1) {
|
|
|
|
|
QString file_path = gamelist->getLastFilterResultItem();
|
|
|
|
|
const QString file_path = gamelist->GetLastFilterResultItem();
|
|
|
|
|
|
|
|
|
|
// To avoid loading error dialog loops while confirming them using enter
|
|
|
|
|
// Also users usually want to run a different game after closing one
|
|
|
|
@ -83,22 +83,25 @@ void GameListSearchField::setFilterResult(int visible, int total) {
|
|
|
|
|
label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString GameList::getLastFilterResultItem() const {
|
|
|
|
|
QStandardItem* folder;
|
|
|
|
|
QStandardItem* child;
|
|
|
|
|
QString GameList::GetLastFilterResultItem() const {
|
|
|
|
|
QString file_path;
|
|
|
|
|
const int folder_count = item_model->rowCount();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < folder_count; ++i) {
|
|
|
|
|
folder = item_model->item(i, 0);
|
|
|
|
|
const QStandardItem* folder = item_model->item(i, 0);
|
|
|
|
|
const QModelIndex folder_index = folder->index();
|
|
|
|
|
const int children_count = folder->rowCount();
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < children_count; ++j) {
|
|
|
|
|
if (!tree_view->isRowHidden(j, folder_index)) {
|
|
|
|
|
child = folder->child(j, 0);
|
|
|
|
|
file_path = child->data(GameListItemPath::FullPathRole).toString();
|
|
|
|
|
if (tree_view->isRowHidden(j, folder_index)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QStandardItem* child = folder->child(j, 0);
|
|
|
|
|
file_path = child->data(GameListItemPath::FullPathRole).toString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return file_path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -123,7 +126,7 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
|
|
|
|
|
edit_filter->setPlaceholderText(tr("Enter pattern to filter"));
|
|
|
|
|
edit_filter->installEventFilter(key_release_eater);
|
|
|
|
|
edit_filter->setClearButtonEnabled(true);
|
|
|
|
|
connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::onTextChanged);
|
|
|
|
|
connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::OnTextChanged);
|
|
|
|
|
label_filter_result = new QLabel;
|
|
|
|
|
button_filter_close = new QToolButton(this);
|
|
|
|
|
button_filter_close->setText(QStringLiteral("X"));
|
|
|
|
@ -133,7 +136,7 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
|
|
|
|
|
"#000000; font-weight: bold; background: #F0F0F0; }"
|
|
|
|
|
"QToolButton:hover{ border: none; padding: 0px; color: "
|
|
|
|
|
"#EEEEEE; font-weight: bold; background: #E81123}"));
|
|
|
|
|
connect(button_filter_close, &QToolButton::clicked, parent, &GameList::onFilterCloseClicked);
|
|
|
|
|
connect(button_filter_close, &QToolButton::clicked, parent, &GameList::OnFilterCloseClicked);
|
|
|
|
|
layout_filter->setSpacing(10);
|
|
|
|
|
layout_filter->addWidget(label_filter);
|
|
|
|
|
layout_filter->addWidget(edit_filter);
|
|
|
|
@ -159,16 +162,22 @@ static bool ContainsAllWords(const QString& haystack, const QString& userinput)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Syncs the expanded state of Game Directories with settings to persist across sessions
|
|
|
|
|
void GameList::onItemExpanded(const QModelIndex& item) {
|
|
|
|
|
void GameList::OnItemExpanded(const QModelIndex& item) {
|
|
|
|
|
const auto type = item.data(GameListItem::TypeRole).value<GameListItemType>();
|
|
|
|
|
if (type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir ||
|
|
|
|
|
type == GameListItemType::UserNandDir || type == GameListItemType::SysNandDir)
|
|
|
|
|
item.data(GameListDir::GameDirRole).value<UISettings::GameDir*>()->expanded =
|
|
|
|
|
tree_view->isExpanded(item);
|
|
|
|
|
const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir ||
|
|
|
|
|
type == GameListItemType::UserNandDir ||
|
|
|
|
|
type == GameListItemType::SysNandDir;
|
|
|
|
|
|
|
|
|
|
if (!is_dir) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto* game_dir = item.data(GameListDir::GameDirRole).value<UISettings::GameDir*>();
|
|
|
|
|
game_dir->expanded = tree_view->isExpanded(item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Event in order to filter the gamelist after editing the searchfield
|
|
|
|
|
void GameList::onTextChanged(const QString& new_text) {
|
|
|
|
|
void GameList::OnTextChanged(const QString& new_text) {
|
|
|
|
|
const int folder_count = tree_view->model()->rowCount();
|
|
|
|
|
QString edit_filter_text = new_text.toLower();
|
|
|
|
|
QStandardItem* folder;
|
|
|
|
@ -224,7 +233,7 @@ void GameList::onTextChanged(const QString& new_text) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameList::onUpdateThemedIcons() {
|
|
|
|
|
void GameList::OnUpdateThemedIcons() {
|
|
|
|
|
for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) {
|
|
|
|
|
QStandardItem* child = item_model->invisibleRootItem()->child(i);
|
|
|
|
|
|
|
|
|
@ -276,7 +285,7 @@ void GameList::onUpdateThemedIcons() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameList::onFilterCloseClicked() {
|
|
|
|
|
void GameList::OnFilterCloseClicked() {
|
|
|
|
|
main_window->filterBarSetChecked(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -317,11 +326,11 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
|
|
|
|
|
}
|
|
|
|
|
item_model->setSortRole(GameListItemPath::SortRole);
|
|
|
|
|
|
|
|
|
|
connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::onUpdateThemedIcons);
|
|
|
|
|
connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons);
|
|
|
|
|
connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
|
|
|
|
|
connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
|
|
|
|
|
connect(tree_view, &QTreeView::expanded, this, &GameList::onItemExpanded);
|
|
|
|
|
connect(tree_view, &QTreeView::collapsed, this, &GameList::onItemExpanded);
|
|
|
|
|
connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded);
|
|
|
|
|
connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded);
|
|
|
|
|
|
|
|
|
|
// We must register all custom types with the Qt Automoc system so that we are able to use
|
|
|
|
|
// it with signals/slots. In this case, QList falls under the umbrells of custom types.
|
|
|
|
@ -338,17 +347,17 @@ GameList::~GameList() {
|
|
|
|
|
emit ShouldCancelWorker();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameList::setFilterFocus() {
|
|
|
|
|
void GameList::SetFilterFocus() {
|
|
|
|
|
if (tree_view->model()->rowCount() > 0) {
|
|
|
|
|
search_field->setFocus();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameList::setFilterVisible(bool visibility) {
|
|
|
|
|
void GameList::SetFilterVisible(bool visibility) {
|
|
|
|
|
search_field->setVisible(visibility);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameList::clearFilter() {
|
|
|
|
|
void GameList::ClearFilter() {
|
|
|
|
|
search_field->clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -397,10 +406,11 @@ void GameList::ValidateEntry(const QModelIndex& item) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GameList::isEmpty() const {
|
|
|
|
|
bool GameList::IsEmpty() const {
|
|
|
|
|
for (int i = 0; i < item_model->rowCount(); i++) {
|
|
|
|
|
const QStandardItem* child = item_model->invisibleRootItem()->child(i);
|
|
|
|
|
const auto type = static_cast<GameListItemType>(child->type());
|
|
|
|
|
|
|
|
|
|
if (!child->hasChildren() &&
|
|
|
|
|
(type == GameListItemType::SdmcDir || type == GameListItemType::UserNandDir ||
|
|
|
|
|
type == GameListItemType::SysNandDir)) {
|
|
|
|
@ -408,11 +418,12 @@ bool GameList::isEmpty() const {
|
|
|
|
|
i--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return !item_model->invisibleRootItem()->hasChildren();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameList::DonePopulating(QStringList watch_list) {
|
|
|
|
|
emit ShowList(!isEmpty());
|
|
|
|
|
void GameList::DonePopulating(const QStringList& watch_list) {
|
|
|
|
|
emit ShowList(!IsEmpty());
|
|
|
|
|
|
|
|
|
|
item_model->invisibleRootItem()->appendRow(new GameListAddDir());
|
|
|
|
|
|
|
|
|
@ -472,7 +483,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
|
|
|
|
|
context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) {
|
|
|
|
|
void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path) {
|
|
|
|
|
QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
|
|
|
|
|
QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location"));
|
|
|
|
|
QAction* open_transferable_shader_cache =
|
|
|
|
@ -690,12 +701,15 @@ void GameList::SaveInterfaceLayout() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameList::LoadInterfaceLayout() {
|
|
|
|
|
auto header = tree_view->header();
|
|
|
|
|
if (!header->restoreState(UISettings::values.gamelist_header_state)) {
|
|
|
|
|
// We are using the name column to display icons and titles
|
|
|
|
|
// so make it as large as possible as default.
|
|
|
|
|
header->resizeSection(COLUMN_NAME, header->width());
|
|
|
|
|
auto* header = tree_view->header();
|
|
|
|
|
|
|
|
|
|
if (header->restoreState(UISettings::values.gamelist_header_state)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We are using the name column to display icons and titles
|
|
|
|
|
// so make it as large as possible as default.
|
|
|
|
|
header->resizeSection(COLUMN_NAME, header->width());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QStringList GameList::supported_file_extensions = {
|
|
|
|
|