eden/src/yuzu/game/game_tree.cpp
crueter 102d254530
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run
[desktop] Clean up game list code, fix external watcher crash, and fix macOS flickering (#4106)
- Remove unnecessary icon update code (the UI reloads this stuff anyways); test on Windows please
- Cleaned up a bunch of duplicated/unused code within the game list
- Fix the game list constantly reloading on macOS
  * When you reconstruct the entire directory list on the watcher the directoryChanged signal fires on macOS--seems like a behavioral change that occurred somewhere in the 6.8 release cycle--and it would enter an infinite loop very quickly
  * To fix this, only the differences between the current and old watch list are accounted for on both ends.
  * Since this bug is now fixed, macOS uses Qt 6.11.1 now. Should theoretically improve our situation.
- Fix the external content watcher crashing; the worker would attempt to read files that didn't exist without any bounds since its cache was still pointing to that file.

This supersedes and replaces #4099.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4106
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-19 22:55:29 +02:00

153 lines
5.2 KiB
C++

// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QApplication>
#include <QHeaderView>
#include <QScroller>
#include <QScrollerProperties>
#include "qt_common/config/uisettings.h"
#include "qt_common/game_list/game_list_p.h"
#include "qt_common/game_list/model.h"
#include "yuzu/game/common.h"
#include "yuzu/game/game_tree.h"
GameTree::GameTree(QWidget* parent) : QTreeView{parent} {
setAlternatingRowColors(true);
setSelectionMode(QHeaderView::SingleSelection);
setSelectionBehavior(QHeaderView::SelectRows);
setVerticalScrollMode(QHeaderView::ScrollPerPixel);
setHorizontalScrollMode(QHeaderView::ScrollPerPixel);
setSortingEnabled(true);
setEditTriggers(QHeaderView::NoEditTriggers);
setContextMenuPolicy(Qt::CustomContextMenu);
setAttribute(Qt::WA_AcceptTouchEvents, true);
setStyleSheet(QStringLiteral("QTreeView{ border: none; }"));
connect(this, &QTreeView::expanded, this, &GameTree::OnItemExpanded);
connect(this, &QTreeView::collapsed, this, &GameTree::OnItemExpanded);
}
void GameTree::SetModel(GameListModel* model) {
QTreeView::setModel(model);
LoadInterfaceLayout();
UpdateColumnVisibility(model);
}
void GameTree::OnItemExpanded(const QModelIndex& item) {
const auto type = item.data(GameListItem::TypeRole).value<GameListItemType>();
const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir ||
type == GameListItemType::UserNandDir ||
type == GameListItemType::SysNandDir;
const bool is_fave = type == GameListItemType::Favorites;
if (!is_dir && !is_fave) {
return;
}
const bool is_expanded = isExpanded(item);
if (is_fave) {
UISettings::values.favorites_expanded = is_expanded;
return;
}
const int item_dir_index = item.data(GameListDir::GameDirRole).toInt();
UISettings::values.game_dirs[item_dir_index].expanded = is_expanded;
}
void GameTree::SaveInterfaceLayout() {
UISettings::values.gamelist_header_state = header()->saveState();
}
void GameTree::LoadInterfaceLayout() {
auto* hdr = header();
if (hdr->restoreState(UISettings::values.gamelist_header_state))
return;
hdr->resizeSection(GameListModel::COLUMN_NAME, 840);
}
void GameTree::UpdateColumnVisibility(GameListModel* model) {
Q_UNUSED(model)
setColumnHidden(GameListModel::COLUMN_ADD_ONS, !UISettings::values.show_add_ons);
setColumnHidden(GameListModel::COLUMN_COMPATIBILITY, !UISettings::values.show_compat);
setColumnHidden(GameListModel::COLUMN_FILE_TYPE, !UISettings::values.show_types);
setColumnHidden(GameListModel::COLUMN_SIZE, !UISettings::values.show_size);
setColumnHidden(GameListModel::COLUMN_PLAY_TIME, !UISettings::values.show_play_time);
}
QString GameTree::GetLastFilterResultItem() const {
QString file_path;
auto* model = qobject_cast<GameListModel*>(QTreeView::model());
if (!model)
return {};
for (int i = 1; i < model->rowCount() - 1; ++i) {
const QStandardItem* folder = 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 (isRowHidden(j, folder_index)) {
continue;
}
const QStandardItem* child = folder->child(j, 0);
file_path = child->data(GameListItemPath::FullPathRole).toString();
}
}
return file_path;
}
int GameTree::FilterClosedResultCount(GameListModel* model) {
int children_total = 0;
auto hide_favorites_row = UISettings::values.favorited_ids.size() == 0;
setRowHidden(0, model->invisibleRootItem()->index(), hide_favorites_row);
for (int i = 1; i < model->rowCount() - 1; ++i) {
auto* folder = model->item(i, 0);
const QModelIndex folder_index = folder->index();
const int children_count = folder->rowCount();
for (int j = 0; j < children_count; ++j) {
++children_total;
setRowHidden(j, folder_index, false);
}
}
return children_total;
}
void GameTree::ApplyFilter(const QString& edit_filter_text, GameListModel* model) {
int children_total = 0;
int result_count = 0;
if (edit_filter_text.isEmpty()) {
children_total = FilterClosedResultCount(model);
emit FilterResultReady(children_total, children_total);
return;
}
setRowHidden(0, model->invisibleRootItem()->index(), true);
for (int i = 1; i < model->rowCount() - 1; ++i) {
auto* folder = model->item(i, 0);
const QModelIndex folder_index = folder->index();
const int children_count = folder->rowCount();
for (int j = 0; j < children_count; ++j) {
++children_total;
const QStandardItem* child = folder->child(j, 0);
if (Yuzu::FilterMatches(edit_filter_text, child)) {
setRowHidden(j, folder_index, false);
++result_count;
} else {
setRowHidden(j, folder_index, true);
}
}
}
emit FilterResultReady(result_count, children_total);
}