From c9152cd27b8279c012fd82d54fe7fc10e50dfce2 Mon Sep 17 00:00:00 2001 From: Mateus Rodrigues Date: Tue, 19 May 2026 22:51:44 -0300 Subject: [PATCH] feat(ui): Input/Label widgets via FSlateBrush + Lembrar Acesso MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - UI_Input_Master refatorado: SizeBox -> Overlay -> Background/Stroke (UImage) + EditableTextBox. Cor magenta default eliminada — UImages atrás pintam o visual via DT_UI_Styles. - UI_Label_Master novo: wrapper "label + input" com 2 slots de UI_Input_Master (Stacked/Inline). Reaproveita o input puro como composição reutilizável. - UILabel_Base/UIInput_Base (C++): hierarquia limpa, propagação de Variant/ HintText/FontSize/PreferredWidth/Height/bIsPassword pai->filho com passthrough em 0/vazio (override só quando setado). - FUIStyleInput*: campos avulsos (BackgroundColor/Texture/DrawAs/Margin) trocados por FSlateBrush completo — DT expõe agora Image, ImageSize, Matiz, Desenhar como, Ladrilhos, Margin, UV no Designer. - 11 texturas Hyper copiadas (T_Bar_Gradient_Background_Long + 10 borders) e referenciadas em DT_UI_Styles row Default (Backgrounds + Strokes das 4 variantes Box/Outline/Underline/Search). - ZMMOThemeSubsystem.Initialize: força chamada de ResolveActiveUIStyle mesmo quando ActiveTheme não muda — evita ActiveUIStyle ficar vazio quando DefaultThemeAsset é nullptr (sintoma: Brushes não renderizavam em PIE/runtime). - "Lembrar Acesso": UZMMOLoginSaveGame nova (USaveGame com SavedUsername + bRememberAccess), load assíncrono em NativeOnActivated, save/delete em HandleLoginClicked. Slot "LoginCache" em Saved/SaveGames/. Padrão alinhado com jogos comerciais — nunca persiste senha (CWE-256); refresh token + DPAPI virão quando auth real entrar. - WBP_Login: Input_User/Password agora são UI_Label_Master_C; checkbox Lembrar bindada por nome (RememberAccess -> UUICheckBox_Base*). Co-Authored-By: Claude Opus 4.7 (1M context) --- Content/ZMMO/Data/UI/DT_UI_Styles.uasset | Bin 33436 -> 52184 bytes Content/ZMMO/UI/FrontEnd/WBP_Login.uasset | Bin 0 -> 186612 bytes Content/ZMMO/UI/Shared/UI_Input_Master.uasset | Bin 0 -> 35799 bytes Content/ZMMO/UI/Shared/UI_Label_Master.uasset | Bin 0 -> 30816 bytes .../T_Bar_Gradient_Background_Long.uasset | Bin 0 -> 6068 bytes .../Hyper/UI_Borders/T_Line-512-10.uasset | Bin 0 -> 4826 bytes .../T_Rounded_Corner_border_white.uasset | Bin 0 -> 10130 bytes .../UI_Borders/T_Rounded_Corner_white.uasset | Bin 0 -> 8520 bytes .../UI_Borders/T_Square_FullWhite.uasset | Bin 0 -> 8197 bytes .../T_border_frame_long_2048-512.uasset | Bin 0 -> 9768 bytes .../T_border_frame_long_4096-512.uasset | Bin 0 -> 8189 bytes .../T_border_frame_rectangle_1024-512.uasset | Bin 0 -> 7787 bytes .../UI_Borders/T_border_frame_square.uasset | Bin 0 -> 8682 bytes ...T_border_frame_thick_glow_2048-1280.uasset | Bin 0 -> 12735 bytes .../T_border_frame_thin_2048-1280.uasset | Bin 0 -> 9457 bytes Source/ZMMO/Data/UI/UIStyleTokens.h | 554 +++++++++++++++--- .../Game/UI/FrontEnd/UILoginScreen_Base.cpp | 342 +++++++++++ .../Game/UI/FrontEnd/UILoginScreen_Base.h | 105 ++++ .../Game/UI/FrontEnd/ZMMOLoginSaveGame.cpp | 3 + .../ZMMO/Game/UI/FrontEnd/ZMMOLoginSaveGame.h | 37 ++ Source/ZMMO/Game/UI/Widgets/UIInput_Base.cpp | 325 ++++++++++ Source/ZMMO/Game/UI/Widgets/UIInput_Base.h | 156 +++++ Source/ZMMO/Game/UI/Widgets/UILabel_Base.cpp | 181 ++++++ Source/ZMMO/Game/UI/Widgets/UILabel_Base.h | 131 +++++ Source/ZMMO/Game/UI/ZMMOThemeSubsystem.cpp | 7 + 25 files changed, 1768 insertions(+), 73 deletions(-) create mode 100644 Content/ZMMO/UI/FrontEnd/WBP_Login.uasset create mode 100644 Content/ZMMO/UI/Shared/UI_Input_Master.uasset create mode 100644 Content/ZMMO/UI/Shared/UI_Label_Master.uasset create mode 100644 Content/ZMMO/UI/Textures/Hyper/Bars/T_Bar_Gradient_Background_Long.uasset create mode 100644 Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_Line-512-10.uasset create mode 100644 Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_Rounded_Corner_border_white.uasset create mode 100644 Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_Rounded_Corner_white.uasset create mode 100644 Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_Square_FullWhite.uasset create mode 100644 Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_border_frame_long_2048-512.uasset create mode 100644 Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_border_frame_long_4096-512.uasset create mode 100644 Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_border_frame_rectangle_1024-512.uasset create mode 100644 Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_border_frame_square.uasset create mode 100644 Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_border_frame_thick_glow_2048-1280.uasset create mode 100644 Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_border_frame_thin_2048-1280.uasset create mode 100644 Source/ZMMO/Game/UI/FrontEnd/UILoginScreen_Base.cpp create mode 100644 Source/ZMMO/Game/UI/FrontEnd/UILoginScreen_Base.h create mode 100644 Source/ZMMO/Game/UI/FrontEnd/ZMMOLoginSaveGame.cpp create mode 100644 Source/ZMMO/Game/UI/FrontEnd/ZMMOLoginSaveGame.h create mode 100644 Source/ZMMO/Game/UI/Widgets/UIInput_Base.cpp create mode 100644 Source/ZMMO/Game/UI/Widgets/UIInput_Base.h create mode 100644 Source/ZMMO/Game/UI/Widgets/UILabel_Base.cpp create mode 100644 Source/ZMMO/Game/UI/Widgets/UILabel_Base.h diff --git a/Content/ZMMO/Data/UI/DT_UI_Styles.uasset b/Content/ZMMO/Data/UI/DT_UI_Styles.uasset index 4ae300317595837e7e0a072a79901540ae99ffc7..2406533038a36315aeb7b7fb4055fd559f2adca0 100644 GIT binary patch literal 52184 zcmeHQ33wF65^f1{BLM|bL?g-#Nk|}s1a@-~5==`sz}&2HG;00EB>4pEUq zltV$eK|nn|xCen&AMc>eoK(CI@xTJ?P~&akn0+}n#lF35yNT}x`y<|S@! z9MFVPI(E$N-K}GWWRpxTsdKmB?kt~>lY>96;XR)gVn=v^wLhE$wjpHchcb=-rQ>lC zl>G}OtQ7KYpw}UpW~a5t9TM+wO4(_-!zHtu{F>a^0JZVFOqld_e4(;9d#lN_TY zCmE67q){NyTrSC-Tv+6Ax?woUkv09Z>T!BMI@HAWsG$KENu0?%BH!sKwp&t7&U~w# z1U73j5yX~fxQJyz-gM|pIW0>X<1VJ1xw&b>*IUq>+}NTbTS;26+h(;(S&js!qbRe$ zWO0mkkq0JCE`^$M6CF;ol*KYMCa9Y&J7d|>v8hw8HHehz1$$wpo(_~1wAYg~T1Q=)pX zkhEP(N6Rc3CMytjwOm#p6EchKBP2WdD*cC#n$j}4$*zPFyQ$D>HrZ??DGrl`azDO5 zbrmQzH^r1I*|Lj@B&V607BOx#-gYfiWGj})=~qf`zaE%U={P&BCR?&A(=FM}RvUS- z*VArRKGjOIBn!0yyX16{oPqnNuvR!MC5a9?q6hmMz68sNb2x12RD+V!T|xqDu6+bX zJ?VYhh4@yYz`->H=uXja+<7`!QU1l0SYa1geQNn? zre1cP>Bw`FCHqougT~6$0ZFdo-k|KM<@Brqt9gW-YL|q*^-XDc-FI6p?gH}e&}G}? zCcE8^LUQr7u}j&2I>wNp-7TNjV&cby<&kFd9$X`9V2oVLwiyRj$PFBmX&oz(Uze90 zVeHOgR{;tBshNf8I8!PzIjNo#tu|Y%t*F364!ky`HuNO7e0XY~Ioh}xBe|qFD4*pt z*{SI{>8O;Dmf_L{*2ZGmMmyVCho(a}<*PL_ zrF>`_yK<4%5$!rdJJ@BmOUfv=NhG1Dpe3kXwxtY{#cCxxPba@EGlG2qt=L88ZX3D_ zT(_LD*p*4$#75%3OJIhTV6x{^!%BVj%n(q0f|O?}wzyRL(5^h>$vfsV7EL$fHg$Yv^GNHg}yLvhfZ1gB|qtczUNvPl%QvbVjs z(4*G=Uo40LB<;;AVWS1?StUgh>3@6N*_*uQMMZ%^Yuh{g4jAO6a8y)sp($S?AJ!SS z3jAAOVHvD~9w9xq(AmdpW>i!^hb&^`!mUq(ISC?Ctxl)I$z()kU1tdLl?y^-UUKaB z9pTL@@?`+{TJ8Bv&L_oLaUFc;M@1!>YW9~#}0y>T%_cVVM5QLJ`O?Jt~r;zmBFRW-^nI09DL2ZrNrcKI8ayYGH zDMFfTIiX$1hre~3sG&d@X|iumM-2tS$+9h*QXuwFG*xU;=E!1-B+#5b*>iK+`ezHqM4QpA({@XpobU-iP##-^*|okUOq~ z8ENL4e%}H30m(&bKh=B_;7X#+VRC!$;rxoP|6n{0GsQGp%44|zA&GDW^C?V%fi2qC zg4vj1ce`(j7RwxVH=mHno<7-y0KC9~QQGs4fOo)o7)cIZG9|a08o!Gep8fa;myt$; zE2ovZ!&~#m-yk={?sk#9l0VFF)u+JWqElzbYxY75`q&(!$+LA#{n&-WW+8L71ej|> z4!uaS64&|eC*e}|V`AJCwj2VMB&!`K+dG<+eE?OnshoH4m&A|XfaWt|CbcGsw7#Zn z3zTFC#U{)^XRN^S6{d7K=J{mfcBGCvTWb%~bFhVI*QIa#0#&T3$q=jK{A3**T8vmJ`@{rsR z?@9z(s;S5`lHHydKOXvy(dO9;**8Glsa88`BWu$a;V@rPiX2V6wC3AN;3& zZgW7A@>o*l3~X~?G^h=x9Z!X2XX^BYw)b<`C2}_4orVBS{fY~7C1+Y*x>YjM)yDB#_m~n4BDDzhp3ixTAI`g8j@zuEOzG65S9i>^InGvJ(L7%Az7VyGKLA` zY^lXGNFc8+Zpr3XjFHKXh&jtyS=sDljm>V|X&&?pnLMT&clQrF1-i#ZPpo_@6*P?e z%me~^Wql~pAgbH9xx-i(GtTFBI;b8)KYsSdmXw-K*8p^uOmUb^HtSfE+v>1`3R##= znz&1*fH-7gQ@Z7J_y(|#bdv>UF4A@QgCn6YcrC{lT})H1m6>c{DWp}qHbyW+2L5D0 z?cJY)ZrN2BdF6$hOrFbd60q*RTF_sXTfXWbL3iJ7mn)(Z2-jqBOsg+hSsHgcETqw{ zdiOIPIr^6sn;v!JYJn)bPxpg6-efIwS_@6i5|TMQWH0M#IlPg(@8~}VgyU?xecJ)H z=#nqxj%FU~{2!2=A-NpIbcw@MWLVPr3|IjtBH)Z0eA9YRLI%v>r2fzCZLFji0-TMr zb_O%caE#7$JB#UB^63S20wIR;P|{8A0#-|8`;ILcFO3Q1jwXHFsTt#?6o}r-BlWBy z$z@;z5I>ev9~pmPBOocY+(*U$s2mt2kA`O=g+6NBFjP>RG@Z&6hgfu#~ zkgCB=reFL(E&?lN777w)(1!6hLlF;&8!`v$hwE?8VshkVHcSYo65eC|Wrd)1$@V-4 z+5hOWqpTmEmCel7UoL==@&qulB43D_ISY&YgZ5lZI&4g<{gO-QSlz8zViV!CFhtEEZx9mC{n89=5TY#73HC<98Ii7qSqq_>TM zaYB&6C99>;k;wuT*R8|i76ySXA(kR<_lU7V)o>9;j=s~=1s#Pexkt4l9$E{P0|kKU zw}FmQus*oo+D3;uN`84WE>;F<4kygxryrnqeqGo&7Y?axB6k!7WniZjjleYM_a z_0d(b+ge1{AG;{Q@-oX&M9$^RSr67v&r_sPl8x{APurM1G2(BqQZwte`g!R4piIw^OK*`+bXu$3K9jBT7Irvmh6laozSql`~PD zM`G@BgL7j^a5FOU(B@`BC0S_0(-U@gmRW&IHnD^2mS%ucWZUTm4huk9dJ01=H)IK02g#&CWJ=vlqFwshk%K?*x#IZt9`6w zBTef)H#<~)sSML_OM%I5C55Tio)7Yr^Suo{!F(_|wjv+hsg8FP7CP+o1e8a2C0!xR zE?gm5IqW2-+#+-2^$h8?WB96!%M#>kb(*q%%(g2bIgC6xYqt4HNDe2hru|qmy^6Hq zSvk@rV}s}J|E22XhLO*w&l*_ua>GgP<3Ag#UT#;Cza_19!e!~Ve7j-s`j3XUt0YTy zMea`_i8=g~l-bUGU)g0mo{~f-KW7MC$b+}gB&Uy)ZXDA43!>Acz-nRMj>qrsU8!P) zAbQ6L=Rztjm+gS&eLBF9E$fbf2Mo&O?7$A<4-TUmD4^aZ6 zc?N3H*8>?I<3fwL`b=pGHOM!Ya4B$C(}`2y?iuZezp?r*qhJq26qUQ1v~0}^So8TE zoyUR))rNT6H-2`y^V!)Qy>s{i#^V9SCQU`cNLcHqKhjhroJ@+m@Lc;pYnX^p+2`i= z*H9pgO#N!q1q}tlN%Y&-&(lz#D>2MmwMZVLO4C<44jkU_$xPU_&dTxU8BLdjHe`{t zJTuab`wM@>?5Je85C`y$e|pO9+CmJ8v>b6iUjZiGZWjlOUJ;B!f<+-viSoFH5@sUt zcAEa_y=5+-E|jFlF?x>{dIHA;IjQhh-uQl1$#+gA-+7gMzpLc?LnYq@m3$Xf@-Y*pynm*@YgCR$4V8QW zD*0-v(QU-&cq!7~_ec;gGix{gx2KPPss-l z$23*J_cP^drh@M$%6Fp*zO$6CxeC4?Dc?;h_|8zin^o|grhF|_@SUQ3x2WJdLHQc1 z;QN8{U9W=gd&<{R1>bSX*Tf4SC3ClC&-8E%2a|ab?FOE`@pYg`mMzNS{EP8qHKrGY z`hZj)1=OeKYnRAOXaA%=UZ=PEWC?_9;wL^*kWn$JtTZw2ZB0lQYznYfVt_m26+_qH z@*owQGm!q2j{`kJX@kZEwW1G6ncUV<@EnC?r$KtFps1i$iBTc zqeFE>U`!z}=12AKVg)E9%dz2;cn4?-yPD((?HG56N||61D}ak08{~vde4hyCc*>H# z@b__~lwk@9tAS&AU0)1`F@u9WPk{_gF=Bokrte}3$q>(ZC_NJc#GpZc7U>NkzJgI` zHxFFj#~NUz4F_~*C_9r|$GZolaHK=R`ksS?4=^pm6cWb3`}P)Qi0a;nG|+)q%l1W- z@T8Xl-}xwT4-7eYEF5AD<75(#YVbQNXOJ4UC0a1>7;Ysj=4X zcMP$X9lf9hcC7+xKAvJs`s#XP^t2Gx1z5tvp;`y~Yt8R9VbTUx7+Ub7Twc9^r#|Ux z_$GJ{Yd#3rqE*1&Ir+vTjdP6SAMZ;YuGIK%tpc8Wb(bM`{7D1o0R+6GRe*6tpaB#Z z5md(XK>x5qwG!tp>S6>Pa6L@cDj>J-(q3~u2r-6zIE}U7tX2V_yjNa0Y5+l?0}j(X zmJ+*i1cfW1t2rPMY=FTb78oa!yd#m)gli+SY5__UHft52G+~=o0ZJ48msSBvG~T3D zfD%kMYZagb)3>z>P=e_UtpY?W1Ph#gIRXGoAJQs72_z3|6`%x?DOv?oIgo7B+5#nz z{G?TYFCg&->))erC6Ek)RnP^mO2cTt~+6M)=)5T~^I8BQ3s`tlJ+HypN=q1|x! z?CZyHimIRCjMfc@{LL|r3dBjTeue{E_;4sgr@;TpbmApcE5G6PA7r`@Q;43$;7`P{ z6UYNGF!1qXHNSuix^2P^2~$zPrG;L<>oe%3&7dk`c2fQ9Ins;aj6<5Z`T`Qu3#`@o z4R z*b={eRql|%bIWk37NFcAgU65IP%S{&f+lkLOB3+b&Ygw^D8TE&7VdAtAvW)RnZz_K z8U!1i63ooS6cQc~#P0Byxzt}~JAawnu6X)9GHV`P<<6Vt(LL-8=m~dWU;_T}D?9B#{CEE5l6fW8|kQmU30-l&S(V%&BMFG$P zeRN~F&+>Ex4uEFg#d4nHvV3=9Dben`o~rRgAKgl$`zESJ%|yNjd6f}CWB_J}6L~A7 zsrF^%Grw;z zwf+M8nAoLQRW7EG)RfQH#IwazRb6VgRSlHfs9Zuk;JR1q3fP;bn(c_ke(u_u43XBs9;3-R3xT<^* zp_x_z%Ebu0?+b_6WWOy?K8O&+ifa%cA}Bag>&p=U5bS3nj)|p9HgC z( zQ7AwJ!gQo5YZQTy0bGA%$R3-fbY=5d zjdx)Ix0ixRNb_1S88cm&iW-mcQ?L#x{fX*jV?6gcu^^`@Yh=dfZ*PtG+gl@kreH$a zVPn0(Uip2BCCF4MJUc}14m^gLVozT2p@N5?Ytdc`oDmA(DNBOrUp7)AQF-od)4Hnt zq+*>_>L)*=Y_CJG3^PS1NPzNUQ`~2 z=Ezd1ip7{wCdgpLdEGqCMg|?}WJQ49uNtI_X`Dbx-?&PU?x2oz%tUL@zn2^l{DYBh zfb8Jp$|IfWKs^KhZQqt+tkStE9=jn8TW4iBjq|=eE za@eRkrW^Q|A>G>nYN8NZ_dU|BZA9bfjZe zqQ`XTEOMe6>F&W|dQ2xP;;LG`UR6l9Uq?D-qB_zcWXXwYq`Mc3=}0Fl;_5=Wt?{Jv zYKiH-)sc>wsE%|9S#qKp=>}mj9qD96TvbR{dLf={ZhMuiUcb?ij+v;AbO>2;q8jP$ z#$s0<>5SRE4gB|(+okW*q2>Wwuh2f-=BNa+x&2jwbo+FqV-}+$9XgJjs7AWMSnSFp zo%k%Ben+DGQ=L~B=}6X9a_{x9j&wTGUBitnhdM&CvX! zkMcd8ATKrg9Y>{8`O)-W_#(^9fgeBof7TMX z^v_zB+rF%|awz%?)mVlpB+OB`le~l(UL3y~e%5j;wpMg$zYeB+{gik64KIR@B29@6 z?6un-m?#=eRD(067`%tXu_g()N~3;-K`8i|#}=H=yiI>kE|fz0AKeT6jTfY%A2879nB zGvIrD2pYnH3{pPe9g$gV$v8m(p0b2RUdjRQu2lg1e*^eH4*Zt^u$Ylr1#J7oV4R!Y z(D?0>7WP{8e60e^rwr(V z!s{R{JA{Z1`qxm1_Yddmku;*GxBt#Th-Q%H(s;^2dJPh)AxJClcj=L|JXU%n4SMiw z9WFr&*0(!Q5mo9Z`gVt^M*`Tb6Xm}J&D)plbabd`2Ho|4EhJ7 zzMng(en0TP*>fkg8gEWlFK|snJAaux-tZYxf2D&f)1MBmXhw3f7_Z6%D!Zq{4T`6i53bdCQ&|MpMGJ%e=EzkC zSJ@bqVLe+z`T4}Q4z9A^#9eCr&L09>wZ8M`2V6-^Pwau0h@t%J@6~uSSOiyiB=hgx z7SO>}2Um0`o&?bo7V~g|x76F?_d2+urv6{S)$g{z49|Uv&#{|oHgOLiuW}Q2C4bym z+{E3A%;JnaP7r{nEMfc9%GXqiv2}Ts{xLieJ-z^GN|*YG((6pa%u$#^ z0uaKETQIGJzrnsT8v4qZiW!ye#RC0N%oJO#+j)8P&9^pmJ4Ztyx}7sk_}o_WGw)Vo zJm;%S-OhD8XBxPi*T$={ozKGI^%4~a#XjIWGc@i&-rGGw0`JrNPl#cYsd$N{7oC^>t+bnjieDeJB!u|9{=d)f4~# literal 33436 zcmeHP33yY*_8%xp1%!ga!-Z!lvO_}`N*CHRX5>#smi9KeZ3Ag;$U-TolpQyA zSwsYpO+dv3cs_&&h>CzbaY1AkWS1QmMBzVY?%eeDHVOAa%g5uN`+dpWIdjgLnKNg8 zb7t;MHjU}<#_u<7+{h0=s9tS^{=_L<(|68p(kf=_tjnhxUAHYOSkS?_=J~B52tn}y z*Rl3(Q{Fl}^qZi0r}uYi)fm&pH*b+YHq*57;J3||@BZTSgFj)~+2U0{pVH2_Qh#sQ zLe09A&~}&>j6bR#ObM%cCL<$LrDL3oDmPW7Gvwu_=INbf7S@3|>Y$7G`<+_&3twFy zMW`iCBV#n(qM~%V=q}yDQj)ucC#QrZMTbR3>ms6~qIFTp2>5{+_X9bD5o(3gzi=Kd z18AV>7nmmG`(rwMEVLYW{`-6I#p5v{Y42$h);HH~db1X$flg@DeW)&OUdqPC0ZnkO zW5?X&t{rn&3(Gjz&Rs*hk#dC2_y4}O=W;AWu83lDHr!0d^%jrjX~H)ij~ig@FEPO) zDtj2`ReGb{Y;&rTI6Ir0Sult-I?*4gofDuop-gXKoGdV)NM`tm2Sp`dbYyC~@MK*~ z)EJGd)rm$gOUrEF&i3SnvUW7Ms7a$BpgA0@GquFV*_|*{=;X?5Lp_1s3lAf*yp+)3 zF-$4Y*ld=vOqbJQwz39JXXkACV#dUkI?(gu#+QSS6r^x=Bb&ilIfsogvL>`~M}tA& zP}(At(kSWsI^?zsdd3;Bfxiz!XU&B_)+cnG*})W8u+*yWUK<)nNPJt5&OA5a0g~^< z*-ID;dcSzvWoV?fD9g^`4wz8po<*fROAccO!k!k3i+O_HWgW~~(f+KnJDXyef>ejD z%*vFQjf}-omd-II%suT$#&WPmK{`{wT5@eR)^5b(jnwPaPuvF;SzIi-_)&Swhkz*q z8{ckbEU6B?leHSn7W7{HTqlvwFykWDgonn;+8rpb?~#e56`ZLog~LN}EIZ*x7%nZx zS+cMNS-Z0g1=U&gA`F+7vzzeHn9mwVz;N-|B+kOwQN7;D4^+^2v-JO|_$6K!h*8tr zXJeKT+Ywz3wBo{&6~w$GujdM#=&eH;Pe5aN{XpXDcqTaa<%&A`m`%=N^!dQWJNTwK zom>gJv3B^P`-$kV@IusV&d8O-AbQru*s)Pk%odBrVk>6Q(X|8WHp7esxeiuqcR7j; zcE*aG!jA1#hFV0h>xfnt)@nuFPftdqjU-F#w6fpTIMTN=C+oJ;@m`%bHo{7i7`sW2 zXQa`|ny_chj2pQdno)_&n{w|}t~<1s@8HU*Y!N6WIoz-umxV<-TX75MH9urIjLB?9 zyDz40;u&EWf#!0c**gaAfjL;g=yK?>H(F5A&pI-GI>uUr$1h|4r2#MmI<}B;S)6Fp z4KCpyByY2`Mrr_GHaP#ZEd>5AXT(Pl;s=W(!NSnQHCZ@OWiyLK(CP?$JX7 z$M=N+|_=;uP0|W566hYSn|4r zq3u_@j#VHKj+z|W*HM8$1X{d#Lwal9P0|l>;rR{C>5)4-udQ$9-Wd4+f`NTHGER`q z9*)x5ywXlKdj$GB?qsHH_DJ-{;Ox>TJ+A}AN%u+Zbh9gV*D+RDL5x8u3*tQS=0y4ttg!c8wEdf{sB8?;7;Q~H`nL$YW zO0H*}PVCtZlraD6lc2swG3UUGM8{eOKz$DjSBmBbGQCKKvzXAV%>l-`P=Yf(&B$@B z+j+?HGZ3jagfD?g$r7c<1I(h^fuAC}?&DV=ppW(Ut4D4?|rUW-IN8VZ#p*JOvO9R3EE1yE)9(iui_K zBZJx;Ed}EPa=Z19z7&`SP3^^5S#%}fvj<`N_i~jKu=dQtEHi7wVepg3zxxEp{NT7n zHTyn14Nc3mLZpQ{E$SH$4av0XUG_p8R^f$v&cC2SH$`VIK`Zp<6Nxf`EyIPEN3>>P zivfg}A8p13jU^oZDlxea`L{(cP|bZrN)YU_i=G#xN+}uw6`p$l}n(4#Qzc z1U{#o!*=WP_58~%Fg1%Y4#q1_I%i}o=HZOf%vr%?BveFAoMjV19c3b`py^`7I#@`v z7!#}pD00xqAvisseb`#so_@q~vk@rZ z!)Nb@{*qEHZeEccRvr}Gr=^vz2!k^1400ug{77VR?8liJY|y&v%x%=0^UI8cy8A^J0N7%vn7lbFA>OfW?&{D3RyD2 z^&IHIMg0!JoAF?v zg>6cYthR|T5ELNj7Rc1-Y75F#LF z6$*eCDkhV})n&&as3A9%@9!k`+BFzhn&&rG4m5S(n4>TN0-4|KtbZYAJp|sBsJ$7y zp~RlsOn-K^NK+PGKLOO5j}IVA43*)rt-CrQ8%!V&MHrJb**?G=O!K zcGH zhl-frp)%x=o4g8$%i}{s6u)cG@|@dJkUWWPoCbl`t?Sjf@#00t6y&U|I43*0PG2eugO-t zV_?zv#Br%`ZUJY5HoQS__F(ZM&)}dt$->};yZu~SXILvM046U99Vl244QR%*I7^33 zg=Gy+UJY>ei5`9LTo4FVIxb)L=%eSL0TRpiYjEf|+03~Iq{6&wT3(q3OIKA3o+x>X z3b{=y7|B>Gz7gOy40j4_G%|}&-B;ppZ4 zL$50kh(K|h9-5;-AQB}^UH&HQyF8e`V#1$T_YHADy13!<$m@S6?yBN~;03~YCcY&P zV!(|kEl+%SDOo~2Jpl?8`*$c+5-L^#l}N8zm@t$QTjKPlkBfe!%zJ722Y&3bkKUVU zDo*y(%qh$V0SR0;<2%5!RpJxsJxa^YVSR^W@SVkchh^~ng86=u!FLAp9r3_NN$6+c z2B+I(@@<#Nw?ii1S2Fo_%H;d6OunyW^6iqz_l-=xZ)Ng*CzJ1cnS8rt^8Fx_Z;wnq zVJ>)@M`Hi0?L*%&T6Plq^Lh__LLC=q89eVkA%pKX%y&`-->;bOlnlNrnD4X1j_Ss8p6FyA>DeCILWc^Q1CG2d|yeAh9ZlF4Ir0ZW1Q9xZ^!-`*|& zdf~#V+`0tXLVB};rhiL#4pMr3oL(HrXG-B+0WTNSC)DYwK0$1QaN{Kf9hKv`Dhp|T zON2h6T?h6m43A)9K3fe5jtve; ziB&zrx*Yht2Hqu5<(Zs$R<<;Bkb|>^bP5hJnDH5`lPR$g=CE$vV#1;#W4d&MGSHKu z>&lnJ7-LwIDY~#rSX4wzbYZyBR8U}yW@DJ}g2IBB0^p}BI61M0lM*5r68zi%(ogqk zBRu1R&{^mwytEK1I;()8f-!|N0^z#*yI3g%KrhmU4;DIrQ#ge}CveAv2~njGT;e6* z!q+@LA)fi6=$_t^Bn`exDW#aEkU+pj<{qT5`dOt4+ALoi_xX~h z-TSXtPg?MtQU%PB$#Gdx$Krk+H-#twT!c%u4KjLljz7_SN_-nbVglIUpS=97DVY9p z19e}e3R;bCs=j~kX9*h9u6Ss{(@GW8nU=2Zx&I+`+$0t00!$jXWZU4#{jbN58MlrI zLkm9R<>f1Q?VGfO!=d{~^FhHPr3wzrDpH?poTnZ&Elmvy7AsZo>Y6=>|jx(vsz8dy z3zRC5g6Tq~3Z!8AmQn>$Fm)+aAO%zB?I-{+Em5jK3M8CT1yUffDpgSJKr&xx3#35u zg;E9HfW#B5Z=}klK++GQ5n&f6#(_B^0T2?dK!~6WG&fkF;*@~NH5?A>y%hr8P7B3V z~EklVOZSksiNg70BhAQ1|N~Nk46nR%y(VeN3No zPK`N^?e24&Jip_hyFbRsy89gGA?i5N2q|b4Ey$uNB<{#mi04|!VONvmj9H}fnR*V*^v8uNU+8T${|*;j?$zRF6x2{9`GRG zGuo3r?+8+=K)RqeR;obyyaV1shD+RMpIs17OF+ygc==mX0Q1_1n6Y(hADrhY5)z}5T8oR((V;8s^+ulQCeY%Wz zhXF3rPN@QTT|yiOKTj2SLkTS{mrh!5C^1vSFGdP&Ct3g#J&E4GOH=V!&89RE7Au$p z^trT9Ea1~#U?cITBUK;G`Dx|5n#u!TTSYs?L9N^(cy#(R&)sA#^wXRkEn67fz4XYad zT);8iL9GzKiC0vCCPnkRH>Nx4M>;ZB{YXb4i%(?7bUmn8%_H5a1 z>h+xNCqL5pk*?~`2xKE&svqfy1;n^>&z#etElH^SUWw@r`;pF%bhm+YX?~=;cag6A zdJ@{$_8!^29`YldAL(ub=~C_((y4Qk6NJwvPKW=Tj#|D3&*?VC>d?ma_XyG*^dp@g z>23q*p1Na5Cw`aC|3u=BoYMi)A;UfL?De=G>HJ7{8%Q^jBAt<@@MK5)F0Yo-A@YDS z;S$cZATNHV=k0Z6cyv3Buoj(q9C;MmfYa z%2ois2-r#$cz7YHTs_u%VWMW7_d=w3S!uh(@2xLb(ndYWhxWu*ycbsd^T-$vP56Y; zq=V5L2p@S6@M+6x{e1@v#xt}99tI;=o`*52-XFqD@%7u1 zd|$sUnd0lWCHd~(mgIOa^c+e9SAdUj`TqAT|9{!=|ClORi%%<{=yv`DDgW;KXNe`W zpW-R)bB0QP7A|1J>mC&Shti~AbRFo5*GuuT0^1SA7%yIM0it~@iH9{SY1=(uLt^3} zS~!rVkko-MfXs5Z!B-W@!oouLA8k(Nd1S>WM?iJWzm@?u6@UCMpz&h_-dj_zWgsn9 zevfST4*0iw#9?aVZ~u=9h$-Mp_G=l!N+P72#&mdN)^Cf;L*usW0-HYWR>|KCJs6!` NXWM_;ccFCP|39eC_t*dc diff --git a/Content/ZMMO/UI/FrontEnd/WBP_Login.uasset b/Content/ZMMO/UI/FrontEnd/WBP_Login.uasset new file mode 100644 index 0000000000000000000000000000000000000000..1d64da3e34eadd36d01b15e9cf688cf2c322f38b GIT binary patch literal 186612 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 C1Qc-q literal 0 HcmV?d00001 diff --git a/Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_Line-512-10.uasset b/Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_Line-512-10.uasset new file mode 100644 index 0000000000000000000000000000000000000000..a6b2aef17681004af0b799fb39bb9745c53278d2 GIT binary patch literal 4826 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 SFm1MU!?{UIMbBCE4E{g1-+%r9 literal 0 HcmV?d00001 diff --git a/Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_Rounded_Corner_white.uasset b/Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_Rounded_Corner_white.uasset new file mode 100644 index 0000000000000000000000000000000000000000..26fd61da7625bf8c32c155082a889c5b261b3c9a GIT binary patch literal 8520 zcmeHMc|cRg)}Ig{gndvAug_2Tq2WXh2&?GB33jh7D`2_WF(Qg ziWM806f7V!$t;N&+JQn*8YvR9cuCPhX;vDUmc$l{vPe-QxEfh(D2nm61?~UT= zVtxu&%1;XwhY;cnWSzYG14xcQfiQIyNJXTl<56O?#AR@k9&`wWf)UMz>Yr9n%zOzs z!t7q`)Xc<^Y@t9X3McZA?knGUjxzXJ=w|19TZlDVn3;`j7xRT#9HA(akRU;CLuJ{sa+S9oub4d}r%;kxukXabor+g)y^WQSE8rz!M?TGfg%3?blF$E0 z2jk@tA=6^`1teOocbo?i49$|vbsurbc2Z|Jc!0T)LVlJ6t(AsqL+F5^4D+rFa~ICQ zyf7l46j9EHW~B?0!f+t5j07($nTBkemTJJHVf+;t5`NYS%BbQ`>ViRA7+zySJ`|a< z#T=M2EFy?25=yhtgGc2bf*Xs4I1vWvUr}klSehBm&laN{4)?mWp;MX}BjBf!V&vl7 zZUGr2PpAjza8;tEP|Vj*Ts{&6xT%HBAhglN1{q6dcA2K!UsR-$5*RF`T48 zZghYb()^?s4h9s5*FY$8M|V#&B>WFgG|NSfg%BXN03TXNlABir3l~mG2x^<%C^_^G zm|YQaB<7ZzK^PS8BXS?i{*auE`7by(hl;r*<(7PBxCNo%KmFUWJVg$QdI%phj*7Oq zqE@Yxa(`es^$E(cULrSJRrv(v6bC<2wQm^Pqf`a^Q)wIUrYQ!%0s10R>V$r*YDAY( zUSA|&XeUJ%2e~zR`jIf#bYG;mDTiP%U-OG{gep`*>z z*3;F|Gn#H}WME)qV?Nb%`Yc;V2YXvPJ0~}PPbU{27dtzzFmE4ra7bu~FQrS(=(grmx80 zuM|t9Ik|cH1*PlX*icsfX2q6&Y<=h5>TU0BudDxX*Y1x#{$$UAgNF_uIokO7u`f@y ze)aXK)8Dk6zi{z~j!Tz+ymI}QUvJ#(zID5&w}0Tl;KSb^J$~{O`~p9fB=>BbUq;w3 zHFb5mIs^QospaBAH&WNIcGWZvh++_{O>ErOX_*FA)qd8(oH-}@zFCT>U3-$P`}J4) z!8OXWi8=QA-|}q4u~EOSAYD2QFCN_p`JsnxZ|=+N+Hx+a^$2uo3+}LPhV)FheM}rGqe>&FJm3Y6`WRiaJk>?!iTYW^?fLV>- z>;E!&Ri~V^cGkl5sCeF2l8mnUPvT~2c6Jb<5u(J~;Q%TEh$b-3WuMpYP7)-dLsopykpza^DBera6|5 zYq^^{N){Ze*zsFi_m`=9UB&@c!{(-?k&%p~VADv3H9RKTYL`R>BhzukEyJd9(<%2= zHkQ|ks)WhI`Ue}kIx0JU%kO+tJ<#h|S68xtn{a=_zPi5rd*2?pBSTKtpHy3X)Ht+C z`^3im!X@|TRyAl!%I;P(YUa{PB90CEoh|VyaD0t-|K4x!Rg(`08QM{`Y2Cva4X>7X zYuneA%zKI-FK_vXD(rWvy{LTdQ`|f?r{UPD1xuEjY&q?=sp!(ti)#;eMEP4Jeaz2Y znpW#v=U8*>aK0?b;&P22ZY9o0xki=VMKj}wv`zPmf9kNL;Ro;&^)QNgp+ zjkQ{41bnzt{{)^=KM)?zX&KbruB$-y|D}B0Q8-_?hi;QH?fvX)*+2Iczm;8m#B*v< z%Z6^Z%6SDwy#@sj7w#+msIxb&d)n5n;{h{xPfPtRecNB#VSE4h&t=a3)sK#xH@UgE zzVvR9{`|NZNIHmDOa#j|u2pc;G|FFN0!4R+)^9{!x_^J;$yt5N^p9Wfao_!**6Ec7 z%cg?bLBC%UOT)LgzdN@wSa*|48z;b&_TaGD506i_3}noi*5H4he>Tr2jC<~n0?UE? zh3F1M;Rb|ZwDr{aMQimOzUDVm7fs{ZY~V~GXIfQUTfp9V*O^_OckZd{O@=D{7u#q2XBsF>+!cxHf&}pBPN^&V{%0_JpY{j z!MgW$F1xxkJqV>8lObaniu%px&c}GmS5{o^cYarLHMBxEz<0T8=f$;_7vp^7X;?HF zA3C(ZQ8XEw4n^(DTbt1?Ud`wue1H9Th{L^P_KS3IdEUR?axcDHY_#~Z-JSU|v}V%0 z#)Kd$ptT+PaMTjEizO~BCR+u`~5{twP!2aUS?tK-hM*ncakPU;6&2q0hc= zKV9f*yEv+>(aU4dBkzmKy>C=+WX;`VdwE-NbD7`%4EmMvIrvxJ9pCD2n$mc-ow-A*x4F+WB0K0f$ z*Pepbz@kaRHrcjEt3qcS_%KrAukQCT=Yp1c@_?SHnwGl6ohJJcevOX3%)I30GXvkA zURV6~hk-8I1jwNF{o`;-h zV>4b`V`YP3B7AkuAvt^jnT4;z@s(aeWM)%_xz2U>c6ar5 zck_fkm?S*NgX-~4NpqV+q!GlNRKh!jAl=hlJl)gXQr**C-Capn66AyUVr*T07?DB> z04ksc+E^7WM{olV$RYRwB84yDW55Y6Z`{aO1n8Cub`wKN0P+x1mmVHFDTqubqyh=` z8iXy)0w73C1>eKfEe{t&!8m+@gcQjKaYhEI06c(-1SRl15G)l&vE=Aj29cFT3NU(y z*H|Pa5ryiA<*-E@20hY7yC4rR&JxHc#0V~cE>t#*ATOYHJZ1ry2gbIPkOe?T@{v>r zKbuQtjkpV`%M0hmBymK-%rS_FLO4uFq(x){1|#=g85qEX7Lyp|!{`c0WFi0=ID|k< zA{ZDc%q2w;>AYNFxG;^RyrHlmK-QkY%_xszgRO!{0Y?5{#pEIijEvfgF&ry7UIHHx?ciDHJ2J9mGf&q~ifpyCfF4Y$$stF0uzGy8r|7wzO04{uPMCMijpXqR~2uN#XxYvsFET4{Qy!zETF-f;>fkVqS98?UI~0)z%Bz)9i+s3R0n_z z701K-dwi7zm`XHh!U_;7@l!2I$PErdAl2Z&p#TJys9HnG*il`G+U(F&jR71uN}?jH zI$AlS61!71{iQiH-?Vt!8tpGat4%e(%iDU3CC?Xx025Xc#e>-(OMz6i+sj#Fd;yjf zE=s@Q{W)~s4-Pyar65dVLvdaZpqYwJrN4?-Y}n?hoyRMSh?)fRrCS=e>+?4U+7^xUy<=kHNSW{GYt)-*=Tb&u?u9&%$vrSEMBv@_Y@r1Cvs2jK-tV z)Rb1qYX)>W9TrO-O69Ib8s^VaShL(EXa33^a|VWbBCyX5cw8T@6LHeHBnz5ADC#k*G9 zJ39OPd1RT7yonU$0({PN*JRV_dNYADXsdHxx#X*5K1nVIe|#+qY#%c*zNo0$##0 z0ZzWzG8z89m{9I)qmV68lS1OYJYpIt_o2FsrJp#x$vcmFZMjtvat@tccUvung>KU> zGLxU89}WDEp^J=i3zy}nm)=pI8B}Rndw$WBbq08;P)|AHD`!MVxf7|SQANrPU}emF zhK358iVE=uZ>t47g!J)$3Y4R9ZDAy^xyC$?EFYHmMbHYoY^{*E#JhR=w#4Q(nxD~( za|KA?OE7krw$9hAI`=Z|f|^6@H@D4HnD*3j~b)M|ROcZnEv z)4ru|W4LT@KBu>H9=&V9lmc4DW|ZgbFHy_T|ATI98x*1gnVH}z8$xGlBh63=8kuSD!TwBf=c{(xCtchBx$85HzK{A~IYjVbI zYHF>=t{=rxc^DZcF@5njV(izCGG6s*!NjQeGvU)`Hd|1E2$N3@lMN3K4-5?S_xH=6 yxWTs2XR=x2OQkl=x`yeyndw{1^i5awDzyk!hquNI9E(+!Mq!NQ185VW#eSAi zW|0Zd;`6S+)g6Eru#m3aXFeOpU zVGayrYIuX8Z}T+^Y*?~6c!4%}o957FiCpvKO&X}-xk9tq5GBi}Jnn&rNujkI9NX0L z{KxPtViyFaqfX#qQlt*z*?Nji!%40XDmcD*E=qY@T0l!AGxIO6N<{|Gb8*XQhOJM?gB=o$51y^fdn*|v9X!ViHOvAs zV9deN?g2JL&K#eZiJ5iqZDE0mBFl%|ywJu-gzKt1V+M}PPZvlLf%@cnx>auV`J7`z zZjn=rw6DJ}FA11Rw>POdm_biS zDk+MGlsWWx4MqT?bvzrA$d4kP6igissA=AKxMb>gVXT_zpn@E{C)C0Rsv(geBf%un zAj$mQtVC3lg%k%@oVpZ1N2=M`tr8oWEjzk+^E#I-2(!$@%#pzk(T#3Yd!3a69|@Ds zK3};3HEUrF#{F3Osc|aCTgyXzT-LvximRbE(pJNUMe;)S#Sc?reILg*Q<3Cb4~@dY ziq%=%iv9TZuKRJA4{}tUjo{qru!do}NZPU!UJMqOIy0L>kPOEzWF+9BGh&!&rX*eJ zSc%1nnVmu2?abbz_GXgBtGq{Xl43JVrk8KqiC*|5A=2EP(|q#F>lmQoKrHIy^ZR#W zJ^NiV{0;T4%6e}@U7*23Hf1Mv;oPa-%C$qPvKDua>^XY#9Ffe{5%0*4@j2Xq2A9)S z?Y9%|qsD`nD!6ZaiIJD)=_Ri| zjnk&2mxz@shhWG`KQ6a6cLN;+i3;qn$4rM^|jyJEZ>5rOXLU z!qViFq4y6PK4Q$+ta0PBi;7E1%gP^gI9=|_D$i7(zhQdgjG42VgS2IKD?3M&BJPd~eSSr)Sj(7s6XSM> zYAm?$y&sq}04%~$cs_Nr91X#7IK%v)Im2qT_4sWN5fQrtwz=Ns4N<26D&{=1T@d*Y zD@rj`9k2)6SnLY&>DmS-bOeMV0&f5dwi-s5OQkRe2krnqPrOGMZViWKXXKhQ{45;b zC8jMbJ6%vxT2eNltf0IQd1$}JSsdf!=LgH13u!?~agZqr2J?$bCKMDGQ>K{72bBVp zcd{Zl)=|wgQ4V2&te9b5^f1BGwtYUsa!eD;u>ex>09RSpK=4eX!FxU`AtVyzgOA@4 zaMBhg!bx)137&=!jfirs6y_I|Ex4AwneQ41WQ^i3IFx=fbUw zqm>DTC`a&U?q)cV5*ZANF_bbLFisHAKU9Yhp>j0<6suqvk>EKnUoMt7I77QVAqqkq zfW#_7LBtu;lo@Qel7`|D4@Lc&N?*V&@NG8%AVs*EXM)}^LM~DEMa>Pu##%a`3IL`k z)20aq73=tRDtKG`?R*U%q;fPeQp81c3*43?YH*pIl!FfjIB78v1-UBl$Q*-5m>6Gu zWkq*}@VmxzR*)hLr-m%M1T(7f3iY%{*}heDx2xzTNOW@ta*F$*87?FXT;KdX4Zr*# zWyTuWf(mNTnwD^rG|F`cccT@KRxwK%Lq)CflUVV%IFPgoSe-~(VeF%AdWgC9 z*D0@j%-py!_MinhK-$8=Jp`R18S+gpO#Eztbc-fF7rzuB-Ij^q3kgF)%n}fSCB4-}0s)lgWM$#J6egcRq#KcH^ z!Qgi;ByA4vSd1@U+;()*j4J2yJkNg)pE#Imi0PA2N{fv=hVB6rm1%_1Vk5_g>i`K4 zCrH|$$HazipSiILQ%$|n1$(D<-#ARReZQvji({?3pXZMlbALE;R8<$uo?mL7I)xfnxsZ$i|#R71;<>0p4;?78OQ;y~6IlW$w z$)5t3hMTnHb_Orh4>!(P;hh=uoZkcDn2>MnwF=63P-*l!yn3VL%>lZ}$vW)mNFSpw jC4I%vw+#ArhQ2-V4sV0`|0tVgIPm89lgdjRU-170qprv0 literal 0 HcmV?d00001 diff --git a/Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_border_frame_long_2048-512.uasset b/Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_border_frame_long_2048-512.uasset new file mode 100644 index 0000000000000000000000000000000000000000..15f1a3fe62926d439012321ee54fb49612d062cd GIT binary patch literal 9768 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 literal 0 HcmV?d00001 diff --git a/Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_border_frame_long_4096-512.uasset b/Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_border_frame_long_4096-512.uasset new file mode 100644 index 0000000000000000000000000000000000000000..8af5f787a88bcb2370d1ca5c2fb8d65de22626fb GIT binary patch literal 8189 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 vfh2%Coum>g#4&J2jJ!OUc> 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 q%*PzZq?16>v5C9zVtRqt>{Q?rRXQi2nczPvQjt literal 0 HcmV?d00001 diff --git a/Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_border_frame_square.uasset b/Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_border_frame_square.uasset new file mode 100644 index 0000000000000000000000000000000000000000..2a29099ebf0427c9287992a2def878d248460b17 GIT binary patch literal 8682 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 literal 0 HcmV?d00001 diff --git a/Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_border_frame_thin_2048-1280.uasset b/Content/ZMMO/UI/Textures/Hyper/UI_Borders/T_border_frame_thin_2048-1280.uasset new file mode 100644 index 0000000000000000000000000000000000000000..44cda407c1e8ee6744b606d9126577cadbd84bbe GIT binary patch literal 9457 zcmeHMd3aM*7C&jaFVL1nK*V%`7D|&gw9v{|Xxb)iI&D+hl!A_5`kK79fi#IPX-g4? z;94C-(T=!`wcv`5-vBPi=pf9f<1nZnI*u$3BTEqZbOsR>1nr#jmh`nmw)Xq{$HVt} z-@EtR-#y#C_nfff)@ePb`}+DClmO8o0N-FsM9sh3{8Q5mPkh?`LHuLKKbfAAfPE`J zT5f+{XFB@Dyz=MLU)i#)1N**g*?8phqV7{M`?A;TUNflEv9AM}tFYCSGOetptkcbM zni|&QYU8YIHDg_@sWV#i4z`tbdOh@BS9x+3P+OEJU20uS`#7w z!my?9MUngvv|~$8XB@!wQ1+nWqZg`PFy`!UxOr>k51AV8hmW2!Zan?vZC~hgAJE!_ zLxP0izfibu(&CUr{L09v)8}T?unv~-u$j5)Tz(wj=zDX1VjL&&v~Q1c1HEj*_5V9o z=#&1UclyYj)3M{EY-}`sYig~W-R;#ByEwMa)VPSXdf~J&bIwTFnBLiJcd~Ft;q6A$ z7_{nq6zu7yNB=hhzjPeObXIdN^pCd_j2jQzts0?6^98}H^jJH2e6^JrD( z_9)~mb~#*JC9?!#C%-$Zzr*eX|NhyjWZ7a@tD9pz9=pqFaB;1S7aYf{{VE*Mo7pAa zHjb_3L+gCL)yFdSjX94`1nGEUL?%W9h@bg!zgU~jd{?)1WM+Kka;I_i+j{M3gt%df${3Z|3g zc;$`GCYPlG4T(HnbRuhm)Yq>Mqlr9|DBzukPgqEh3VU;l*Y0fQ1zoXWy`E=8FEauh zj4t*VC$L6czhT^S=x#<69@MWbQh$y zOn94?Ggp?@TB=-#+sdGI-c_wQOExyw78@*e`VxJGSqF|U>fa@knQIM}qS6{&9)xc= zKE4WjhWCJcZ%Op!qPH5fhc0*;u1zCBqb&$>D6g! z{bf_%c1VasiF%caWC%%6C=(QYZ^E@GXPAN~NjpV|GBhkaA~GsE1{q$ChY*EQ84{`t z3kyXh70YoPLKDIgld^T;qvkOY$v2JGu3WP&GNowKjxja6Po&Ohj&E@Sh+3 z>*GCp_Z>WR_=x}LXU9H2{?*B^Pkr<4cc;&MPkJdpDX4{i18I!~DZbK#qv>Fa&bzdXO#;Ae_~xqW+SYYn_SFMi=J#k;#%ymDy% z#b=C1E~!Qk>HxKv=-_QlX))afI-8Ah>jglmfmvs%=NPwqVBvs)VD-UOD9sHwtCF$w z!St=2HP{`j6H~Zk)pe?5{dF3XlP%$}UzMrSaUPe`?(qsWp+#qFwbLiZr+pes9jS)# zP*#K)tEQ6S7PDTr12aJjfqE{fh{SsG0#&5i?QBk-u1YrBF~{^Wt!`e*?3sCac{65d zXXeuwtyNi)%a3TYo7g<w44mR8pRm2a|gC6J@j{cA3^l(0=qo5Rar$0aDK zlTv06A2L)5FQK9s_}azp@Uk4wXEp?;$*LO4<4YEpbAddErJK&-ZefaB7^jnUV1bIO zacwN{rHn_&q~~KEZtEYSu&6MV6xkUMpN(Yk!GS%coU6?ZqXf-<<(R&Q7V%q3=FJGf~3*>bi2F4#n?=4DhWJ%6iQ|pT(AW6pn27<4wf@D znLAvSE*r}$!WS}>&74;!^2*fdRVAzgD_wGzz#^7hV%Dw_vUXagwx+bG-#Fv2XTF{D zwlNM#RlbD6=mpBe4UEGhh(HdP=$8%ilN)A05k$e(uGa|V-^v&B5z&E%55iT_v zyg~rqT&Z59Ly<^s96BM0ND{d;JWmEc1R(uPC%3_+fbfRDW7`wo4ZNUN_=`mhmu;s5pV!W;v0atrOn#7c*U95 zl}aUfC7tK&<^STJxSn8u53l`Ry*H`-*GIUF8<436pIY%TV;k(L!~S3o@zUJtOMFbo zCw~^>cQHP{5}yie<-p z*024LKNbWTgTs<1%4F9SPgnOMTkczouG(_$1}Ry9u~Z8Ka3UdhR44%{E#T%$>Q1A1-gqI z;`03=kLO%J%uXK^&X)<-Aln|Z{E(53*H%w*zx9Rym+uEhFTu&2I|R7de%S0IIGHcl zF31gPJIc|7JS~*Y7M>@RKUNC-;5*UKMP-YW@#ARNvU6ev=fnR$33_<862$Ux%#DG+Cfh+-tcu460 z-%cTw92%7JD7d%#4hbnFK^Y{e=3I(YEi$PToxYe@C=^r2A!42Jji`V^F=ZSY7W6bI zQnwe1sp1f^wlyehh$W BackgroundTexture = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") + FMargin BackgroundMargin = FMargin(8.f); +}; + +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleButtonStroke +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") + FLinearColor BorderNormal = FLinearColor::Transparent; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") + FLinearColor BorderHover = FLinearColor::Transparent; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button", meta = (ClampMin = "0")) + float BorderWidth = 1.f; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button", meta = (ClampMin = "0")) + float CornerRadius = 8.f; +}; + +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleButtonFont +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") + FLinearColor TextColor = FLinearColor::White; + + /** CommonTextStyle define fonte/tamanho/peso; TextColor sobrescreve a cor. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") + TSubclassOf TextStyle; +}; + +/** + * View FLAT composta da variante de botão (Background + Stroke + Font). + * Mantida com a forma antiga (BgNormal/BgHover/.../TextStyle no topo) para + * NÃO QUEBRAR o hook BP_ApplyUIStyle nos WBP_Master existentes. É construída + * pelo UUIButton_Base::ResolveVariant a partir dos 3 sub-mapas do tema. + */ USTRUCT(BlueprintType) struct ZMMO_API FUIStyleButtonVariant { @@ -198,14 +289,21 @@ struct ZMMO_API FUIStyleButtonVariant UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") FLinearColor TextColor = FLinearColor::White; - /** - * Estilo de texto desta variante (padrão Hyper/CommonUI: CommonTextStyle). - * Define fonte/tamanho/peso. A COR é sobreposta pelo TextColor acima - * (FUIStyle manda na cor por tema). Permite Primary/Secondary/Danger/Ghost - * com tipografias diferentes. Preenchido em DT_UI_Styles por tema. - */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") TSubclassOf TextStyle; + + // Campos extras vindos das sub-categorias (background texture + stroke): + UPROPERTY(BlueprintReadOnly, Category = "UI Style|Button") + TObjectPtr BackgroundTexture = nullptr; + + UPROPERTY(BlueprintReadOnly, Category = "UI Style|Button") + FMargin BackgroundMargin = FMargin(8.f); + + UPROPERTY(BlueprintReadOnly, Category = "UI Style|Button") + float BorderWidth = 1.f; + + UPROPERTY(BlueprintReadOnly, Category = "UI Style|Button") + float CornerRadius = 8.f; }; USTRUCT(BlueprintType) @@ -213,74 +311,85 @@ struct ZMMO_API FUIStyleButton { GENERATED_BODY() - /** - * Defaults = paleta Aurora Arcana (styles.css .btn-*). Garante que mesmo - * sem DataTable (preview de designer) o botão já saia estilizado. - */ FUIStyleButton() { - // .btn-primary — dourado, texto quase preto - Primary.BgNormal = FLinearColor(FColor(201, 167, 90)); // gold - Primary.BgHover = FLinearColor(FColor(231, 200, 115)); // gold-hi - Primary.BgPressed = FLinearColor(FColor(201, 167, 90)); - Primary.BgDisabled = FLinearColor(FColor(201, 167, 90, 107)); - Primary.BorderNormal = FLinearColor(FColor(201, 167, 90)); - Primary.BorderHover = FLinearColor(FColor(231, 200, 115)); - Primary.TextColor = FLinearColor(FColor(10, 10, 18)); // ~#0A0A12 + // ---- Backgrounds (Primary/Secondary/Danger/Ghost) ---- + FUIStyleButtonBackground PriBg; + PriBg.BgNormal = FLinearColor(FColor(201, 167, 90)); // gold + PriBg.BgHover = FLinearColor(FColor(231, 200, 115)); // gold-hi + PriBg.BgPressed = FLinearColor(FColor(201, 167, 90)); + PriBg.BgDisabled = FLinearColor(FColor(201, 167, 90, 107)); + Backgrounds.Add(TEXT("Primary"), PriBg); - // .btn-secondary — painel sólido + borda - Secondary.BgNormal = FLinearColor(FColor(17, 24, 42)); // panel-2 - Secondary.BgHover = FLinearColor(FColor(22, 32, 58)); // #16203A - Secondary.BgPressed = FLinearColor(FColor(17, 24, 42)); - Secondary.BorderNormal = FLinearColor(FColor(58, 75, 120)); // border - Secondary.BorderHover = FLinearColor(FColor(78, 163, 255)); // blue - Secondary.TextColor = FLinearColor(FColor(244, 240, 230));// text + FUIStyleButtonBackground SecBg; + SecBg.BgNormal = FLinearColor(FColor(17, 24, 42)); // panel-2 + SecBg.BgHover = FLinearColor(FColor(22, 32, 58)); + SecBg.BgPressed = FLinearColor(FColor(17, 24, 42)); + Backgrounds.Add(TEXT("Secondary"), SecBg); - // .btn-danger - Danger.BgNormal = FLinearColor(FColor(217, 92, 92, 36)); // rgba(.14) - Danger.BgHover = FLinearColor(FColor(217, 92, 92, 66)); // rgba(.26) - Danger.BgPressed = FLinearColor(FColor(217, 92, 92, 36)); - Danger.BorderNormal = FLinearColor(FColor(217, 92, 92)); // danger - Danger.BorderHover = FLinearColor(FColor(217, 92, 92)); - Danger.TextColor = FLinearColor(FColor(240, 182, 182)); // #F0B6B6 + FUIStyleButtonBackground DngBg; + DngBg.BgNormal = FLinearColor(FColor(217, 92, 92, 36)); + DngBg.BgHover = FLinearColor(FColor(217, 92, 92, 66)); + DngBg.BgPressed = FLinearColor(FColor(217, 92, 92, 36)); + Backgrounds.Add(TEXT("Danger"), DngBg); - // .btn-ghost — transparente + borda suave - Ghost.BgNormal = FLinearColor::Transparent; - Ghost.BgHover = FLinearColor(FColor(22, 32, 58, 128)); - Ghost.BgPressed = FLinearColor::Transparent; - Ghost.BorderNormal = FLinearColor(FColor(58, 75, 120, 140)); // border-soft - Ghost.BorderHover = FLinearColor(FColor(78, 163, 255)); - Ghost.TextColor = FLinearColor(FColor(174, 182, 200)); // text-2 + FUIStyleButtonBackground GhoBg; + GhoBg.BgNormal = FLinearColor::Transparent; + GhoBg.BgHover = FLinearColor(FColor(22, 32, 58, 128)); + GhoBg.BgPressed = FLinearColor::Transparent; + Backgrounds.Add(TEXT("Ghost"), GhoBg); + + // ---- Strokes ---- + FUIStyleButtonStroke PriS; + PriS.BorderNormal = FLinearColor(FColor(201, 167, 90)); + PriS.BorderHover = FLinearColor(FColor(231, 200, 115)); + Strokes.Add(TEXT("Primary"), PriS); + + FUIStyleButtonStroke SecS; + SecS.BorderNormal = FLinearColor(FColor(58, 75, 120)); + SecS.BorderHover = FLinearColor(FColor(78, 163, 255)); + Strokes.Add(TEXT("Secondary"), SecS); + + FUIStyleButtonStroke DngS; + DngS.BorderNormal = FLinearColor(FColor(217, 92, 92)); + DngS.BorderHover = FLinearColor(FColor(217, 92, 92)); + Strokes.Add(TEXT("Danger"), DngS); + + FUIStyleButtonStroke GhoS; + GhoS.BorderNormal = FLinearColor(FColor(58, 75, 120, 140)); + GhoS.BorderHover = FLinearColor(FColor(78, 163, 255)); + Strokes.Add(TEXT("Ghost"), GhoS); + + // ---- Fonts ---- + FUIStyleButtonFont PriF; PriF.TextColor = FLinearColor(FColor(10, 10, 18)); + Fonts.Add(TEXT("Primary"), PriF); + FUIStyleButtonFont SecF; SecF.TextColor = FLinearColor(FColor(244, 240, 230)); + Fonts.Add(TEXT("Secondary"), SecF); + FUIStyleButtonFont DngF; DngF.TextColor = FLinearColor(FColor(240, 182, 182)); + Fonts.Add(TEXT("Danger"), DngF); + FUIStyleButtonFont GhoF; GhoF.TextColor = FLinearColor(FColor(174, 182, 200)); + Fonts.Add(TEXT("Ghost"), GhoF); } - // .btn-primary — dourado, texto quase preto + /** Cores/textura de fundo por variante (chave FName). */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FUIStyleButtonVariant Primary; + TMap Backgrounds; - // .btn-secondary — painel sólido + borda + /** Borda (cor, espessura, raio) por variante. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FUIStyleButtonVariant Secondary; + TMap Strokes; - // .btn-danger + /** Tipografia (cor de texto, CommonTextStyle) por variante. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FUIStyleButtonVariant Danger; - - // .btn-ghost — transparente + borda suave - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FUIStyleButtonVariant Ghost; + TMap Fonts; + // Globais comuns às variantes: UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") FMargin Padding = FMargin(22.f, 12.f); UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") float MinHeight = 48.f; - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - float CornerRadius = 8.f; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - float BorderWidth = 1.f; - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") float HoverScale = 1.02f; @@ -445,33 +554,332 @@ struct ZMMO_API FUIStyleTooltip FLinearColor TextColor = FLinearColor(FColor(244, 240, 230)); // Text }; -/** Spinner/throbber indeterminado (ex.: "Conectando..." no Boot). */ +// ===================================================================== +// FUIStyleSpinner — padrão multi-map (Hyper-style). Sub-mapas por categoria +// (Colors, Layouts) com a MESMA CHAVE de variante. Permite criar variantes +// como "Default", "Small", "Loading", etc. +// ===================================================================== +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleSpinnerColors +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Spinner") + FLinearColor Color = FLinearColor(FColor(201, 167, 90)); // Gold + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Spinner") + FLinearColor TrackColor = FLinearColor(FColor(58, 75, 120, 140)); +}; + +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleSpinnerLayout +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Spinner") + float Radius = 28.f; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Spinner") + float Thickness = 3.f; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Spinner", meta = (ClampMin = "1")) + int32 NumberOfPieces = 8; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Spinner", meta = (ClampMin = "0.05")) + float Period = 0.9f; +}; + +/** View FLAT composta da variante de Spinner. */ +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleSpinnerVariant +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadOnly, Category = "UI Style|Spinner") + FUIStyleSpinnerColors Colors; + + UPROPERTY(BlueprintReadOnly, Category = "UI Style|Spinner") + FUIStyleSpinnerLayout Layout; +}; + USTRUCT(BlueprintType) struct ZMMO_API FUIStyleSpinner { GENERATED_BODY() - /** Cor das peças do spinner. Default = Gold (Aurora Arcana). */ + FUIStyleSpinner() + { + Colors.Add(TEXT("Default"), FUIStyleSpinnerColors()); + Layouts.Add(TEXT("Default"), FUIStyleSpinnerLayout()); + } + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Spinner") - FLinearColor Color = FLinearColor(FColor(201, 167, 90)); // Gold + TMap Colors; - /** Cor do "trilho"/peças apagadas (UCircularThrobber não usa; reservado). */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Spinner") - FLinearColor TrackColor = FLinearColor(FColor(58, 75, 120, 140)); + TMap Layouts; +}; - /** Raio do anel (px). */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Spinner") - float Radius = 28.f; +// ===================================================================== +// FUIStyleCheckBox — padrão multi-map (Hyper-style). Sub-mapas por categoria +// visual (Fills, Strokes, Layouts) com a MESMA CHAVE de variante. Cada +// UUICheckBox_Base referencia o nome (FName Variant) e o C++ compõe. +// ===================================================================== +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleCheckBoxFill +{ + GENERATED_BODY() - /** Espessura da peça (px). */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Spinner") - float Thickness = 3.f; + /** Preenchimento da caixa quando marcada. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|CheckBox") + FLinearColor BoxColor = FLinearColor(FColor(201, 167, 90)); // Gold - /** Número de peças do UCircularThrobber. */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Spinner", meta = (ClampMin = "1")) - int32 NumberOfPieces = 8; + /** Cor do "tique"/glifo de check. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|CheckBox") + FLinearColor CheckColor = FLinearColor(FColor(18, 22, 34)); // Bg0 - /** Período de uma volta (s). */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Spinner", meta = (ClampMin = "0.05")) - float Period = 0.9f; + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|CheckBox") + FLinearColor HoveredColor = FLinearColor(FColor(224, 196, 130)); + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|CheckBox") + FLinearColor PressedColor = FLinearColor(FColor(178, 146, 74)); + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|CheckBox") + FLinearColor DisabledColor = FLinearColor(FColor(90, 96, 112, 140)); +}; + +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleCheckBoxStroke +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|CheckBox") + FLinearColor BorderColor = FLinearColor(FColor(58, 75, 120, 140)); + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|CheckBox", meta = (ClampMin = "0")) + float BorderThickness = 1.5f; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|CheckBox", meta = (ClampMin = "0")) + float CornerRadius = 4.f; +}; + +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleCheckBoxLayout +{ + GENERATED_BODY() + + /** Lado da caixa (px). */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|CheckBox", meta = (ClampMin = "8")) + float BoxSize = 22.f; +}; + +/** View FLAT composta da variante de CheckBox — usada pelo BP hook. */ +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleCheckBoxVariant +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadOnly, Category = "UI Style|CheckBox") + FUIStyleCheckBoxFill Fill; + + UPROPERTY(BlueprintReadOnly, Category = "UI Style|CheckBox") + FUIStyleCheckBoxStroke Stroke; + + UPROPERTY(BlueprintReadOnly, Category = "UI Style|CheckBox") + FUIStyleCheckBoxLayout Layout; +}; + +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleCheckBox +{ + GENERATED_BODY() + + FUIStyleCheckBox() + { + Fills.Add(TEXT("Default"), FUIStyleCheckBoxFill()); + Strokes.Add(TEXT("Default"), FUIStyleCheckBoxStroke()); + Layouts.Add(TEXT("Default"), FUIStyleCheckBoxLayout()); + } + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|CheckBox") + TMap Fills; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|CheckBox") + TMap Strokes; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|CheckBox") + TMap Layouts; +}; + +// ===================================================================== +// FUIStyleInput — padrão multi-map (estilo Hyper): sub-mapas independentes +// por categoria visual (Backgrounds, Strokes, Typography). O widget escolhe +// um nome único de variante (ex.: "Box") que existe em CADA sub-mapa; +// reaproveita-se sem dor (ex.: mesma Stroke em vários Backgrounds). +// ===================================================================== +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleInputBackground +{ + GENERATED_BODY() + + /** + * Brush completo do background (Image, Image Size, Matiz, Desenhar como, + * Ladrilhos, Margin/9-slice, UV). Mesma UI de FSlateBrush exposta no UMG. + * Se Brush.ResourceObject for nulo, pinta cor sólida (procedural — DrawAs + * = Image sem textura). + */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input") + FSlateBrush Brush; + + /** Tint multiplicado por cima do Brush em estado de hover. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input") + FLinearColor HoveredColor = FLinearColor::White; + + /** Tint multiplicado por cima do Brush em estado de focus. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input") + FLinearColor FocusedColor = FLinearColor::White; +}; + +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleInputStroke +{ + GENERATED_BODY() + + /** + * Brush completo da borda (Image, Image Size, Matiz, Desenhar como, + * Ladrilhos, Margin). Use textura 9-slice (DrawAs=Box) ou Image para + * stroke decorada estilo Hyper Search. + */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input") + FSlateBrush Brush; + + /** Espessura procedural (efeito visual; usado por widgets que renderizam stroke vector). */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input", meta = (ClampMin = "0")) + float BorderThickness = 1.f; + + /** Raio do canto procedural (efeito visual; usado por widgets que renderizam RoundedBox). */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input", meta = (ClampMin = "0")) + float CornerRadius = 6.f; +}; + +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleInputTypography +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input") + FLinearColor TextColor = FLinearColor(FColor(244, 240, 230)); + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input") + FLinearColor HintColor = FLinearColor(FColor(150, 160, 180, 200)); + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input", meta = (ClampMin = "1")) + float Size = 16.f; + + /** Fonte específica desta tipografia. Se vazia, usa FUIStyle.Text.BodyFont. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input") + FSlateFontInfo FontOverride; +}; + +/** + * View "composta" da variante (Background + Stroke + Typography). Não é + * editada diretamente no DT — é o que o UUIInput_Base monta ao resolver + * a chave Variant nos 3 sub-mapas. Mantida aqui para uso no C++. + */ +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleInputVariant +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadOnly, Category = "UI Style|Input") + FUIStyleInputBackground Background; + + UPROPERTY(BlueprintReadOnly, Category = "UI Style|Input") + FUIStyleInputStroke Stroke; + + UPROPERTY(BlueprintReadOnly, Category = "UI Style|Input") + FUIStyleInputTypography Typography; +}; + +/** + * Campo de texto — padrão multi-map (Hyper-style). 3 sub-mapas independentes + * com a MESMA CHAVE (nome da variante: "Box", "Outline", "Underline", + * "Search", ou customizada). O UUIInput_Base usa Variant=FName e busca o + * pedaço em cada sub-mapa; assim dá pra reaproveitar (ex.: a mesma Stroke + * em variantes com Backgrounds diferentes). + * + * Construtor já popula com defaults bonitos inspirados na Login. + */ +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleInput +{ + GENERATED_BODY() + + FUIStyleInput() + { + auto MakeBg = [](const FLinearColor& Tint) + { + FUIStyleInputBackground B; + B.Brush.TintColor = FSlateColor(Tint); + B.Brush.DrawAs = ESlateBrushDrawType::Image; + return B; + }; + + // ---- Backgrounds ---- + Backgrounds.Add(TEXT("Box"), MakeBg(FLinearColor(FColor(9, 13, 24, 235)))); + Backgrounds.Add(TEXT("Outline"), MakeBg(FLinearColor(0, 0, 0, 0))); + Backgrounds.Add(TEXT("Underline"), MakeBg(FLinearColor(0, 0, 0, 0))); + Backgrounds.Add(TEXT("Search"), MakeBg(FLinearColor(FColor(17, 24, 42)))); + + auto MakeStroke = [](const FLinearColor& Tint, float Thickness, float Radius) + { + FUIStyleInputStroke S; + S.Brush.TintColor = FSlateColor(Tint); + S.Brush.DrawAs = ESlateBrushDrawType::Box; + S.Brush.Margin = FMargin(4.f); + S.BorderThickness = Thickness; + S.CornerRadius = Radius; + return S; + }; + + // ---- Strokes ---- + Strokes.Add(TEXT("Box"), MakeStroke(FLinearColor(FColor(58, 75, 120, 140)), 1.f, 6.f)); + Strokes.Add(TEXT("Outline"), MakeStroke(FLinearColor(FColor(58, 75, 120)), 1.f, 6.f)); + Strokes.Add(TEXT("Underline"), MakeStroke(FLinearColor(FColor(58, 75, 120, 160)), 2.f, 0.f)); + Strokes.Add(TEXT("Search"), MakeStroke(FLinearColor(FColor(58, 75, 120, 140)), 1.f, 18.f)); + + // ---- Typography ---- + FUIStyleInputTypography DefaultType; + DefaultType.TextColor = FLinearColor(FColor(244, 240, 230)); + DefaultType.HintColor = FLinearColor(FColor(150, 160, 180, 200)); + DefaultType.Size = 16.f; + Typography.Add(TEXT("Box"), DefaultType); + Typography.Add(TEXT("Outline"), DefaultType); + Typography.Add(TEXT("Underline"), DefaultType); + Typography.Add(TEXT("Search"), DefaultType); + } + + /** Cores/textura de fundo por variante (chave FName). */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input") + TMap Backgrounds; + + /** Borda (cor, espessura, arredondamento) por variante. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input") + TMap Strokes; + + /** Tipografia (cor de texto/hint, tamanho, fonte) por variante. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input") + TMap Typography; + + /** Padding interno do campo (px, uniforme) — comum a todas variantes. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input", meta = (ClampMin = "0")) + float Padding = 10.f; + + /** Altura mínima do campo (px). */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input", meta = (ClampMin = "0")) + float MinHeight = 44.f; + + /** Largura mínima PADRÃO do campo (px). Designer mostra esse tamanho. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Input", meta = (ClampMin = "0")) + float MinWidth = 240.f; }; diff --git a/Source/ZMMO/Game/UI/FrontEnd/UILoginScreen_Base.cpp b/Source/ZMMO/Game/UI/FrontEnd/UILoginScreen_Base.cpp new file mode 100644 index 0000000..b337f4f --- /dev/null +++ b/Source/ZMMO/Game/UI/FrontEnd/UILoginScreen_Base.cpp @@ -0,0 +1,342 @@ +#include "UILoginScreen_Base.h" + +#include "ZMMO.h" +#include "CharServerOpcodes.h" +#include "UIButton_Base.h" +#include "UICheckBox_Base.h" +#include "UILabel_Base.h" +#include "ZMMOLoginSaveGame.h" +#include "Kismet/GameplayStatics.h" +#include "UIFrontEndFlowSubsystem.h" +#include "ZMMOThemeSubsystem.h" +#include "ZeusCharServerSubsystem.h" +#include "ZeusPacketWriter.h" +#include "ZeusPacketReader.h" +#include "CommonTextBlock.h" +#include "Components/Border.h" +#include "Engine/GameInstance.h" +#include "Engine/DataTable.h" +#include "UI/UIStyleRow.h" + +namespace +{ + // Mesmo helper da Boot: runtime usa o tema ativo; design-time carrega a + // row "Default" de DT_UI_Styles para o Designer ver o fundo. + FUIStyle ResolveLoginStyle(const UUserWidget* Widget, const FUIStyle& Fallback) + { + if (Widget) + { + if (const UGameInstance* GI = Widget->GetGameInstance()) + { + if (const UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) + { + return Theme->GetActiveUIStyle(); + } + } + } + // Design-time relê o DT a cada chamada: editar o DT_UI_Styles e + // RECOMPILAR o WBP já reflete (sem reiniciar o editor). + FUIStyle DesignStyle; + bool bHas = false; + if (const UDataTable* DT = LoadObject(nullptr, + TEXT("/Game/ZMMO/Data/UI/DT_UI_Styles.DT_UI_Styles"))) + { + if (const FUIStyleRow* Row = DT->FindRow( + FName(TEXT("Default")), TEXT("UILoginDesign"), false)) + { + DesignStyle = Row->Style; + bHas = true; + } + } + return bHas ? DesignStyle : Fallback; + } + + // Token de DEV: o StubTokenValidator aceita `dev:` ou `dev:`. + // Um nome de usuário comum NÃO valida — então em dev mapeamos para + // `dev:` (o número digitado, se houver; senão 1). Auth real + // (HTTP /auth/login + JWT) entra em tarefa dedicada. + FString MakeDevToken(const FString& User) + { + const FString Trimmed = User.TrimStartAndEnd(); + if (!Trimmed.IsEmpty() && Trimmed.IsNumeric()) + { + return FString::Printf(TEXT("dev:%s"), *Trimmed); + } + return TEXT("dev:1"); + } +} + +void UUILoginScreen_Base::NativePreConstruct() +{ + Super::NativePreConstruct(); + RefreshUIStyle(); // preview do fundo no Designer (DT_UI_Styles "Default") +} + +void UUILoginScreen_Base::NativeOnActivated() +{ + Super::NativeOnActivated(); // base: RefreshUIStyle + BP_OnScreenActivated + + if (Btn_Login) + { + Btn_Login->OnClicked().AddUObject(this, &UUILoginScreen_Base::HandleLoginClicked); + } + if (Btn_Back) + { + Btn_Back->OnClicked().AddUObject(this, &UUILoginScreen_Base::HandleBackClicked); + } + + UZeusCharServerSubsystem* Char = GetCharServer(); + if (Char && !bRawBound) + { + Char->OnRawMessage.AddDynamic(this, &UUILoginScreen_Base::HandleCharRawMessage); + bRawBound = true; + } + + bAwaitingAuth = false; + const bool bConnected = Char && Char->IsConnected(); + SetBusy(false); + if (Btn_Login) + { + Btn_Login->SetIsInteractionEnabled(bConnected); + } + SetStatus(bConnected ? IdleText : NotConnectedText); + + // "Lembrar Acesso": tenta restaurar usuário salvo (async, não trava UI). + LoadRememberedAccess(); +} + +void UUILoginScreen_Base::NativeOnDeactivated() +{ + if (Btn_Login) + { + Btn_Login->OnClicked().RemoveAll(this); + } + if (Btn_Back) + { + Btn_Back->OnClicked().RemoveAll(this); + } + if (bRawBound) + { + if (UZeusCharServerSubsystem* Char = GetCharServer()) + { + Char->OnRawMessage.RemoveDynamic(this, &UUILoginScreen_Base::HandleCharRawMessage); + } + bRawBound = false; + } + Super::NativeOnDeactivated(); +} + +void UUILoginScreen_Base::RefreshUIStyle_Implementation() +{ + Super::RefreshUIStyle_Implementation(); + + const FUIStyle Fallback; + const FUIStyle AS = ResolveLoginStyle(this, Fallback); + + if (Border_Bg) + { + // Fundo data-driven (DT_UI_Styles) — sem hard-ref de textura no WBP + // (§5). Fallback p/ cor se vazio. + if (AS.ScreenBackground.GetResourceObject() != nullptr) + { + Border_Bg->SetBrush(AS.ScreenBackground); + } + else + { + Border_Bg->SetBrushColor(AS.Palette.Bg0); + } + } + if (Text_Status) + { + Text_Status->SetColorAndOpacity(FSlateColor(AS.Semantic.OnSurfaceMuted)); + } +} + +UZeusCharServerSubsystem* UUILoginScreen_Base::GetCharServer() const +{ + const UGameInstance* GI = GetGameInstance(); + return GI ? GI->GetSubsystem() : nullptr; +} + +void UUILoginScreen_Base::SetBusy(bool bBusy) +{ + if (Btn_Login) + { + Btn_Login->SetIsInteractionEnabled(!bBusy); + } + if (Input_User) + { + Input_User->SetIsEnabled(!bBusy); + } + if (Input_Password) + { + Input_Password->SetIsEnabled(!bBusy); + } +} + +void UUILoginScreen_Base::SetStatus(const FText& InText) +{ + if (Text_Status) + { + Text_Status->SetText(InText); + } +} + +void UUILoginScreen_Base::HandleLoginClicked() +{ + if (bAwaitingAuth) + { + return; + } + + UZeusCharServerSubsystem* Char = GetCharServer(); + if (!Char || !Char->IsConnected()) + { + SetStatus(NotConnectedText); + return; + } + + const FString User = Input_User ? Input_User->GetText().ToString() : FString(); + const FString Pass = Input_Password ? Input_Password->GetText().ToString() : FString(); + if (User.TrimStartAndEnd().IsEmpty() || Pass.IsEmpty()) + { + SetStatus(EmptyFieldsText); + return; + } + + // "Lembrar Acesso": salva/apaga ANTES do envio do auth — atende a + // expectativa "marquei → vai lembrar próxima vez" independente do + // resultado da autenticação (senha errada ainda lembra do usuário, + // padrão dos launchers comerciais). + SaveOrClearRememberedAccess(User); + + // Escopo dev: token aceito pelo StubTokenValidator (senha ignorada no + // stub; mantida na UI para o fluxo real futuro). + const FString Token = MakeDevToken(User); + + FZeusPacketWriter W; + W.WriteStringUtf8(Token); + + const bool bSent = Char->SendCharRequest( + ZMMOCharOp::C_CHAR_AUTH_REQUEST, W.GetBuffer()); + if (!bSent) + { + UE_LOG(LogZMMO, Warning, TEXT("Login: falha ao enviar C_CHAR_AUTH_REQUEST.")); + SetStatus(NotConnectedText); + return; + } + + bAwaitingAuth = true; + SetBusy(true); + SetStatus(AuthenticatingText); + UE_LOG(LogZMMO, Log, TEXT("Login: C_CHAR_AUTH_REQUEST enviado (token dev).")); +} + +void UUILoginScreen_Base::HandleBackClicked() +{ + if (const UGameInstance* GI = GetGameInstance()) + { + if (UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem()) + { + Flow->RequestBack(); + } + } +} + +void UUILoginScreen_Base::HandleCharRawMessage(int32 Opcode, const TArray& Payload) +{ + if (!bAwaitingAuth) + { + return; // só nos interessa a resposta do auth que disparamos + } + + if (Opcode == ZMMOCharOp::S_CHAR_AUTH_OK) + { + bAwaitingAuth = false; + FZeusPacketReader R(Payload.GetData(), Payload.Num()); + FString AccountId, Username; + R.ReadStringUtf8(AccountId); + R.ReadStringUtf8(Username); + uint64 ExpiresMs = 0; + R.ReadUInt64(ExpiresMs); + UE_LOG(LogZMMO, Log, TEXT("Login: S_CHAR_AUTH_OK (account=%s user=%s) → ServerSelect."), + *AccountId, *Username); + + if (const UGameInstance* GI = GetGameInstance()) + { + if (UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem()) + { + Flow->RequestEnterServerSelect(); + } + } + } + else if (Opcode == ZMMOCharOp::S_CHAR_AUTH_REJECT) + { + bAwaitingAuth = false; + FZeusPacketReader R(Payload.GetData(), Payload.Num()); + uint16 Reason = 0; + R.ReadUInt16(Reason); + UE_LOG(LogZMMO, Warning, TEXT("Login: S_CHAR_AUTH_REJECT (reason=%u)."), Reason); + SetBusy(false); + SetStatus(FText::Format( + NSLOCTEXT("ZMMO", "LoginRejectFmt", "{0} (cód. {1})"), + RejectedText, FText::AsNumber(Reason))); + } +} + +void UUILoginScreen_Base::LoadRememberedAccess() +{ + if (!UGameplayStatics::DoesSaveGameExist( + UZMMOLoginSaveGame::SlotName, UZMMOLoginSaveGame::UserIndex)) + { + return; + } + + FAsyncLoadGameFromSlotDelegate Delegate; + Delegate.BindUFunction(this, + FName(TEXT("HandleLoadLoginCacheCompleted"))); + UGameplayStatics::AsyncLoadGameFromSlot( + UZMMOLoginSaveGame::SlotName, UZMMOLoginSaveGame::UserIndex, Delegate); +} + +void UUILoginScreen_Base::HandleLoadLoginCacheCompleted(const FString& /*InSlotName*/, + int32 /*InUserIndex*/, USaveGame* LoadedGame) +{ + const UZMMOLoginSaveGame* Save = Cast(LoadedGame); + if (!Save || !Save->bRememberAccess || Save->SavedUsername.IsEmpty()) + { + return; + } + if (Input_User) + { + Input_User->SetText(FText::FromString(Save->SavedUsername)); + } + if (RememberAccess) + { + RememberAccess->SetIsChecked(true); + } +} + +void UUILoginScreen_Base::SaveOrClearRememberedAccess(const FString& User) +{ + const bool bRemember = RememberAccess ? RememberAccess->IsChecked() : false; + if (bRemember) + { + UZMMOLoginSaveGame* Save = Cast( + UGameplayStatics::CreateSaveGameObject(UZMMOLoginSaveGame::StaticClass())); + if (Save) + { + Save->SavedUsername = User.TrimStartAndEnd(); + Save->bRememberAccess = true; + UGameplayStatics::AsyncSaveGameToSlot( + Save, UZMMOLoginSaveGame::SlotName, UZMMOLoginSaveGame::UserIndex); + } + } + else if (UGameplayStatics::DoesSaveGameExist( + UZMMOLoginSaveGame::SlotName, UZMMOLoginSaveGame::UserIndex)) + { + // Checkbox desmarcada: apaga save antigo (não vamos lembrar mais). + UGameplayStatics::DeleteGameInSlot( + UZMMOLoginSaveGame::SlotName, UZMMOLoginSaveGame::UserIndex); + } +} diff --git a/Source/ZMMO/Game/UI/FrontEnd/UILoginScreen_Base.h b/Source/ZMMO/Game/UI/FrontEnd/UILoginScreen_Base.h new file mode 100644 index 0000000..37a4d95 --- /dev/null +++ b/Source/ZMMO/Game/UI/FrontEnd/UILoginScreen_Base.h @@ -0,0 +1,105 @@ +#pragma once + +#include "CoreMinimal.h" +#include "UIActivatableScreen_Base.h" +#include "UILoginScreen_Base.generated.h" + +class UUIButton_Base; +class UUICheckBox_Base; +class UUILabel_Base; +class UCommonTextBlock; +class UBorder; +class USaveGame; +class UZeusCharServerSubsystem; + +/** + * Tela de Login — reconstruída a partir do mock do Zeus UMG Forge + * (WBP_LoginScreen) com os widgets compartilhados (UI_Button_Master, + * UI_CheckBox_Master). Camada Abstract; o WBP concreto (WBP_Login) herda + * DIRETO desta classe (ARQUITETURA.md §3.3). + * + * Auth (escopo atual — dev): ao clicar "Entrar", envia C_CHAR_AUTH_REQUEST + * (opcode 2000) pelo WebSocket do UZeusCharServerSubsystem com um token + * `dev:` (StubTokenValidator aceita em dev; auth real virá em tarefa + * dedicada). S_CHAR_AUTH_OK → Flow::RequestEnterServerSelect; reject → + * mensagem de erro. Sem hard-ref de asset de tema (§5). + */ +UCLASS(Abstract, Blueprintable) +class ZMMO_API UUILoginScreen_Base : public UUIActivatableScreen_Base +{ + GENERATED_BODY() + +protected: + virtual void NativePreConstruct() override; + virtual void NativeOnActivated() override; + virtual void NativeOnDeactivated() override; + virtual void RefreshUIStyle_Implementation() override; + + /** Textos de status/feedback da autenticação. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Login") + FText IdleText = FText::FromString(TEXT("Acesso protegido pelo Farol da Manhã.")); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Login") + FText AuthenticatingText = FText::FromString(TEXT("Autenticando…")); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Login") + FText NotConnectedText = FText::FromString(TEXT("Sem conexão com o Gateway.")); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Login") + FText EmptyFieldsText = FText::FromString(TEXT("Informe usuário e senha.")); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Login") + FText RejectedText = FText::FromString(TEXT("Acesso negado pelo Gateway.")); + + // ---- Widgets do WBP (nomes esperados) ---- + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Border_Bg; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Input_User; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Input_Password; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr RememberAccess; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Btn_Login; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Btn_Back; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Text_Status; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Text_Version; + +private: + UZeusCharServerSubsystem* GetCharServer() const; + void SetBusy(bool bBusy); + void SetStatus(const FText& InText); + + void HandleLoginClicked(); + void HandleBackClicked(); + + UFUNCTION() + void HandleCharRawMessage(int32 Opcode, const TArray& Payload); + + /** "Lembrar Acesso" — async load do cache local (LoginCache slot) e + popula Input_User + Check_Remember se houver entrada salva. */ + void LoadRememberedAccess(); + + UFUNCTION() + void HandleLoadLoginCacheCompleted(const FString& InSlotName, int32 InUserIndex, USaveGame* LoadedGame); + + /** Salva (se checkbox marcada) ou apaga (se desmarcada) o cache de Login. + Chamado em HandleLoginClicked — antes do envio do auth, atende a + expectativa "marquei → vai lembrar próxima vez" independente do + resultado da autenticação. */ + void SaveOrClearRememberedAccess(const FString& User); + + bool bRawBound = false; + bool bAwaitingAuth = false; +}; diff --git a/Source/ZMMO/Game/UI/FrontEnd/ZMMOLoginSaveGame.cpp b/Source/ZMMO/Game/UI/FrontEnd/ZMMOLoginSaveGame.cpp new file mode 100644 index 0000000..cad1be3 --- /dev/null +++ b/Source/ZMMO/Game/UI/FrontEnd/ZMMOLoginSaveGame.cpp @@ -0,0 +1,3 @@ +#include "ZMMOLoginSaveGame.h" + +const FString UZMMOLoginSaveGame::SlotName = TEXT("LoginCache"); diff --git a/Source/ZMMO/Game/UI/FrontEnd/ZMMOLoginSaveGame.h b/Source/ZMMO/Game/UI/FrontEnd/ZMMOLoginSaveGame.h new file mode 100644 index 0000000..9cdab81 --- /dev/null +++ b/Source/ZMMO/Game/UI/FrontEnd/ZMMOLoginSaveGame.h @@ -0,0 +1,37 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/SaveGame.h" +#include "ZMMOLoginSaveGame.generated.h" + +/** + * Cache local da tela de Login. Persiste APENAS o nome do usuário (texto + * plano — risco zero, é só rótulo) e o estado da checkbox "Lembrar Acesso". + * + * NÃO persistir senha aqui (CWE-256). Quando o auth real (HTTP/JWT) entrar, + * adicionar `FString EncryptedRefreshToken` cifrado via DPAPI + * (CryptProtectData no Windows / equivalente em outras plataformas) — o token + * é opaco e revogável, padrão dominante em jogos comerciais + * (Battle.net/Riot/Epic). Senha em si nunca toca o disco do cliente. + * + * Arquivo: [Project]/Saved/SaveGames/LoginCache_0.sav (binary blob das + * UPROPERTY não-Transient via UE serializer). + */ +UCLASS() +class ZMMO_API UZMMOLoginSaveGame : public USaveGame +{ + GENERATED_BODY() + +public: + UPROPERTY() + FString SavedUsername; + + UPROPERTY() + bool bRememberAccess = false; + + /** Slot do arquivo .sav (sem extensão). */ + static const FString SlotName; + + /** Index de usuário do SO — 0 para single-player local. */ + static constexpr int32 UserIndex = 0; +}; diff --git a/Source/ZMMO/Game/UI/Widgets/UIInput_Base.cpp b/Source/ZMMO/Game/UI/Widgets/UIInput_Base.cpp new file mode 100644 index 0000000..4a3a4a6 --- /dev/null +++ b/Source/ZMMO/Game/UI/Widgets/UIInput_Base.cpp @@ -0,0 +1,325 @@ +#include "UIInput_Base.h" + +#include "ZMMOThemeSubsystem.h" +#include "Engine/GameInstance.h" +#include "Engine/DataTable.h" +#include "Components/EditableTextBox.h" +#include "Components/SizeBox.h" +#include "Components/Image.h" +#include "Styling/CoreStyle.h" +#include "Styling/SlateBrush.h" +#include "UI/UIStyleRow.h" + +namespace +{ + // Mesmo helper das outras bases: runtime usa o tema ativo; design-time + // carrega a row "Default" de DT_UI_Styles para o Designer ver igual. + FUIStyle ResolveInputStyle(const UUserWidget* Widget, const FUIStyle& Fallback) + { + if (Widget) + { + if (const UGameInstance* GI = Widget->GetGameInstance()) + { + if (const UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) + { + return Theme->GetActiveUIStyle(); + } + } + } + FUIStyle DesignStyle; + bool bHas = false; + if (const UDataTable* DT = LoadObject(nullptr, + TEXT("/Game/ZMMO/Data/UI/DT_UI_Styles.DT_UI_Styles"))) + { + if (const FUIStyleRow* Row = DT->FindRow( + FName(TEXT("Default")), TEXT("UIInputDesign"), false)) + { + DesignStyle = Row->Style; + bHas = true; + } + } + return bHas ? DesignStyle : Fallback; + } + + // Pinta um UImage com o FSlateBrush vindo do DT (já tem Image, ImageSize, + // TintColor, DrawAs, Tiling, Margin/9-slice, UV — tudo). Brush vazio + // (sem ResourceObject e DrawAs!=NoDrawType) força Image procedural com + // Tint da própria struct — evita brush nulo que vira magenta. + void PaintImage(UImage* Img, const FSlateBrush& Brush) + { + if (!IsValid(Img)) { return; } + FSlateBrush B = Brush; + if (!B.GetResourceObject() && B.DrawAs != ESlateBrushDrawType::NoDrawType) + { + // Sem textura: pintura procedural (cor sólida). + B.DrawAs = ESlateBrushDrawType::Image; + } + Img->SetBrush(B); + // SetColorAndOpacity = identidade; cor já vem do Brush.TintColor. + Img->SetColorAndOpacity(FLinearColor::White); + } +} + +TArray UUIInput_Base::CollectVariantOptions() +{ + TArray Options; + for (const TCHAR* N : { TEXT("Box"), TEXT("Outline"), TEXT("Underline"), TEXT("Search") }) + { + Options.Add(N); + } + if (const UDataTable* DT = LoadObject(nullptr, + TEXT("/Game/ZMMO/Data/UI/DT_UI_Styles.DT_UI_Styles"))) + { + if (const FUIStyleRow* Row = DT->FindRow( + FName(TEXT("Default")), TEXT("UIInputVariantOptions"), false)) + { + for (const TPair& P : Row->Style.Input.Backgrounds) { Options.AddUnique(P.Key.ToString()); } + for (const TPair& P : Row->Style.Input.Strokes) { Options.AddUnique(P.Key.ToString()); } + for (const TPair& P : Row->Style.Input.Typography) { Options.AddUnique(P.Key.ToString()); } + } + } + return Options; +} + +TArray UUIInput_Base::GetVariantOptions() const +{ + return CollectVariantOptions(); +} + +FUIStyleInputVariant UUIInput_Base::ResolveVariant(const FUIStyleInput& I) const +{ + // Padrão multi-map (Hyper-style): compõe a variante buscando o nome + // (Variant FName) em CADA sub-mapa independente. Se um sub-mapa não + // tiver a chave, cai nos Defaults inline (sempre GC-safe — só + // cores/floats no constructor de FUIStyleInput). + static const FUIStyleInput Defaults; + FUIStyleInputVariant V; + + if (const FUIStyleInputBackground* E = I.Backgrounds.Find(Variant)) { V.Background = *E; } + else if (const FUIStyleInputBackground* E2 = Defaults.Backgrounds.Find(Variant)) { V.Background = *E2; } + else { V.Background = Defaults.Backgrounds.FindRef(TEXT("Box")); } + + if (const FUIStyleInputStroke* E = I.Strokes.Find(Variant)) { V.Stroke = *E; } + else if (const FUIStyleInputStroke* E2 = Defaults.Strokes.Find(Variant)) { V.Stroke = *E2; } + else { V.Stroke = Defaults.Strokes.FindRef(TEXT("Box")); } + + if (const FUIStyleInputTypography* E = I.Typography.Find(Variant)) { V.Typography = *E; } + else if (const FUIStyleInputTypography* E2 = Defaults.Typography.Find(Variant)) { V.Typography = *E2; } + else { V.Typography = Defaults.Typography.FindRef(TEXT("Box")); } + + return V; +} + +void UUIInput_Base::ApplyStyle(const FUIStyleInputVariant& V, float InPadding, + const FSlateFontInfo& ThemeFont, float FontSizePx) +{ + // Background e Stroke (UImages) — seguro em design-time e runtime. + if (IsValid(Background)) + { + PaintImage(Background, V.Background.Brush); + } + if (IsValid(Stroke)) + { + PaintImage(Stroke, V.Stroke.Brush); + } + + if (!IsValid(Input)) + { + return; + } + + // IMPORTANTE: NÃO mexer no UEditableTextBox em design-time. Qualquer + // chamada (SetForegroundColor / SetIsPassword / SetHintText / SetWidgetStyle) + // dispara SEditableText::SynchronizeTextStyle → DetermineFont via FAttribute + // delegate dangling → crash de "FSlateFontInfo::FSlateFontInfo() reading + // 0xff..." nesta build da engine. Em design-time só pintamos Background/ + // Stroke (UImages, path estável). Preview do HintText/Password só em PIE. + if (IsDesignTime()) + { + return; + } + + // SÓ chamadas pontuais — NÃO chamar SetWidgetStyle (copia FEditableTextBoxStyle + // inteiro incluindo TextStyle.Font). DetermineFont callback durante Prepass + // do Slate dereferencia FSlateFontInfo dangling se a Font veio do DT com + // FontObject UObject que ficou em estado intermediário → crash em + // FSlateFontInfo::FSlateFontInfo() durante SEditableText::SynchronizeTextStyle. + // Background brushes do EditableTextBox: configurar transparente direto no + // asset UI_Input_Master no Designer (one-time setup) — o Background/Stroke + // (UImages) atrás do EditableTextBox é quem pinta o visual via DT. + Input->SetForegroundColor(V.Typography.TextColor); + Input->SetIsPassword(bIsPassword); + if (!HintText.IsEmpty()) + { + Input->SetHintText(HintText); + } + + // Suprime warnings de parâmetros não usados (assinatura mantida pra + // compatibilidade — InPadding/ThemeFont/FontSizePx podem ser usados + // futuramente quando o caminho de SetWidgetStyle for re-habilitado de + // forma segura). + (void)InPadding; + (void)ThemeFont; + (void)FontSizePx; +} + +void UUIInput_Base::RefreshUIStyle() +{ + const FUIStyle Fallback; + const FUIStyle AS = ResolveInputStyle(this, Fallback); + const FUIStyleInput& I = AS.Input; + const FUIStyleInputVariant V = ResolveVariant(I); + + // Tamanho via Width/Height Override (mais agressivo que MinDesired — força + // tamanho exato). Resolução em 3 camadas: + // 1. PreferredWidth/Height UPROPERTY > 0 → override explícito (designer) + // 2. User mexeu DIRETO no Size_Root.WidthOverride (Designer) → preserva + // 3. Senão → aplica do DT (I.MinWidth/MinHeight) + if (IsValid(Size_Root)) + { + auto ApplyDimension = [](USizeBox* Box, bool bIsWidth, float PreferredVal, + float ThemeVal, float& LastThemeApplied) + { + const bool bHasManualFlag = bIsWidth ? Box->IsWidthOverride() : Box->IsHeightOverride(); + const float CurrentVal = bIsWidth ? Box->GetWidthOverride() : Box->GetHeightOverride(); + auto Setter = [Box, bIsWidth](float V) + { + if (bIsWidth) { Box->SetWidthOverride(V); } + else { Box->SetHeightOverride(V); } + }; + + if (PreferredVal > 0.f) + { + // (1) Override explícito via UPROPERTY. + Setter(PreferredVal); + LastThemeApplied = -1.f; // user controla via UPROPERTY a partir daqui + } + else if (bHasManualFlag && LastThemeApplied >= 0.f && CurrentVal != LastThemeApplied) + { + // (2) User editou direto no SizeBox no Designer — preserva. + } + else if (bHasManualFlag && LastThemeApplied < 0.f) + { + // (2b) Flag manual ativo mas C++ nunca aplicou nesta instância + // (fresh load): assume que é override do user no asset — preserva. + } + else + { + // (3) Aplica do DT e marca como "C++ aplicou". + Setter(ThemeVal); + LastThemeApplied = ThemeVal; + } + }; + + ApplyDimension(Size_Root, /*bIsWidth=*/true, PreferredWidth, I.MinWidth, LastThemeWidthApplied); + ApplyDimension(Size_Root, /*bIsWidth=*/false, PreferredHeight, I.MinHeight, LastThemeHeightApplied); + } + + // Size: 0 na instância → usa V.Typography.Size (do DT da variante). + const float ChosenSize = (FontSize > 0.f) ? FontSize : V.Typography.Size; + // Font: V.Typography.FontOverride se válida; senão FUIStyle.Text.BodyFont. + const FSlateFontInfo& ThemeFont = V.Typography.FontOverride.HasValidFont() + ? V.Typography.FontOverride : AS.Text.BodyFont; + ApplyStyle(V, I.Padding, ThemeFont, ChosenSize); + + BP_ApplyInputStyle(I); +} + +FText UUIInput_Base::GetText() const +{ + return IsValid(Input) ? Input->GetText() : FText::GetEmpty(); +} + +void UUIInput_Base::SetText(FText InText) +{ + if (IsValid(Input)) + { + Input->SetText(InText); + } +} + +void UUIInput_Base::SetHintTextRuntime(FText InHint) +{ + HintText = InHint; + if (IsValid(Input)) { Input->SetHintText(InHint); } +} + +void UUIInput_Base::SetIsPasswordRuntime(bool bInIsPassword) +{ + bIsPassword = bInIsPassword; + if (IsValid(Input)) { Input->SetIsPassword(bInIsPassword); } +} + +void UUIInput_Base::NativePreConstruct() +{ + Super::NativePreConstruct(); + RefreshUIStyle(); +} + +void UUIInput_Base::SynchronizeProperties() +{ + Super::SynchronizeProperties(); + RefreshUIStyle(); +} + +void UUIInput_Base::NativeConstruct() +{ + Super::NativeConstruct(); + + if (!bInputBound && IsValid(Input)) + { + Input->OnTextChanged.AddDynamic(this, &UUIInput_Base::HandleTextChanged); + Input->OnTextCommitted.AddDynamic(this, &UUIInput_Base::HandleTextCommitted); + bInputBound = true; + } + + if (!bThemeBound) + { + if (const UGameInstance* GI = GetGameInstance()) + { + if (UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) + { + Theme->OnThemeChanged.AddDynamic(this, &UUIInput_Base::HandleThemeChanged); + bThemeBound = true; + } + } + } + RefreshUIStyle(); +} + +void UUIInput_Base::NativeDestruct() +{ + if (bInputBound && IsValid(Input)) + { + Input->OnTextChanged.RemoveDynamic(this, &UUIInput_Base::HandleTextChanged); + Input->OnTextCommitted.RemoveDynamic(this, &UUIInput_Base::HandleTextCommitted); + bInputBound = false; + } + if (bThemeBound) + { + if (const UGameInstance* GI = GetGameInstance()) + { + if (UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) + { + Theme->OnThemeChanged.RemoveDynamic(this, &UUIInput_Base::HandleThemeChanged); + } + } + bThemeBound = false; + } + Super::NativeDestruct(); +} + +void UUIInput_Base::HandleThemeChanged(FName /*NewThemeId*/) +{ + RefreshUIStyle(); +} + +void UUIInput_Base::HandleTextChanged(const FText& Text) +{ + OnTextChanged.Broadcast(Text); +} + +void UUIInput_Base::HandleTextCommitted(const FText& Text, ETextCommit::Type CommitMethod) +{ + OnTextCommitted.Broadcast(Text, CommitMethod); +} diff --git a/Source/ZMMO/Game/UI/Widgets/UIInput_Base.h b/Source/ZMMO/Game/UI/Widgets/UIInput_Base.h new file mode 100644 index 0000000..cdd51a4 --- /dev/null +++ b/Source/ZMMO/Game/UI/Widgets/UIInput_Base.h @@ -0,0 +1,156 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CommonUserWidget.h" +#include "UI/UIStyleTokens.h" +#include "UI/UIStyleTypes.h" +#include "UIInput_Base.generated.h" + +class UEditableTextBox; +class USizeBox; +class UImage; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FUIInputTextChanged, const FText&, Text); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FUIInputTextCommitted, const FText&, Text, ETextCommit::Type, CommitMethod); + +/** + * Campo de texto SÓ-INPUT (sem label). Padrão visual inspirado no UI_Search do + * Hyper: SizeBox raiz → Overlay → Background (UImage) + Stroke (UImage) + + * Input (UEditableTextBox). Os UImages recebem a aparência (cor ou textura + * 9-slice via DT_UI_Styles); o EditableTextBox fica transparente e só + * recebe fonte/cor de texto — assim a cor magenta default (brush vazio) é + * eliminada. + * + * Variantes visuais são data-driven via FUIStyle.Input (Backgrounds/Strokes/ + * Typography) — chave FName (Box/Outline/Underline/Search/custom). Para + * compor com rótulo + layout (Stacked/Inline), use UUILabel_Base. + */ +UCLASS(Abstract, Blueprintable) +class ZMMO_API UUIInput_Base : public UCommonUserWidget +{ + GENERATED_BODY() + +public: + /** Re-resolve os tokens do tema ativo e reaplica. */ + UFUNCTION(BlueprintCallable, Category = "UI Style") + void RefreshUIStyle(); + + UFUNCTION(BlueprintPure, Category = "Input") + FText GetText() const; + + UFUNCTION(BlueprintCallable, Category = "Input") + void SetText(FText InText); + + UFUNCTION(BlueprintCallable, Category = "Input") + void SetHintTextRuntime(FText InHint); + + UFUNCTION(BlueprintCallable, Category = "Input") + void SetIsPasswordRuntime(bool bInIsPassword); + + UPROPERTY(BlueprintAssignable, Category = "Input") + FUIInputTextChanged OnTextChanged; + + UPROPERTY(BlueprintAssignable, Category = "Input") + FUIInputTextCommitted OnTextCommitted; + + /** + * Tamanho da fonte do campo. 0 = usa o padrão do DT_UI_Styles + * (FUIStyleInputTypography.Size). > 0 = sobrescreve aqui no Designer. + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input", meta = (ClampMin = "0")) + float FontSize = 0.f; + + /** Placeholder exibido quando vazio. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input") + FText HintText; + + /** + * Variante visual do input (dropdown vem de FUIStyle.Input.Variants no + * DT_UI_Styles + as 4 clássicas como fallback). Data-driven, sem enum. + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input", + meta = (GetOptions = "GetVariantOptions")) + FName Variant = TEXT("Box"); + + /** Opções do dropdown de Variant (data-driven do DT_UI_Styles). */ + UFUNCTION() + TArray GetVariantOptions() const; + + /** Helper compartilhado p/ UUILabel_Base (mesma lista de variantes). */ + static TArray CollectVariantOptions(); + + /** + * Largura preferida do campo (px). 0 = usa o padrão do DT + * (FUIStyle.Input.MinWidth). > 0 = sobrescreve aqui no Designer. + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input|Size", meta = (ClampMin = "0")) + float PreferredWidth = 0.f; + + /** + * Altura preferida do campo (px). 0 = usa o padrão do DT + * (FUIStyle.Input.MinHeight). > 0 = sobrescreve aqui no Designer. + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input|Size", meta = (ClampMin = "0")) + float PreferredHeight = 0.f; + + /** Campo de senha (oculta os caracteres). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input") + bool bIsPassword = false; + +protected: + virtual void NativePreConstruct() override; + virtual void NativeConstruct() override; + virtual void NativeDestruct() override; + virtual void SynchronizeProperties() override; + + /** Hook opcional: o WBP pode refinar além do que o C++ aplica. */ + UFUNCTION(BlueprintImplementableEvent, Category = "UI Style", + meta = (DisplayName = "Apply Input Style")) + void BP_ApplyInputStyle(const FUIStyleInput& InputStyle); + + // SizeBox raiz (controla PreferredWidth/MinHeight). Opcional: se o WBP + // não tiver, fallback fica ileso (sem set de tamanho). + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Size_Root; + + // Background pintável (cor + textura 9-slice opcional). + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Background; + + // Stroke / borda (cor + textura 9-slice opcional via FUIStyleInputStroke). + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Stroke; + + // Campo de texto cru. Fica transparente — Background/Stroke pintam atrás. + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Input; + +private: + FUIStyleInputVariant ResolveVariant(const FUIStyleInput& I) const; + void ApplyStyle(const FUIStyleInputVariant& V, float InPadding, + const FSlateFontInfo& ThemeFont, float FontSizePx); + + UFUNCTION() + void HandleThemeChanged(FName NewThemeId); + + UFUNCTION() + void HandleTextChanged(const FText& Text); + + UFUNCTION() + void HandleTextCommitted(const FText& Text, ETextCommit::Type CommitMethod); + + bool bThemeBound = false; + bool bInputBound = false; + + /** + * Último valor que o C++ aplicou no Size_Root->WidthOverride a partir do + * tema. Se o valor atual do SizeBox for diferente, presume-se que o user + * editou manualmente (no Designer) — C++ deixa intacto. -1 = não aplicado + * (próxima chamada aplica do tema). + */ + UPROPERTY(Transient) + float LastThemeWidthApplied = -1.f; + + UPROPERTY(Transient) + float LastThemeHeightApplied = -1.f; +}; diff --git a/Source/ZMMO/Game/UI/Widgets/UILabel_Base.cpp b/Source/ZMMO/Game/UI/Widgets/UILabel_Base.cpp new file mode 100644 index 0000000..077f30f --- /dev/null +++ b/Source/ZMMO/Game/UI/Widgets/UILabel_Base.cpp @@ -0,0 +1,181 @@ +#include "UILabel_Base.h" + +#include "UIInput_Base.h" +#include "UICommonText_Base.h" +#include "Components/VerticalBox.h" +#include "Components/HorizontalBox.h" +#include "CommonTextBlock.h" + +UUIInput_Base* UUILabel_Base::ActiveInput() const +{ + if (LabelLayout == EUIInputLabelLayout::Inline) + { + return IsValid(Input_Inline) ? Input_Inline : Input_Stacked; + } + return IsValid(Input_Stacked) ? Input_Stacked : Input_Inline; +} + +void UUILabel_Base::ApplyLayout() +{ + // Só alterna VISIBILIDADE (nunca muta a árvore — isso crasha em + // construção/thumbnail do editor). + const bool bInline = (LabelLayout == EUIInputLabelLayout::Inline); + if (IsValid(Box_Stacked)) + { + Box_Stacked->SetVisibility(bInline + ? ESlateVisibility::Collapsed : ESlateVisibility::SelfHitTestInvisible); + } + if (IsValid(Row_Inline)) + { + Row_Inline->SetVisibility(bInline + ? ESlateVisibility::SelfHitTestInvisible : ESlateVisibility::Collapsed); + } +} + +void UUILabel_Base::PropagateInputProps() +{ + auto Apply = [this](UUIInput_Base* In) + { + if (!IsValid(In)) { return; } + // Variant: controle de aparência do UILabel_Base — sempre propaga. + In->Variant = Variant; + // bIsPassword: sempre propaga (controle explícito). + In->bIsPassword = bIsPassword; + // Overrides "0/vazio = passthrough": só sobrescreve se o UILabel_Base + // definiu valor explícito; assim o user pode setar PreferredWidth/Height, + // FontSize, HintText DIRETO no UI_Input_Master filho e o pai não zera. + if (FontSize > 0.f) { In->FontSize = FontSize; } + if (!HintText.IsEmpty()) { In->HintText = HintText; } + if (PreferredWidth > 0.f) { In->PreferredWidth = PreferredWidth; } + if (PreferredHeight > 0.f) { In->PreferredHeight = PreferredHeight; } + In->RefreshUIStyle(); + }; + Apply(Input_Stacked); + Apply(Input_Inline); +} + +TArray UUILabel_Base::GetVariantOptions() const +{ + return UUIInput_Base::CollectVariantOptions(); +} + +TArray UUILabel_Base::GetLabelTextRoleOptions() const +{ + return UUICommonText_Base::GetThemeTextRoleOptions(); +} + +void UUILabel_Base::RefreshUIStyle() +{ + auto SetupLabel = [this](UCommonTextBlock* L) + { + if (!L) { return; } + if (!LabelText.IsEmpty()) { L->SetText(LabelText); } + if (UUICommonText_Base* CT = Cast(L)) + { + if (CT->TextRole != LabelTextRole) { CT->TextRole = LabelTextRole; } + CT->RefreshUIStyle(); + } + }; + SetupLabel(Label_Stacked); + SetupLabel(Label_Inline); + + PropagateInputProps(); +} + +FText UUILabel_Base::GetText() const +{ + const UUIInput_Base* In = ActiveInput(); + return In ? In->GetText() : FText::GetEmpty(); +} + +void UUILabel_Base::SetText(FText InText) +{ + if (UUIInput_Base* In = ActiveInput()) + { + In->SetText(InText); + } +} + +void UUILabel_Base::SetHintTextRuntime(FText InHint) +{ + HintText = InHint; + if (IsValid(Input_Stacked)) { Input_Stacked->SetHintTextRuntime(InHint); } + if (IsValid(Input_Inline)) { Input_Inline->SetHintTextRuntime(InHint); } +} + +void UUILabel_Base::SetIsPasswordRuntime(bool bInIsPassword) +{ + bIsPassword = bInIsPassword; + if (IsValid(Input_Stacked)) { Input_Stacked->SetIsPasswordRuntime(bInIsPassword); } + if (IsValid(Input_Inline)) { Input_Inline->SetIsPasswordRuntime(bInIsPassword); } +} + +void UUILabel_Base::BindInputDelegates() +{ + if (bInputBound) { return; } + if (IsValid(Input_Stacked)) + { + Input_Stacked->OnTextChanged.AddDynamic(this, &UUILabel_Base::HandleChildTextChanged); + Input_Stacked->OnTextCommitted.AddDynamic(this, &UUILabel_Base::HandleChildTextCommitted); + } + if (IsValid(Input_Inline)) + { + Input_Inline->OnTextChanged.AddDynamic(this, &UUILabel_Base::HandleChildTextChanged); + Input_Inline->OnTextCommitted.AddDynamic(this, &UUILabel_Base::HandleChildTextCommitted); + } + bInputBound = true; +} + +void UUILabel_Base::UnbindInputDelegates() +{ + if (!bInputBound) { return; } + if (IsValid(Input_Stacked)) + { + Input_Stacked->OnTextChanged.RemoveDynamic(this, &UUILabel_Base::HandleChildTextChanged); + Input_Stacked->OnTextCommitted.RemoveDynamic(this, &UUILabel_Base::HandleChildTextCommitted); + } + if (IsValid(Input_Inline)) + { + Input_Inline->OnTextChanged.RemoveDynamic(this, &UUILabel_Base::HandleChildTextChanged); + Input_Inline->OnTextCommitted.RemoveDynamic(this, &UUILabel_Base::HandleChildTextCommitted); + } + bInputBound = false; +} + +void UUILabel_Base::HandleChildTextChanged(const FText& Text) +{ + OnTextChanged.Broadcast(Text); +} + +void UUILabel_Base::HandleChildTextCommitted(const FText& Text, ETextCommit::Type CommitMethod) +{ + OnTextCommitted.Broadcast(Text, CommitMethod); +} + +void UUILabel_Base::NativePreConstruct() +{ + Super::NativePreConstruct(); + ApplyLayout(); + RefreshUIStyle(); +} + +void UUILabel_Base::SynchronizeProperties() +{ + Super::SynchronizeProperties(); + ApplyLayout(); + RefreshUIStyle(); +} + +void UUILabel_Base::NativeConstruct() +{ + Super::NativeConstruct(); + BindInputDelegates(); + ApplyLayout(); + RefreshUIStyle(); +} + +void UUILabel_Base::NativeDestruct() +{ + UnbindInputDelegates(); + Super::NativeDestruct(); +} diff --git a/Source/ZMMO/Game/UI/Widgets/UILabel_Base.h b/Source/ZMMO/Game/UI/Widgets/UILabel_Base.h new file mode 100644 index 0000000..1373036 --- /dev/null +++ b/Source/ZMMO/Game/UI/Widgets/UILabel_Base.h @@ -0,0 +1,131 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CommonUserWidget.h" +#include "UI/UIStyleTokens.h" +#include "UI/UIStyleTypes.h" +#include "UILabel_Base.generated.h" + +class UUIInput_Base; +class UCommonTextBlock; +class UVerticalBox; +class UHorizontalBox; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FUILabelTextChanged, const FText&, Text); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FUILabelTextCommitted, const FText&, Text, ETextCommit::Type, CommitMethod); + +/** + * Composição "label + input" — dois arranjos pré-montados (Box_Stacked, + * Row_Inline) onde cada Input_* é uma instância concreta de UI_Input_Master + * (UUIInput_Base). Alterna VISIBILIDADE entre layouts (Stacked vs Inline); + * nunca muta a árvore (mutar em construção/thumbnail crasha o editor). + * + * Encaminha a API de texto pro input ativo. Variant/HintText/FontSize/ + * PreferredWidth/bIsPassword são propagados pros DOIS filhos (preview + * correto em qualquer LabelLayout). + */ +UCLASS(Abstract, Blueprintable) +class ZMMO_API UUILabel_Base : public UCommonUserWidget +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category = "UI Style") + void RefreshUIStyle(); + + UFUNCTION(BlueprintPure, Category = "Input") + FText GetText() const; + + UFUNCTION(BlueprintCallable, Category = "Input") + void SetText(FText InText); + + UFUNCTION(BlueprintCallable, Category = "Input") + void SetHintTextRuntime(FText InHint); + + UFUNCTION(BlueprintCallable, Category = "Input") + void SetIsPasswordRuntime(bool bInIsPassword); + + UPROPERTY(BlueprintAssignable, Category = "Input") + FUILabelTextChanged OnTextChanged; + + UPROPERTY(BlueprintAssignable, Category = "Input") + FUILabelTextCommitted OnTextCommitted; + + // ---- Rótulo ---- + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Label") + FText LabelText; + + /** Categoria tipográfica do rótulo (dropdown vem do DT_UI_Styles). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Label", + meta = (GetOptions = "GetLabelTextRoleOptions")) + FName LabelTextRole = TEXT("Label"); + + UFUNCTION() + TArray GetLabelTextRoleOptions() const; + + /** Posição do rótulo (label acima x label à esquerda). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Label") + EUIInputLabelLayout LabelLayout = EUIInputLabelLayout::Stacked; + + // ---- Encaminhado pro input ---- + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input", + meta = (GetOptions = "GetVariantOptions")) + FName Variant = TEXT("Box"); + + UFUNCTION() + TArray GetVariantOptions() const; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input") + FText HintText; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input", meta = (ClampMin = "0")) + float FontSize = 0.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input|Size", meta = (ClampMin = "0")) + float PreferredWidth = 0.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input|Size", meta = (ClampMin = "0")) + float PreferredHeight = 0.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input") + bool bIsPassword = false; + +protected: + virtual void NativePreConstruct() override; + virtual void NativeConstruct() override; + virtual void NativeDestruct() override; + virtual void SynchronizeProperties() override; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Box_Stacked; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Row_Inline; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Label_Stacked; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Input_Stacked; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Label_Inline; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Input_Inline; + +private: + UUIInput_Base* ActiveInput() const; + void ApplyLayout(); + void PropagateInputProps(); + void BindInputDelegates(); + void UnbindInputDelegates(); + + UFUNCTION() + void HandleChildTextChanged(const FText& Text); + + UFUNCTION() + void HandleChildTextCommitted(const FText& Text, ETextCommit::Type CommitMethod); + + bool bInputBound = false; +}; diff --git a/Source/ZMMO/Game/UI/ZMMOThemeSubsystem.cpp b/Source/ZMMO/Game/UI/ZMMOThemeSubsystem.cpp index 6803d72..de29915 100644 --- a/Source/ZMMO/Game/UI/ZMMOThemeSubsystem.cpp +++ b/Source/ZMMO/Game/UI/ZMMOThemeSubsystem.cpp @@ -27,6 +27,13 @@ void UZMMOThemeSubsystem::Initialize(FSubsystemCollectionBase& Collection) } ResolveActiveTheme(); + + // Garantia: se ResolveActiveTheme não disparou ResolveActiveUIStyle (porque + // ActiveTheme não mudou — caso comum quando DefaultTheme é nullptr e nada + // resolveu), força a carga do DT_UI_Styles agora para não deixar + // ActiveUIStyle como FUIStyle() default (brushes vazios → nada renderiza + // em runtime). O fallback interno acha a linha "Default" do DT. + ResolveActiveUIStyle(ActiveThemeId); } void UZMMOThemeSubsystem::Deinitialize()