From 11641be04e4e47f88a12582fdbccf88aae12ae44 Mon Sep 17 00:00:00 2001 From: pineappleEA Date: Thu, 15 Apr 2021 00:16:40 +0200 Subject: [PATCH] early-access version 1600 --- README.md | 2 +- dist/license.md | 3 + dist/qt_themes/colorful/icons/48x48/star.png | Bin 0 -> 1248 bytes dist/qt_themes/colorful/style.qrc | 1 + dist/qt_themes/default/default.qrc | 1 + dist/qt_themes/default/icons/48x48/star.png | Bin 0 -> 686 bytes .../qt_themes/qdarkstyle/icons/48x48/star.png | Bin 0 -> 725 bytes dist/qt_themes/qdarkstyle/style.qrc | 1 + .../icons/48x48/star.png | Bin 0 -> 725 bytes .../qdarkstyle_midnight_blue/style.qrc | 1 + license.txt | 1 + src/core/hle/kernel/k_resource_limit.cpp | 13 +-- src/core/hle/kernel/k_resource_limit.h | 12 +-- src/core/hle/kernel/kernel.cpp | 7 +- src/core/hle/kernel/svc.cpp | 2 +- src/yuzu/configuration/config.cpp | 14 +++ src/yuzu/game_list.cpp | 102 +++++++++++++++++- src/yuzu/game_list.h | 5 + src/yuzu/game_list_p.h | 26 ++++- src/yuzu/uisettings.h | 1 + 20 files changed, 165 insertions(+), 27 deletions(-) create mode 100755 dist/qt_themes/colorful/icons/48x48/star.png create mode 100755 dist/qt_themes/default/icons/48x48/star.png create mode 100755 dist/qt_themes/qdarkstyle/icons/48x48/star.png create mode 100755 dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/star.png diff --git a/README.md b/README.md index 3eb97dbbe..a7dff10f5 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 1599. +This is the source code for early-access 1600. ## Legal Notice diff --git a/dist/license.md b/dist/license.md index e9bc87656..7bdebfec1 100755 --- a/dist/license.md +++ b/dist/license.md @@ -12,6 +12,7 @@ qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com qt_themes/default/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/default/icons/48x48/star.png | CC BY-ND 3.0 | https://icons8.com qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com qt_themes/qdarkstyle/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com @@ -20,6 +21,7 @@ qt_themes/qdarkstyle/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com qt_themes/qdarkstyle/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com qt_themes/qdarkstyle/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team qt_themes/qdarkstyle/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/qdarkstyle/icons/48x48/star.png | CC BY-ND 3.0 | https://icons8.com qt_themes/colorful/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com qt_themes/colorful/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io qt_themes/colorful/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com @@ -28,5 +30,6 @@ qt_themes/colorful/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com qt_themes/colorful/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com qt_themes/colorful/icons/48x48/plus.png | CC BY-ND 3.0 | https://icons8.com qt_themes/colorful/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/colorful/icons/48x48/star.png | CC BY-ND 3.0 | https://icons8.com \ No newline at end of file diff --git a/dist/qt_themes/colorful/icons/48x48/star.png b/dist/qt_themes/colorful/icons/48x48/star.png new file mode 100755 index 0000000000000000000000000000000000000000..43b5d52ed7cca57aec41d75670cf29cfa98e849d GIT binary patch literal 1248 zcmV<61Rwi}P)8gt_A)#g&J3Av1fne{HUlM%5 zO~jxG(WMVzp9(7c6xkOEDlCE^iJ$~4g2v4VT|sl*)>2qY&Fw|fwB4QAxzE23>$)?u zGk4~W_u7Z{z%ZOU&vVZI|D6AGp64}<75vDzx#y+mD;_xkb+3OAbWaiZk3H=s@GZ zkNC=WM4v7~Q_tGPIO_)42yjY>Aj~bbB_AJ)DrdA6aMm#fzHAi?r!7&YjI09se#7l} z`JX{k#=t`{831KzNX)gxd-|iu8(jqw`R71WD(ZjCZzMMy*})b`1=P233RebCBc>Mv zhw{GrgMr35wtUav_2#aNfO^}fk;^t?v;Z^kxF4sDSAV6lgbJQXyD@d*te=EC!_egO zmi{F4eQ6v4o!BrB-)3RnY@C|GwSZ=TW`oT{Zq_xOLur?(1h#lgiLWt#2sj7&RgSZm z-|yj_!Tj?$JB#m~v-FpJ6$z+k+TFY8~QWWvPo&9udm`*#Hr0MHNX zm*KP*G3~}nrfi<9$+cUm^P^I%6}ymn)-S@j&p|E!rDQ-COmqP_vMI#wN=S}vgr7}k=VXS%%=-9eju;EXU?z@4K0n}^eKWxbZzXai{=^)d| zM;aNJ`T^+48z32gLuU4vB{P3k!&cK>$QJC&@}sT6+*{+zJ>Hj8BAdc7uI!uw|1+~I zD{27`bzCKMbkz&Mj=CiiAUjF#dH;BuZAB(uE1vr9LPwr>1GF_l$sqKum+ahZc5OgS z)=+&&88bB%B@gT}wRI|_Y*_I16OhtTNT$#?Q>q>);rBq#>Lzjn7eR^gk_qTc!FQ@* z%e$Lx`FEk-5yHQbjx12R(M zfl!-Yr8BuY_CXz+SgAU@OtD`kh7q1XC<*ECL(Ryw)F_#LLz~U?G}7-?^gsG+z7DtE z8XEx^Yo7sH9+vc``9pVFTjx8L{N`<&nz%#aWl(lxj;ln*7`C?92sl$)74Un_5(^Wl z{abC>!M{i6+2YKVMEbx>=KLj~y+JV?E*%C_s6AjPUEyHrHP1S%HS>nm&Uml|wCx>Qn$v_kxk zQ1d_?d0>Hj|EH@icons/48x48/folder.png icons/48x48/plus.png icons/48x48/sd_card.png + icons/48x48/star.png icons/256x256/plus_folder.png diff --git a/dist/qt_themes/default/default.qrc b/dist/qt_themes/default/default.qrc index 2182f33f3..b195747a3 100755 --- a/dist/qt_themes/default/default.qrc +++ b/dist/qt_themes/default/default.qrc @@ -10,6 +10,7 @@ icons/48x48/folder.png icons/48x48/plus.png icons/48x48/sd_card.png + icons/48x48/star.png icons/256x256/yuzu.png icons/256x256/plus_folder.png diff --git a/dist/qt_themes/default/icons/48x48/star.png b/dist/qt_themes/default/icons/48x48/star.png new file mode 100755 index 0000000000000000000000000000000000000000..740f7f3e75da9160a8db6a1941b159509a7ab8ee GIT binary patch literal 686 zcmV;f0#W^mP)uX@gHlmi;094*=0#KY(pZ%dK9QUqM}k03ZhVI zH6l^bNj#$05i~lL3h}5Ul3ms#5)ui!8>98NKQwM& z486@7w3!Rp8QFg8yR_iK)-5bnjjnzgpRl+Y19ue6>%n?#!ye)4@6M6G6l<^oeZmF! zB}~Cw%(k!$B*pua;%kLD+mF@Bd$G0!V|XWQL*5E^bp-E|*YEJAUV#g;PlK}JVzuz} zFYycER<<%DjXrP~-<5x*r^ioWi=K&H_}l{0)oaJmtnw)vG28<3U$8f?e9CG(D^Y$F zTZ+o3EXSh~Ukzsjd{;r1NLKgP;t$)|MS>Kw@5!IEanS930+{4FesDW7sVg8U5y z&df!()0KjG^%xf#$wkoyb{5R5$Bqa!XQd)6>&>1*6AGLWirj%A#m^D-=)~5%Nktln zd8>2Rp7#wDA?60}b!_>*95U~50w?h?W6q=qTZn3oWRRG^6)daDtKht_uWyupC}snV z&dhg(rxmprOg>Yu#`#F@;9-qHU*TYmI{WalMy-2I^qs;2;dE3z*k_zoY+dL~a=*?} zGs^!6gH?l!kl-c?hIDoiEyyl5TX8d;p8_g@-I=FHvbcU0IaAS UE=gn{XaE2J07*qoM6N<$f)bHM>Hq)$ literal 0 HcmV?d00001 diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/star.png b/dist/qt_themes/qdarkstyle/icons/48x48/star.png new file mode 100755 index 0000000000000000000000000000000000000000..90d423a1d4c1e05ccec0a01fa34abca9fe99676d GIT binary patch literal 725 zcmV;`0xJE9P)_t6%-62 zVr82q2v*vNjbd-7sEw6YRw`;*Au;C>A_fF=QIp5w_Hek|?d?ovvLg4u7RTQAeeXLn zyE7jvb?MS28bBE+=dHsFpda|2T>Y`?tcsOb(ukyDa$SyCCp)c4X8n|W{fGxmW%IG(!0i{rNL(aezNm<_Y1^sIq8_-96v&zaeuqH(9)t-X;C2fn8;Z;z&BvnNxWrZ0C<<9_zG~)OT1-b1Mn&b@#DbGWr?>;6o5x7DE>9D zDTsK>L=SLp1;jrF)&>>dWaLIf@ecv7V=iHZ#CAz!8~vaYn?A6 zZwYt+ELi!6LTGL-v=Mkiy|LL7A^&0Oo!icons/48x48/folder.png icons/48x48/plus.png icons/48x48/sd_card.png + icons/48x48/star.png icons/256x256/plus_folder.png diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/star.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/star.png new file mode 100755 index 0000000000000000000000000000000000000000..90d423a1d4c1e05ccec0a01fa34abca9fe99676d GIT binary patch literal 725 zcmV;`0xJE9P)_t6%-62 zVr82q2v*vNjbd-7sEw6YRw`;*Au;C>A_fF=QIp5w_Hek|?d?ovvLg4u7RTQAeeXLn zyE7jvb?MS28bBE+=dHsFpda|2T>Y`?tcsOb(ukyDa$SyCCp)c4X8n|W{fGxmW%IG(!0i{rNL(aezNm<_Y1^sIq8_-96v&zaeuqH(9)t-X;C2fn8;Z;z&BvnNxWrZ0C<<9_zG~)OT1-b1Mn&b@#DbGWr?>;6o5x7DE>9D zDTsK>L=SLp1;jrF)&>>dWaLIf@ecv7V=iHZ#CAz!8~vaYn?A6 zZwYt+ELi!6LTGL-v=Mkiy|LL7A^&0Oo!icons/48x48/folder.png icons/48x48/plus.png icons/48x48/sd_card.png + icons/48x48/star.png icons/256x256/plus_folder.png diff --git a/license.txt b/license.txt index 86e7b3c1b..495f3e676 100755 --- a/license.txt +++ b/license.txt @@ -358,6 +358,7 @@ chip.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com plus.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com sd_card.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com +star.png | CC BY-ND 3.0 | https://icons8.com Note: Some icons are different in different themes, and they are separately listed diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp index d7a4a38e6..d05b34ea3 100755 --- a/src/core/hle/kernel/k_resource_limit.cpp +++ b/src/core/hle/kernel/k_resource_limit.cpp @@ -2,21 +2,16 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. - #include "common/assert.h" -#include "core/core.h" #include "core/core_timing.h" -#include "core/core_timing_util.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/svc_results.h" namespace Kernel { constexpr s64 DefaultTimeout = 10000000000; // 10 seconds -KResourceLimit::KResourceLimit(KernelCore& kernel, Core::System& system) - : Object{kernel}, lock{kernel}, cond_var{kernel}, kernel{kernel}, system(system) {} +KResourceLimit::KResourceLimit(KernelCore& kernel, const Core::Timing::CoreTiming& core_timing_) + : Object{kernel}, lock{kernel}, cond_var{kernel}, core_timing(core_timing_) {} KResourceLimit::~KResourceLimit() = default; s64 KResourceLimit::GetLimitValue(LimitableResource which) const { @@ -83,7 +78,7 @@ ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) { } bool KResourceLimit::Reserve(LimitableResource which, s64 value) { - return Reserve(which, value, system.CoreTiming().GetGlobalTimeNs().count() + DefaultTimeout); + return Reserve(which, value, core_timing.GetGlobalTimeNs().count() + DefaultTimeout); } bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) { @@ -114,7 +109,7 @@ bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) { } if (current_hints[index] + value <= limit_values[index] && - (timeout < 0 || system.CoreTiming().GetGlobalTimeNs().count() < timeout)) { + (timeout < 0 || core_timing.GetGlobalTimeNs().count() < timeout)) { waiter_count++; cond_var.Wait(&lock, timeout); waiter_count--; diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h index 58ae456f1..4542317d0 100755 --- a/src/core/hle/kernel/k_resource_limit.h +++ b/src/core/hle/kernel/k_resource_limit.h @@ -2,9 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. - #pragma once #include @@ -15,8 +12,8 @@ union ResultCode; -namespace Core { -class System; +namespace Core::Timing { +class CoreTiming; } namespace Kernel { @@ -37,7 +34,7 @@ constexpr bool IsValidResourceType(LimitableResource type) { class KResourceLimit final : public Object { public: - explicit KResourceLimit(KernelCore& kernel, Core::System& system); + explicit KResourceLimit(KernelCore& kernel, const Core::Timing::CoreTiming& core_timing_); ~KResourceLimit(); s64 GetLimitValue(LimitableResource which) const; @@ -75,7 +72,6 @@ private: mutable KLightLock lock; s32 waiter_count{}; KLightConditionVariable cond_var; - KernelCore& kernel; - Core::System& system; + const Core::Timing::CoreTiming& core_timing; }; } // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index f7d3f218a..5c4f45ab4 100755 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -72,7 +72,7 @@ struct KernelCore::Impl { KMemoryLayout memory_layout; DeriveInitialMemoryLayout(memory_layout); InitializeMemoryLayout(memory_layout); - InitializeSystemResourceLimit(kernel, system, memory_layout); + InitializeSystemResourceLimit(kernel, system.CoreTiming(), memory_layout); InitializeSlabHeaps(); InitializeSchedulers(); InitializeSuspendThreads(); @@ -142,9 +142,10 @@ struct KernelCore::Impl { } // Creates the default system resource limit - void InitializeSystemResourceLimit(KernelCore& kernel, Core::System& system, + void InitializeSystemResourceLimit(KernelCore& kernel, + const Core::Timing::CoreTiming& core_timing, const KMemoryLayout& memory_layout) { - system_resource_limit = std::make_shared(kernel, system); + system_resource_limit = std::make_shared(kernel, core_timing); const auto [total_size, kernel_size] = memory_layout.GetTotalAndKernelMemorySizes(); // If setting the default system values fails, then something seriously wrong has occurred. diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index fcffc746d..bebb86154 100755 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -2156,7 +2156,7 @@ static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) LOG_DEBUG(Kernel_SVC, "called"); auto& kernel = system.Kernel(); - auto resource_limit = std::make_shared(kernel, system); + auto resource_limit = std::make_shared(kernel, system.CoreTiming()); auto* const current_process = kernel.CurrentProcess(); ASSERT(current_process != nullptr); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 16ea2b5f5..060a2b45b 100755 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -936,6 +936,13 @@ void Config::ReadUIGamelistValues() { UISettings::values.row_2_text_id = ReadSetting(QStringLiteral("row_2_text_id"), 2).toUInt(); UISettings::values.cache_game_list = ReadSetting(QStringLiteral("cache_game_list"), true).toBool(); + const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites")); + for (int i = 0; i < favorites_size; i++) { + qt_config->setArrayIndex(i); + UISettings::values.favorited_ids.append( + ReadSetting(QStringLiteral("program_id")).toULongLong()); + } + qt_config->endArray(); qt_config->endGroup(); } @@ -1479,6 +1486,13 @@ void Config::SaveUIGamelistValues() { WriteSetting(QStringLiteral("row_1_text_id"), UISettings::values.row_1_text_id, 3); WriteSetting(QStringLiteral("row_2_text_id"), UISettings::values.row_2_text_id, 2); WriteSetting(QStringLiteral("cache_game_list"), UISettings::values.cache_game_list, true); + qt_config->beginWriteArray(QStringLiteral("favorites")); + for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { + qt_config->setArrayIndex(i); + WriteSetting(QStringLiteral("program_id"), + QVariant::fromValue(UISettings::values.favorited_ids[i])); + } + qt_config->endArray(); qt_config->endGroup(); } diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 9afd5b45f..2056a86a8 100755 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -84,6 +85,10 @@ void GameListSearchField::setFilterResult(int visible, int total) { label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible)); } +bool GameListSearchField::SearchFieldEmpty() { + return edit_filter->text().isEmpty(); +} + QString GameList::GetLastFilterResultItem() const { QString file_path; const int folder_count = item_model->rowCount(); @@ -187,7 +192,9 @@ void GameList::OnTextChanged(const QString& new_text) { // If the searchfield is empty every item is visible // Otherwise the filter gets applied if (edit_filter_text.isEmpty()) { - for (int i = 0; i < folder_count; ++i) { + tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), + UISettings::values.favorited_ids.size() == 0); + for (int i = 1; i < folder_count; ++i) { folder = item_model->item(i, 0); const QModelIndex folder_index = folder->index(); const int children_count = folder->rowCount(); @@ -198,8 +205,9 @@ void GameList::OnTextChanged(const QString& new_text) { } search_field->setFilterResult(children_total, children_total); } else { + tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), true); int result_count = 0; - for (int i = 0; i < folder_count; ++i) { + for (int i = 1; i < folder_count; ++i) { folder = item_model->item(i, 0); const QModelIndex folder_index = folder->index(); const int children_count = folder->rowCount(); @@ -280,6 +288,13 @@ void GameList::OnUpdateThemedIcons() { .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::DecorationRole); break; + case GameListItemType::Favorites: + child->setData( + QIcon::fromTheme(QStringLiteral("star")) + .pixmap(icon_size) + .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), + Qt::DecorationRole); + break; default: break; } @@ -427,6 +442,13 @@ void GameList::DonePopulating(const QStringList& watch_list) { emit ShowList(!IsEmpty()); item_model->invisibleRootItem()->appendRow(new GameListAddDir()); + item_model->invisibleRootItem()->insertRow(0, new GameListFavorites()); + tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), + UISettings::values.favorited_ids.size() == 0); + tree_view->expand(item_model->invisibleRootItem()->child(0)->index()); + for (u64 id : UISettings::values.favorited_ids) { + AddFavorite(id); + } // Clear out the old directories to watch for changes and add the new ones auto watch_dirs = watcher->directories(); @@ -446,7 +468,7 @@ void GameList::DonePopulating(const QStringList& watch_list) { tree_view->setEnabled(true); const int folder_count = tree_view->model()->rowCount(); int children_total = 0; - for (int i = 0; i < folder_count; ++i) { + for (int i = 1; i < folder_count; ++i) { children_total += item_model->item(i, 0)->rowCount(); } search_field->setFilterResult(children_total, children_total); @@ -478,6 +500,9 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { case GameListItemType::SysNandDir: AddPermDirPopup(context_menu, selected); break; + case GameListItemType::Favorites: + AddFavoritesPopup(context_menu); + break; default: break; } @@ -485,6 +510,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { } void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path) { + QAction* favorite = context_menu.addAction(tr("Favorite")); + context_menu.addSeparator(); 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 = @@ -503,6 +530,9 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri context_menu.addSeparator(); QAction* properties = context_menu.addAction(tr("Properties")); + favorite->setVisible(program_id != 0); + favorite->setCheckable(true); + favorite->setChecked(UISettings::values.favorited_ids.contains(program_id)); open_save_location->setVisible(program_id != 0); open_mod_location->setVisible(program_id != 0); open_transferable_shader_cache->setVisible(program_id != 0); @@ -513,6 +543,7 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0); + connect(favorite, &QAction::triggered, [this, program_id]() { ToggleFavorite(program_id); }); connect(open_save_location, &QAction::triggered, [this, program_id, path]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path); }); @@ -576,7 +607,7 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { const int row = selected.row(); - move_up->setEnabled(row > 0); + move_up->setEnabled(row > 1); move_down->setEnabled(row < item_model->rowCount() - 2); connect(move_up, &QAction::triggered, [this, selected, row, game_dir_index] { @@ -614,6 +645,18 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { }); } +void GameList::AddFavoritesPopup(QMenu& context_menu) { + QAction* clear_all = context_menu.addAction(tr("Clear")); + + connect(clear_all, &QAction::triggered, [this] { + for (u64 id : UISettings::values.favorited_ids) { + RemoveFavorite(id); + } + UISettings::values.favorited_ids.clear(); + tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), true); + }); +} + void GameList::LoadCompatibilityList() { QFile compat_list{QStringLiteral(":compatibility_list/compatibility_list.json")}; @@ -728,6 +771,57 @@ void GameList::RefreshGameDirectory() { } } +void GameList::ToggleFavorite(u64 program_id) { + if (!UISettings::values.favorited_ids.contains(program_id)) { + tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), + !search_field->SearchFieldEmpty()); + UISettings::values.favorited_ids.append(program_id); + AddFavorite(program_id); + item_model->sort(tree_view->header()->sortIndicatorSection(), + tree_view->header()->sortIndicatorOrder()); + } else { + UISettings::values.favorited_ids.removeOne(program_id); + RemoveFavorite(program_id); + if (UISettings::values.favorited_ids.size() == 0) { + tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), true); + } + } +} + +void GameList::AddFavorite(const u64& program_id) { + const auto favorites_row = item_model->item(0); + + for (int i = 1; i < item_model->rowCount() - 1; i++) { + const auto folder = item_model->item(i); + for (int j = 0; j < folder->rowCount(); j++) { + if (folder->child(j)->data(GameListItemPath::ProgramIdRole) == program_id) { + QList list; + for (int k = 0; k < item_model->columnCount(); k++) { + list.append(folder->child(j, k)->clone()); + } + list[0]->setData(folder->child(j)->data(GameListItem::SortRole), + GameListItem::SortRole); + list[0]->setText(folder->child(j)->data(Qt::DisplayRole).toString()); + + favorites_row->appendRow(list); + return; + } + } + } +} + +void GameList::RemoveFavorite(const u64& program_id) { + const auto favorites_row = item_model->item(0); + + for (int i = 0; i < favorites_row->rowCount(); i++) { + const auto game = favorites_row->child(i); + if (game->data(GameListItemPath::ProgramIdRole) == program_id) { + favorites_row->removeRow(i); + return; + } + } +} + GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} { connect(parent, &GMainWindow::UpdateThemedIcons, this, &GameListPlaceholder::onUpdateThemedIcons); diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 58059a3c4..1f5ab605d 100755 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -112,10 +112,15 @@ private: void RefreshGameDirectory(); + void ToggleFavorite(u64 program_id); + void AddFavorite(const u64& program_id); + void RemoveFavorite(const u64& program_id); + void PopupContextMenu(const QPoint& menu_location); void AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path); void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected); void AddPermDirPopup(QMenu& context_menu, QModelIndex selected); + void AddFavoritesPopup(QMenu& context_menu); std::shared_ptr vfs; FileSys::ManualContentProvider* provider; diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index f25445f18..e41a40425 100755 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -29,7 +29,8 @@ enum class GameListItemType { SdmcDir = QStandardItem::UserType + 3, UserNandDir = QStandardItem::UserType + 4, SysNandDir = QStandardItem::UserType + 5, - AddDir = QStandardItem::UserType + 6 + AddDir = QStandardItem::UserType + 6, + Favorites = QStandardItem::UserType + 7 }; Q_DECLARE_METATYPE(GameListItemType); @@ -310,6 +311,28 @@ public: } }; +class GameListFavorites : public GameListItem { +public: + explicit GameListFavorites() { + setData(type(), TypeRole); + + const int icon_size = std::min(static_cast(UISettings::values.icon_size), 64); + setData(QIcon::fromTheme(QStringLiteral("star")) + .pixmap(icon_size) + .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), + Qt::DecorationRole); + setData(QObject::tr("Favorites"), Qt::DisplayRole); + } + + int type() const override { + return static_cast(GameListItemType::Favorites); + } + + bool operator<(const QStandardItem& other) const override { + return false; + } +}; + class GameList; class QHBoxLayout; class QTreeView; @@ -324,6 +347,7 @@ public: explicit GameListSearchField(GameList* parent = nullptr); void setFilterResult(int visible, int total); + bool SearchFieldEmpty(); void clear(); void setFocus(); diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index ce3945485..5ba00b8c8 100755 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -74,6 +74,7 @@ struct Values { QString game_dir_deprecated; bool game_dir_deprecated_deepscan; QVector game_dirs; + QVector favorited_ids; QStringList recent_files; QString language;