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 0000000000..b777ebb207 --- /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 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 Mr>mdKI;Vst05T~zPXGV_ 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 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?Fkg&dz0$52&wylyQ$U=n(-v9;Y{GwC^Q#~`?WJ6;M1#?S1LsLr&BNH73BLhP- zeFIBl^ABV%1lWkzbNuoRN>iO1Lymiz{*qe0|||V|8zCE>^3HOI*uJ@arrNsVqp< z4@xc0FD*(=buCNHD^bSg`;yEwMC72j0VIRd?Wq-+IJALeAlkr50TM$t6}bgg&PAz- zCHX}m`T03^kW>}`3ql2FXHcp#SI`L0%uC5HFV?itN7sUgZ-i;7E{P?nc18w<7PaTps)4DIadMijrGa6ZuAyP7 zg|204vaxPbs$r_BWr~@lajF^2A|%5wEb=ePOwY_q%t2BE@=Ho)idC|aMN(2?qJ^%Z zS)!S)nW>4HZem)BnXYlFS)ze)N{XpviUrIHEXH71<&jxjl3!Gbl!{>`QgCV^swhk% zClg#k6(klFr-JgItr9E)!izLWet?iR;CyVAm<%f0?2ZAW=VY97Aq1u+V&3b!G}`DrEPiAAZ7>8W`o#N>R4si@k}jknPU)de}3 zG>C}e#4^gFfw)`+@g-6$qI(e$)!+a|E#**3D2P#DRk#g74NoEpV2G)x+Hf16oROH9 zo|0dy_cV-6yUnzLZJ6*DK=fGQ^||Dw#)yplw4?lH8{#}Gr<3{f8e%8fat zVD*+}1_lNO`i3A9sufupU27zMt;o{oS`j)!^D;~9jG(rm2_xwO)u52#J-8q-In~b0 z)YRD2Mju@mT}MD>MQTojOJ;6rUU6oAo}GyWR1dlYR*jLkG$KhrHA12asaXcjgAmam z7dJaD8+~wV64bH;OTkMnS}~vwqm}Uxb4M+KgaeV}XmAl3P!MIK!37BiBFWLsqfNH`Eljs_Qz0R>Su8eEWYAd(yn zE+PX8qHHv{AmKnHIT~C<1{6fuXmCNofk<*RxQGlWh_cb(f`kK+qqrn9U z2O`PQ;36`hAj(FA3la`QlB2;zWI#cbjRqGa9Ec=GgNw+3f+!meE=V{KNsa~=kpTr! zHX2-za3GQ#4K5-B3ZiT@xFF#`Bsm&fLqG_ z5gAYrWuw6b2?rv{(cmI7pdiXdg9{Q4M3SSyMPxuhl#K=#Bpiq&@dg(cVhd+#UW%z;rW914tqk%1uiX1Hn`#?dA9GK(3HDh_Z`01^i}nR z^aY2n3g;#Zm(6&&_GVGgG9#H}qb-`nl1rw&ob~i)RaQhtw$bxX?sbjt75B&Nn;$>v z`2+z5ts)Nw76%aer`gcaG5A$s>nAF@^URdwK1_-G z=lgSufG30fIVOvImp|TXgg^XI+7QGPcDg||T`Er3c)R+(kF!s2pUgNfmcd8!^CLkI z_avRzU6T9}norl&G-UX{Il^4@;l>;egVQqnCo+F3Fexy=(SPX!Q#P8%AHP>4YrxGR zVY7=dIy7N57hC^&aqb7wlV6-QWpc=0$9R8+4&Q;=0Pa)$kIb@MM6lkHEAZ+qF@`{fU;?>lGjkGlMmrC@p2-D=~7o80&6 z_3esx&ffh!{lMH;FBx)9{xv!NOyk};vpxFJ6Td!Ao~nDZmwioW+)_(3cgeiGgdabH zQ~v&lcxqZ{D3=hXx=qV(R`TUAQJK1${=;9(>-Rh}%T8FmiQjwIv^Srt+_g5U?oaS8 z3znQ+cu}NG{$9}J+9;d#@`5fY*jfXf{wx(?VB~oIoxv+m+~=!j_X^NXVNX{-mvv4F FO#r2c8$$p9 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 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?Fbf6&8& zW8=Xe3{f|`)-F|Pnjo<=np;R%V8-s*zYib%J9Yc!&G*aAcQcna9Q?3<;R4~Wre8u9 zb-#?cRJeYH!sIZ{WQ9zdtFb!+b)%NNR;XoCkl(nBkx}+-#PqkH-KKM=@lKJxx@hU0 zbcWV|CzZ?DtM}Yrx8?pc){Uwu6W1x%ePjr@ko>!g`_cCUOLBx98Q+?!=L3C*9jvq1KDKjZ!#$_HfGX0I-4ICbipxa5ylSzqKEURFP0W_{An zDP4WgwBy@>4}u4eGBTYAb*@#a}>Rrgo@ z7q3Wr)XIDO6*gd-66?`?WHr{WJfh)`pwK zf2mifXSs2kC0*jpf=xv&XD7KdUSPQC@Qp!WkFr?uFNaGNh8Ox{jxurlc#ssq%yOq) z`M7;laC>(muf?n^hI`I|Px|L=njb%QHczj#FNzopr0A(rjLI3~& 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!ia0y~yU@!n-4mJh`hH$2z?FU|>mi z^mSxl*x1kgCy|wbfq}EYBeIx*f$uN~Gak=hk;1^hnCR)^7?R=qcG5;KW=9d%5 zz6!1Gn4SB5dE@V#7x%92&Dy;1=I4X6ZQspPEVQlL`}AJOmkoAXGgJR(}!2uRcNM^S(l2DX^x0zO4bdHNhhvs2vnK4GI6no zQt)B7i7w09dX*?pxMsC@j>N$`-#UGIw=S+W>szq(E6Yr&>^|++ZpCE|^Pep%f4|B( zW!>>*R~789zx{ObM&kdh=z5*~d-l$eQCuIOA;QHv;gWfOSKeN^RU|>mi z^mSxl*x1kgCy|wbfq}EYBeIx*f$uN~Gak=hk;1^h*y`!x7?R=qcE(1pBMt&>%?l!; zBC?e=9+oNJ;f{!WVI2`6b1Nq8abpy#b}g^tk+1-r+%GkN~cci1%9L!jm2ggZ-uJAJ1=nm2Q?b3J?R*;$*OxmpKL2@>;r^t-0X zuidxu_}13#{-5Kwc&jEZuDbAPhW-@e@DL%hTL=5}8SJK7YjH|*9_VFx;Ko#-%(!DB z!yOi3htkWHcf__!U(2~Jm^W8{52K9D&sVEku9pwA4_bNT*>_r=eN zrTbo&Qm?*h5^L7`EWWlkA~msDCv0*=#5c19v3pmXH$|L%^fl~F_5pnhh3>-#9|hM7 zt3_7y#a6ey;Z6T$H2=u nsIg}MH3KD%L-k5sb_}xf18Nt3-V@Kjz`)??>gTe~DWM4f;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!ia0y~yU@!n-4mJh`hH$2z?Fv-AVHUl4=0}CqYb|D{%(jT0`FvXb zzLxKtjruCga|%3mtM58`D%aqbfI_bdGa~~FOUI3eOx&w~eq0*1HkY&K%+!2A>)%UG zzm(7TFV>>a&=$V>=e>82e!GXKr{tQvD|PSEzI^4KpScZ#fWqawmz|A|@s^kU>{+8L z{Crm4Mk809-zzIM{zNY4yUoGCFz@__m$Rp4%#A$v^1%0X3=uZd8lqR-Ne^3>@a)$w z{_D@(?H@S3mt#nH>n%3P3d?mEvcKCgTUK1I!}Nqpm1dvg6Uz?|&Q{KNv;4^S2j4mB{(QSL zk#S0itzqN&c|WgL6(=nV`E#eOr>`s=nfVUa@2R$!klQ-hUF7KD2)Shws^;s{hSc9=yN4>W+QIzIpq%sb+KE z)p;;2?jDO^x#ETCIfn!|R_zhgStGV?`<*w1kA5 + + 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 0000000000..413fc81da7 --- /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!ia0y~yU=RRd4mJh`2Kmqb6B!s7Sc;uILpV4%IBGajIv5xj zI14-?iy0Ug9)d7qfrN}60|SF(iEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$ zQVa}?e4Z|jAr`$$CmH%3b`WsApD=j|$J)zE7X@~5?=W8Um_dd`G2mUp5y1ysG73yw zR^pEjiL$agIEeZ@RH&ZL6v=Y4Ligk9i)a4W*Qbg9#FSdUY;8*ec=>nl7 z^`F&Gw)}dsU}w_<%agi$*Pf4ftlEBkPxR^cT6X%5EKSE=R&n{Lm;AXZ$8`8?p3$1S z2e|rj>l3u6@v{H3E`K_MJK^}j`cK<;zPCQpssH|h|LT2nuT(QIFfe$!`njxgN@xNA D=zpT4 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 0000000000..94d5ae8aaf --- /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 0000000000..27a6cc87d3 --- /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 14a0cf6f94..d1a0ee1bea 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 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7?s>X6hFJK|y__3(I6$EF z;c_kpRs}{82EGXm%u%emTn88*Ft9f;sWh~NF`k*Z(>mBm$myHEQE~Zw+uvIa^1>q? zZ`o;^+zyksK6W@i_>;8~4uW%BQV2XK=dcV*=^ND=; z8-avQ>pMx8HI1{*6}G&c(X^tY_@utylKBq|YhN&{Bpdl`3>BYHxYxfk$8Nt!$Y(X< zU0lByZZ@v6?){q>%#hDv@y(PyVD|K_^Oh=jZte}7xcf&rC+7*lRHvE$bq-tzDXZ-c iVgGzx!+7pf^J*{cEJ@qxs~8v<7(8A5T-G@yGywplo^8hf literal 0 HcmV?d00001 diff --git a/dist/qt_themes/default/icons/256x256/plus_folder.png b/dist/qt_themes/default/icons/256x256/plus_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..ae4afccc768e0a2163b23d5c457a821ef536dd73 GIT binary patch literal 3135 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 A6951J literal 0 HcmV?d00001 diff --git a/dist/qt_themes/default/icons/48x48/chip.png b/dist/qt_themes/default/icons/48x48/chip.png new file mode 100644 index 0000000000000000000000000000000000000000..3efdf301eb52a587f18b7d2f94d6ea64773a0231 GIT binary patch literal 15070 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?F-n*TA>HIW;5GqpB!1xXLdixhgx^GDXSWj?1RPsv@@_H?<^Dp&~aYuh^=>Rtapb zRbH_bNLXJ<0j#7X+g8aDWT8TYZ-9bxeo?A|sh*i`vZ1ksg1M!hp{b>Xk%^9ik%6I^ zzJaB_p^2`6p_PH9m4TrG6e!toDJUq|6s4qD1-ZCEtt(1NvsKC{DJihh*Do(G*DE*H z%P&gTH?*|0)Hg8FH!{*KN=ef#uFNY*tkBIXR)!b?Gsh*hIJqdZpd>RtPXT0ZVp4u- ziLH_n$Ren%3Q&tGatq)VWVoJ!(xM!&(^8W4Q*%;t zQ}arS^$qn5vFaKEjt+vp<%kyOG{m6daTPJWSZeoCsHv5h`9VI*DP zlxF3bS6q^qmz?V9Vpow{pqH7MVwGrMoM@PsWTb0iXkw~sYGPudo0O7hs%vPTmYiZ_ zoM>idnFtOe^F*?UP&T24h(JdF~kryL)1rr z;xwletlrYhz`(#j-w;GXwIWNSYmLOO6zUS^4%5!5y`VI*Ckk{41e1Q#SG zr`nmBni`wh=%Wjx>j=oKNX?0G$;?g7E6&W%voo=P>Oq&lsxcCmMkEQSMo3g4Rp;QC zgop;YxY=>p=!07!pcV;O3ZDLH#eh1DR>nij9km1!4n&fp!9`?1L6nUK7bF~rBu9gb z$bf<<8x1Z zNOCl|hzux*veDpzgaeV}XmAl3P!MIK!37BiBFWLsqfNH`Eljs_Qz0R>Su8eEWYAd(ynE+PX8qHHv{AmKnHIT~C< z1{6fuXmCNofk<*RxQGlWh_cb(f`kK+qqrn9U2O`PQ;36`hAj(FA3la`Q zlB2;zWI#cbjRqGa9Ec=GgNw+3f+!meE=V{KNsa~=kpTr!HX2-za3GSz8(dt7t$(R` zDYi=GO7?cAE`2d$U|>*4_6YK2V5m}MU}$J&VEFl;fuZ3g14F3+1H-EX1_rAc3=HD= zlj4uMF)%Qhc)B=-RNQ(q^Q?DBpv19yGtr+j<4tx7W(w}oa0v`lYd%li+P`Ep)7H+RL2>sb;PUAY&RZk=G(T6}j8bNlq_=W?>=ZN)A5_ZXZpem>{< z@iUg?=PV11_A>or-0d*`(>uof4fdaIg~$i6^);|QHD%0i)Y`xob8O-*6$h22%{fx{ z+CGW#zURm=-Q#(_9jr%&li$3<42UX{E5;ob0iISdx-8DfntW-;##`=@^R zc3|Ae534SP`<9CwP?pK^WwT_-(|o^E@X^dqdET=*Dwgj_TcC80@xoi71q?n6M_K9v zwESE%qdPuEHE1rFyP%?%X@28<$t&dv@0KvGKXqhP=G zRtMUBR!(m45bNYN`}wNW=%Hl79ku)SSw3Hg$gN?F*?iKRX%AyqkJiasz3B|MYgTr# zb}Z^}JI(aKM}#4U>C9>8SF;0eALyRLu;kfl^NDRX%sjWAoHjn-JG(}|as4apTc7v5bY^jtg*wB^pMHPS^_Ux8oBj0rTW-bNU^u%*UdW8+{be%~Nd^&} z&lgv2%UNqV`$*=`Lq@Me7qIT&J|khu8*}nxpN{GMI45>d%Wt1KFXT*KAE$nK9^5$Rx?OU*qHli>8q78-%~~Ft{!E~UoZ1%*E;ES z{;TwtR`~L6DP33KFY~UG*}j~yV!mK9U*Dre zllk(RxL9=;b%(xAXv%chFw2ayM!&|{@81WT=W|*Lgp?-Mr>@&DH-XhVPQibyaQJ(&y`nvtbM+dk*V_7lBEwh8wEJ9qac zTO10ja$rxlnDVl8#|RG15=%_&nc*L*YgP|zX< zCk}o!21bW7qMsto&oFFrK6}MQJhD(M!j_F!&UYhwo>BgqyBkb}5|kOu5X62KwKwJ8 zTRu;{5vBU>o44iUmw_U3JfGGj2zKamM6Ige`90nGL*~3geVWm|s_SBps$QEtqnG7| xM*|Wu-N9&{?J@5J!+CqBz1ttz`l40;^zoJUk7mBv#K6G7;OXk;vd$@?2>>ZUZTtWL 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 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?FqBQa7C;27b}Zl>!OJouQZ(|Z9XAqZeCrzecsL4!O!^h-`IWE`)sp)v++^A z#~WLo|7i4c-rBi+N3U}7FP)2rN^{IJY65Bxt=wX4}(Se!kmcX}c&tNT#bbkuhq=>iSztmYFsH1-D9b_x-4{|KHKs5k4hH*Z_Jr{ck$Zo zCAM94yF*{x-w=2ueec_P%!2pY zS}DsGmHXvCmO8njHsRGt#-QNmk``Uw2Y)&_$CXVj5S_J5Ypv7&KO(0XgC;XCQDgV; z<(n`|M#0q5A*-n2$`gi_Ct(t2>!lYM2a12y{JDy2!@Ug?3pMw~tmOKzBvJWs(CzDm zd$(&}4tme?<4S^r_%e+nR(}6Ff9ieSk*xlA$^L+wN2Ko@d8pOApHXz5@msOX;C==M O1_n=8KbLh*2~7Yce-WDi literal 0 HcmV?d00001 diff --git a/dist/qt_themes/default/icons/index.theme b/dist/qt_themes/default/icons/index.theme index ac67cb2367..1edbe64084 100644 --- a/dist/qt_themes/default/icons/index.theme +++ b/dist/qt_themes/default/icons/index.theme @@ -1,10 +1,13 @@ [Icon Theme] Name=default Comment=default theme -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/icons/16x16/lock.png b/dist/qt_themes/qdarkstyle/icons/16x16/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..c750a39e855800fe4cde217a1bf28f841030e501 GIT binary patch literal 304 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7*pj^6T^Rm@;DWu&Co?cG za29w(7Bet#3xhBt!>lEak-aeD5gi-JuC z0xscgOhHVt9_&F(We)6pvKj1C9N4!xu-|GdQ3(6+hclT?$l~sNmF?5#o_)4Nl)<#% zdAD@H38n>{dVL4~ZPjG5xX%&5`t{oNUAm20S2=hXE?atMOsSJqx?`|&$`TvJ4--nJ zUUA=X?)uy%h2k44CYDU#eg5<5r28M{q(Ar~-F4=|muV$2H}3w}Cs2Dpe}c5ko4-%4 z8eaOuqQ27O>164{R;-7=PIuTV^g-#wH4A3t3pEU}NsMoQOW59GU|?YIboFyt=akR{ E0C@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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 bzv^eszIo=ppPexS0|SGntDnm{r-UW|wLkz{ 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 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 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!ia0y~yU@!n-4mJh`hH$2z?F|1*Xx zz6A)vjMIQmSyCcx-VxVnUA4RJ-|$|RqVdSm_HF3zd+(pr@|6e{#@X9+R(rktwCh8C zxq?!j{rg3Cs`7IRI<+JA9GTa7RmR~g;}ryv#VWydeP5XNfvp9(^DTbmhizuAWO#f+ WQ)xlqB7X)31_n=8KbLh*2~7ap#e&TM 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 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?FcxAsLNtXYJ1xag=DAe_m$JJg21Oh0~g5ga~qLOMjG-_DxUBF0I`lyk<+t ze`e{8o~mMuj%$N5E^#$gv^x4OJjng8W1raehiaR5?<_1W7n;e=S9bsQ?b>^DcgF6p zUD2>)-IC{%%tGX6PrB!|OZ{bp=y|1&)0RX}s=KNYcVyO*&Gp*%r){~gVaMrJOFZwN zW_oIUfayZ%qx1v6b1Io;FxxS9pEEN)V6uVb%iJ2iA8a;7n}iSOY~a~b_Nm)JJ$jz` z=G%(rZa-1A)wNI--;=BIa_RS*(vfvXPqR<@6?g6Qwzrj)_bs0#-_O4<@zdYAOJH`0g4s%koS?=V zOPDr#vL;RDI-(}p;R~1ecbh5eiqf1p;gOB~rrjZ`-wT$mIWT#a>#9kgwSMP*QQ2_l z!^=~7iJ}&%Y43vPJ`yz89DX45K{j)e;u^;3wLdp{%$imbf63bPKmSx;&wQ1;6HB(7 b`Dm{Gecr!S9Ho*B3=9mOu6{1-oD!M<82&8j literal 0 HcmV?d00001 diff --git a/dist/qt_themes/qdarkstyle/icons/index.theme b/dist/qt_themes/qdarkstyle/icons/index.theme index 558ece40b8..d1e12f3ef0 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 efbd0b9dc4..c2c14c28a1 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 d511905c16..2b858f9a74 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 0456248ac4..f2f116a873 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 75fcbfea34..727836b173 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 184fdd3298..e747a4ce2f 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 d18b96519d..65947c59bd 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 f8f8bd6c5f..a2b58aba56 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 ece534dd64..f5abb759d8 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 77f358630b..8c6621c982 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 7c3074af9e..46ec965165 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 ac57229d56..b2de9545b9 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 501608ddc2..b7398b6c78 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 ffcabb4954..a1ce3c0c35 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 a62cd69115..76348db69c 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 f2f116a873..b2683faf85 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 65947c59bd..e5627abd4c 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 a2b58aba56..cf5bd3a39c 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 f5abb759d8..13623f5269 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 8c6621c982..e1e69bc1a3 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 b2de9545b9..3146e054cf 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 b7398b6c78..7d16188cbc 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 e5627abd4c..51ced635bf 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 cf5bd3a39c..7ed77fd9cc 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 13623f5269..047061e6cf 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 3146e054cf..72c3eb0690 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 b2683faf85..f594106bf9 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 51ced635bf..cab982385b 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 047061e6cf..87eb71c174 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 e1e69bc1a3..c715bcef41 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 72c3eb0690..6d249cb3ea 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 cab982385b..c525d3f170 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 7ed77fd9cc..e781afb16d 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 c715bcef41..fd21a97615 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 46ec965165..6e52fca89e 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 76348db69c..c572900062 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 c525d3f170..d5fab2f1fd 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 87eb71c174..a8d888fee9 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 e781afb16d..878d944138 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;