From bb23fbc74194b1519e645239b988acc479f873f2 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 5 Aug 2026 14:36:53 +0100 Subject: [PATCH] Remove 'always load' extra opt-in for extensions * If an extension is enabled in the extension manager, it will be loaded by default after that until disabled --- docs/how/how_python_extension.rst | 6 +- docs/imgs/Screenshots/ExtensionManager.png | Bin 14533 -> 13174 bytes docs/python_api/ui_extensions.rst | 13 +-- .../Windows/Dialogs/ExtensionManager.cpp | 75 +++++++----------- qrenderdoc/Windows/Dialogs/ExtensionManager.h | 1 - .../Windows/Dialogs/ExtensionManager.ui | 75 +++++------------- qrenderdoc/Windows/PythonShell.cpp | 14 +++- 7 files changed, 66 insertions(+), 118 deletions(-) diff --git a/docs/how/how_python_extension.rst b/docs/how/how_python_extension.rst index bbaf62ad0..c23457e0b 100644 --- a/docs/how/how_python_extension.rst +++ b/docs/how/how_python_extension.rst @@ -45,11 +45,11 @@ To configure installed extensions, open the extension manager by opening the :gu Extension Manager: Configures installed extensions. -From here a list of all installed extensions is presented. By default extensions are not loaded, to load an extension either select it and press :guilabel:`Load` or else tick the box in the :guilabel:`Loaded` column in the list. As with python modules, once loaded an extension cannot be unloaded until the program is restarted. +From here a list of all installed extensions is presented. By default extensions are not enabled or loaded, to load an extension you tick the box in the :guilabel:`Enabled` column. -Once an extension is loaded to enable it permanently, tick the :guilabel:`Always Load` checkbox when the extension is selected. Once enabled this means the extension will always load when the UI starts without any user interaction. Extensions must be loaded first to ensure they can load without errors or crashes before enabling them to load at startup. +Once an extension is enabled it will be loaded each time RenderDoc starts, and as normal for python modules, once loaded an extension cannot be unloaded. To disable an extension uncheck the :guilabel:`Enabled` checkbox and the extension will be disabled the next time you restart RenderDoc. -A loaded extension can be reloaded by pressing the :guilabel:`Reload` button which will attempt to unload and load the extension again to refresh the code. This may break so if you encounter any problems it's recommended that you restart the program to ensure a clean reload. +A loaded extension can be reloaded if the files have changed on disk from the :doc:`python scripting <../window/python_scripting>` window. This may break especially in the presence of callbacks, open UI, registered handlers or other persistent pieces of code. If you encounter any problems it's recommended that you restart RenderDoc to ensure a clean reload. Writing extensions ------------------ diff --git a/docs/imgs/Screenshots/ExtensionManager.png b/docs/imgs/Screenshots/ExtensionManager.png index d19ce7a7b825d4467b1454964bad583a93543f6e..d7bb3f618f3ab20c867b989f4ac10cf204f35a18 100644 GIT binary patch literal 13174 zcmb_@2UJtrwrIo-qDN^;7wJ{$p(;{DAb|@@p#@l=id9v81Ih}$lhzOJ=ZL2_LTq)h!Qy|Ehz{DB3FJS{{#dg5eEK@ zE|CBu3nQalz}Gpm2Wk&Mpu$kH!{_IL`!fztl;l8p-Sl|i=Ca)*T?Y{8>X*~MGgO?9 zUjc)+92K5ALTybQU7jONKpM{xHjexd1ubR~{(JoQ1S!raIf6if^UCrMv|LA4$K5K} zOuVKx4t9AvKS0MqO1_qeoo42$Otg4)MX7QYoGX3B-}ei5`1PjT02g= z5MdpMDiBt=b5ofmnd0+JN+NAUiHcyS&{j*QkxLe2Ad`@b8}nGJT^Kvqm#M$lEv-$^ z8Gx_5)f4KpkLrYsJPvu}F5Uh*_w~9G{XL2!TSm~UwyB)r?slAfKk$J4cB?Y*Shy8G zS^INQp}Agm?j!4QQ?e#K9oENF^F|B9_6%!?>|(*}3G{{lcv_ZY1Ilq2lO%C3BuQoi zY;Wb>ts*8Z7803wP$YhANh#}qq?Dz^_9$)~pHO3aup3RXyRVs!_gQ83?kxB2eJ5a8 z?SD>dqG_>PhKXMhvO8qT=wsu0SE7&l3|0Jz(hWLMS)Zm`_J_TqfovnmWgZpMw$6FW0`tez{{teBH!0aMo;Mdn3usfnJsln}4E9 zDI4?E7Jag%T!TAwvaMteDOcPGAZXU`g)~h%Pfyq1YU%jAmm8F_=gT?4h{b9i&Pb+I zum-{ooo~e0>?H_$<6Q()W}@q9usHip!{e-B_2!L(Bu^*$BzFRn=aPu>;WU*J>xL4H zeqRaJywS~>=Wb1@gx%;+GC1n+p>J7E*^9JHarL6~p~aSJ9=2Df>^V}_6W3Vt4oZU& zHPh0g5w5cypDScSb`4I_cgO!|`7Txi4xyX$>q?z=4!6&|x-M9spOM}+J8Am>`}jMoBN z;lg4lhyCPtALq$?%4naHLS=os6MGt|KUz2##brV+hSuK=q2M3$_@ZEszKO+NQz+yL zR7u(ERo2v}ZE1^<@!T{~mfAe-=-X@Hs7HUjC`*MklDBWCQuf64^F+FC_h&Oo^95v| zY;LpW?RRty9ct4L9S;Coi*PF`WcFdw@sfXD&}=7XnKU&P#hP~#UmQ^}ZQETpy&-P% z;l-r?=L)Wn(6Q}urb=)Ku%$XC6*dYvWhk&iDdG#onulJL3lni)hswSAgSX0j?gWHR zQe!FXS6pX$oIQm?dVIDH#gC^C1rD3eE3bHNQYeod&-s@7@Prs>dLE>3uDzd~kxZ;| zb)k=u-@sx%R|p46e*VFPHSuA@>Qlx{Mt^mYadV+fs#;)*teBbLObjsY5rVDlW5m}E zFxfME3)yQY3)zvbQ$~5pGmV^A_35w^oOK6yPR795D*rl{@tBz()YzuU&>12pW5CQs z&9R?2&SQ~4x)YWFpK3f0V(f77M0EyL@98|8@(?1hKXaf&ie%yV=LcgF(f4RwQ2vRs zaB)FyYXJJs53wDQYNim2rY;yTM@m@jD*LZV9s8Eo3Ix)uO34r-e@`3CL7S51d_-TZ zo=v4$6DLBAMiYeVWD3`}{8z-QW-$G#OrAAAR1XG476AK0te(Xkgm`Vdz-<$k>*(0F zSC?f9{{SCNpPgi+Ux13~qg(O&W+z8eLF)$lkl%Qcc4RK51}4SLX$?Kyh(F(s|0Y0R zy^MGDrK_{=>6)w&Alhw3UZW@a&eGx>C!GxVFokUhO+Jh~#9`9IV(qn|hS&vr%G>#~W3y0i^AoADukdXpx~gs`MUe`n4~@@=hsw)zqv4%#&+lbq#zNNndWS7zE|Ju|hhs;!ms;_!c^KpBcUQW4d=}oS zOK(p_$D%{re`bEslyT}aDMZUinCo?U$r=ck@8*WbP7pNgyD+wuEa81zp;3&x+&awVkuVwKQUChojQ9#uIk`Jq+f#Tf*Bu1A7Ylylp@*};4vGzYt=UAc#_ZH zy_DV?WM?u`o*TSJpa^Ft?2Cw>VW^%e^k6OWB9G7>TM!EqX1dJy}hiR1u(d z|F#m^tG#I)`B)FKFNDc~hyK!41flM+Oqm(Pf7~9}1(LfwlU_5!C%Zn3#|!xeJ1*7T zC-DrL@=SGy0!An0CkORR;GYC@U-MFDFnJY$QFdOTYup`w7UR`I2#0RUeO+JMch-ei zkGqpa^tBnqF|}t7=E6|W&D$twGJM@SDIge-Padz+a@sX?qImG1DDah{oA=8mp1W8{ z?C`*tws1U#1xL01p2r^_*4vE%J2FYiIq>*H-3MxhEgG0own+VBZ-d@sm!ZrSYNPk! zo7fhnSQi|*;rp9X&h28IiC__z<*xO~E#iz221Ua{&5Bt#>CN@%{Tcz=)2kvlkBO~( z^N{eE-yY$~--j&vzHc*9bGme_ubXMUK>Y9S{l2W+2d_8fyrHq%&_aI)%Yb67)=qxM)0$ z3_QmfbO!XfSBVLL#>@c#45t9S0)YRzCMbIu;L~=T;5&XgBh@AbO%XQK1=c2!>6FwC zTA)_}C}Byd?VSjprh5 zc3$f+@?JB3%+i0U!8Pbbt@A2$a_6ZTDULEh`#5V_bF}R;)ZosQb_YYLXzd(xP2O+$ z%n{JNoHC{Xp0h_&_u53hbc9N9^L%RO)+yo)G2Cg*oX%m9#0F#DYzEEHisth(*xUdd z6lso8jpd`dj(Sb_vjEF?CCm%hYCUT+)e)amyw$;ID)YyPL?3vl~ZOX6okvTz8OKs+pDX~H1UkQnw(OL zSFPMo&+KXttTsODiC{5lWg%%UtTRHfZ%jJTz0O>kZwpw);ra(y1Ts@s%V=e?26{E?(!TSRVq zBg3JX$@yWpO~kE&D@cocD<0(1=c)k#jD?a>23)}MEiFP6|2< z*hIiw8Ba~YBGH{Ih{wilo!P?;6tM$bm@ggI_C3^n6-|!iz7{me6nGyW%OCk(QjaUt z7y0sY7tWUwjC`VTuP)`OTlC@1eK?1P;IfDrw2j_I-}8YJRU0CcORD)Sszwn62-N}> zq13K%=e{N@hw<`*U=U{@l%FF~j-(;(g~Z(U;B5KjLWviMJ;MQq@V zgU@!D3|xWa4hQna9P3v>Dr`p4YO!|DKx?LpmDgeH*-%@+I>xY!geLb~9~{#Cp#u#! zh;+M4hU$BngxzMq0A`1_={1{C9v3+HUK2(Ef}Q;!(OrG0>H z)xq%onVvB(Nq87w(`wKq=t21o1atCphNI5cW=yqpySZ%^mSCLj?N$USPI&LuSed|M z4~>x7_^x-2d}ejR>?a95*Oa~Gg5lb|Ir&LPt|?4U+yv4lpFY<#8Ev>e;XV3#yT^=i z-_)DcjZkv({jBfjTpiMZv|%svjTO;>o@``~%wc-TuK9PuLG}S3+sBuW97*x)U9ppi z18EPt)RWO?Lk*MWwNbWBouQkaALUl*i^ognRj1`q<%$({Msc+?Yok8XgoD1phnvqg zboHt&(rU=5QrA@ZZA7}q*g24K&fqQ@AHJquw08b*Z-!2=&y_;%$1a~&;l4xa^#MM% z2RkUA!sra%ENeRPFIlo#yWI-Q{iFB!R6cJ_zjM!!O! zzF1caHRur#=3R;xbNd2S-ojGs42NA6tR__Ym6DXhJ)MYS?6PC7&wqlOy zCGzLD!@D{A*{>$G3JzULGvEjgcpmuz;0+%)u7^mT6&ZItum%IH>E47xpb?4(>&&Sv@B9gH?R z=0%I#>@|^fP9|s1Qk#LQW7jIe8|0E^txdbub-nsTZ}&sBeGLr*x31no1uCe!3S4V; zu!TuF%ux{=jsJYG6T`^;&Rt%q!c%hwujUvP+cV*89rI-?e(f9Bk9)0#OxTsB!|;y0 zTAb*)T|UnvNaExiV%9Kx3%?A^y(MgQU^Aqw)L;jyXn) zmKN@}Q;ImZ<>ZepdQR8r)eA_w2Lw zHq-HsnYWF4DR5qRFh2Tm88QSnuI-SHmq*nqa!L#2$nL{cQGV3~i*@VJsJ50+>2k|_ zy-P?zxpyDeo+^IM8NbewE)lmT)!<(y-{iYSDm-)Y>r^Z+m}=fIa0?5R2sVxVVy_ZX0I7eP5}znt2?1Q0+X z0eC~e&Hau@{>3N$6=(cEO#|Scx;oIGlTTxv{|N2>GHvMp3zWY?q`x`xUs2dE{7z%2 zU-1`!-=7rzU#I<=@IU0;e?Z``Py>McUo8GVPy2iH_fJLxr2P(D{v)1$$>X0K`0JHF zH^DD%{3*8o(X{_aPPPhUc_=^WEGS1>V)-OGst<^xuF4+%yaj5|V-f7@PLSk){fGGL z@9a3e9{hy9qHzxN6y&vCXa~Oa=@O`ba+Z$oG4Lwy_1~2L7U!=}PO!{&vd;U+Jzs$Y zr05>)*%HE_!pU)Ri3U^vLhUe3)7AkHKp%21{tBl5OU{^D_+aj6(NOd`5K^xOJxFDU zNE$C=-enB}rnDlgknJCC45wk$6V`46%7fYH$mIO^I^)8*5rM;W5(W1~g?JUkADq!7kWMHtM`Q}Tec4U@F@)PxFDiQb}G0UW&xe{h@ zS7+3OO{=!nkpzNV!H11$rl+`g`OCO_^cla6iLCa*s@;eidBw72UEQ5Fbh8sN+w#;g zd(0iim#%`qrTxXl#kvO7+sHt+s8#Nf-Lfy{2Ejn?YeT`SQq0{0M3n)`g~=&;4vu}&rSo8H8LxxsoO1*{nA0F&T%Xy ztQf^rDr`nt`9vl{BZR!HJMx1Jl{H}IS~Ih+hYdy%L?09}Z>epY?c6^0N%rH@ZV5)V z=iZb2QB$rz0A1tSTBO+ z`uiVhB6iI-L^AXhMX(ev+ zfJGfOd9jGS6Ok3W*v;lFcZmgqk0*KIbTGdnVmzw(V7RPpXt*s2>}27&qRL)^$~toL-} zh}=dh)`rX7OHojS+eVvOL6z;&GhVBSpY0EU8i0pUiUCB8uju)U0Vys%G*`{xQ`fYV zZD)m5Z8BhAIW{nyT7UL$YKfQ5{FUDRBau2)-qQr@)Z6@0Y6RO{zu8JfU3{o-@0?=V z`aw_K8PLZ~6x3~Og^EFvpCPC8xz&Kx;8S;!SK0!YUPpJ|N+w#f`vQDdK3q>!zjib3 zX>Eu1^9Q0mR%D>3(a3g-}uZ6H7-i`pvKKH0mhzAQOTPi1W#EO&-L^Js zgJeAhl8?&`Gs(V_T?Z8m&S~L!EaT7AN;SGIlU@JZo>?(EolS$CkjjrZ+b|OGFInWj z5&xG)VFoxbIHp&GLIVuCb~$@>k{7=QsGg1-4bwf%{P$Lfd@a19cyQ zjouymRzZU#xetkC!~75{E?(`{l>nQtn-_hln4|O(-N|bh$BdB|8?Gr$4#$!~ zOPE~ro8(s-+eV5JSLl3oEC%(DHMURkC)_s*_(bfK+xVMIb+*;Mv$QQV`q@Y~D89Jz0$Cos_#uif$}BgKZ;{xG0z9 zfjE(XyeDue#|Jui8ZE78sKJ$ulpwlM)nWdnfNSI~L%6fXR42Ujs=T?6Nj2j}3<0CY zYtz2k&xL94-PyORzT~)k9{z}YHMQ9`5EPePY&Cc339!6x zB_jPkf~C;}1?7E}8Z2mqql_-T2{t)GzO`wpOgA{!-}{O4lnW|cn9Y?1<*w6xeP|subOo^0Hj}|-Nlxv#2lKQpn!N+(zsKQq zXeM2K_Iq07#@*-6y+Sw7r$>nNCf4SFwLJp_Vg;n#ZnJ#Lvxv*_$~zIltRzE-U0gAf z8Ww|B4A1q{3)G})7zpuVO7Il>nf5de(KfY>5``#kA3vk_fGY?56`+CSN>sVsn4F4jQ*=Q@i*g4W{glG>NaG^dFjBH&IG1x_tY#Mk%@o6(jT&@Tl9Bv zJP+eItM(e5ZU`YvZ}#4Pp>j6v;-Ef&xfo;|W?L)HK$?FDF32bPM`!oXJgi-F!0_gy zzw8&2&h-niI5PJ=dxyC^N}hnY!@5V&OA>YfM{#$wj&i4{8uc&;kF}s+wT5`9?E78hC@ZifC znDcm!H&;P{0f#va8tjV}6FR`yFceQdr-1d*)6nVtU-YF{ISa7B0B)%p`8a zT~^yLJS_>%-eW!Nu^<+0oZ(1asv1u>g4ol5$9a)~r!4A3R!YQM-ObA;FOWG~4J1aE z=9PI4=CwOiByL^btuN{_xy+2g1L$+~8_0mE4&0Uo9t)aS?>iK}7EDIcGFsSW7pgv! z-SdQrBD^{va7J^&ux6jmop!|STPlzSy>RG+wU`(UIz&1?+iTkllFV2mH>BZ}HT>Kb z?zXgzEH|ux@IKAu$E1U;;R|n1EOa^+vI&b`7Fl8sMG#)>dkSjLWDn^}QJcD6O)enZ zTIo;E#%P5KA1|`g;qr1aRRiKB7Y^hanlKI!(V44(?9SCN!#6@ zsFi?USeAjsf0Gt>ayQ)dHDY1vCK&p-Fi&c(hLLPF!V;G*qDMByWHM3?H6B*q>^Zl? zrrn|o8$qNYdXEzo%!jPuPC1id9?{Lvqz~lO7-BTJq0%e#;hnMTH#c%88_r@#u|sU^ zem|4*niEKvF#)Dj>V5p__xer#&EfnC{eIU+f7MFUMt&W;T0xOl401HYDk`^#g93TdSNw3gZPg>zmaNzS> z`1s!XP)QpD>$t|=W(S7P5#_2B+R7OBn;RBDxWzrGb?vU2w738ZunMcoGB)yFS5!P- zBekMwVaf$=B)6*6$FeWP4zA$`P>JX!PS$vD)obZfBZu+V53Za648J~vO=+N-pl6EA z>d1_GEEbH+!a%_vmabFhibCBZsKg%u)(;U5EwtBKV&QQNaq7tAubMz+bsih4V!Uti zAuZgf6@X|5Au&feFcd5Q7?K64P9#gIDEamrC2VfW!@WG3E4JcECy^f2-BsF@LH|_7 z+B2C#K)rJ8Oh&^gTW!I=0${0=Frvrnpy&EhGk- z%-5~Zmu>Z>&I1U6PXp?|d-uOz|CtpEhg(h7c^wf6LZDaI1GsyDGIv;C#2+SWQlMuK zXagjf8pR z{6xg6W+BLfme4`n<{m6Y~+n4l?_%N(I5mvEFDCn}t0Fnih<{4cx z^=e5A*Erm#A|S7YqjY~DosdvV@O@v{flmp>%gjvn+94*ovy3)--z;7P>$BCudGj@O z>+4z`M+{HnCa(^XTHh4USg{Kt$6CDEFFeVu1{QO<;Kg|G&(VxM>`RD5o}^oi;FkJOf{U_itc328@l+-IT1Y<8%S`e zmAoQ`A?cFEA@WPU98vqV`S``^4l*A&qiF88!?Wt%mH6GElB0^pmJ`G4+~@dT9Qohk z&#?9_tFx9~enZe=lZ6GN({Z^Q=i9le^_8ijj^1tN(=CNBttW@s;4f+z%2u?@)b(1= zPChhbBln?5BOkRf-0E7eyxi@Ap7k^?y(RAF?w30G)v2kOb=)qV`Q(fZy_Lw`oyiIZ zC9Kn%A(Oh6Hq%=5VQEEFxo(8E%Q*)md&cB+sd-139FppU9F~Gn$8Q>-g|``KwoqZ9*?j}M2YBkt0Ut` z!M--ENviA7a=XQ8{yJ%r^)*)K^kka|XFUYgc@7`3S3Ga2J&feLT!uF8Sbh3%K&LJ2 zdHML%$4DdFpSv1*dWNMvAr@k{2+O@*MOYr~eGPjR*z9cE{Fr5Tm|SsnZj9;E1wv&z zUuD1BPrk_#j?t4i{wWZVD(feyT7ZkGiieBtfwpqSsYBf90~%qDUK}=RM5Ldz+_X>g9n^pKp6+ zv4$_~W;L34I5>jq$9yX5zjEY5GFKvji*i&sm*-Y+&2;a6b^mp=(`BbgmzbSWOeH+O z#rD%T4~=#83>jLoBxB~%koal~KY1?OR&N@60hK=EVaZtnl4{OpdsuVzBK>h!snItb zFxzfcX*+9JWTQ&tg5mm=%Si^AvRPjBtCd<=@p=@QG>LZcY$;x>~{&^|8HW>2qsr zY}s1r2oF}R=iNi?hbZ~OLlGa%&qwji4p&kKBb;8#G`zvU8AG+bHL-OEGLlP6s`0jM zbKXGxRiYvy5?3l$ITUrfs}j1@#05yn+hhL8#t{Jp=m>^qxNgXCZrns0VU|pq`>mIk z2d%j&C!ZvxsR~q}gkZg_3**5UzQ4;2)?vssP4J89f31DQ4F*5& zszO|DwqoW0DnNkG{8a}6nyD;v!m;D*D)w3(Qhx32gm-6{F#{|ZWoZA`c0~WZb{%eG z>8vxY${orbnTm@>-$?*pyc7m)k{w!P6fZSyX#bfJKmJzAIRrW^L4ugQl)E*{h!z&V zVKN&Q6Z|%3&{>ahR9JWfAze5hE~YYT=@`mS`U=hd5R!aba`^-#B5r4~{3_bFP=CqhP3x8a_Y2w(8a3Eh=`J>fhnw;2w83I4gqU9yXlSq8_1tt$7@$XwGmj zm1=F=FWQ>vc6Ytu_CT{Jxnm1K8Pl?ul6=t$@ZTmz@6B%(6`M@;m+8W@1mw4GX55<# zEwkr_Crt~7e3OuE2G$>UFTH26K-ZFz8?tB!PDLG8JzF>b`GT&wj)ybIe|>$rIqxbg;5|wKXti(R)zmj z8~(3qDd5si>tH|@3*`8J{{MCRdl0BkC?oX}hgX}if=sK9Cf&UZhc6=ZGNtr;kN647 zd}irt-IXQzV04JpTxBb@*PFSLD@g9HGl|`UwQgJHTjRM}(%MO0o^srS0aQP3nhEL` zBP|}Y?j7AMb|Jx)OhFk#(bV@Sc3wcj(5PyhR?QVXh@}!l526w$4(3Zh&m1iILMBxq zP(DnMD0TWeBY)SZF4l6e)^WC(e4zgV-nK@__9%7vN?m*Wk}qo9j_I}aXEtn#UTqpF zdv8%+Lo6lyBW_bqP3j{#fj2k*qrsa)trT@!k`eB1VC8k0u5@(1;qc6d)$hC0Dz`fQ ziMn&zic#}Z4x_QuMXBBmLnu1v{aK7g-syvzG8yT_(G0E8kL~#suMPKYJtkxOXcFTDAHtA?MOy{ED&y&6p@pUE7PA^S+uy)nBE#F=|L#Qb79$PrnD-`2fcFfrs_~) z@{8+LvgEdO8Dyih^udz3GJbTT-}Q*_rQiUROl*4X70Zx{L^T^b9&${XgRM-L4A@9| ziZP~WpSOrCOy`~2zHTiO_kx3S1K&^zUOXOIBz=}g1w6UV?*AbX^ju6c%|EXihVgC)9xY=sdk85r^!3ZbqzewO>wS27o=T&GdOPSJLlaWhaswK6Dqz+BVD4>5m{3*cTT*odYKIk032*EsG{ybNFWS z23ww@lWcE63sJ~t%EVA9#aopkcct)e{CIMM*(<}&aK}++ta-yfE6cg^0i-!E6M}H}=#V)N8^El|%t0I{=YgE&~ zp_N*V{asz4YR`@9_qpPmZV#io=wP*H#?loD6+VU^b%(>>Hhmpvd9^5{k5Dl)KDk#X zp;Vl^V3zc~VMK@;1kOc*&nvyQy5&-OQt*MCE0h;wM|`N@yW4A9(TnZK>@hDR9IkM2 z%itxUk=D{!beE~JR8Vd)@uwk?947Ii+;CYd+1jB^ zYpYmfW3cgMZT^|CKH-i`lbp8hji>OX_2$N&Q?zJD zx+SRH&%p{E@maLapa?L;HUK__tMl2TZ43&)@OWU&sTu zzXyL+#7}AY)$aq~1C5ElR{4$m-v$BTzx?pOd*lBpMgP7QfXP3&hbiU25SE8{3_%5V zQ@EqhsCPiZPVc=p{VRR%EOA(X0BEUBex?Av{^eNPFDn1405ou;pbH=>pgpI`L>3cy z&l?0%xmWi+1z4@&#)H#_z(0FUmqB-5|E?5J95e2B815A1!pnhzdJa{($+LHFuS%E? z$sU_Kz|k&b z8vUPt6{--q>AkHu-Zb z63rrH9m^1tc)DpaWpl^f+wNc#>c{|k?nB7TB};4nxR)-*a{3hEC`+y!ubj44ms|+k z7gzy-(Jbq~blmSI*KDT=Bm~ZSPHZJAK|lpM;_EeAS0xrd#kS2)y}cH~TII28gCu!X z`X$ex8iIBuemvbok(W-3#s6}&5cH4AG4S+mDe?yJdl4PcU%wYQ*Ma8+f!O_?LX<$D dyU!1OC`cc8E6y_H0Y8fYDJwwa^W>hr`Cq9j_Rs(T literal 14533 zcmb_@2Ut^E({2T-yNzB5yo*%h2N3o3)MsR!x59$n z-Qge;WabmO+9-IA;r{!VO|C{CH*QdwN#3Yo!HZUVFP{wPI%PriC$nIvL&sgnc+%RI zyGeKE)}XGsZSBHw;%@Fzd3xQ>`hxE@i4PkAvms($TnDySKyNpuj<(QftW>|m<-09+ zf5=_F+r;t%H~DlnmAa)~WM+=#WIRj-d84`B+vEny#!ce}${Mm%avIuHJh{mfu@_r@ zgi0D_Y(?96%hRL8u#1=^+Sc33B5a>d>6_6Bx0^$xH?~x22=!7Mr1nQchOVRD$IUDg zCzECA8?zWwR{NiYDmC{48=oz5UTe*&2=j7pDCgzQq0PHMtpW zaKKEh3O%8Z7abmRxmY;q(XR?B&;S4=9*YI>bY$~|49NUmW16w93`=3q?k?TRIx*;^+)S!@I= z&V=L(i3W&OE=haCo#^v}X6}JM%`^#m5t=N$2+Fgra#Y^V)>Po&8Fb2?8CCtx0f*?# z=KT7*0eG+1y0l>TS-l%zZy423>&86ZaATfsMiA$x*QKs`U~ILOwLf4_*-PN){xCDoVc=jwRW7wLQqK19wAxl-c6Y+d z@@BpFpu`RD^-tq+ok)geYn7vE;fSAGvI{KshfE3XhfM4nd9~i*O>XXnE)xv)ofG!2 z=ntV!y(g1eHZsL(?|c#qni)S#+1uinplmj*6%E{N+3?$)*!Qb{K9grnlfKasB%06^ z?<}&IDm+~793Eme9%#?44paNMTR#u?;;T!we}uU6PTHbwGxFH*A7DMI*-i>kyLta`*Y#Y@)QvK3v6!Y&G5TghS^GhCIgb+tP41jcmk25{hDSNK;Yx{eelf~Shc4amK>Vs#%$rYS|`O@~&fF!Fq?pHwq^4BU@ zgA=wUpU-%f%WQNB-mKe?A)O3-y<0xP)H+#zT2@~#7$6ejaVKDusd8?|wzW_oa7~Z4 znZaJqxiuwbC1@7d52z zYBKG$3a*~ChBR#`GgU4m<;l%Vc-Qd<945PNrl`<=oN;FgnmG~7t3Ev(^5p+i$-%hy zjA}@B<0X~Y#~DAg^afaEY;Q>J2;VA})RO0aVtONK?|aA~&!r!@;Uon9#WU!f)IUE; zWZ<+*27b@E0!*UIZ$iQ-e?4HD5HhD+jtI=7L0TwOc13vhhX_h!c2Gceqf-PWZQB-r z`~f`7G41AA4OMNp4B>9qmhW;*CnI&KTLd4;>JTl#%o$X_qOEtuPo=}1QX2jrxP$5$ zG)2N@PEB8`4R#@n02qmjG|?2D|o^%nEQLx6YtgxkA>jO>}L;>j?Bf> zz@)f2wIMD0wntdUIcwCiClA!FQ9!g7uj?WUb{2uJg_S3aaGG~aLW04yD4)Sv) zd&jagYo2az(iaT8oQ~ptEjR!v!-HD2bc({L9CmwrLv?==^;}rUcW=qHMI0Q(PwZlK zlglyg=#Qeb9H@vjx#|~TNCVfEh>iHx)q^RK=97W1Fy_Yzo*ahjdnm)r;?X2Tx2Ai3 z(vfr3`eX1)8?NMV^N-2O)fp!Dp~;qfaxdmbnEWnJ@Tu8q_)o=R!=OnXFEH*dFYm-w z(Ocwd?b1maTF;&pgW?i`zKOl31au01(< z)!r&lkf)7@S@&o3!=ZTqu3{D&0=X)A%_m+3x{UyG*P)led zIo|2^)#dxB$I!h__Y`ICW7vYI1KM20AGa&&sj|HzeTcS>gtdzqo-9=?^lxDzVorC) z5=0)?W=uFBx={CsJP%WZXKgUTui1fjYxoY_V=(04Bg|c*3P1Mv;Jxb_8Ud|Db}JZ4 zd8V%r&&DJrcFj4WZRrm`Vcbc@9K6J#V6oG+=+*lD&chgTJ-ht@pG1s5g4BCGOsr$h zalPE&!Ql=ug83v!0*#Z_)ZZEx#6HRQ-VLMix_7eoZCoV@$3U@z-EJXfrV!a_3y~5* zHK~SpZpPIiy>6W<{IY1iyYMXG^o}PeA4{^kDAeR;3)pK-*YGr^HDuIberva8rSOz6 zcYL}xkU=_-wm=nvV{n_shNLB=Mo2ayz$c3l1MwtlwRfX1o&ut!Y^a=3wMdNajk^g$ zbQlGs<9a&NTD@S7KD6FrSUt?^=9+u6g903ZZLK%_iF$`*{CtWlLK+YPn5bYfaQP~w zUA?{4WTe+ZgjfzEZeb@Cu5&OiOol(Zzc7B$m<$BeRMc_}ht?9r1@C&V$DbaIzY3zQ zTp}X|3%{C9<#-lp6^cZ7rayzryF-sHq0b=AL&zI+tYhVtQgHdTL3f>J(EP<%TyKc? zB74$qUb$D95;P!|@wmx9MDXNYG2<;BRO&*cp+mbL;p|ooK=QBV&fTk?ckE{{McVC3 zC!J0RcyFJcpovbmODT?5CU(7z?*=Y5FP!Y-PY=9Lci$^}iO64LwTyw;YOPI2Bn>~! zi7~JfasD z=U{&Lx7&%&T;OZ3CVZ-z-G+&JceN!|FX6UqgEq|>p2Hb$dIOlKu_?N z$#1&K!wqAuE{^Yif&~gl)hueGYB`wN@#EKetCu75KkGYKFb?V>jv5q#hLAcoAGD&8 zi96f^OwS)H*i2jA9@MR<7W=ZR<=lm9)-*gE?;*Qh+X3Alr`&(?>)v2S1$%dyU+`XV z?5}&AEPc^Yl#Il>(A2LxjFdz7A%IZ$JtG}E8o0&%af=hpLRtA)+ol+347-uX(NR6?5yA zGn+o&;~_(^LO3Uj;fMmYPGRjre+iQX1;|PeG9XKsF%b)wes+DoMmLjwOyNc z5@8TsU=3YtD5(oZ5cNI*q4c%zu(nJjFXUVUTLkQpv*s1_gVYb4JP6dj2%OtS{*wLg4?t(i2>_%B2=&+dzZCUbX}?tTa}gewl$4a1 zn0Ov!#g-iHZDeHRfu&kWG9(5ST)F-8Bp-zgSN6VObthRT_u3bUvNctj-B3z#=!?4m zlz!p;bpiN05;>?PHwtXHja<;%Hm1Hda{gs0_nVlVGRe3rkuXQK{aiox?g&b@THZx< zT#a8=p4Ki&WjJj`CN`2`;3xp5Ptse?FgtM*>H11TR$Vg`)6~IP1{-tZ+D;FgS#rr2 z{b|T*aI>By73};Y+<5Ku((KMM)G zIe!*%=3Xwn(Kn6#P{Sg5H*JWgRc=lH?9<}(?_lj572QzHiI=(ES?;F-8s2l_`psGG zoVJWa!Hd_F%tX3cB4FdSQNB_4z^Co>p`rrXVKy}7Z=*^sYL921tn&=@ArMZ~+3YC! zt4N-x%0reW8dbrj;~QuAH~`xA8Hd%lOum*${` z*Wp}aN1>D3YKv;~0#e(ucp}`^A^Vc%mYzuqZP~@+e7n+U{&qS2<_zvP#*r1yl2Qtk zv{rwh;`*#OAi4a$9j*;JB^TcUs^XQ0i`<^OKpl?k%q6xy_eBC)fqY7J1H?}qaTleT zz|Y>~7vOog!+GH=pzj-yD_?o9aj`(cB6@CyMgV%3yKL=q4R9?Ip%I^_NcNA0kjMRv zpUmoYr#bx@Hjai8C82XHKjc0<$qH3I-ndtlLw)5V@`qe##5GH`yD1vw9XbpVVVEyJ zH`XbYMLiCd(hP&mCjIZbYbh7kl(HpPv^bb*Frx~^La&S}1H7-Kk3r$44HqQ1pn?54 zv{^sc0DQbS$RHQ@o`P4zt1UIMt^JYMaUK&d!F48Y)a@O>?rN~93nkhJsK^z<=puXK zQLp5X%ZXD_F9PbOav2`S2O-oEl`{rzuBy-M#h6H}?V6mJE1s|#E=72F(+*|LX)7mr zIwaewxN)a}wWd?rt9(RijtAK><;qX@ay^tc%Wxb1i9E~JrP3K*amSZ=8OL#i*{q|N zYhX@mk%Ps9*6dMU+r_tbHk z*c`Vm(O{i{z4&X{$=k3J&s@6$N4XX+5#+ z7=Q9NU8Cm}Eu!SVQND}n8V>x*ekWOh? zWRSqmyEfp3^riCT^C7$K7un$>;&6+~xW2S2Ht6DFB(3bkkO8!rdns!5M*$qMIjzu_ z_-bmCAUVWd@8)j3NCBpR@?F3 zHPxINwb7vR2xL2U>=@54a@1#A5j>u{_b5cau)z91Vz-vO5NTM+-_uro6C&)fJLNBiH)?%x;i7r_9u z@+af_%>@5to&PZGKPc}nwE@)SH(mM9I{t@Ye^H-*WH^6{2r!G^M(|&V(En=Kf80(% z;T)fcNbP0NPmilsr$aC(DJf|yqx&)y=;h<j=!|90RQOj+Yr*hXgDs;Hh%G0;bx5Hoh?Gx^Lw z9}_ZRIIitJ<0!XP{)0=mJd~iIf^%~vTDJog{Cc^D0^8ra4ks)$$FGI^V(FOM%cazc zx(=*-wtY|Z3N2-U!ESRUcPT=DIflRPd%#8k+tP?h2@yrSW}2GzXMzF?Fjuo*1GT7OjhJHDFPzDCHtpU{`WiSzf<+I$m&e3FJW!w2!zgc zB9LENkZ3d*g0KGLXsPg{9@_-rC@aRD~Q5(86tAfiIJT!WruNs4xdHm7!TgF-QZ*b+ej z%H4!;Z_BDImyh9pNJE+}7^;GJSu!ql76ztz|_@C3v2fP;F zM<7JD<_O*+h~;g^ZkDP_L-K*ka7V(dA`N2L-BqGtxuj8utelM4cNIuY&>fUBnsY;7sFgJzjgD#sy?;6~4*H~wN=h)@)x-8LG;mBBk`kwF6akDZI?BHiyuE8*3 zJ~jO)$o!pdK3n%D_vbGgb|l&wlf-2Hl*0P8nCtf3sHgupjErf8QRM~+&JKHv%>dmkEg17FP%{c4|s zR35M?ix?Pl;IQHqZ1xLYIBiwb%9F$6Pm1|O18b^Rsl*?2eVNtC;m%Tm9v^3ONm%9`Z^~=Ryr(1N{_*>D zVkYlg5GC!I2;$duxgG+jC3u#heNb85lMYeFQH=I^acD;D_()>OY>S7Aj#!^r_KFZ^B9#GLa=J0~L}6tCh@g+O>vOKf=*fKnO+7~t=)mF8LExM%My zc~Dv)Ii|G94o)spR(N|07SZSzq!fQSBkh^W+=aJD4Hy)QN@Dj!eJ}D5zps1ig(kjn zgvP&eUTsGzC&ELH#jl@Zf3uFiowE_HqxaNt_Toa4)7IDK7Ntl>C;P}p`uHNcHO~5D zxS3YSnbd!bsTIix6~#EA0tGf$(F)2oA1-fa$1x7p{XC9?IjgYi!w0DaBCDR%(tX>) z`o>db0Tatti-sl93KVMH9>k{wl&ZPW)!hZK>R0}-LEj2q=%o*wpbPkmMf;733ZS5iI*>^lMk6iw8BJ)0@M9Q;M|@LjsOn0~+OT z{4o}}TEpz!@^ZsTmy-VyJkW)ZSPMw|9Jct|T2GSsU&)rQ?0NAc`*Ad~o=kT$6viOe5on#?luCuAL?+ zs}#bTmKk1tkG>}kjku4Rf=yKa$UJe}dWp{DNUTuL6%(puL(N@ZP8;e%BftXNY5F8$ z6m>^-nSBqN$zEym6HKdiHPG8HJc`^;=Jw{5a^}wMlrHqd2*j%pf5kh*BQXA_f^PmP13YMH*7ZO2L@(G`&m|8YS=shmZDOD6{gM z3U3!xyA-6u7*981=BlY%<=HVjZBhBeszk62v1ndTc9Xt9!(DFBZ)6kYte)5C6@1_Y1mOoVOtR9Uj4W(1XMwdEMKb-nbLzkJL@9#qrnS+Y%b~p3vkkP$w z<}~~Qqa)dF4!NiRZ7>OgiCUFc-14Az-^tmT&WHHQtdmGe5@fq}WJlXWpkNBT?`YGz zfhD*FBHbFlHB}#3y6IL#o}#R@Vv@m{D|W?88koNR6GCAh=Gj;eYHzfhUbv{~*Q_&S zp(GY(R2#k2&xHai4}j_x*QrI_MPF~-L(TUJ-e(IHTtitF0U=gjBx;9 zXvx3+dZd}fuk7l-277-?(U;upM5C2~pK~BT7K^7eH*fwhSN}lbas^btlgt*0)X470 zM4W5byk+K3h=dv4ueP#Tr|x-_>uOKCX_U^$4kW-m;9Mfyo>X2se&uHjkdp@`zmR@J z-!SrSkCVpS+tRdVa9J@QA0?J;Zen8TQg5Y3kpO$4a)3k<#Mjv+&=ceqb=kO-!M@nuJc9NvUwL>I*V+&sFP|VcZ|Zl zR2#P}f^4~!D|r#=%&2x9?R?A64;#+G5p#XBpQ^XRg7qtxS}W>g8LT}mMo)}(-BjD1 z)q{*zcI|?dn|=rkJ#17L>MZ4{RUaKpCBr`n{FH)`lw%P90XR z!UM|;#{Bm>-`MWh28}HC@*iZo6mWMu%?`6?oE;V=FFj~S;}Gg6WSpq$)}E?!k-^1S zBAZw*lRmXlqdFfr7g-N=>#js2!U;kqoTIrm5mzGCgL+TBd6Rr^X!BpkJ3Q<$RF^ft50 zC08E8CvrzjxUS)?`rxg#$uev2X{Ev7#iFkUabEj;YYwyDT9Rx=o%M>M_~fGXy-_K! zF)i_7&W@c_9mIQ57F$e=gz(J__AaN)4M)D?M|7cbwVQvC)Ner;yd2!Hw%m>B7rG4B z=i=k<*O#qysLNN@k7{2=HXYDB)>9efvi2ND^j;jzly|ea1yO3(pY4c*Iah_VaU7Ch zgXl)WmUh2}+TuuGK$*r~+3Vt}Hg@mYh967@^aWar8Eg&2Slqr(CfJhJV)D3vf^c3g zrpdaojAha;>CWkS8+`77dqnMj=);9MD#CzWR0 zp%Tv2rv%CY7>l+uPnr*$_F zWyg>_E5ka9e@{Q&VmUGTiQau>c-&=(uh(8-l?pO(XXxcA%i_7>LjURRzsr;bE9-eqLHG!>?=iIM?yXxxZUDD*(4(SU!@<*%-PhHp4iz1KLI%Rit`d?cp7Hwj7&p7uI=r>> z=dX}0K54@>gqpgU?>(+qwyI9TVB5`KrjfAUszg?K42@v>(~`?du8!BApDTHYjl8OG zq1(;wYP$EcYQAeovMXdKIi zgQ|``4;$X0bnl0#*QqoY9^R*;<@;RLuli#g0S&gcOJnKClt>#=yVI2hqiy6=&sujD zj#k|`Aw(-KMVY=R(?{ca;ydE{e4@A_zohAE2qw?@eYpR6zAdEZ+r^)%>~yw983}QS zwA_4MaFvdTljI-TZH3cjPw*^bspM9Rx@~P&;s=$oAD9wBe9j8vzc~@`M&k}@gPvbI zxK?KtqBdyDauo!zrh){=C%0%o8@pA?3@+kT$;6=u;P;7yw=^lhrOe+VX;1WCuQz29 zvE^JvS3z9ETYa|F%DTlz1SRo(8SLz2-BQDXSmRli?9$-q1%9$FXB~8o+k98w5$$_Z znv3Gl*}JHo$OM9zI2jZ8Tkwc~0kVcbTfk6IB*n;1~!+1_#U z+}vZ6vxz#LM^yd3#tQg<)~5m(Zve9DxN@(5(-cIblIvW-H&>bMDIWNs+W6J)2ryml{=^_p)k)!t*^vhe#UVYPaeVEGae5+ zCAQc2UyJi`bbDMvE&wqXIqf;;9M@6l*3x+sLKdZr2|u_pumps`&_={pPGMbksI~W; zl(Cj=k5q7)Oc~~Bl&`4l%jo*_+d8tM5c5&`QZl$jVyAF{LTRNEQKP;7ChkGC+2VOR zARRJE$4r~PY!xe5x{}E|K*ubk*newNcJygf?vNd=shK|Q^jsDg-<9YM46a zFROOA>f1T;xfThL&!|H=Am`@zh967iEL^ob7iLZG=PKWmK^hSb@ShhDYA@4#Z-ugl z@tD#mj8?)6KyPhh2p}c`!b$+(f4`m`{!_y8Z<+AlX~16@;(yHz{yO=?9649QuUt+? zZCO(up)QzRzQx^UQQK~(US2&yTpI1mFWTCdSad2_^fgJkY=(#h^&}LAyFG=&RG}fJ z6$|6Jn!@Vd2Ya9Sa$a*6To-M#!?b5|Moev9zqxj+-F(>pDJ>*-O715&L>#Vz2iP3n zBE*^zf`jB*%+Hk|;DO$n;N6X`Ilf+am80(T#`v-FmY2o`lI+BBv}`u3lCU{hCbo8* zYK{on%tL%N9h#JRIldV2NQ&?E_)~RnHNWI_U2H2 z5nXfNWhN6?nb3OBFwB#|zCfmLC;@zs1=cLlAlhb}qME>+yp#$Ocb~i1g z8FCnX67rEjGaFZRDYHuJie~QN0)$rkh!{3+Yo+*cWnvg_K2hl8e`i-*gN%8=qwvFwK;ivpTu2J5D_Y0n#V<#g+13wU<*eD+jjQU8l_Y%)znbK`DcX;> zPDAji2Z+y)TS{0n#UY^DfzW-&Oce&wEL{=9tdlWwp3xSDJoNzhXrH;H(qzz!l*qcP zzE-xJ?T6pq@3%dX-OEL2hGjMemYvg%?e(aZTDptl8}d z^AY)a?1$0eIbxf%*?#BcF8|@QxoqLFX*GT(Y3K8Oeg&CNv+MoavB(v>j!Lr;bb7cI zxIv!ur>e-M{>H(<+*wgRjp8L`pB;}&{TZtl2MQAf7~Bi2M!ChyQgNUBqshqVOX(`i z&nmZ1N-j5g3VlQdilqtjk<@Aw&ZnmKj0r;)t2eni|POm9)Rp&Y{BED-#;iRkv4tW;PT&iF)+xVGO>7l;~o8=g;&_fWOl%*c<3(FeKkbkeH;$t(@(*4C`ilDG`J9kK^c;|2 zbY(-8Ms`@zUHBs)_56|HPj6O&Yqii2Y#m3gk)GlVNMo!Du&4yaUgD-qOxYtq#b#+<`1(W9Jy{=R`+s zOw^!BgkJZB$gbky=#x2=PGWFuIaC|KM1FuT?CxvD#4YIOq6=@u@$x;@(MYmn6|C%S zh}EStz03=}Fl1EKH>I8yr6{hQ)Wy#|ZQ)jCG9eht1A>o)!k0w-B``JF!4VMtquA$L zjhwWn8s1Z#x!=rZmK?+#WAjRk3knbE4T(&Pb7M?nh8spmZvsed2f1kKV-@RCdRX$2 z3_`(+vODpV_AT$TuIh{G$n3BxBWx~nu-CEzEBu1=OEAcJl=dpdO|#>kT9@P-LL056 z1HVgBW$7cyMW1zzDkV*!xdH=xio|FlD~uOC*s04N;Z%PK$PMtIqAS^myIV*4yab?E zX&I|6e^hRpPy2oNl=5ldwnU>YPEg;x*y@s!_^{o_;E=hDuC)o#AiOFj!lDg;+O8wn zijyf>xATC0X4%+_A9E}JBOd)Kto`qy=P&m1KVjJjA3QEGQKJ_0(oISU=wl)Id)4hZ z^W&A_Vt*iWAK`-gZ>2Sfgg*;vhz0z^R{G-Ct)JRN)}xQTr+kQQDzVX(SKbWe z=>tMcMsk9GwfALM%En+)8p=)TkFL-_ZsADk8v#bQO;8oEM7G7x0jdS$R8l>2GuKm;t4dbPIFoq80DEjcXR z<_?EN>Ka8_?fQ`2L1hdi#dnMx4@6){9N5@%Vvw&MW62iBI-ei$=|FN{qs^_bYF2(? z)$Ka8ln~=ef)w=*=0Y2%&GzmZn$`AS`9P;$n*V@qS`6Gd z9hsh~(mm95qnn@^Ohb*Z)dnKflYUsaMhxMR`GgNp;aL&4a2I`-XBJ~rTf-n)AnX-Z zix$3JyPJlUi(stfDqq>>s>M=MrdIAguJv3~5?Gng*ZV5sc^VPch9fmJ6ZvU1+3wF| zkX_&nw4J4Poz9$iO>(*=#ok%fSUG)l@a!mg2rZwHuBE3~+=iiYQY}|Vyx;XD+ReO| z9%95WN400p1bJ{5-sRR+)aU2E zMJD%G?#`23A$6NK-$p+0f^!8EtP{r77s*(T{a(e%i}Kn=$kgV_sxAss8#cKA2QnpxW<+Gux zpfaqeSF*WYOih9vnRN?5S}xIvd|L@->qbg;xJoqq)9|Q>FGC;zZXxvZerw+-mJkK& zy_jivK)%?<@lfrcNGyY0`--p=}5w(Zuinlj@8dQpqli{i1 z3E6r@T>Oi2l9P#E+wgYh3g-gss1)s|w3jGC7|czO7bi@e$G?(HT=w*+%&e$+Vfc0OUx2|;qrsuHq~V9g3j46XS~*+aG;I8x>E%+hY7JFR#PR%jh1 zo3c90dweeJ)YNCV@?@+^%p7s$2?GKA#w$PKfl+2|Nw16RwacL0;u zK;Yj;+A}3Xj5f)8u3n#oEAI=Uuc?F{N_AgTDHy5{;QZ}8XfslMyt{>$#}Mv2%jU7V z;71sX&RosPF)8TJ3-GujA{KTBd~6i*Pq&mRcb7fq#uTBPTD zV7WC7+^}KerkvSiohKz?iubVIeRp;7+B{omhOyZ6 zACzpnUvzeu=KfGAa#@>Lp^J%@dBaTZ(YNbeoIv#}b^l)~K7TgxynPad0jknKp8u)> zjVF)c-#aTn2?3oYrszcVNT9no`#*LYH{5#kySaFNH;l=l^P#ZwGEgc1$)nibbXm8| zYPi}33?v)VU#g}@2JJW;?yUS7HT~Hz^bf5m1?E^sJ7>!s0Xbb$iu9m9&7dHM$Gw}e zKwB{=CkpV8#*9k=99}VPnMV(SbmX8oG%UD%RjwrRSeWE7P|OO&gdV47c{1yFbvAdD ze`-Ig_|Ne%1KvF@;dWHwS*kQsIJXhe#)_hzAboaLEALnoE*-!2Fj<)SS1H|9j38Gn zmo{BjR|p&zn80=7kAW_WU(otHV!Hx*kWTNp-Ah@R?K2<*(n2!JMFqp`HMuG2RxuZoCx%Q jzX>h`Gz2`HJtbm+e&2QKz{7z5G6cy` window, which you can jump to by clicking :guilabel:`View Output`. You can filter the output with the drop-down above the output text. - -.. note:: - The output this will not show any messages from before the window was opened - you can click :guilabel:`Reload` after opening the python scripting window to see some new messages. - -To avoid bugs in extensions from making the UI unusable easily, python extensions are not loaded by default. Once you've loaded an extension if you wish to have it automatically load on startup, you can do so by enabling :guilabel:`Always Load`. +To load the extension, tick the :guilabel:`Enable` checkbox for its entry the extension manager. Python modules can't be unloaded but they can be reloaded if changes are made to the files on disk, which can be done from the :doc:`python scripting <../window/python_scripting>` window or the status bar. Editing your extension ---------------------- diff --git a/qrenderdoc/Windows/Dialogs/ExtensionManager.cpp b/qrenderdoc/Windows/Dialogs/ExtensionManager.cpp index 0f684383f..cadc8b47a 100644 --- a/qrenderdoc/Windows/Dialogs/ExtensionManager.cpp +++ b/qrenderdoc/Windows/Dialogs/ExtensionManager.cpp @@ -48,7 +48,7 @@ ExtensionManager::ExtensionManager(ICaptureContext &ctx) RDHeaderView *header = new RDHeaderView(Qt::Horizontal, this); ui->extensions->setHeader(header); - ui->extensions->setColumns({tr("Package"), tr("Name"), tr("Loaded")}); + ui->extensions->setColumns({tr("Package"), tr("Name"), tr("Enabled")}); header->setColumnStretchHints({1, 4, -1}); } @@ -56,7 +56,7 @@ ExtensionManager::ExtensionManager(ICaptureContext &ctx) ui->version->setText(lit("---")); ui->author->setText(lit("---")); ui->URL->setText(lit("---")); - ui->alwaysLoad->setEnabled(false); + ui->status->setText(lit("---")); QObject::connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); @@ -89,7 +89,7 @@ void ExtensionManager::PopulateExtensionList() RDTreeWidgetItem *item = new RDTreeWidgetItem({e.package, e.name, QString()}); item->setCheckState( - 2, m_Ctx.Extensions().IsExtensionLoaded(e.package) ? Qt::Checked : Qt::Unchecked); + 2, m_Ctx.Config().AlwaysLoad_Extensions.contains(e.package) ? Qt::Checked : Qt::Unchecked); ui->extensions->addTopLevelItem(item); } @@ -114,13 +114,8 @@ void ExtensionManager::loadExtension(RDTreeWidgetItem *item) { // if the load succeeds, set us as checked. Otherwise, unchecked QString errors = m_Ctx.Extensions().LoadExtension(e.package); - if(errors.isEmpty()) + if(!errors.isEmpty()) { - item->setCheckState(2, Qt::Checked); - } - else - { - item->setCheckState(2, Qt::Unchecked); RDDialog::critical(this, tr("Failed to load extension"), tr("Failed to load extension '%1':\n" "%2") @@ -133,28 +128,6 @@ void ExtensionManager::loadExtension(RDTreeWidgetItem *item) } } -void ExtensionManager::on_alwaysLoad_toggled(bool checked) -{ - RDTreeWidgetItem *item = ui->extensions->currentItem(); - if(!item) - return; - - int idx = ui->extensions->indexOfTopLevelItem(item); - - if(idx >= 0 && idx < m_Extensions.count()) - { - const ExtensionMetadata &e = m_Extensions[idx]; - if(!e.name.isEmpty()) - { - m_Ctx.Config().AlwaysLoad_Extensions.removeOne(e.package); - if(checked) - m_Ctx.Config().AlwaysLoad_Extensions.push_back(e.package); - - m_Ctx.Config().Save(); - } - } -} - void ExtensionManager::on_extensions_currentItemChanged(RDTreeWidgetItem *item, RDTreeWidgetItem *) { update_currentItem(item); @@ -166,22 +139,25 @@ void ExtensionManager::on_extensions_itemChanged(RDTreeWidgetItem *item, int col { ui->extensions->setCurrentItem(item); - bool loaded = m_Ctx.Extensions().IsExtensionLoaded(item->text(0)); + QString package = item->text(0); - // if the extension is loaded, don't allow unchecking - if(loaded && item->checkState(2) != Qt::Checked) + bool loaded = m_Ctx.Extensions().IsExtensionLoaded(package); + bool enabled = (item->checkState(2) == Qt::Checked); + + // if the extension is unloaded and the user just enabled it, try to load it. + if(!loaded && enabled) { - item->setCheckState(2, Qt::Checked); - return; + loadExtension(item); } - // if the extension is unloaded, if we're now checked then try to load it. If - // we're unchecked allow that (it is a code-change after we failed to load) - if(!loaded) - { - if(item->checkState(2) == Qt::Checked) - loadExtension(item); - } + // update the config after, in case the extension immediately has crahed + m_Ctx.Config().AlwaysLoad_Extensions.removeOne(package); + if(enabled) + m_Ctx.Config().AlwaysLoad_Extensions.push_back(package); + + m_Ctx.Config().Save(); + + update_currentItem(item); } } @@ -218,10 +194,17 @@ void ExtensionManager::update_currentItem(RDTreeWidgetItem *item) else ui->author->setText(e.author); - bool loaded = item->checkState(2) == Qt::Checked; - ui->alwaysLoad->setEnabled(loaded); + const bool enabled = m_Ctx.Config().AlwaysLoad_Extensions.contains(e.package); + const bool loaded = m_Ctx.Extensions().IsExtensionLoaded(e.package); - ui->alwaysLoad->setChecked(m_Ctx.Config().AlwaysLoad_Extensions.contains(e.package)); + if(loaded && enabled) + ui->status->setText(tr("Loaded (Enabled at startup)")); + else if(loaded && !enabled) + ui->status->setText(tr("Loaded (Restart required to disable)")); + else if(!loaded && enabled) + ui->status->setText(tr("Failed to load (Enabled at startup)")); + else if(!loaded && !enabled) + ui->status->setText(tr("Disabled")); } } } diff --git a/qrenderdoc/Windows/Dialogs/ExtensionManager.h b/qrenderdoc/Windows/Dialogs/ExtensionManager.h index 5089c3472..9a3bf677d 100644 --- a/qrenderdoc/Windows/Dialogs/ExtensionManager.h +++ b/qrenderdoc/Windows/Dialogs/ExtensionManager.h @@ -48,7 +48,6 @@ public: private slots: // automatic slots - void on_alwaysLoad_toggled(bool checked); void on_extensions_currentItemChanged(RDTreeWidgetItem *item, RDTreeWidgetItem *); void on_extensions_itemChanged(RDTreeWidgetItem *item, int col); diff --git a/qrenderdoc/Windows/Dialogs/ExtensionManager.ui b/qrenderdoc/Windows/Dialogs/ExtensionManager.ui index 84ef9647c..8b3f3d776 100644 --- a/qrenderdoc/Windows/Dialogs/ExtensionManager.ui +++ b/qrenderdoc/Windows/Dialogs/ExtensionManager.ui @@ -140,6 +140,23 @@ + + + Status: + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + + + + + + + + + Description: @@ -149,7 +166,7 @@ - + QFrame::Panel @@ -165,62 +182,6 @@ - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - 0 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Always Load - - - - - - - diff --git a/qrenderdoc/Windows/PythonShell.cpp b/qrenderdoc/Windows/PythonShell.cpp index 986a9f938..473e34861 100644 --- a/qrenderdoc/Windows/PythonShell.cpp +++ b/qrenderdoc/Windows/PythonShell.cpp @@ -1758,7 +1758,7 @@ void PythonShell::projectExplorer_contextMenu(const QPoint &pos) // if this is the root node of a UI extension, add options to filter output/reload if(item->parent() == m_UIExtensions) { - QString itemPath = item->data(0, Qt::UserRole + 1).toString(); + rdcstr itemPath = item->data(0, Qt::UserRole + 1).toString(); contextMenu.insertAction(contextMenu.actions()[0], &reloadExtension); contextMenu.insertSeparator(contextMenu.actions()[1]); @@ -1783,6 +1783,18 @@ void PythonShell::projectExplorer_contextMenu(const QPoint &pos) QObject::connect(&reloadExtension, &QAction::triggered, [this, itemPath]() { m_Ctx.Extensions().LoadExtension(itemPath); }); + if(!m_Ctx.Config().AlwaysLoad_Extensions.contains(itemPath)) + { + reloadExtension.setEnabled(true); + reloadExtension.setText(tr("Enable extension")); + reloadExtension.setIcon(Icons::add()); + + QObject::connect(&reloadExtension, &QAction::triggered, [this, itemPath]() { + m_Ctx.Config().AlwaysLoad_Extensions.push_back(itemPath); + m_Ctx.Config().Save(); + }); + } + QObject::connect(&explorerOpen, &QAction::triggered, [this, diskLocation]() { QDesktopServices::openUrl(diskLocation); });