From 7fc5af36226676d2ebc07a7a3d3cd82c536f206a Mon Sep 17 00:00:00 2001 From: fearlessTobi Date: Wed, 1 May 2019 14:59:55 +0200 Subject: [PATCH 1/8] Add assets and licenses --- dist/license.md | 31 ++++++++++ dist/qt_themes/colorful/icons/16x16/lock.png | Bin 0 -> 330 bytes .../colorful/icons/256x256/plus_folder.png | Bin 0 -> 4643 bytes .../colorful/icons/48x48/bad_folder.png | Bin 0 -> 15494 bytes dist/qt_themes/colorful/icons/48x48/chip.png | Bin 0 -> 582 bytes .../qt_themes/colorful/icons/48x48/folder.png | Bin 0 -> 460 bytes dist/qt_themes/colorful/icons/48x48/plus.png | Bin 0 -> 496 bytes .../colorful/icons/48x48/sd_card.png | Bin 0 -> 680 bytes dist/qt_themes/colorful/icons/index.theme | 14 +++++ dist/qt_themes/colorful/style.qrc | 15 +++++ dist/qt_themes/colorful/style.qss | 4 ++ .../colorful_dark/icons/16x16/lock.png | Bin 0 -> 401 bytes .../qt_themes/colorful_dark/icons/index.theme | 8 +++ dist/qt_themes/colorful_dark/style.qrc | 57 ++++++++++++++++++ dist/qt_themes/default/default.qrc | 14 +++++ dist/qt_themes/default/icons/16x16/lock.png | Bin 0 -> 279 bytes .../default/icons/256x256/plus_folder.png | Bin 0 -> 3135 bytes .../default/icons/48x48/bad_folder.png | Bin 0 -> 1088 bytes dist/qt_themes/default/icons/48x48/chip.png | Bin 0 -> 15070 bytes dist/qt_themes/default/icons/48x48/folder.png | Bin 0 -> 410 bytes dist/qt_themes/default/icons/48x48/plus.png | Bin 0 -> 316 bytes .../qt_themes/default/icons/48x48/sd_card.png | Bin 0 -> 614 bytes dist/qt_themes/default/icons/index.theme | 5 +- .../qt_themes/qdarkstyle/icons/16x16/lock.png | Bin 0 -> 304 bytes .../qdarkstyle/icons/256x256/plus_folder.png | Bin 0 -> 3438 bytes .../qdarkstyle/icons/48x48/bad_folder.png | Bin 0 -> 1098 bytes .../qt_themes/qdarkstyle/icons/48x48/chip.png | Bin 0 -> 15120 bytes .../qdarkstyle/icons/48x48/folder.png | Bin 0 -> 542 bytes .../qt_themes/qdarkstyle/icons/48x48/plus.png | Bin 0 -> 339 bytes .../qdarkstyle/icons/48x48/sd_card.png | Bin 0 -> 676 bytes dist/qt_themes/qdarkstyle/icons/index.theme | 7 ++- dist/qt_themes/qdarkstyle/style.qrc | 7 +++ license.txt | 16 +++++ 33 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 dist/license.md create mode 100644 dist/qt_themes/colorful/icons/16x16/lock.png create mode 100644 dist/qt_themes/colorful/icons/256x256/plus_folder.png create mode 100644 dist/qt_themes/colorful/icons/48x48/bad_folder.png create mode 100644 dist/qt_themes/colorful/icons/48x48/chip.png create mode 100644 dist/qt_themes/colorful/icons/48x48/folder.png create mode 100644 dist/qt_themes/colorful/icons/48x48/plus.png create mode 100644 dist/qt_themes/colorful/icons/48x48/sd_card.png create mode 100644 dist/qt_themes/colorful/icons/index.theme create mode 100644 dist/qt_themes/colorful/style.qrc create mode 100644 dist/qt_themes/colorful/style.qss create mode 100644 dist/qt_themes/colorful_dark/icons/16x16/lock.png create mode 100644 dist/qt_themes/colorful_dark/icons/index.theme create mode 100644 dist/qt_themes/colorful_dark/style.qrc create mode 100644 dist/qt_themes/default/icons/16x16/lock.png create mode 100644 dist/qt_themes/default/icons/256x256/plus_folder.png create mode 100644 dist/qt_themes/default/icons/48x48/bad_folder.png create mode 100644 dist/qt_themes/default/icons/48x48/chip.png create mode 100644 dist/qt_themes/default/icons/48x48/folder.png create mode 100644 dist/qt_themes/default/icons/48x48/plus.png create mode 100644 dist/qt_themes/default/icons/48x48/sd_card.png create mode 100644 dist/qt_themes/qdarkstyle/icons/16x16/lock.png create mode 100644 dist/qt_themes/qdarkstyle/icons/256x256/plus_folder.png create mode 100644 dist/qt_themes/qdarkstyle/icons/48x48/bad_folder.png create mode 100644 dist/qt_themes/qdarkstyle/icons/48x48/chip.png create mode 100644 dist/qt_themes/qdarkstyle/icons/48x48/folder.png create mode 100644 dist/qt_themes/qdarkstyle/icons/48x48/plus.png create mode 100644 dist/qt_themes/qdarkstyle/icons/48x48/sd_card.png diff --git a/dist/license.md b/dist/license.md new file mode 100644 index 000000000..b777ebb20 --- /dev/null +++ b/dist/license.md @@ -0,0 +1,31 @@ +The icons in this folder and its subfolders have the following licenses: + +Icon Name | License | Origin/Author +--- | --- | --- +qt_themes/default/icons/16x16/checked.png | Free for non-commercial use +qt_themes/default/icons/16x16/failed.png | Free for non-commercial use +qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/default/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/default/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com +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/qdarkstyle/icons/16x16/checked.png | Free for non-commercial use +qt_themes/qdarkstyle/icons/16x16/failed.png | Free for non-commercial use +qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/qdarkstyle/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com +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/colorful/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/colorful/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/colorful/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com +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 + + \ No newline at end of file diff --git a/dist/qt_themes/colorful/icons/16x16/lock.png b/dist/qt_themes/colorful/icons/16x16/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..fd27069d807e26186c8a610bdbcc0ce172562af6 GIT binary patch literal 330 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc!TX*rjv*HQTYGJ}4mk+4vg>ROSwGo6XvJPv zzlZ+oGn97-o-r-r&$VfgJ0KmA>2yufc!z{~qGQbm$8Tq=^*@-iIzITdZ+^wR?aTAG zy=^&a8Kl52V{ys6@63-oYYcdPN9cVtn5|nNv)ozk?u}LA z|1=jF*xy&`&-?QyY@&mH_rsJ||S^eTg=tDnm{r-UW|<05{U literal 0 HcmV?d00001 diff --git a/dist/qt_themes/colorful/icons/256x256/plus_folder.png b/dist/qt_themes/colorful/icons/256x256/plus_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..760fe6245e72cd0144b4dd198465db6c902ad400 GIT binary patch literal 4643 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 N&`{A*F1u|N`hPMhH%|Zn literal 0 HcmV?d00001 diff --git a/dist/qt_themes/colorful/icons/48x48/bad_folder.png b/dist/qt_themes/colorful/icons/48x48/bad_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..a7ab7a1f635d6c2aaee7ce3490ecbf0082d34bba GIT binary patch literal 15494 zcmeI3e{2+G8po%F0G9AWW_E!ci(Q` zG*RO}?`C&)pZEJd&olFR-{;x+XP#(WS~sQQ)(VQErZm+1n&3BUd?rnR#}(y+kHfDU z;`J*vikkYa@hRJW@6=fo<+`G@1oVJ^i6E&_izurhU`a*ea5hD`=cVGJ6b8B}1lpBY zt@-NPN6jWht~IZ4_*sA43p$kgo&;#_S=u7?ge8}3p697>rvw-v3Utwwibi6Zkg7GO z;|lP%G0d1v=_z`+*1W(-XbSinOp-L?nG`vc!N0h=Q1|K|ijfU%W%rRjorEC1qY7H8EThzdxV5%FW~VA-M|nVCoozbY$j-6djJYXojsjG~!n3|K$1sEcEYQaraPjw}gn zY9cD?9wjQa11278cQb{-qM7+!C3w|{nt-PTJT^B|YPiTNz0-t-n5K&{2{iaTaKNG{ zvLHLe5HE!!TCzzlT9oY^?X&}UkQ^e*+hnVYv!@~Y{l)GhLi<#y)2J30I@4HWRf6FQ zr)34K!)lY-Xcx=1(HsYyvOKNEGh8yeot82<$kpQ0Q!-qRfQhk%3qI#+*YU5)R zHTWI;zD!Y_@Tt-MAVmo~C~Btq?3(xPr>OE74ZZ~}ssCI#yDWSre#^vb7k6Ksf4C(% z{lix!_M=6WCpY)qwC&u$k>h=j4pd#5b$b4Q=~VB{T{|xSE$0QTRZ!` zci(lx=O4W?V{?Eyc&Xy^|KCNw$eVA!wr%yd{=v7VH&F9Vl{;16(Bju_zBXijy{WwQ z#Kx-jTUXPzgLfbKV%Ld-y=5;xNG+-U>eCzR>q2uMJXAR?P<>(lx{V#nuDm-gaqZvF zPhwBZp7wWT&{S@s^6&3v9ozOs&6@Z7*4@TVo^*@*P}!>G!F?4Iwj7u_`IA|_mrgp$ z=d1h6&hMBz_1OBx$^Y2$DIb21N*&l63@;iV3|Ac9^XH*)&;OyPap%Nw*VorLlfUdA zoG=veKi+ljeDag~&U?d0A2>5OKEC(Ka}U{GezWc!%jQFCyy3UMZa?-y|79xj!8i8z zKf9;z6n~gmwRPZs(sugn(-U7?zIu;~ud9s3g4YL|rDf1R^*r*x+E z7qd64+j4v$`S9V-_;7IFo6{B_dhW_sJ#};5s5%;4+|^vUYwf>gbluU{)Vuxx_klZZ z^vU5T*EW4HPO6;vw=bQ)-&(zU-^sq)4xTuEa!TsItE8*GWxL+#tvuGcecIj)`X5*R s^QP0D`*wfPI<#j7RW|A3*VLll%v>_i@YJ7-CxsgpF7@sIb@0*e0i}o=LjV8( literal 0 HcmV?d00001 diff --git a/dist/qt_themes/colorful/icons/48x48/chip.png b/dist/qt_themes/colorful/icons/48x48/chip.png new file mode 100644 index 0000000000000000000000000000000000000000..6fa15899950c576b742d4bf340c40296d2673583 GIT binary patch literal 582 zcmV-M0=fN(P)7Hv$NlKH@gFOfWh#;p`a7> zHS|=Xi|Jg_aj&Q#lU501AZa|+UAa*$TBPeBeF7jKv8Dk59qm|`?ej*L3uX^12YbBVuC(8l1+gV$k**_t@c>ZJWBZB=;`hL$Y!X2M?Al&7anv=x5Djc` zF{wX=?AZ&m9kLRimbq{p`;P&?xFf(F2AivLfXd3&7a92KYV;p~>3ie@1>}zj8+*Yu zi1xtn5y8a+fD4SCBnjp`f!P{6Z7!DRe2Uhd-S73pU@#lExS?7!1ks16+4{ U)=xYDaR2}S07*qoM6N<$f@LZ5LI3~& literal 0 HcmV?d00001 diff --git a/dist/qt_themes/colorful/icons/48x48/folder.png b/dist/qt_themes/colorful/icons/48x48/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..498de4c62940559bcfa3c609f7e7474ee8d86ae3 GIT binary patch literal 460 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSL74G){)!Z!hD1*n$B+!?x05z{F*}O5F27(eFndMoj+0rh z&40xH>tws))HAoNDb{TF1AmTbg1vvM{SK{ta!*3~yJ+!)lAVWV&RycPFhE13E=fG| z{;|wSu|F(+sy(|uFZrH++0<`EXXd#6OY88yk-96naQ3a=s-IZ*Mcjyg^Hped$L!qi z%Nu{^ytsFDZ`S68H$NYoZToJXVxeu_-lz9MzHG4Dnwk1|k- z)~iH;!ZoYKb0iMl`PS*vyLEB3S>J-KUs+~KW%p^nb}KG(nEz~9`TJGQDeI0eyQ*M+ z{q3idHxmD6Mc3=>-?Mj)jN*D=sBp1PxMbenmA6-}RqQ-41{geD{an^LB{Ts5V+X_j literal 0 HcmV?d00001 diff --git a/dist/qt_themes/colorful/icons/48x48/plus.png b/dist/qt_themes/colorful/icons/48x48/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..bc2c47c91a761228dc4ebdc8df8713119d5fec3a GIT binary patch literal 496 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSL74G){)!Z!hE`7($B+!?w=*_+9dQt7YhDl$6_Ks1@vuz! z4tGT43+sponOiYwj~k;{wQG4DkAwy2Zg3Lya~2uNf$D f9I999vSW~)A5gpS^PYHMEHQYx`njxgN@xNA;AYB_ literal 0 HcmV?d00001 diff --git a/dist/qt_themes/colorful/icons/48x48/sd_card.png b/dist/qt_themes/colorful/icons/48x48/sd_card.png new file mode 100644 index 0000000000000000000000000000000000000000..29be71a0d4307c9653e22a31e77e43e87a907beb GIT binary patch literal 680 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmSQK*5Dp-y;YjHK@;M7UB8wRq zm==RDW6GWQPN1MtHtq;Vd>H;fm=>1H|)A$7QK1qM}^gEEoP_8wuqkjd|Lj#mhYU6`YOzG z3OshJ?>c%a*Wj0cLaz!lBLfRd$Bl0)O(AZoA2_|2 zV@P=GEjIbIxbWIwx0MehCOz%?!g2CYInzR>!=Il@rdhfl;!oqh5W}o8|%MTCER?c{{{K)qQ-#P03e7iG|aY~7;VdMFE zKd)C6CoK!*SN^s;VEIDUki|rx&;Pjux^xdRKfQmY^1#l7q~L9Oq6;|U%=helTax6e zzT$mev19znYfG8le-f5Hw0{4G@7bZM|IJt)yuZHcj(x?xdHc7iW^>=wc`z;R9*bbP v;)UrshXgoQ?Ge;jBerh)oi~M#elm*noME_lluHAcsu(<7{an^LB{Ts5yq7Sw literal 0 HcmV?d00001 diff --git a/dist/qt_themes/colorful/icons/index.theme b/dist/qt_themes/colorful/icons/index.theme new file mode 100644 index 000000000..b452aca16 --- /dev/null +++ b/dist/qt_themes/colorful/icons/index.theme @@ -0,0 +1,14 @@ +[Icon Theme] +Name=colorful +Comment=Colorful theme +Inherits=default +Directories=16x16,48x48,256x256 + +[16x16] +Size=16 + +[48x48] +Size=48 + +[256x256] +Size=256 diff --git a/dist/qt_themes/colorful/style.qrc b/dist/qt_themes/colorful/style.qrc new file mode 100644 index 000000000..af2f3fd56 --- /dev/null +++ b/dist/qt_themes/colorful/style.qrc @@ -0,0 +1,15 @@ + + + icons/index.theme + icons/16x16/lock.png + icons/48x48/bad_folder.png + icons/48x48/chip.png + icons/48x48/folder.png + icons/48x48/plus.png + icons/48x48/sd_card.png + icons/256x256/plus_folder.png + + + style.qss + + diff --git a/dist/qt_themes/colorful/style.qss b/dist/qt_themes/colorful/style.qss new file mode 100644 index 000000000..413fc81da --- /dev/null +++ b/dist/qt_themes/colorful/style.qss @@ -0,0 +1,4 @@ +/* + This file is intentionally left blank. + We do not want to apply any stylesheet for colorful, only icons. +*/ diff --git a/dist/qt_themes/colorful_dark/icons/16x16/lock.png b/dist/qt_themes/colorful_dark/icons/16x16/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..32c505848ebc0ac4c84f8b544e94d077270297f4 GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf2?- zhak*YAR(g%WJs2{MwA5SrEak- z(Yth#q2FN#0oVHplb3L;y{vRmU?=wu<3*1dWLOjf-ZdN%e8456z{F)G{`in6E4zb( zsLw-%>gi08EH^83Kd!!b=8t`Sn)pA?pQ{(Fa#t$iN$Ars>l4@DZ z)p0^Xf#-0)mH6>OzJ#@Rv%Xw!+^eF|%AT;j*!}ck`zHZ@6~CV@5K2=2S^Z?ouO|z3 zHa)OBsk?XW`H07=?br82pMI}pr|-zpbnImnmydeMpR00AhtK92t+{)Et1q`cL3ocAD?=SeT-Z%G3H8A)XJYD@<);T3K0RZTKqN4x+ literal 0 HcmV?d00001 diff --git a/dist/qt_themes/colorful_dark/icons/index.theme b/dist/qt_themes/colorful_dark/icons/index.theme new file mode 100644 index 000000000..94d5ae8aa --- /dev/null +++ b/dist/qt_themes/colorful_dark/icons/index.theme @@ -0,0 +1,8 @@ +[Icon Theme] +Name=colorful_dark +Comment=Colorful theme (Dark style) +Inherits=default +Directories=16x16 + +[16x16] +Size=16 diff --git a/dist/qt_themes/colorful_dark/style.qrc b/dist/qt_themes/colorful_dark/style.qrc new file mode 100644 index 000000000..27a6cc87d --- /dev/null +++ b/dist/qt_themes/colorful_dark/style.qrc @@ -0,0 +1,57 @@ + + + icons/index.theme + icons/16x16/lock.png + ../colorful/icons/48x48/bad_folder.png + ../colorful/icons/48x48/chip.png + ../colorful/icons/48x48/folder.png + ../colorful/icons/48x48/plus.png + ../colorful/icons/48x48/sd_card.png + ../colorful/icons/256x256/plus_folder.png + + + + ../qdarkstyle/rc/up_arrow_disabled.png + ../qdarkstyle/rc/Hmovetoolbar.png + ../qdarkstyle/rc/stylesheet-branch-end.png + ../qdarkstyle/rc/branch_closed-on.png + ../qdarkstyle/rc/stylesheet-vline.png + ../qdarkstyle/rc/branch_closed.png + ../qdarkstyle/rc/branch_open-on.png + ../qdarkstyle/rc/transparent.png + ../qdarkstyle/rc/right_arrow_disabled.png + ../qdarkstyle/rc/sizegrip.png + ../qdarkstyle/rc/close.png + ../qdarkstyle/rc/close-hover.png + ../qdarkstyle/rc/close-pressed.png + ../qdarkstyle/rc/down_arrow.png + ../qdarkstyle/rc/Vmovetoolbar.png + ../qdarkstyle/rc/left_arrow.png + ../qdarkstyle/rc/stylesheet-branch-more.png + ../qdarkstyle/rc/up_arrow.png + ../qdarkstyle/rc/right_arrow.png + ../qdarkstyle/rc/left_arrow_disabled.png + ../qdarkstyle/rc/Hsepartoolbar.png + ../qdarkstyle/rc/branch_open.png + ../qdarkstyle/rc/Vsepartoolbar.png + ../qdarkstyle/rc/down_arrow_disabled.png + ../qdarkstyle/rc/undock.png + ../qdarkstyle/rc/checkbox_checked_disabled.png + ../qdarkstyle/rc/checkbox_checked_focus.png + ../qdarkstyle/rc/checkbox_checked.png + ../qdarkstyle/rc/checkbox_indeterminate.png + ../qdarkstyle/rc/checkbox_indeterminate_focus.png + ../qdarkstyle/rc/checkbox_unchecked_disabled.png + ../qdarkstyle/rc/checkbox_unchecked_focus.png + ../qdarkstyle/rc/checkbox_unchecked.png + ../qdarkstyle/rc/radio_checked_disabled.png + ../qdarkstyle/rc/radio_checked_focus.png + ../qdarkstyle/rc/radio_checked.png + ../qdarkstyle/rc/radio_unchecked_disabled.png + ../qdarkstyle/rc/radio_unchecked_focus.png + ../qdarkstyle/rc/radio_unchecked.png + + + ../qdarkstyle/style.qss + + diff --git a/dist/qt_themes/default/default.qrc b/dist/qt_themes/default/default.qrc index 14a0cf6f9..d1a0ee1be 100644 --- a/dist/qt_themes/default/default.qrc +++ b/dist/qt_themes/default/default.qrc @@ -5,7 +5,21 @@ icons/16x16/checked.png icons/16x16/failed.png + + icons/16x16/lock.png + + icons/48x48/bad_folder.png + + icons/48x48/chip.png + + icons/48x48/folder.png + + icons/48x48/plus.png + + icons/48x48/sd_card.png icons/256x256/yuzu.png + + icons/256x256/plus_folder.png diff --git a/dist/qt_themes/default/icons/16x16/lock.png b/dist/qt_themes/default/icons/16x16/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..496b58078983bc3c4f7dc2808fd02b8deb8b7b67 GIT binary patch literal 279 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 R--$o_0FmJCSnELf{@+>sS2O?s literal 0 HcmV?d00001 diff --git a/dist/qt_themes/default/icons/48x48/bad_folder.png b/dist/qt_themes/default/icons/48x48/bad_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..2527c1318575236c92c8111413bcdf1caeacbd27 GIT binary patch literal 1088 zcmV-G1i$->Bv zV-t`A)Thk=dx7@wDOa%@|HR#P99R-Iae(y!$kwwxA#(@dP(ZS^>>?4oO6L}VxYLqh zk{7__>d~kXoaEmbm8O7Gz}LX1z^olu%)1|!eGjYz>(1*U-J!5?5?EVLz*(#h^1p=Z zfMu?>*I@3WnFR*I<>d;{bYvu!oyo6;QepYQW54>zjr<8H$mDeHbHg;hGfep~D1){y@M=EY^MKd>h3Z5mAZ8eQ0P8hTU8hQyDqSM7x!h%`FuS8h zYQgD5A|N}I+6eU7ez9o&kjRXUCo*H>W=VdlbX3`rOZRrW(qG51r4qV;bN&4{it@I{ z?gpwK=;$1tKLhVoJA{mx*6AI-V9S?fEydzaSKDX6k)5*ty-k|vcTk`4D8dvkyP!@C9p|$O* z;+}CUh{uf=bAzb@fMBkyv)36j(RZ-UGi*$Rvl`X5?ZAq-ABQmoW8`MM&;M^p$x^o|en)cXu8>dtj}w`({fW%jepNZNK*`(nnyFFSiDT`DK2i2>!8$hm z3hT-E#VFQECji-WYFMB@K%My^x^;5HgP(MMd2}Lfwnl5!g1~7}UcLgQEPa!^%PrIZ0000-5wKq$5jO694>6y=%h=GkT^4aKI|4y3fD+Ey#D-E7;1B%7Pvn6xk& zWK_6{BRY;^eRKp=aBO{#Vi`ILXdf!|F*-%5I4Wbkz`fzl)#`|zP158vO*wkUKTl?o z{q>yh_xC&dIgjo?yQ8A~#_X)QSpb0SvQkGCywb{d>J<1}lQD8HyiD_z)=2>598tci zN0#Qy1E64mP+cq6I#)BC=+Ut*v4PixJU*xfz_vW(W4TSdtZv|&1aG0{+}npWYQa^g zsWCfgr>~fA7D`+F{My#?YOZw?XLV_o+p}yT1}56e+85k?gltvwu z$eRi^E0lujT4#m2SoHI1v(BRB^u_{pzE!6;TMG=PWoiShw@|c|(wnrjo}sM_tyjlC z8haLuY<^cGQ{`A0F9-iB)HKVokD;hwFsKU}b)vtC(p#-oiZ)ONgBB{ZQj1q+Lt3wt z7fUjl$H7aSU+~F-=v6Cu*#!b8Kno4JKaS4wv z;CDxd=He*c&3kySEJ2?>t?xwK^$pX zr&QhI)X{su8>{F(@szQK^>Xd?&N<4p!#v&{?3R^R)(ZwSi^~ z7RHdjlrCUs8c7mUrsQynE}^j{O%5Z?7{}y5l&Lx3{&BG~`=_#?S|pE)V;V)jhn4Mu zhi&31pSQ_IB^wcCVpTE4qFeOCVexjOjY`*yxS}hKDf3D)>*e?|haEQP1i{6yw2{@b z4F;`AZ!&AmCX-3q;9|{MeSRb7G8kEl#mYt@I-RI}Txy5N1(e+qPwo1PsS*3Q)NA|# zJn2|BqESZwS4*TxGpAPVJ&<} z;O!Q(*=V*=sm-a53L(V1YaIgIzml>lO$BjJYDZdQ-9$#DBW{dM74q*qI+L+h_^<$< zQ>es)%GgOxY=pl#NZ!%koES|cO7fJ&g6I@tC zsDR)?X<%c53yTO95L_q?Y)o)r5upNt3#EaL2`(%mR6uZ{G_Wzjg++u42riTcHYT{R zh)@B+h0?&r1Q!+&Dj>K}8rb-vxU!P3{_$S;zF!c&*w_2#V}+-Fu0tE4jz7NhhoMu?I;RbM_~i3ne7Bw2t;qdavVD&W>QyvIU z{?4otw=Q`pr~cTk!39MN_HA>|J$X;$%ItGfo6TKi@7@=_qyzl&Sy5jvHE&&AOWy&{ z;ZtALoOFYNPH>xHU}NT?n=dSU_eABoo_`8r0V#Y%iF|8@{}LXlT;;*zIiFLT&9FUOL!2u;%^i*Vo>iQIrSrx~uy4v=%G``$x)uYtm(gf3(0L z$Yh4~ZCkFn+;Zu;Pb{W6VCJ&(1N+}_zi4ecB8@Zs8|Ssh)L5ep^jn7S8zyoUx3whTrSHV$apRznaf%2D2Z0*%R3} zynp^reC{2e9aV{)^CJg4=5?$&KK$$WWD zT&%i_xno51Rw@%Pam#&aJ-*k|V6 zNia#=yP&;Q>*4o*3~_5NuTnAdZaBXxy!C)%CzF20`KSjQT^a?=(*BA_O|3brZR6AU zm3jNio}0(Bw6JCEJ+XJX!SmB->>(!$*B7OibBuI;$KTQm7sb)<+z z-hnHY=gKQS);{0K$W(c3$FMrJn#2I ox0hEwbbe{9HvPGF;g%Yv9YS`z3r_L10t1`D)78&qol`;+0Qc#!<^TWy literal 0 HcmV?d00001 diff --git a/dist/qt_themes/default/icons/48x48/plus.png b/dist/qt_themes/default/icons/48x48/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..dbc74687b177ea85ffa48be3864db8a49bed76cb GIT binary patch literal 316 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmSQK*5Dp-y;YjHK@;M7UB8wRq z_>O=u<5X=vX`rBFiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$Qb0vJJzX3_ zG8*5`G~{bB;9(Izayj?|=d9|es88(c6hvcGm{vW4t_NTMu#(^ zpCZlAFl=)^d&NaOvQR9-mW@}=cO!eAQU02{8%%`~lo`zs#C{gFH|5`3K2N<7rTXrh zx8>xQfg*A|pVlP^cIb0Nt*YSpJ>B|4=Db6Fn$f+g>tc_pUYk9mm*s{>0}?Ua!DycC nG4BM!d3&e5+aKBbqE-L&@s;+EX1>`3^aO*atDnm{r-UW|DM4-g literal 0 HcmV?d00001 diff --git a/dist/qt_themes/default/icons/48x48/sd_card.png b/dist/qt_themes/default/icons/48x48/sd_card.png new file mode 100644 index 0000000000000000000000000000000000000000..edacaeeb5629c5cf4a86d24eb35a4d37c45bfa77 GIT binary patch literal 614 zcmV-s0-61ZP)GCwTZX5jU!|FEYZSsY&U3pP<_Iwzcs%X_a|&TtoJw&o0_Rp8L!{RsKiLMm-(8 zjIO;}=ocfstv?W~6um2vz7~x6FgCYmg!%5YIX$9pQXGnfVF6mWtAE<0rZnY;1-ORu z&3V}!ssfy`^T13!RRON(-L6%f2%ktxROZ0mC!4`O#esdB1N*JU5{0l2e>jubge>mPSJ^&&?%8KcL>WvAo_9+JoM2kO zsn>V#-&Rc~i~Ae_tY5EP-=*8Ab(MpM;j*Q7#*{i)r8@>Yr!28i{4k+p>J|4L=dRCP zQYgN$Vq(bz-seA`PP+eLPWpo{(p_gRe3@1fbK~xheFC)y^e0Hmy!rd&s^O(iEb1#g wo=%oNY{h!`>vV^`LLZb)T(e+SzEHyuo5c9`w}kC2php-yUHx3vIVCg!0C@XzqyPW_ literal 0 HcmV?d00001 diff --git a/dist/qt_themes/qdarkstyle/icons/256x256/plus_folder.png b/dist/qt_themes/qdarkstyle/icons/256x256/plus_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..303f9a321890fc4c2054e2cff498adf23f654b70 GIT binary patch literal 3438 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 F|2wrn09yb6 literal 0 HcmV?d00001 diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/bad_folder.png b/dist/qt_themes/qdarkstyle/icons/48x48/bad_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..4a9709623476fcf4070c4e3da2bb17375222082e GIT binary patch literal 1098 zcmV-Q1hxB#P)9Ay}WpLce(X`;3^T0>M+iWlD0Lu0^XBOwVv zn~fMSUMNL8AyUxd0WYK_EusyAwOT=XBP!~()_4&xN=PpRBb11E?nUcvwt_U;=Fr`p z&x_sJtYeZrW{1K)u(03%|KGds%k2C!%s`zw6`}mX%)-E(Kr^thKyM0|1EwX-t&hDD zFtbO^EN-QntFhssRsy}dVrt_2WxU1OyKm(i}YHv1kMt#F_agNyVaHtrKKm&x58%DsI zSD3mZz8HI=7>&RJoZi`S8`#sU&CB#^Y_R*PLOOv32szFnAoDjd>oKFvj5bM6W|wRc z$$cxd&gU0_1c(hrb^+a9ol3bUTF0iQTgRrR-L!trEi~sZri|8u zC-3jx>=ly!0bYa}1~8E8+7ZgTec8GpmD-;_An6leqGkaS(ar~fu5$YYyA#pKt-j$s zHMyCK4en?(lmCjFBOG>~jSocT0fc+gD$hDYF7^h{>kUYnsm&+wa$Y zP^+F;wDaNC&yRnU8!@vq%jd$Fq-aHEeI)Mf*ye_u?}3KZ)|m7huAK)Ex*6FEYe=5b zg~nT2#>dY?#`u}M$#yf_QVruC>5%g-YnTT}Ur4>*IyQB*b!_UWnT{2ZyxuNZ81afQ zFabQ2t*-{A&Fo7cU54KgNpY`;MI%FkzA|*yMz&{m=iMK)d@(r_21bDgfJ?K}Hv*5B zAv-JSsa(%}7peF_=U$`(#WV`e+Vc4u z1}-nA&!iy-K+=am4=_&V4SYHLk`o3Yt#u;)*9dF|uFQUlG7liLgSl-~(j+jMeNEa@ ztVkYz=Cr`#nMd_kl%Z1!bN{;zNSX(JuT}fa2P9orsn$vePRV%wUoq}`UK9Za QM*si-07*qoM6N<$f^>%fC;$Ke literal 0 HcmV?d00001 diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/chip.png b/dist/qt_themes/qdarkstyle/icons/48x48/chip.png new file mode 100644 index 0000000000000000000000000000000000000000..973fabd052e389c28ef36c482cf44d764795a3b4 GIT binary patch literal 15120 zcmeI3Yj6|S8HU$(Vmk)Qoe9Lmkg&+6m=I}oziec2u?&`rjBA|O0@`RTt?UI!yO!3L zY?_RnwopoO8c#cQGKHB=8PYTfBsSAh96)J8rk!{k3gdQ4(ga8##*ka7xs?t*E6LKa zEOU}fejd$8`}v&r{l2r$xp@EVj=I`4g#{%A1VI$qt8Mk*s*=9>x!|`k`^I)~xkspW zh6qCTru5DFl7+A ztuoUpHJSLxtpy;m1U+85-nKeX4*XRiZxuy>rl@c@tPE?Fe6WR5o6TlQrJ*z$1yCqL zodJ=FC<39R>A|GnyXDFHMp0O~P6Ne*NJL7o${*0wV zVpRt~kU*Nz8EWVhSgM{4@$Er3Th#$>r8Lvsi<~z#Ia4x8Ju{A-jc_yAq@K7fwiMzs zWC8a|R|Z)|K8O1GruJC%*KF(PXPPLo1q zRA}@K8a1so(i-D3m5ElVpd@i+S`G*A;k=!hasam(&H*XYbAbKhVMOLjWr4L&9*>*$ z@|D~+}XLLw7zvv!*mG$=XF zLwhv3W`kMU$2yNuVPJJCg;~cM6pYrZ_v%2eSF4XfbU0x9wA41<-7f8xL~74hObywm zrCt~0z)8pWAdNKouUaBQnrWq~xe!>NozS`kt2cI*t!LY&woZ2VxY(H_Fu@Qj?YIj0 zrEk)~WL|xnif!r$su{Xs{N*xR}@+ zs4djAwp8V~zDb%>Uwce%HmJadQcJ%HE8LxKb>7(ucc)uZ&PM~BXw@XDr8Pq?>Ew+c z3+ur{0&6ws_0sN5Z%%jAaS_(nWaGg84N0p~XG(a|J2D!bGZ~?dgfTu<(7*H8OvYQm z!vc6tp^^_OQztpO5x(Idbw}TDVmKWx0@Fao7#9)|EWo&68ps&qLL!0%7#Bj0>iLj4>`GB3OWN!8DLD#)U)#3otI21~SID zkceOb#s$+r#uyh85iG#CU>e96<3b{W1sE4h0~upnNJOvzK*kss5)mxGxL_K{ z7~?`Bf&~~COamEXTu4N)0ONvbAmcmYDoDNh#|FUreqr!p->&@A<=~}2(p_EeAc(HT z1kwFNg1B)TTrUztJ9yi0+(i&{KS31peUBesO%T~$yKQAdLl;XP(xT+?LIsKlja%+6LX`cH6;MezNzm zoo}rE(>sHL?5(p`y&tk2zfu()9ewxNPQK;iA?*kDJ_m8A@K&}p`?oS<(GPN-B(BJe z1-kD0gi{yFCbEid?EJW|W62`&g@vtujn2(}?ckweS@`X98=}^ok1U=QT~(gr9$B%h z_b=Oy>lXy7ZZ{8~8!`V+S(Wqd=!(K<$yjgczh6Fnr0ukFDH{89qpe}E9{q;8chf6Q4={~%-W_!oh9;b`k zF?P%Qe9xn2w#vS^`_MsWPhQ#ROJm`Ew|58c`h4R_;oDC{2W1bq?z>!jWw`PDY}cxj zvIEw2OD5!ng!cPSMBkY+@Xry$-6f`vM)THYT`T+7ADi#*KWH6XHM{@SBR2ReM#fZT~9QQ~w9L4#0H) literal 0 HcmV?d00001 diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/folder.png b/dist/qt_themes/qdarkstyle/icons/48x48/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..0f1e987d6aa24b1d2f26d40181ec05b62c5862da GIT binary patch literal 542 zcmV+(0^$9MP)gj)+A>{26oZicm$UB2*Eo2vvkCLKUHkP(`RBR1vBO{U->e2_Tfp5glb)i-)#n zBcA=4?Za$q@u&fICB1B4$pc9Tzz0cZBlCX;n71A~gm$An5;~C7Xj)nV&r(DzBy9m+ zl_ZzNH#|G*J2tSDQd&f2Y*W%(U}LOrBO;cPK1q5L5g(=ESA#B5TF%l{rK~)Xx+?BT gmh@}z-x~VXU+F%DB-VK&7XSbN07*qoM6N<$g3QzC#sB~S literal 0 HcmV?d00001 diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/plus.png b/dist/qt_themes/qdarkstyle/icons/48x48/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..16cc8b4f44d52009d4a3f4c9d46f5bb0e20babcf GIT binary patch literal 339 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmSQK*5Dp-y;YjHK@;M7UB8wRq z_>O=u<5X=vX`rBFiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$Qb0u)JY5_^ zG8*6BbmVI?5NLh4@}X2!l-S-D>HaqF9f#jLiMc75c-1D{o4(=YB4d5u$$lLMo6j=x zUSPWWW2?jBcdI2P%slmKUl-$FpN4&IYeSNQ8*V0P=El9>^0jUMGlnd_1qi~7(|}J| zQX*~M5!Y#5wY%=$@Lrap@yOElZRqcN@1NB2l?WEb+1qqhd%gU$>qC9Hf>NIS`$c!E z@^cD0wIlW%nb&z$#^EgE6$FvRD#3MqUzqlRtp&OBEq>*PZDy`yczi-rX+hv3f1uwO NJYD@<);T3K0RY^^g3SN` literal 0 HcmV?d00001 diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/sd_card.png b/dist/qt_themes/qdarkstyle/icons/48x48/sd_card.png new file mode 100644 index 0000000000000000000000000000000000000000..0291c6542d05a40a350bb5bdbd99a15c01467161 GIT binary patch literal 676 zcmV;V0$crwP)9;%C77Yw$6jD~9oIODt?HsBRZt0bRw zmeDdcYUphqIq4;3u%|?CJop9~c1NoqP%q z@Y%`Q*)ca5-Xpe{WHeKj&Fr-6S~db994g2rfWrm(!R+N?CEIowoxHK^cpumcqyn)C z`3@YHbj?|xsY|8`Ay=W@MD8lI=ciCA5SvsWHmN{tQi0f{0B??l6*y&P?o*xO z5iqk?z*XRD17abp0hfLGu}GShbYIdtHUAHlO-XMg-H~*(%<(rT_n!W$2z40%0000< KMNUMnLSTXz{w(PL literal 0 HcmV?d00001 diff --git a/dist/qt_themes/qdarkstyle/icons/index.theme b/dist/qt_themes/qdarkstyle/icons/index.theme index 558ece40b..d1e12f3ef 100644 --- a/dist/qt_themes/qdarkstyle/icons/index.theme +++ b/dist/qt_themes/qdarkstyle/icons/index.theme @@ -2,10 +2,13 @@ Name=qdarkstyle Comment=dark theme Inherits=default -Directories=16x16,256x256 +Directories=16x16,48x48,256x256 [16x16] Size=16 - + +[48x48] +Size=48 + [256x256] Size=256 \ No newline at end of file diff --git a/dist/qt_themes/qdarkstyle/style.qrc b/dist/qt_themes/qdarkstyle/style.qrc index efbd0b9dc..c2c14c28a 100644 --- a/dist/qt_themes/qdarkstyle/style.qrc +++ b/dist/qt_themes/qdarkstyle/style.qrc @@ -1,6 +1,13 @@ icons/index.theme + icons/16x16/lock.png + icons/48x48/bad_folder.png + icons/48x48/chip.png + icons/48x48/folder.png + icons/48x48/plus.png + icons/48x48/sd_card.png + icons/256x256/plus_folder.png rc/up_arrow_disabled.png diff --git a/license.txt b/license.txt index d511905c1..2b858f9a7 100644 --- a/license.txt +++ b/license.txt @@ -337,3 +337,19 @@ proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. + + +The icons used in this project have the following licenses: + +Icon Name | License | Origin/Author +--- | --- | --- +checked.png | Free for non-commercial use +failed.png | Free for non-commercial use +lock.png | CC BY-ND 3.0 | https://icons8.com +plus_folder.png | CC BY-ND 3.0 | https://icons8.com +bad_folder.png | CC BY-ND 3.0 | https://icons8.com +chip.png | CC BY-ND 3.0 | https://icons8.com +folder.png | CC BY-ND 3.0 | https://icons8.com +plus.png (Default, Dark) | CC0 1.0 | Designed by BreadFish64 from the Citra team +plus.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com +sd_card.png | CC BY-ND 3.0 | https://icons8.com From 2d8eba5bafd7fe9da00c8a57c605a503c3ece478 Mon Sep 17 00:00:00 2001 From: fearlessTobi Date: Wed, 1 May 2019 23:21:04 +0200 Subject: [PATCH 2/8] yuzu: Add support for multiple game directories Ported from https://github.com/citra-emu/citra/pull/3617. --- src/yuzu/configuration/config.cpp | 42 +- src/yuzu/configuration/configure_general.cpp | 5 - src/yuzu/configuration/configure_general.ui | 7 - src/yuzu/game_list.cpp | 426 ++++++++++++++----- src/yuzu/game_list.h | 44 +- src/yuzu/game_list_p.h | 111 ++++- src/yuzu/game_list_worker.cpp | 83 ++-- src/yuzu/game_list_worker.h | 25 +- src/yuzu/main.cpp | 85 ++-- src/yuzu/main.h | 8 +- src/yuzu/main.ui | 1 - src/yuzu/uisettings.h | 20 +- 12 files changed, 664 insertions(+), 193 deletions(-) diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 0456248ac..f2f116a87 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -517,10 +517,35 @@ void Config::ReadPathValues() { UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString(); UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString(); UISettings::values.screenshot_path = ReadSetting(QStringLiteral("screenshotPath")).toString(); - UISettings::values.game_directory_path = + UISettings::values.game_dir_deprecated = ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString(); - UISettings::values.game_directory_deepscan = + UISettings::values.game_dir_deprecated_deepscan = ReadSetting(QStringLiteral("gameListDeepScan"), false).toBool(); + int gamedirs_size = qt_config->beginReadArray(QStringLiteral("gamedirs")); + for (int i = 0; i < gamedirs_size; ++i) { + qt_config->setArrayIndex(i); + UISettings::GameDir game_dir; + game_dir.path = ReadSetting(QStringLiteral("path")).toString(); + game_dir.deep_scan = ReadSetting(QStringLiteral("deep_scan"), false).toBool(); + game_dir.expanded = ReadSetting(QStringLiteral("expanded"), true).toBool(); + UISettings::values.game_dirs.append(game_dir); + } + qt_config->endArray(); + // create NAND and SD card directories if empty, these are not removable through the UI, + // also carries over old game list settings if present + if (UISettings::values.game_dirs.isEmpty()) { + UISettings::GameDir game_dir; + game_dir.path = QStringLiteral("INSTALLED"); + game_dir.expanded = true; + UISettings::values.game_dirs.append(game_dir); + game_dir.path = QStringLiteral("SYSTEM"); + UISettings::values.game_dirs.append(game_dir); + if (UISettings::values.game_dir_deprecated != QStringLiteral(".")) { + game_dir.path = UISettings::values.game_dir_deprecated; + game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan; + UISettings::values.game_dirs.append(game_dir); + } + } UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList(); qt_config->endGroup(); @@ -899,10 +924,15 @@ void Config::SavePathValues() { WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path); WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path); WriteSetting(QStringLiteral("screenshotPath"), UISettings::values.screenshot_path); - WriteSetting(QStringLiteral("gameListRootDir"), UISettings::values.game_directory_path, - QStringLiteral(".")); - WriteSetting(QStringLiteral("gameListDeepScan"), UISettings::values.game_directory_deepscan, - false); + qt_config->beginWriteArray(QStringLiteral("gamedirs")); + for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { + qt_config->setArrayIndex(i); + const auto& game_dir = UISettings::values.game_dirs.at(i); + WriteSetting(QStringLiteral("path"), game_dir.path); + WriteSetting(QStringLiteral("deep_scan"), game_dir.deep_scan, false); + WriteSetting(QStringLiteral("expanded"), game_dir.expanded, true); + } + qt_config->endArray(); WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files); qt_config->endGroup(); diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 75fcbfea3..727836b17 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -19,22 +19,17 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) } SetConfiguration(); - - connect(ui->toggle_deepscan, &QCheckBox::stateChanged, this, - [] { UISettings::values.is_game_list_reload_pending.exchange(true); }); } ConfigureGeneral::~ConfigureGeneral() = default; void ConfigureGeneral::SetConfiguration() { - ui->toggle_deepscan->setChecked(UISettings::values.game_directory_deepscan); ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); } void ConfigureGeneral::ApplyConfiguration() { - UISettings::values.game_directory_deepscan = ui->toggle_deepscan->isChecked(); UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); UISettings::values.theme = diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index 184fdd329..e747a4ce2 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui @@ -24,13 +24,6 @@ - - - - Search sub-directories for games - - - diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index d18b96519..65947c59b 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -34,7 +34,6 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve return QObject::eventFilter(obj, event); QKeyEvent* keyEvent = static_cast(event); - int rowCount = gamelist->tree_view->model()->rowCount(); QString edit_filter_text = gamelist->search_field->edit_filter->text().toLower(); // If the searchfield's text hasn't changed special function keys get checked @@ -56,19 +55,9 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve // If there is only one result launch this game case Qt::Key_Return: case Qt::Key_Enter: { - QStandardItemModel* item_model = new QStandardItemModel(gamelist->tree_view); - QModelIndex root_index = item_model->invisibleRootItem()->index(); - QStandardItem* child_file; - QString file_path; - int resultCount = 0; - for (int i = 0; i < rowCount; ++i) { - if (!gamelist->tree_view->isRowHidden(i, root_index)) { - ++resultCount; - child_file = gamelist->item_model->item(i, 0); - file_path = child_file->data(GameListItemPath::FullPathRole).toString(); - } - } - if (resultCount == 1) { + if (gamelist->search_field->visible == 1) { + 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 gamelist->search_field->edit_filter->clear(); @@ -88,9 +77,31 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve } void GameListSearchField::setFilterResult(int visible, int total) { + this->visible = visible; + this->total = total; + label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible)); } +QString GameList::getLastFilterResultItem() { + QStandardItem* folder; + QStandardItem* child; + QString file_path; + int folder_count = item_model->rowCount(); + for (int i = 0; i < folder_count; ++i) { + folder = item_model->item(i, 0); + QModelIndex folder_index = folder->index(); + int childrenCount = folder->rowCount(); + for (int j = 0; j < childrenCount; ++j) { + if (!tree_view->isRowHidden(j, folder_index)) { + child = folder->child(j, 0); + file_path = child->data(GameListItemPath::FullPathRole).toString(); + } + } + } + return file_path; +} + void GameListSearchField::clear() { edit_filter->clear(); } @@ -147,45 +158,112 @@ static bool ContainsAllWords(const QString& haystack, const QString& userinput) [&haystack](const QString& s) { return haystack.contains(s); }); } +// Syncs the expanded state of Game Directories with settings to persist across sessions +void GameList::onItemExpanded(const QModelIndex& item) { + GameListItemType type = item.data(GameListItem::TypeRole).value(); + if (type == GameListItemType::CustomDir || type == GameListItemType::InstalledDir || + type == GameListItemType::SystemDir) + item.data(GameListDir::GameDirRole).value()->expanded = + tree_view->isExpanded(item); +} + // Event in order to filter the gamelist after editing the searchfield void GameList::onTextChanged(const QString& new_text) { - const int row_count = tree_view->model()->rowCount(); - const QString edit_filter_text = new_text.toLower(); - const QModelIndex root_index = item_model->invisibleRootItem()->index(); + int folder_count = tree_view->model()->rowCount(); + QString edit_filter_text = new_text.toLower(); + QStandardItem* folder; + QStandardItem* child; + int childrenTotal = 0; + QModelIndex root_index = item_model->invisibleRootItem()->index(); // If the searchfield is empty every item is visible // Otherwise the filter gets applied if (edit_filter_text.isEmpty()) { - for (int i = 0; i < row_count; ++i) { - tree_view->setRowHidden(i, root_index, false); + for (int i = 0; i < folder_count; ++i) { + folder = item_model->item(i, 0); + QModelIndex folder_index = folder->index(); + int childrenCount = folder->rowCount(); + for (int j = 0; j < childrenCount; ++j) { + ++childrenTotal; + tree_view->setRowHidden(j, folder_index, false); + } } - search_field->setFilterResult(row_count, row_count); + search_field->setFilterResult(childrenTotal, childrenTotal); } else { int result_count = 0; - for (int i = 0; i < row_count; ++i) { - const QStandardItem* child_file = item_model->item(i, 0); - const QString file_path = - child_file->data(GameListItemPath::FullPathRole).toString().toLower(); - const QString file_title = - child_file->data(GameListItemPath::TitleRole).toString().toLower(); - const QString file_program_id = - child_file->data(GameListItemPath::ProgramIdRole).toString().toLower(); + for (int i = 0; i < folder_count; ++i) { + folder = item_model->item(i, 0); + QModelIndex folder_index = folder->index(); + int childrenCount = folder->rowCount(); + for (int j = 0; j < childrenCount; ++j) { + ++childrenTotal; + const QStandardItem* child = folder->child(j, 0); + const QString file_path = + child->data(GameListItemPath::FullPathRole).toString().toLower(); + const QString file_title = + child->data(GameListItemPath::TitleRole).toString().toLower(); + const QString file_program_id = + child->data(GameListItemPath::ProgramIdRole).toString().toLower(); - // Only items which filename in combination with its title contains all words - // that are in the searchfield will be visible in the gamelist - // The search is case insensitive because of toLower() - // I decided not to use Qt::CaseInsensitive in containsAllWords to prevent - // multiple conversions of edit_filter_text for each game in the gamelist - const QString file_name = file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + - QLatin1Char{' '} + file_title; - if (ContainsAllWords(file_name, edit_filter_text) || - (file_program_id.count() == 16 && edit_filter_text.contains(file_program_id))) { - tree_view->setRowHidden(i, root_index, false); - ++result_count; - } else { - tree_view->setRowHidden(i, root_index, true); + // Only items which filename in combination with its title contains all words + // that are in the searchfield will be visible in the gamelist + // The search is case insensitive because of toLower() + // I decided not to use Qt::CaseInsensitive in containsAllWords to prevent + // multiple conversions of edit_filter_text for each game in the gamelist + const QString file_name = + file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} + + file_title; + if (ContainsAllWords(file_name, edit_filter_text) || + (file_program_id.count() == 16 && edit_filter_text.contains(file_program_id))) { + tree_view->setRowHidden(j, folder_index, false); + ++result_count; + } else { + tree_view->setRowHidden(j, folder_index, true); + } + search_field->setFilterResult(result_count, childrenTotal); } - search_field->setFilterResult(result_count, row_count); + } + } +} + +void GameList::onUpdateThemedIcons() { + for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) { + QStandardItem* child = item_model->invisibleRootItem()->child(i); + + int icon_size = UISettings::values.icon_size; + switch (child->data(GameListItem::TypeRole).value()) { + case GameListItemType::InstalledDir: + child->setData( + QIcon::fromTheme(QStringLiteral("sd_card")) + .pixmap(icon_size) + .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), + Qt::DecorationRole); + break; + case GameListItemType::SystemDir: + child->setData( + QIcon::fromTheme(QStringLiteral("chip")) + .pixmap(icon_size) + .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), + Qt::DecorationRole); + break; + case GameListItemType::CustomDir: { + const UISettings::GameDir* game_dir = + child->data(GameListDir::GameDirRole).value(); + QString icon_name = QFileInfo::exists(game_dir->path) ? QStringLiteral("folder") + : QStringLiteral("bad_folder"); + child->setData( + QIcon::fromTheme(icon_name).pixmap(icon_size).scaled( + icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), + Qt::DecorationRole); + break; + } + case GameListItemType::AddDir: + child->setData( + QIcon::fromTheme(QStringLiteral("plus")) + .pixmap(icon_size) + .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), + Qt::DecorationRole); + break; } } } @@ -230,12 +308,16 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide item_model->setHeaderData(COLUMN_FILE_TYPE - 1, Qt::Horizontal, tr("File type")); item_model->setHeaderData(COLUMN_SIZE - 1, Qt::Horizontal, tr("Size")); } + item_model->setSortRole(GameListItemPath::TitleRole); + 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); - // 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. + // 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. qRegisterMetaType>("QList"); layout->setContentsMargins(0, 0, 0, 0); @@ -263,38 +345,67 @@ void GameList::clearFilter() { search_field->clear(); } -void GameList::AddEntry(const QList& entry_items) { +void GameList::AddDirEntry(GameListDir* entry_items) { item_model->invisibleRootItem()->appendRow(entry_items); + tree_view->setExpanded( + entry_items->index(), + entry_items->data(GameListDir::GameDirRole).value()->expanded); +} + +void GameList::AddEntry(const QList& entry_items, GameListDir* parent) { + parent->appendRow(entry_items); } void GameList::ValidateEntry(const QModelIndex& item) { - // We don't care about the individual QStandardItem that was selected, but its row. - const int row = item_model->itemFromIndex(item)->row(); - const QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); - const QString file_path = child_file->data(GameListItemPath::FullPathRole).toString(); + auto selected = item.sibling(item.row(), 0); - if (file_path.isEmpty()) - return; + switch (selected.data(GameListItem::TypeRole).value()) { + case GameListItemType::Game: { + QString file_path = selected.data(GameListItemPath::FullPathRole).toString(); + if (file_path.isEmpty()) + return; + QFileInfo file_info(file_path); + if (!file_info.exists()) + return; - if (!QFileInfo::exists(file_path)) - return; - - const QFileInfo file_info{file_path}; - if (file_info.isDir()) { - const QDir dir{file_path}; - const QStringList matching_main = dir.entryList({QStringLiteral("main")}, QDir::Files); - if (matching_main.size() == 1) { - emit GameChosen(dir.path() + QDir::separator() + matching_main[0]); + if (file_info.isDir()) { + const QDir dir{file_path}; + const QStringList matching_main = dir.entryList({QStringLiteral("main")}, QDir::Files); + if (matching_main.size() == 1) { + emit GameChosen(dir.path() + QDir::separator() + matching_main[0]); + } + return; } - return; - } - // Users usually want to run a diffrent game after closing one - search_field->clear(); - emit GameChosen(file_path); + // Users usually want to run a different game after closing one + search_field->clear(); + emit GameChosen(file_path); + break; + } + case GameListItemType::AddDir: + emit AddDirectory(); + break; + } +} + +bool GameList::isEmpty() { + for (int i = 0; i < item_model->rowCount(); i++) { + const QStandardItem* child = item_model->invisibleRootItem()->child(i); + GameListItemType type = static_cast(child->type()); + if (!child->hasChildren() && + (type == GameListItemType::InstalledDir || type == GameListItemType::SystemDir)) { + item_model->invisibleRootItem()->removeRow(child->row()); + i--; + }; + } + return !item_model->invisibleRootItem()->hasChildren(); } void GameList::DonePopulating(QStringList watch_list) { + emit ShowList(!isEmpty()); + + item_model->invisibleRootItem()->appendRow(new GameListAddDir()); + // Clear out the old directories to watch for changes and add the new ones auto watch_dirs = watcher->directories(); if (!watch_dirs.isEmpty()) { @@ -311,9 +422,16 @@ void GameList::DonePopulating(QStringList watch_list) { QCoreApplication::processEvents(); } tree_view->setEnabled(true); - int rowCount = tree_view->model()->rowCount(); - search_field->setFilterResult(rowCount, rowCount); - if (rowCount > 0) { + int folder_count = tree_view->model()->rowCount(); + int childrenTotal = 0; + for (int i = 0; i < folder_count; ++i) { + int childrenCount = item_model->item(i, 0)->rowCount(); + for (int j = 0; j < childrenCount; ++j) { + ++childrenTotal; + } + } + search_field->setFilterResult(childrenTotal, childrenTotal); + if (childrenTotal > 0) { search_field->setFocus(); } } @@ -323,12 +441,26 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { if (!item.isValid()) return; - int row = item_model->itemFromIndex(item)->row(); - QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); - u64 program_id = child_file->data(GameListItemPath::ProgramIdRole).toULongLong(); - std::string path = child_file->data(GameListItemPath::FullPathRole).toString().toStdString(); - + auto selected = item.sibling(item.row(), 0); QMenu context_menu; + switch (selected.data(GameListItem::TypeRole).value()) { + case GameListItemType::Game: + AddGamePopup(context_menu, selected.data(GameListItemPath::ProgramIdRole).toULongLong(), + selected.data(GameListItemPath::FullPathRole).toString().toStdString()); + break; + case GameListItemType::CustomDir: + AddPermDirPopup(context_menu, selected); + AddCustomDirPopup(context_menu, selected); + break; + case GameListItemType::InstalledDir: + case GameListItemType::SystemDir: + AddPermDirPopup(context_menu, selected); + break; + } + context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); +} + +void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) { QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location")); QAction* open_transferable_shader_cache = @@ -344,19 +476,86 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0); - connect(open_save_location, &QAction::triggered, - [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); }); - connect(open_lfs_location, &QAction::triggered, - [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); }); + connect(open_save_location, &QAction::triggered, [this, program_id]() { + emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); + }); + connect(open_lfs_location, &QAction::triggered, [this, program_id]() { + emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); + }); connect(open_transferable_shader_cache, &QAction::triggered, - [&]() { emit OpenTransferableShaderCacheRequested(program_id); }); - connect(dump_romfs, &QAction::triggered, [&]() { emit DumpRomFSRequested(program_id, path); }); - connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); }); - connect(navigate_to_gamedb_entry, &QAction::triggered, - [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); - connect(properties, &QAction::triggered, [&]() { emit OpenPerGameGeneralRequested(path); }); + [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); }); + connect(dump_romfs, &QAction::triggered, + [this, program_id, path]() { emit DumpRomFSRequested(program_id, path); }); + connect(copy_tid, &QAction::triggered, + [this, program_id]() { emit CopyTIDRequested(program_id); }); + connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { + emit NavigateToGamedbEntryRequested(program_id, compatibility_list); + }); + connect(properties, &QAction::triggered, + [this, path]() { emit OpenPerGameGeneralRequested(path); }); +}; - context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); +void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) { + UISettings::GameDir& game_dir = + *selected.data(GameListDir::GameDirRole).value(); + + QAction* deep_scan = context_menu.addAction(tr("Scan Subfolders")); + QAction* delete_dir = context_menu.addAction(tr("Remove Game Directory")); + + deep_scan->setCheckable(true); + deep_scan->setChecked(game_dir.deep_scan); + + connect(deep_scan, &QAction::triggered, [this, &game_dir] { + game_dir.deep_scan = !game_dir.deep_scan; + PopulateAsync(UISettings::values.game_dirs); + }); + connect(delete_dir, &QAction::triggered, [this, &game_dir, selected] { + UISettings::values.game_dirs.removeOne(game_dir); + item_model->invisibleRootItem()->removeRow(selected.row()); + }); +} + +void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { + UISettings::GameDir& game_dir = + *selected.data(GameListDir::GameDirRole).value(); + + QAction* move_up = context_menu.addAction(tr(u8"\U000025b2 Move Up")); + QAction* move_down = context_menu.addAction(tr(u8"\U000025bc Move Down ")); + QAction* open_directory_location = context_menu.addAction(tr("Open Directory Location")); + + int row = selected.row(); + + move_up->setEnabled(row > 0); + move_down->setEnabled(row < item_model->rowCount() - 2); + + connect(move_up, &QAction::triggered, [this, selected, row, &game_dir] { + // find the indices of the items in settings and swap them + UISettings::values.game_dirs.swap( + UISettings::values.game_dirs.indexOf(game_dir), + UISettings::values.game_dirs.indexOf(*selected.sibling(selected.row() - 1, 0) + .data(GameListDir::GameDirRole) + .value())); + // move the treeview items + QList item = item_model->takeRow(row); + item_model->invisibleRootItem()->insertRow(row - 1, item); + tree_view->setExpanded(selected, game_dir.expanded); + }); + + connect(move_down, &QAction::triggered, [this, selected, row, &game_dir] { + // find the indices of the items in settings and swap them + UISettings::values.game_dirs.swap( + UISettings::values.game_dirs.indexOf(game_dir), + UISettings::values.game_dirs.indexOf(*selected.sibling(selected.row() + 1, 0) + .data(GameListDir::GameDirRole) + .value())); + // move the treeview items + QList item = item_model->takeRow(row); + item_model->invisibleRootItem()->insertRow(row + 1, item); + tree_view->setExpanded(selected, game_dir.expanded); + }); + + connect(open_directory_location, &QAction::triggered, + [this, game_dir] { emit OpenDirectory(game_dir.path); }); } void GameList::LoadCompatibilityList() { @@ -403,14 +602,7 @@ void GameList::LoadCompatibilityList() { } } -void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { - const QFileInfo dir_info{dir_path}; - if (!dir_info.exists() || !dir_info.isDir()) { - LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toStdString()); - search_field->setFilterResult(0, 0); - return; - } - +void GameList::PopulateAsync(QList& game_dirs) { tree_view->setEnabled(false); // Update the columns in case UISettings has changed @@ -433,17 +625,19 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { // Delete any rows that might already exist if we're repopulating item_model->removeRows(0, item_model->rowCount()); + search_field->clear(); emit ShouldCancelWorker(); - GameListWorker* worker = - new GameListWorker(vfs, provider, dir_path, deep_scan, compatibility_list); + GameListWorker* worker = new GameListWorker(vfs, provider, game_dirs, compatibility_list); connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); + connect(worker, &GameListWorker::DirEntryReady, this, &GameList::AddDirEntry, + Qt::QueuedConnection); connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, Qt::QueuedConnection); - // Use DirectConnection here because worker->Cancel() is thread-safe and we want it to cancel - // without delay. + // Use DirectConnection here because worker->Cancel() is thread-safe and we want it to + // cancel without delay. connect(this, &GameList::ShouldCancelWorker, worker, &GameListWorker::Cancel, Qt::DirectConnection); @@ -471,10 +665,42 @@ const QStringList GameList::supported_file_extensions = { QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")}; void GameList::RefreshGameDirectory() { - if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) { + if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) { LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); - search_field->clear(); - PopulateAsync(UISettings::values.game_directory_path, - UISettings::values.game_directory_deepscan); + PopulateAsync(UISettings::values.game_dirs); } } + +GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} { + this->main_window = parent; + + connect(main_window, &GMainWindow::UpdateThemedIcons, this, + &GameListPlaceholder::onUpdateThemedIcons); + + layout = new QVBoxLayout; + image = new QLabel; + text = new QLabel; + layout->setAlignment(Qt::AlignCenter); + image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200)); + + text->setText(tr("Double-click to add a new folder to the game list ")); + QFont font = text->font(); + font.setPointSize(20); + text->setFont(font); + text->setAlignment(Qt::AlignHCenter); + image->setAlignment(Qt::AlignHCenter); + + layout->addWidget(image); + layout->addWidget(text); + setLayout(layout); +} + +GameListPlaceholder::~GameListPlaceholder() = default; + +void GameListPlaceholder::onUpdateThemedIcons() { + image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200)); +} + +void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) { + emit GameListPlaceholder::AddDirectory(); +} diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index f8f8bd6c5..a2b58aba5 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -19,10 +19,14 @@ #include #include "common/common_types.h" +#include "ui_settings.h" #include "yuzu/compatibility_list.h" class GameListWorker; class GameListSearchField; +template +class QList; +class GameListDir; class GMainWindow; namespace FileSys { @@ -52,12 +56,14 @@ public: FileSys::ManualContentProvider* provider, GMainWindow* parent = nullptr); ~GameList() override; + QString getLastFilterResultItem(); void clearFilter(); void setFilterFocus(); void setFilterVisible(bool visibility); + bool isEmpty(); void LoadCompatibilityList(); - void PopulateAsync(const QString& dir_path, bool deep_scan); + void PopulateAsync(QList& game_dirs); void SaveInterfaceLayout(); void LoadInterfaceLayout(); @@ -74,19 +80,29 @@ signals: void NavigateToGamedbEntryRequested(u64 program_id, const CompatibilityList& compatibility_list); void OpenPerGameGeneralRequested(const std::string& file); + void OpenDirectory(QString directory); + void AddDirectory(); + void ShowList(bool show); private slots: + void onItemExpanded(const QModelIndex& item); void onTextChanged(const QString& new_text); void onFilterCloseClicked(); + void onUpdateThemedIcons(); private: - void AddEntry(const QList& entry_items); + void AddDirEntry(GameListDir* entry_items); + void AddEntry(const QList& entry_items, GameListDir* parent); void ValidateEntry(const QModelIndex& item); void DonePopulating(QStringList watch_list); - void PopupContextMenu(const QPoint& menu_location); void RefreshGameDirectory(); + void PopupContextMenu(const QPoint& menu_location); + void AddGamePopup(QMenu& context_menu, u64 program_id, std::string path); + void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected); + void AddPermDirPopup(QMenu& context_menu, QModelIndex selected); + std::shared_ptr vfs; FileSys::ManualContentProvider* provider; GameListSearchField* search_field; @@ -102,3 +118,25 @@ private: }; Q_DECLARE_METATYPE(GameListOpenTarget); + +class GameListPlaceholder : public QWidget { + Q_OBJECT +public: + explicit GameListPlaceholder(GMainWindow* parent = nullptr); + ~GameListPlaceholder(); + +signals: + void AddDirectory(); + +private slots: + void onUpdateThemedIcons(); + +protected: + void mouseDoubleClickEvent(QMouseEvent* event) override; + +private: + GMainWindow* main_window = nullptr; + QVBoxLayout* layout = nullptr; + QLabel* image = nullptr; + QLabel* text = nullptr; +}; diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index ece534dd6..f5abb759d 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -22,6 +23,16 @@ #include "yuzu/uisettings.h" #include "yuzu/util/util.h" +enum class GameListItemType { + Game = QStandardItem::UserType + 1, + CustomDir = QStandardItem::UserType + 2, + InstalledDir = QStandardItem::UserType + 3, + SystemDir = QStandardItem::UserType + 4, + AddDir = QStandardItem::UserType + 5 +}; + +Q_DECLARE_METATYPE(GameListItemType); + /** * Gets the default icon (for games without valid title metadata) * @param size The desired width and height of the default icon. @@ -36,8 +47,13 @@ static QPixmap GetDefaultIcon(u32 size) { class GameListItem : public QStandardItem { public: + // used to access type from item index + static const int TypeRole = Qt::UserRole + 1; + static const int SortRole = Qt::UserRole + 2; GameListItem() = default; - explicit GameListItem(const QString& string) : QStandardItem(string) {} + GameListItem(const QString& string) : QStandardItem(string) { + setData(string, SortRole); + } }; /** @@ -48,14 +64,15 @@ public: */ class GameListItemPath : public GameListItem { public: - static const int FullPathRole = Qt::UserRole + 1; - static const int TitleRole = Qt::UserRole + 2; - static const int ProgramIdRole = Qt::UserRole + 3; - static const int FileTypeRole = Qt::UserRole + 4; + static const int TitleRole = SortRole; + static const int FullPathRole = SortRole + 1; + static const int ProgramIdRole = SortRole + 2; + static const int FileTypeRole = SortRole + 3; GameListItemPath() = default; GameListItemPath(const QString& game_path, const std::vector& picture_data, const QString& game_name, const QString& game_type, u64 program_id) { + setData(type(), TypeRole); setData(game_path, FullPathRole); setData(game_name, TitleRole); setData(qulonglong(program_id), ProgramIdRole); @@ -72,6 +89,10 @@ public: setData(picture, Qt::DecorationRole); } + int type() const override { + return static_cast(GameListItemType::Game); + } + QVariant data(int role) const override { if (role == Qt::DisplayRole) { std::string filename; @@ -103,9 +124,11 @@ public: class GameListItemCompat : public GameListItem { Q_DECLARE_TR_FUNCTIONS(GameListItemCompat) public: - static const int CompatNumberRole = Qt::UserRole + 1; + static const int CompatNumberRole = SortRole; GameListItemCompat() = default; explicit GameListItemCompat(const QString& compatibility) { + setData(type(), TypeRole); + struct CompatStatus { QString color; const char* text; @@ -135,6 +158,10 @@ public: setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole); } + int type() const override { + return static_cast(GameListItemType::Game); + } + bool operator<(const QStandardItem& other) const override { return data(CompatNumberRole) < other.data(CompatNumberRole); } @@ -146,12 +173,12 @@ public: * human-readable string representation will be displayed to the user. */ class GameListItemSize : public GameListItem { - public: - static const int SizeRole = Qt::UserRole + 1; + static const int SizeRole = SortRole; GameListItemSize() = default; explicit GameListItemSize(const qulonglong size_bytes) { + setData(type(), TypeRole); setData(size_bytes, SizeRole); } @@ -167,6 +194,10 @@ public: } } + int type() const override { + return static_cast(GameListItemType::Game); + } + /** * This operator is, in practice, only used by the TreeView sorting systems. * Override it so that it will correctly sort by numerical value instead of by string @@ -177,6 +208,67 @@ public: } }; +class GameListDir : public GameListItem { +public: + static const int GameDirRole = Qt::UserRole + 2; + + explicit GameListDir(UISettings::GameDir& directory, + GameListItemType dir_type = GameListItemType::CustomDir) + : dir_type{dir_type} { + setData(type(), TypeRole); + + UISettings::GameDir* game_dir = &directory; + setData(QVariant::fromValue(game_dir), GameDirRole); + + int icon_size = UISettings::values.icon_size; + switch (dir_type) { + case GameListItemType::InstalledDir: + setData(QIcon::fromTheme("sd_card").pixmap(icon_size).scaled( + icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), + Qt::DecorationRole); + setData("Installed Titles", Qt::DisplayRole); + break; + case GameListItemType::SystemDir: + setData(QIcon::fromTheme("chip").pixmap(icon_size).scaled( + icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), + Qt::DecorationRole); + setData("System Titles", Qt::DisplayRole); + break; + case GameListItemType::CustomDir: + QString icon_name = QFileInfo::exists(game_dir->path) ? "folder" : "bad_folder"; + setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled( + icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), + Qt::DecorationRole); + setData(game_dir->path, Qt::DisplayRole); + break; + }; + }; + + int type() const override { + return static_cast(dir_type); + } + +private: + GameListItemType dir_type; +}; + +class GameListAddDir : public GameListItem { +public: + explicit GameListAddDir() { + setData(type(), TypeRole); + + int icon_size = UISettings::values.icon_size; + setData(QIcon::fromTheme("plus").pixmap(icon_size).scaled( + icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), + Qt::DecorationRole); + setData("Add New Game Directory", Qt::DisplayRole); + } + + int type() const override { + return static_cast(GameListItemType::AddDir); + } +}; + class GameList; class QHBoxLayout; class QTreeView; @@ -195,6 +287,9 @@ public: void clear(); void setFocus(); + int visible; + int total; + private: class KeyReleaseEater : public QObject { public: diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 77f358630..8c6621c98 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -223,21 +223,38 @@ QList MakeGameListEntry(const std::string& path, const std::stri } // Anonymous namespace GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, - FileSys::ManualContentProvider* provider, QString dir_path, - bool deep_scan, const CompatibilityList& compatibility_list) - : vfs(std::move(vfs)), provider(provider), dir_path(std::move(dir_path)), deep_scan(deep_scan), + FileSys::ManualContentProvider* provider, + QList& game_dirs, + const CompatibilityList& compatibility_list) + : vfs(std::move(vfs)), provider(provider), game_dirs(game_dirs), compatibility_list(compatibility_list) {} GameListWorker::~GameListWorker() = default; -void GameListWorker::AddTitlesToGameList() { - const auto& cache = dynamic_cast( - Core::System::GetInstance().GetContentProvider()); - const auto installed_games = cache.ListEntriesFilterOrigin( - std::nullopt, FileSys::TitleType::Application, FileSys::ContentRecordType::Program); +void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { + using namespace FileSys; + + const auto& cache = + dynamic_cast(Core::System::GetInstance().GetContentProvider()); + + std::vector> installed_games; + installed_games = cache.ListEntriesFilterOrigin(std::nullopt, TitleType::Application, + ContentRecordType::Program); + if (parent_dir->type() == static_cast(GameListItemType::InstalledDir)) { + installed_games = cache.ListEntriesFilterOrigin( + ContentProviderUnionSlot::UserNAND, TitleType::Application, ContentRecordType::Program); + auto installed_sdmc_games = cache.ListEntriesFilterOrigin( + ContentProviderUnionSlot::SDMC, TitleType::Application, ContentRecordType::Program); + + installed_games.insert(installed_games.end(), installed_sdmc_games.begin(), + installed_sdmc_games.end()); + } else if (parent_dir->type() == static_cast(GameListItemType::SystemDir)) { + installed_games = cache.ListEntriesFilterOrigin( + ContentProviderUnionSlot::SysNAND, TitleType::Application, ContentRecordType::Program); + } for (const auto& [slot, game] : installed_games) { - if (slot == FileSys::ContentProviderUnionSlot::FrontendManual) + if (slot == ContentProviderUnionSlot::FrontendManual) continue; const auto file = cache.GetEntryUnparsed(game.title_id, game.type); @@ -250,21 +267,22 @@ void GameListWorker::AddTitlesToGameList() { u64 program_id = 0; loader->ReadProgramId(program_id); - const FileSys::PatchManager patch{program_id}; - const auto control = cache.GetEntry(game.title_id, FileSys::ContentRecordType::Control); + const PatchManager patch{program_id}; + const auto control = cache.GetEntry(game.title_id, ContentRecordType::Control); if (control != nullptr) GetMetadataFromControlNCA(patch, *control, icon, name); emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id, - compatibility_list, patch)); + compatibility_list, patch), + parent_dir); } } void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, - unsigned int recursion) { - const auto callback = [this, target, recursion](u64* num_entries_out, - const std::string& directory, - const std::string& virtual_name) -> bool { + unsigned int recursion, GameListDir* parent_dir) { + const auto callback = [this, target, recursion, + parent_dir](u64* num_entries_out, const std::string& directory, + const std::string& virtual_name) -> bool { if (stop_processing) { // Breaks the callback loop. return false; @@ -317,11 +335,12 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa const FileSys::PatchManager patch{program_id}; emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id, - compatibility_list, patch)); + compatibility_list, patch), + parent_dir); } } else if (is_dir && recursion > 0) { watch_list.append(QString::fromStdString(physical_name)); - ScanFileSystem(target, physical_name, recursion - 1); + ScanFileSystem(target, physical_name, recursion - 1, parent_dir); } return true; @@ -332,12 +351,28 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa void GameListWorker::run() { stop_processing = false; - watch_list.append(dir_path); - provider->ClearAllEntries(); - ScanFileSystem(ScanTarget::FillManualContentProvider, dir_path.toStdString(), - deep_scan ? 256 : 0); - AddTitlesToGameList(); - ScanFileSystem(ScanTarget::PopulateGameList, dir_path.toStdString(), deep_scan ? 256 : 0); + + for (UISettings::GameDir& game_dir : game_dirs) { + if (game_dir.path == "INSTALLED") { + GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::InstalledDir); + emit DirEntryReady({game_list_dir}); + AddTitlesToGameList(game_list_dir); + } else if (game_dir.path == "SYSTEM") { + GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::SystemDir); + emit DirEntryReady({game_list_dir}); + AddTitlesToGameList(game_list_dir); + } else { + watch_list.append(game_dir.path); + GameListDir* game_list_dir = new GameListDir(game_dir); + emit DirEntryReady({game_list_dir}); + provider->ClearAllEntries(); + ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), 2, + game_list_dir); + ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(), + game_dir.deep_scan ? 256 : 0, game_list_dir); + } + }; + emit Finished(watch_list); } diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h index 7c3074af9..46ec96516 100644 --- a/src/yuzu/game_list_worker.h +++ b/src/yuzu/game_list_worker.h @@ -33,9 +33,10 @@ class GameListWorker : public QObject, public QRunnable { Q_OBJECT public: - GameListWorker(std::shared_ptr vfs, - FileSys::ManualContentProvider* provider, QString dir_path, bool deep_scan, - const CompatibilityList& compatibility_list); + explicit GameListWorker(std::shared_ptr vfs, + FileSys::ManualContentProvider* provider, + QList& game_dirs, + const CompatibilityList& compatibility_list); ~GameListWorker() override; /// Starts the processing of directory tree information. @@ -48,31 +49,33 @@ signals: /** * The `EntryReady` signal is emitted once an entry has been prepared and is ready * to be added to the game list. - * @param entry_items a list with `QStandardItem`s that make up the columns of the new entry. + * @param entry_items a list with `QStandardItem`s that make up the columns of the new + * entry. */ - void EntryReady(QList entry_items); + void DirEntryReady(GameListDir* entry_items); + void EntryReady(QList entry_items, GameListDir* parent_dir); /** - * After the worker has traversed the game directory looking for entries, this signal is emitted - * with a list of folders that should be watched for changes as well. + * After the worker has traversed the game directory looking for entries, this signal is + * emitted with a list of folders that should be watched for changes as well. */ void Finished(QStringList watch_list); private: - void AddTitlesToGameList(); + void AddTitlesToGameList(GameListDir* parent_dir); enum class ScanTarget { FillManualContentProvider, PopulateGameList, }; - void ScanFileSystem(ScanTarget target, const std::string& dir_path, unsigned int recursion = 0); + void ScanFileSystem(ScanTarget target, const std::string& dir_path, unsigned int recursion, + GameListDir* parent_dir); std::shared_ptr vfs; FileSys::ManualContentProvider* provider; QStringList watch_list; - QString dir_path; - bool deep_scan; const CompatibilityList& compatibility_list; + QList& game_dirs; std::atomic_bool stop_processing; }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index ac57229d5..b2de9545b 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -216,8 +216,7 @@ GMainWindow::GMainWindow() OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); game_list->LoadCompatibilityList(); - game_list->PopulateAsync(UISettings::values.game_directory_path, - UISettings::values.game_directory_deepscan); + game_list->PopulateAsync(UISettings::values.game_dirs); // Show one-time "callout" messages to the user ShowTelemetryCallout(); @@ -427,6 +426,10 @@ void GMainWindow::InitializeWidgets() { game_list = new GameList(vfs, provider.get(), this); ui.horizontalLayout->addWidget(game_list); + game_list_placeholder = new GameListPlaceholder(this); + ui.horizontalLayout->addWidget(game_list_placeholder); + game_list_placeholder->setVisible(false); + loading_screen = new LoadingScreen(this); loading_screen->hide(); ui.horizontalLayout->addWidget(loading_screen); @@ -660,6 +663,7 @@ void GMainWindow::RestoreUIState() { void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); + connect(game_list, &GameList::OpenDirectory, this, &GMainWindow::OnGameListOpenDirectory); connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this, &GMainWindow::OnTransferableShaderCacheOpenFile); @@ -667,6 +671,11 @@ void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, &GMainWindow::OnGameListNavigateToGamedbEntry); + connect(game_list, &GameList::AddDirectory, this, &GMainWindow::OnGameListAddDirectory); + connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this, + &GMainWindow::OnGameListAddDirectory); + connect(game_list, &GameList::ShowList, this, &GMainWindow::OnGameListShowList); + connect(game_list, &GameList::OpenPerGameGeneralRequested, this, &GMainWindow::OnGameListOpenPerGameProperties); @@ -684,8 +693,6 @@ void GMainWindow::ConnectMenuEvents() { connect(ui.action_Load_Folder, &QAction::triggered, this, &GMainWindow::OnMenuLoadFolder); connect(ui.action_Install_File_NAND, &QAction::triggered, this, &GMainWindow::OnMenuInstallToNAND); - connect(ui.action_Select_Game_List_Root, &QAction::triggered, this, - &GMainWindow::OnMenuSelectGameListRoot); connect(ui.action_Select_NAND_Directory, &QAction::triggered, this, [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::NAND); }); connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this, @@ -950,6 +957,7 @@ void GMainWindow::BootGame(const QString& filename) { // Update the GUI if (ui.action_Single_Window_Mode->isChecked()) { game_list->hide(); + game_list_placeholder->hide(); } status_bar_update_timer.start(2000); @@ -1007,7 +1015,10 @@ void GMainWindow::ShutdownGame() { render_window->hide(); loading_screen->hide(); loading_screen->Clear(); - game_list->show(); + if (game_list->isEmpty()) + game_list_placeholder->show(); + else + game_list->show(); game_list->setFilterFocus(); UpdateWindowTitle(); @@ -1298,6 +1309,45 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); } +void GMainWindow::OnGameListOpenDirectory(QString directory) { + QString path; + if (directory == QStringLiteral("INSTALLED")) { + // TODO: Find a better solution when installing files to the SD card gets implemented + path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir).c_str() + + std::string("user/Contents/registered")); + } else if (directory == QStringLiteral("SYSTEM")) { + path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir).c_str() + + std::string("system/Contents/registered")); + } else { + path = directory; + } + if (!QFileInfo::exists(path)) { + QMessageBox::critical(this, tr("Error Opening %1").arg(path), tr("Folder does not exist!")); + return; + } + QDesktopServices::openUrl(QUrl::fromLocalFile(path)); +} + +void GMainWindow::OnGameListAddDirectory() { + QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); + if (dir_path.isEmpty()) + return; + UISettings::GameDir game_dir{dir_path, false, true}; + if (!UISettings::values.game_dirs.contains(game_dir)) { + UISettings::values.game_dirs.append(game_dir); + game_list->PopulateAsync(UISettings::values.game_dirs); + } else { + LOG_WARNING(Frontend, "Selected directory is already in the game list"); + } +} + +void GMainWindow::OnGameListShowList(bool show) { + if (emulation_running && ui.action_Single_Window_Mode->isChecked()) + return; + game_list->setVisible(show); + game_list_placeholder->setVisible(!show); +}; + void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { u64 title_id{}; const auto v_file = Core::GetGameFileFromPath(vfs, file); @@ -1316,8 +1366,7 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); if (reload) { - game_list->PopulateAsync(UISettings::values.game_directory_path, - UISettings::values.game_directory_deepscan); + game_list->PopulateAsync(UISettings::values.game_dirs); } config->Save(); @@ -1407,8 +1456,7 @@ void GMainWindow::OnMenuInstallToNAND() { const auto success = [this]() { QMessageBox::information(this, tr("Successfully Installed"), tr("The file was successfully installed.")); - game_list->PopulateAsync(UISettings::values.game_directory_path, - UISettings::values.game_directory_deepscan); + game_list->PopulateAsync(UISettings::values.game_dirs); FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list"); }; @@ -1533,14 +1581,6 @@ void GMainWindow::OnMenuInstallToNAND() { } } -void GMainWindow::OnMenuSelectGameListRoot() { - QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); - if (!dir_path.isEmpty()) { - UISettings::values.game_directory_path = dir_path; - game_list->PopulateAsync(dir_path, UISettings::values.game_directory_deepscan); - } -} - void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target) { const auto res = QMessageBox::information( this, tr("Changing Emulated Directory"), @@ -1559,8 +1599,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target) : FileUtil::UserPath::NANDDir, dir_path.toStdString()); Service::FileSystem::CreateFactories(*vfs); - game_list->PopulateAsync(UISettings::values.game_directory_path, - UISettings::values.game_directory_deepscan); + game_list->PopulateAsync(UISettings::values.game_dirs); } } @@ -1724,11 +1763,11 @@ void GMainWindow::OnConfigure() { if (UISettings::values.enable_discord_presence != old_discord_presence) { SetDiscordEnabled(UISettings::values.enable_discord_presence); } + emit UpdateThemedIcons(); const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); if (reload) { - game_list->PopulateAsync(UISettings::values.game_directory_path, - UISettings::values.game_directory_deepscan); + game_list->PopulateAsync(UISettings::values.game_dirs); } config->Save(); @@ -1992,8 +2031,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { Service::FileSystem::CreateFactories(*vfs); if (behavior == ReinitializeKeyBehavior::Warning) { - game_list->PopulateAsync(UISettings::values.game_directory_path, - UISettings::values.game_directory_deepscan); + game_list->PopulateAsync(UISettings::values.game_dirs); } } @@ -2158,7 +2196,6 @@ void GMainWindow::UpdateUITheme() { } QIcon::setThemeSearchPaths(theme_paths); - emit UpdateThemedIcons(); } void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 501608ddc..b7398b6c7 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -30,6 +30,7 @@ class ProfilerWidget; class QLabel; class WaitTreeWidget; enum class GameListOpenTarget; +class GameListPlaceholder; namespace Core::Frontend { struct SoftwareKeyboardParameters; @@ -186,12 +187,13 @@ private slots: void OnGameListCopyTID(u64 program_id); void OnGameListNavigateToGamedbEntry(u64 program_id, const CompatibilityList& compatibility_list); + void OnGameListOpenDirectory(QString path); + void OnGameListAddDirectory(); + void OnGameListShowList(bool show); void OnGameListOpenPerGameProperties(const std::string& file); void OnMenuLoadFile(); void OnMenuLoadFolder(); void OnMenuInstallToNAND(); - /// Called whenever a user selects the "File->Select Game List Root" menu item - void OnMenuSelectGameListRoot(); /// Called whenever a user select the "File->Select -- Directory" where -- is NAND or SD Card void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target); void OnMenuRecentFile(); @@ -223,6 +225,8 @@ private: GameList* game_list; LoadingScreen* loading_screen; + GameListPlaceholder* game_list_placeholder; + // Status bar elements QLabel* message_label = nullptr; QLabel* emu_speed_label = nullptr; diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index ffcabb495..a1ce3c0c3 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -62,7 +62,6 @@ - diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index a62cd6911..76348db69 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include "common/common_types.h" @@ -25,6 +26,18 @@ struct Shortcut { using Themes = std::array, 2>; extern const Themes themes; +struct GameDir { + QString path; + bool deep_scan; + bool expanded; + bool operator==(const GameDir& rhs) const { + return path == rhs.path; + }; + bool operator!=(const GameDir& rhs) const { + return !operator==(rhs); + }; +}; + struct Values { QByteArray geometry; QByteArray state; @@ -55,8 +68,9 @@ struct Values { QString roms_path; QString symbols_path; QString screenshot_path; - QString game_directory_path; - bool game_directory_deepscan; + QString game_dir_deprecated; + bool game_dir_deprecated_deepscan; + QList game_dirs; QStringList recent_files; QString theme; @@ -84,3 +98,5 @@ struct Values { extern Values values; } // namespace UISettings + +Q_DECLARE_METATYPE(UISettings::GameDir*); From 7a8f4840205799d837ac32401b4143c716a8bc3d Mon Sep 17 00:00:00 2001 From: fearlessTobi Date: Fri, 3 May 2019 19:21:57 +0200 Subject: [PATCH 3/8] Address trivial review comments --- src/yuzu/configuration/config.cpp | 4 +- src/yuzu/game_list.cpp | 71 ++++++++++++++++--------------- src/yuzu/game_list.h | 2 +- src/yuzu/game_list_p.h | 21 +++++---- src/yuzu/game_list_worker.cpp | 6 +-- src/yuzu/main.cpp | 6 +-- src/yuzu/main.h | 2 +- 7 files changed, 59 insertions(+), 53 deletions(-) diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index f2f116a87..b2683faf8 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -521,7 +521,7 @@ void Config::ReadPathValues() { ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString(); UISettings::values.game_dir_deprecated_deepscan = ReadSetting(QStringLiteral("gameListDeepScan"), false).toBool(); - int gamedirs_size = qt_config->beginReadArray(QStringLiteral("gamedirs")); + const int gamedirs_size = qt_config->beginReadArray(QStringLiteral("gamedirs")); for (int i = 0; i < gamedirs_size; ++i) { qt_config->setArrayIndex(i); UISettings::GameDir game_dir; @@ -927,7 +927,7 @@ void Config::SavePathValues() { qt_config->beginWriteArray(QStringLiteral("gamedirs")); for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { qt_config->setArrayIndex(i); - const auto& game_dir = UISettings::values.game_dirs.at(i); + const auto& game_dir = UISettings::values.game_dirs[i]; WriteSetting(QStringLiteral("path"), game_dir.path); WriteSetting(QStringLiteral("deep_scan"), game_dir.deep_scan, false); WriteSetting(QStringLiteral("expanded"), game_dir.expanded, true); diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 65947c59b..e5627abd4 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -87,12 +87,12 @@ QString GameList::getLastFilterResultItem() { QStandardItem* folder; QStandardItem* child; QString file_path; - int folder_count = item_model->rowCount(); + const int folder_count = item_model->rowCount(); for (int i = 0; i < folder_count; ++i) { folder = item_model->item(i, 0); - QModelIndex folder_index = folder->index(); - int childrenCount = folder->rowCount(); - for (int j = 0; j < childrenCount; ++j) { + 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(); @@ -160,7 +160,7 @@ 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) { - GameListItemType type = item.data(GameListItem::TypeRole).value(); + const auto type = item.data(GameListItem::TypeRole).value(); if (type == GameListItemType::CustomDir || type == GameListItemType::InstalledDir || type == GameListItemType::SystemDir) item.data(GameListDir::GameDirRole).value()->expanded = @@ -169,11 +169,11 @@ void GameList::onItemExpanded(const QModelIndex& item) { // Event in order to filter the gamelist after editing the searchfield void GameList::onTextChanged(const QString& new_text) { - int folder_count = tree_view->model()->rowCount(); + const int folder_count = tree_view->model()->rowCount(); QString edit_filter_text = new_text.toLower(); QStandardItem* folder; QStandardItem* child; - int childrenTotal = 0; + int children_total = 0; QModelIndex root_index = item_model->invisibleRootItem()->index(); // If the searchfield is empty every item is visible @@ -181,22 +181,22 @@ void GameList::onTextChanged(const QString& new_text) { if (edit_filter_text.isEmpty()) { for (int i = 0; i < folder_count; ++i) { folder = item_model->item(i, 0); - QModelIndex folder_index = folder->index(); - int childrenCount = folder->rowCount(); - for (int j = 0; j < childrenCount; ++j) { - ++childrenTotal; + const QModelIndex folder_index = folder->index(); + const int children_count = folder->rowCount(); + for (int j = 0; j < children_count; ++j) { + ++children_total; tree_view->setRowHidden(j, folder_index, false); } } - search_field->setFilterResult(childrenTotal, childrenTotal); + search_field->setFilterResult(children_total, children_total); } else { int result_count = 0; for (int i = 0; i < folder_count; ++i) { folder = item_model->item(i, 0); - QModelIndex folder_index = folder->index(); - int childrenCount = folder->rowCount(); - for (int j = 0; j < childrenCount; ++j) { - ++childrenTotal; + 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); const QString file_path = child->data(GameListItemPath::FullPathRole).toString().toLower(); @@ -220,7 +220,7 @@ void GameList::onTextChanged(const QString& new_text) { } else { tree_view->setRowHidden(j, folder_index, true); } - search_field->setFilterResult(result_count, childrenTotal); + search_field->setFilterResult(result_count, children_total); } } } @@ -230,7 +230,7 @@ void GameList::onUpdateThemedIcons() { for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) { QStandardItem* child = item_model->invisibleRootItem()->child(i); - int icon_size = UISettings::values.icon_size; + const int icon_size = UISettings::values.icon_size; switch (child->data(GameListItem::TypeRole).value()) { case GameListItemType::InstalledDir: child->setData( @@ -249,8 +249,9 @@ void GameList::onUpdateThemedIcons() { case GameListItemType::CustomDir: { const UISettings::GameDir* game_dir = child->data(GameListDir::GameDirRole).value(); - QString icon_name = QFileInfo::exists(game_dir->path) ? QStringLiteral("folder") - : QStringLiteral("bad_folder"); + const QString icon_name = QFileInfo::exists(game_dir->path) + ? QStringLiteral("folder") + : QStringLiteral("bad_folder"); child->setData( QIcon::fromTheme(icon_name).pixmap(icon_size).scaled( icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), @@ -357,14 +358,14 @@ void GameList::AddEntry(const QList& entry_items, GameListDir* p } void GameList::ValidateEntry(const QModelIndex& item) { - auto selected = item.sibling(item.row(), 0); + const auto selected = item.sibling(item.row(), 0); switch (selected.data(GameListItem::TypeRole).value()) { case GameListItemType::Game: { - QString file_path = selected.data(GameListItemPath::FullPathRole).toString(); + const QString file_path = selected.data(GameListItemPath::FullPathRole).toString(); if (file_path.isEmpty()) return; - QFileInfo file_info(file_path); + const QFileInfo file_info(file_path); if (!file_info.exists()) return; @@ -391,7 +392,7 @@ void GameList::ValidateEntry(const QModelIndex& item) { bool GameList::isEmpty() { for (int i = 0; i < item_model->rowCount(); i++) { const QStandardItem* child = item_model->invisibleRootItem()->child(i); - GameListItemType type = static_cast(child->type()); + const auto type = static_cast(child->type()); if (!child->hasChildren() && (type == GameListItemType::InstalledDir || type == GameListItemType::SystemDir)) { item_model->invisibleRootItem()->removeRow(child->row()); @@ -422,16 +423,16 @@ void GameList::DonePopulating(QStringList watch_list) { QCoreApplication::processEvents(); } tree_view->setEnabled(true); - int folder_count = tree_view->model()->rowCount(); - int childrenTotal = 0; + const int folder_count = tree_view->model()->rowCount(); + int children_total = 0; for (int i = 0; i < folder_count; ++i) { - int childrenCount = item_model->item(i, 0)->rowCount(); - for (int j = 0; j < childrenCount; ++j) { - ++childrenTotal; + int children_count = item_model->item(i, 0)->rowCount(); + for (int j = 0; j < children_count; ++j) { + ++children_total; } } - search_field->setFilterResult(childrenTotal, childrenTotal); - if (childrenTotal > 0) { + search_field->setFilterResult(children_total, children_total); + if (children_total > 0) { search_field->setFocus(); } } @@ -441,7 +442,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { if (!item.isValid()) return; - auto selected = item.sibling(item.row(), 0); + const auto selected = item.sibling(item.row(), 0); QMenu context_menu; switch (selected.data(GameListItem::TypeRole).value()) { case GameListItemType::Game: @@ -523,7 +524,7 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { QAction* move_down = context_menu.addAction(tr(u8"\U000025bc Move Down ")); QAction* open_directory_location = context_menu.addAction(tr("Open Directory Location")); - int row = selected.row(); + const int row = selected.row(); move_up->setEnabled(row > 0); move_down->setEnabled(row < item_model->rowCount() - 2); @@ -532,7 +533,7 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { // find the indices of the items in settings and swap them UISettings::values.game_dirs.swap( UISettings::values.game_dirs.indexOf(game_dir), - UISettings::values.game_dirs.indexOf(*selected.sibling(selected.row() - 1, 0) + UISettings::values.game_dirs.indexOf(*selected.sibling(row - 1, 0) .data(GameListDir::GameDirRole) .value())); // move the treeview items @@ -549,7 +550,7 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { .data(GameListDir::GameDirRole) .value())); // move the treeview items - QList item = item_model->takeRow(row); + const QList item = item_model->takeRow(row); item_model->invisibleRootItem()->insertRow(row + 1, item); tree_view->setExpanded(selected, game_dir.expanded); }); diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index a2b58aba5..cf5bd3a39 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -80,7 +80,7 @@ signals: void NavigateToGamedbEntryRequested(u64 program_id, const CompatibilityList& compatibility_list); void OpenPerGameGeneralRequested(const std::string& file); - void OpenDirectory(QString directory); + void OpenDirectory(const QString& directory); void AddDirectory(); void ShowList(bool show); diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index f5abb759d..13623f526 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -220,12 +220,14 @@ public: UISettings::GameDir* game_dir = &directory; setData(QVariant::fromValue(game_dir), GameDirRole); - int icon_size = UISettings::values.icon_size; + const int icon_size = UISettings::values.icon_size; switch (dir_type) { case GameListItemType::InstalledDir: - setData(QIcon::fromTheme("sd_card").pixmap(icon_size).scaled( - icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - Qt::DecorationRole); + setData( + QIcon::fromTheme(QStringLiteral("sd_card")) + .pixmap(icon_size) + .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), + Qt::DecorationRole); setData("Installed Titles", Qt::DisplayRole); break; case GameListItemType::SystemDir: @@ -235,7 +237,9 @@ public: setData("System Titles", Qt::DisplayRole); break; case GameListItemType::CustomDir: - QString icon_name = QFileInfo::exists(game_dir->path) ? "folder" : "bad_folder"; + const QString icon_name = QFileInfo::exists(game_dir->path) + ? QStringLiteral("folder") + : QStringLiteral("bad_folder"); setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled( icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::DecorationRole); @@ -257,9 +261,10 @@ public: explicit GameListAddDir() { setData(type(), TypeRole); - int icon_size = UISettings::values.icon_size; - setData(QIcon::fromTheme("plus").pixmap(icon_size).scaled( - icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), + const int icon_size = UISettings::values.icon_size; + setData(QIcon::fromTheme(QStringLiteral("plus")) + .pixmap(icon_size) + .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::DecorationRole); setData("Add New Game Directory", Qt::DisplayRole); } diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 8c6621c98..e1e69bc1a 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -354,16 +354,16 @@ void GameListWorker::run() { for (UISettings::GameDir& game_dir : game_dirs) { if (game_dir.path == "INSTALLED") { - GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::InstalledDir); + auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::InstalledDir); emit DirEntryReady({game_list_dir}); AddTitlesToGameList(game_list_dir); } else if (game_dir.path == "SYSTEM") { - GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::SystemDir); + auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SystemDir); emit DirEntryReady({game_list_dir}); AddTitlesToGameList(game_list_dir); } else { watch_list.append(game_dir.path); - GameListDir* game_list_dir = new GameListDir(game_dir); + auto* const game_list_dir = new GameListDir(game_dir); emit DirEntryReady({game_list_dir}); provider->ClearAllEntries(); ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), 2, diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index b2de9545b..3146e054c 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1309,11 +1309,11 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); } -void GMainWindow::OnGameListOpenDirectory(QString directory) { +void GMainWindow::OnGameListOpenDirectory(const QString& directory) { QString path; if (directory == QStringLiteral("INSTALLED")) { // TODO: Find a better solution when installing files to the SD card gets implemented - path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir).c_str() + + path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + std::string("user/Contents/registered")); } else if (directory == QStringLiteral("SYSTEM")) { path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir).c_str() + @@ -1329,7 +1329,7 @@ void GMainWindow::OnGameListOpenDirectory(QString directory) { } void GMainWindow::OnGameListAddDirectory() { - QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); + const QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); if (dir_path.isEmpty()) return; UISettings::GameDir game_dir{dir_path, false, true}; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index b7398b6c7..7d16188cb 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -187,7 +187,7 @@ private slots: void OnGameListCopyTID(u64 program_id); void OnGameListNavigateToGamedbEntry(u64 program_id, const CompatibilityList& compatibility_list); - void OnGameListOpenDirectory(QString path); + void OnGameListOpenDirectory(const QString& directory); void OnGameListAddDirectory(); void OnGameListShowList(bool show); void OnGameListOpenPerGameProperties(const std::string& file); From dfec9c9a437b7478abd8b280f6ce513da595ba73 Mon Sep 17 00:00:00 2001 From: fearlessTobi Date: Sun, 5 May 2019 01:52:17 +0200 Subject: [PATCH 4/8] Address more trivial review comments --- src/yuzu/game_list.cpp | 17 ++++++----------- src/yuzu/game_list.h | 8 +++----- src/yuzu/game_list_p.h | 12 ++++++------ src/yuzu/main.cpp | 6 +++--- 4 files changed, 18 insertions(+), 25 deletions(-) diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index e5627abd4..51ced635b 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -83,7 +83,7 @@ void GameListSearchField::setFilterResult(int visible, int total) { label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible)); } -QString GameList::getLastFilterResultItem() { +QString GameList::getLastFilterResultItem() const { QStandardItem* folder; QStandardItem* child; QString file_path; @@ -389,7 +389,7 @@ void GameList::ValidateEntry(const QModelIndex& item) { } } -bool GameList::isEmpty() { +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(child->type()); @@ -426,10 +426,7 @@ void GameList::DonePopulating(QStringList watch_list) { const int folder_count = tree_view->model()->rowCount(); int children_total = 0; for (int i = 0; i < folder_count; ++i) { - int children_count = item_model->item(i, 0)->rowCount(); - for (int j = 0; j < children_count; ++j) { - ++children_total; - } + children_total += item_model->item(i, 0)->rowCount(); } search_field->setFilterResult(children_total, children_total); if (children_total > 0) { @@ -546,7 +543,7 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { // find the indices of the items in settings and swap them UISettings::values.game_dirs.swap( UISettings::values.game_dirs.indexOf(game_dir), - UISettings::values.game_dirs.indexOf(*selected.sibling(selected.row() + 1, 0) + UISettings::values.game_dirs.indexOf(*selected.sibling(row + 1, 0) .data(GameListDir::GameDirRole) .value())); // move the treeview items @@ -673,9 +670,7 @@ void GameList::RefreshGameDirectory() { } GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} { - this->main_window = parent; - - connect(main_window, &GMainWindow::UpdateThemedIcons, this, + connect(parent, &GMainWindow::UpdateThemedIcons, this, &GameListPlaceholder::onUpdateThemedIcons); layout = new QVBoxLayout; @@ -684,7 +679,7 @@ GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} layout->setAlignment(Qt::AlignCenter); image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200)); - text->setText(tr("Double-click to add a new folder to the game list ")); + text->setText(tr("Double-click to add a new folder to the game list")); QFont font = text->font(); font.setPointSize(20); text->setFont(font); diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index cf5bd3a39..7ed77fd9c 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -24,8 +25,6 @@ class GameListWorker; class GameListSearchField; -template -class QList; class GameListDir; class GMainWindow; @@ -56,11 +55,11 @@ public: FileSys::ManualContentProvider* provider, GMainWindow* parent = nullptr); ~GameList() override; - QString getLastFilterResultItem(); + QString getLastFilterResultItem() const; void clearFilter(); void setFilterFocus(); void setFilterVisible(bool visibility); - bool isEmpty(); + bool isEmpty() const; void LoadCompatibilityList(); void PopulateAsync(QList& game_dirs); @@ -135,7 +134,6 @@ protected: void mouseDoubleClickEvent(QMouseEvent* event) override; private: - GMainWindow* main_window = nullptr; QVBoxLayout* layout = nullptr; QLabel* image = nullptr; QLabel* text = nullptr; diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 13623f526..047061e6c 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -228,13 +228,13 @@ public: .pixmap(icon_size) .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::DecorationRole); - setData("Installed Titles", Qt::DisplayRole); + setData(QObject::tr("Installed Titles"), Qt::DisplayRole); break; case GameListItemType::SystemDir: setData(QIcon::fromTheme("chip").pixmap(icon_size).scaled( icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::DecorationRole); - setData("System Titles", Qt::DisplayRole); + setData(QObject::tr("System Titles"), Qt::DisplayRole); break; case GameListItemType::CustomDir: const QString icon_name = QFileInfo::exists(game_dir->path) @@ -266,7 +266,7 @@ public: .pixmap(icon_size) .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::DecorationRole); - setData("Add New Game Directory", Qt::DisplayRole); + setData(QObject::tr("Add New Game Directory"), Qt::DisplayRole); } int type() const override { @@ -292,9 +292,6 @@ public: void clear(); void setFocus(); - int visible; - int total; - private: class KeyReleaseEater : public QObject { public: @@ -308,6 +305,9 @@ private: // EventFilter in order to process systemkeys while editing the searchfield bool eventFilter(QObject* obj, QEvent* event) override; }; + int visible; + int total; + QHBoxLayout* layout_filter = nullptr; QTreeView* tree_view = nullptr; QLabel* label_filter = nullptr; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 3146e054c..72c3eb069 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1314,10 +1314,10 @@ void GMainWindow::OnGameListOpenDirectory(const QString& directory) { if (directory == QStringLiteral("INSTALLED")) { // TODO: Find a better solution when installing files to the SD card gets implemented path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + - std::string("user/Contents/registered")); + "user/Contents/registered"); } else if (directory == QStringLiteral("SYSTEM")) { - path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir).c_str() + - std::string("system/Contents/registered")); + path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + + "system/Contents/registered"); } else { path = directory; } From 5aaafa6a56101a18759264bbf1ef9293d424f899 Mon Sep 17 00:00:00 2001 From: fearlessTobi Date: Sun, 5 May 2019 03:07:09 +0200 Subject: [PATCH 5/8] Separate UserNand and Sdmc directories --- src/yuzu/configuration/config.cpp | 6 ++++-- src/yuzu/game_list.cpp | 23 ++++++++++++++++------- src/yuzu/game_list_p.h | 29 ++++++++++++++++++++--------- src/yuzu/game_list_worker.cpp | 25 ++++++++++++++----------- src/yuzu/main.cpp | 8 +++++--- 5 files changed, 59 insertions(+), 32 deletions(-) diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index b2683faf8..f594106bf 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -535,10 +535,12 @@ void Config::ReadPathValues() { // also carries over old game list settings if present if (UISettings::values.game_dirs.isEmpty()) { UISettings::GameDir game_dir; - game_dir.path = QStringLiteral("INSTALLED"); + game_dir.path = QStringLiteral("SDMC"); game_dir.expanded = true; UISettings::values.game_dirs.append(game_dir); - game_dir.path = QStringLiteral("SYSTEM"); + game_dir.path = QStringLiteral("UserNAND"); + UISettings::values.game_dirs.append(game_dir); + game_dir.path = QStringLiteral("SysNAND"); UISettings::values.game_dirs.append(game_dir); if (UISettings::values.game_dir_deprecated != QStringLiteral(".")) { game_dir.path = UISettings::values.game_dir_deprecated; diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 51ced635b..cab982385 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -161,8 +161,8 @@ 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) { const auto type = item.data(GameListItem::TypeRole).value(); - if (type == GameListItemType::CustomDir || type == GameListItemType::InstalledDir || - type == GameListItemType::SystemDir) + if (type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir || + type == GameListItemType::UserNandDir || type == GameListItemType::SysNandDir) item.data(GameListDir::GameDirRole).value()->expanded = tree_view->isExpanded(item); } @@ -232,14 +232,21 @@ void GameList::onUpdateThemedIcons() { const int icon_size = UISettings::values.icon_size; switch (child->data(GameListItem::TypeRole).value()) { - case GameListItemType::InstalledDir: + case GameListItemType::SdmcDir: child->setData( QIcon::fromTheme(QStringLiteral("sd_card")) .pixmap(icon_size) .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::DecorationRole); break; - case GameListItemType::SystemDir: + case GameListItemType::UserNandDir: + child->setData( + QIcon::fromTheme(QStringLiteral("chip")) + .pixmap(icon_size) + .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), + Qt::DecorationRole); + break; + case GameListItemType::SysNandDir: child->setData( QIcon::fromTheme(QStringLiteral("chip")) .pixmap(icon_size) @@ -394,7 +401,8 @@ bool GameList::isEmpty() const { const QStandardItem* child = item_model->invisibleRootItem()->child(i); const auto type = static_cast(child->type()); if (!child->hasChildren() && - (type == GameListItemType::InstalledDir || type == GameListItemType::SystemDir)) { + (type == GameListItemType::SdmcDir || type == GameListItemType::UserNandDir || + type == GameListItemType::SysNandDir)) { item_model->invisibleRootItem()->removeRow(child->row()); i--; }; @@ -450,8 +458,9 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { AddPermDirPopup(context_menu, selected); AddCustomDirPopup(context_menu, selected); break; - case GameListItemType::InstalledDir: - case GameListItemType::SystemDir: + case GameListItemType::SdmcDir: + case GameListItemType::UserNandDir: + case GameListItemType::SysNandDir: AddPermDirPopup(context_menu, selected); break; } diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 047061e6c..87eb71c17 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -26,9 +26,10 @@ enum class GameListItemType { Game = QStandardItem::UserType + 1, CustomDir = QStandardItem::UserType + 2, - InstalledDir = QStandardItem::UserType + 3, - SystemDir = QStandardItem::UserType + 4, - AddDir = QStandardItem::UserType + 5 + SdmcDir = QStandardItem::UserType + 3, + UserNandDir = QStandardItem::UserType + 4, + SysNandDir = QStandardItem::UserType + 5, + AddDir = QStandardItem::UserType + 6 }; Q_DECLARE_METATYPE(GameListItemType); @@ -222,18 +223,28 @@ public: const int icon_size = UISettings::values.icon_size; switch (dir_type) { - case GameListItemType::InstalledDir: + case GameListItemType::SdmcDir: setData( QIcon::fromTheme(QStringLiteral("sd_card")) .pixmap(icon_size) .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::DecorationRole); - setData(QObject::tr("Installed Titles"), Qt::DisplayRole); + setData(QObject::tr("Installed SD Titles"), Qt::DisplayRole); break; - case GameListItemType::SystemDir: - setData(QIcon::fromTheme("chip").pixmap(icon_size).scaled( - icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - Qt::DecorationRole); + case GameListItemType::UserNandDir: + setData( + QIcon::fromTheme(QStringLiteral("chip")) + .pixmap(icon_size) + .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), + Qt::DecorationRole); + setData(QObject::tr("Installed NAND Titles"), Qt::DisplayRole); + break; + case GameListItemType::SysNandDir: + setData( + QIcon::fromTheme(QStringLiteral("chip")) + .pixmap(icon_size) + .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), + Qt::DecorationRole); setData(QObject::tr("System Titles"), Qt::DisplayRole); break; case GameListItemType::CustomDir: diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index e1e69bc1a..c715bcef4 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -240,15 +240,14 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { std::vector> installed_games; installed_games = cache.ListEntriesFilterOrigin(std::nullopt, TitleType::Application, ContentRecordType::Program); - if (parent_dir->type() == static_cast(GameListItemType::InstalledDir)) { + + if (parent_dir->type() == static_cast(GameListItemType::SdmcDir)) { + installed_games = cache.ListEntriesFilterOrigin( + ContentProviderUnionSlot::SDMC, TitleType::Application, ContentRecordType::Program); + } else if (parent_dir->type() == static_cast(GameListItemType::UserNandDir)) { installed_games = cache.ListEntriesFilterOrigin( ContentProviderUnionSlot::UserNAND, TitleType::Application, ContentRecordType::Program); - auto installed_sdmc_games = cache.ListEntriesFilterOrigin( - ContentProviderUnionSlot::SDMC, TitleType::Application, ContentRecordType::Program); - - installed_games.insert(installed_games.end(), installed_sdmc_games.begin(), - installed_sdmc_games.end()); - } else if (parent_dir->type() == static_cast(GameListItemType::SystemDir)) { + } else if (parent_dir->type() == static_cast(GameListItemType::SysNandDir)) { installed_games = cache.ListEntriesFilterOrigin( ContentProviderUnionSlot::SysNAND, TitleType::Application, ContentRecordType::Program); } @@ -353,12 +352,16 @@ void GameListWorker::run() { stop_processing = false; for (UISettings::GameDir& game_dir : game_dirs) { - if (game_dir.path == "INSTALLED") { - auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::InstalledDir); + if (game_dir.path == QStringLiteral("SDMC")) { + auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir); emit DirEntryReady({game_list_dir}); AddTitlesToGameList(game_list_dir); - } else if (game_dir.path == "SYSTEM") { - auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SystemDir); + } else if (game_dir.path == QStringLiteral("UserNAND")) { + auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir); + emit DirEntryReady({game_list_dir}); + AddTitlesToGameList(game_list_dir); + } else if (game_dir.path == QStringLiteral("SysNAND")) { + auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir); emit DirEntryReady({game_list_dir}); AddTitlesToGameList(game_list_dir); } else { diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 72c3eb069..6d249cb3e 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1311,11 +1311,13 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, void GMainWindow::OnGameListOpenDirectory(const QString& directory) { QString path; - if (directory == QStringLiteral("INSTALLED")) { - // TODO: Find a better solution when installing files to the SD card gets implemented + if (directory == QStringLiteral("SDMC")) { + path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + + "Nintendo/Contents/registered"); + } else if (directory == QStringLiteral("UserNAND")) { path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "user/Contents/registered"); - } else if (directory == QStringLiteral("SYSTEM")) { + } else if (directory == QStringLiteral("SysNAND")) { path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "system/Contents/registered"); } else { From 13891fd62dc67292c9157f52b5a1bad1541f120e Mon Sep 17 00:00:00 2001 From: fearlessTobi Date: Sat, 8 Jun 2019 00:51:58 +0200 Subject: [PATCH 6/8] Change QList to QVector --- src/yuzu/game_list.cpp | 22 +++++++++++----------- src/yuzu/game_list.h | 3 ++- src/yuzu/game_list_worker.cpp | 2 +- src/yuzu/game_list_worker.h | 5 +++-- src/yuzu/uisettings.h | 3 ++- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index cab982385..c525d3f17 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -537,11 +537,11 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { connect(move_up, &QAction::triggered, [this, selected, row, &game_dir] { // find the indices of the items in settings and swap them - UISettings::values.game_dirs.swap( - UISettings::values.game_dirs.indexOf(game_dir), - UISettings::values.game_dirs.indexOf(*selected.sibling(row - 1, 0) - .data(GameListDir::GameDirRole) - .value())); + std::swap(UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf(game_dir)], + UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf( + *selected.sibling(row - 1, 0) + .data(GameListDir::GameDirRole) + .value())]); // move the treeview items QList item = item_model->takeRow(row); item_model->invisibleRootItem()->insertRow(row - 1, item); @@ -550,11 +550,11 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { connect(move_down, &QAction::triggered, [this, selected, row, &game_dir] { // find the indices of the items in settings and swap them - UISettings::values.game_dirs.swap( - UISettings::values.game_dirs.indexOf(game_dir), - UISettings::values.game_dirs.indexOf(*selected.sibling(row + 1, 0) - .data(GameListDir::GameDirRole) - .value())); + std::swap(UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf(game_dir)], + UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf( + *selected.sibling(row + 1, 0) + .data(GameListDir::GameDirRole) + .value())]); // move the treeview items const QList item = item_model->takeRow(row); item_model->invisibleRootItem()->insertRow(row + 1, item); @@ -609,7 +609,7 @@ void GameList::LoadCompatibilityList() { } } -void GameList::PopulateAsync(QList& game_dirs) { +void GameList::PopulateAsync(QVector& game_dirs) { tree_view->setEnabled(false); // Update the columns in case UISettings has changed diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 7ed77fd9c..e781afb16 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "common/common_types.h" @@ -62,7 +63,7 @@ public: bool isEmpty() const; void LoadCompatibilityList(); - void PopulateAsync(QList& game_dirs); + void PopulateAsync(QVector& game_dirs); void SaveInterfaceLayout(); void LoadInterfaceLayout(); diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index c715bcef4..fd21a9761 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -224,7 +224,7 @@ QList MakeGameListEntry(const std::string& path, const std::stri GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvider* provider, - QList& game_dirs, + QVector& game_dirs, const CompatibilityList& compatibility_list) : vfs(std::move(vfs)), provider(provider), game_dirs(game_dirs), compatibility_list(compatibility_list) {} diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h index 46ec96516..6e52fca89 100644 --- a/src/yuzu/game_list_worker.h +++ b/src/yuzu/game_list_worker.h @@ -14,6 +14,7 @@ #include #include #include +#include #include "common/common_types.h" #include "yuzu/compatibility_list.h" @@ -35,7 +36,7 @@ class GameListWorker : public QObject, public QRunnable { public: explicit GameListWorker(std::shared_ptr vfs, FileSys::ManualContentProvider* provider, - QList& game_dirs, + QVector& game_dirs, const CompatibilityList& compatibility_list); ~GameListWorker() override; @@ -76,6 +77,6 @@ private: FileSys::ManualContentProvider* provider; QStringList watch_list; const CompatibilityList& compatibility_list; - QList& game_dirs; + QVector& game_dirs; std::atomic_bool stop_processing; }; diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 76348db69..c57290006 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -11,6 +11,7 @@ #include #include #include +#include #include "common/common_types.h" namespace UISettings { @@ -70,7 +71,7 @@ struct Values { QString screenshot_path; QString game_dir_deprecated; bool game_dir_deprecated_deepscan; - QList game_dirs; + QVector game_dirs; QStringList recent_files; QString theme; From 053da44ecdc0ca2672d7a40b241c8abd2200638d Mon Sep 17 00:00:00 2001 From: fearlessTobi Date: Thu, 11 Jul 2019 23:02:18 +0200 Subject: [PATCH 7/8] Limit the size of directory icons, fix text when icon size is none --- src/yuzu/game_list.cpp | 3 +-- src/yuzu/game_list_p.h | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index c525d3f17..d5fab2f1f 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -230,7 +230,7 @@ void GameList::onUpdateThemedIcons() { for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) { QStandardItem* child = item_model->invisibleRootItem()->child(i); - const int icon_size = UISettings::values.icon_size; + const int icon_size = std::min(static_cast(UISettings::values.icon_size), 64); switch (child->data(GameListItem::TypeRole).value()) { case GameListItemType::SdmcDir: child->setData( @@ -300,7 +300,6 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel); tree_view->setSortingEnabled(true); tree_view->setEditTriggers(QHeaderView::NoEditTriggers); - tree_view->setUniformRowHeights(true); tree_view->setContextMenuPolicy(Qt::CustomContextMenu); tree_view->setStyleSheet(QStringLiteral("QTreeView{ border: none; }")); diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 87eb71c17..a8d888fee 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -221,7 +221,7 @@ public: UISettings::GameDir* game_dir = &directory; setData(QVariant::fromValue(game_dir), GameDirRole); - const int icon_size = UISettings::values.icon_size; + const int icon_size = std::min(static_cast(UISettings::values.icon_size), 64); switch (dir_type) { case GameListItemType::SdmcDir: setData( @@ -272,7 +272,7 @@ public: explicit GameListAddDir() { setData(type(), TypeRole); - const int icon_size = UISettings::values.icon_size; + const int icon_size = std::min(static_cast(UISettings::values.icon_size), 64); setData(QIcon::fromTheme(QStringLiteral("plus")) .pixmap(icon_size) .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), From c49c3e9f277b3a0b7e1aa1df01f68c1c9ffcf17d Mon Sep 17 00:00:00 2001 From: fearlessTobi Date: Thu, 22 Aug 2019 14:37:31 +0200 Subject: [PATCH 8/8] Fix uisettings include --- src/yuzu/game_list.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index e781afb16..878d94413 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -21,7 +21,7 @@ #include #include "common/common_types.h" -#include "ui_settings.h" +#include "uisettings.h" #include "yuzu/compatibility_list.h" class GameListWorker;