From 037aed30ce257806772b9d6e54780d53f867ad21 Mon Sep 17 00:00:00 2001 From: Mateus Rodrigues Date: Tue, 26 May 2026 00:12:58 -0300 Subject: [PATCH] refactor(ui/base): panel data-driven 3-brush + button via FButtonStyle UIPanel_Base: substitui PanelType/Padding por bUseTheme + 3 chaves (Background/Outline/OutlineEffectKey) lidas de FUIStylePanel. Modo manual (bUseTheme=false) expoe 3 FSlateBrush diretos via FUIPanelBrushSet pra override por instancia. Adiciona BindWidgetOptional OutlineEffect (3o Border sobre Outline pra glow/embossing). Defaults NoDrawType (regra Hyper: brush vazio nao pinta branco). UIButton_Base: remove auto-config (Variants enum, Number/Icon/Size/ InputAction/Timer). Vira shell minimal com BindWidgetOptional + helpers GetActiveUIStyle/GetActiveButtonStyle pra leitura de DT_UI_Styles. UIStyleTokens: FUIStylePanel ganha PanelOutlineEffect map. FUIPanelBrushSet vira 3 FSlateBrush diretos. FUIStyleButton refatora pra 7 sub-maps por variante FName (ButtonStyle=FButtonStyle do Slate, ButtonStroke, ButtonFont, Text/Icon colors). Tudo com defaults NoDrawType. UIStyleTypes: adiciona enum EUIPanelTexture::Rectangle_Square_Gradient_01. Content: UI_Panel_Master e UI_Button_Master_New consomem a API nova; DT_UI_Styles atualizado com rows/keys novas. UIServerCard_Base ajustado pro consumer da API nova. Co-Authored-By: Claude Opus 4.7 (1M context) --- Content/ZMMO/Data/UI/DT_UI_Styles.uasset | Bin 52519 -> 47465 bytes .../UI/Shared/UI_Button_Master_New.uasset | Bin 0 -> 45690 bytes Content/ZMMO/UI/Shared/UI_Panel_Master.uasset | Bin 27175 -> 30990 bytes Source/ZMMO/Data/UI/UIStyleTokens.h | 289 +++--------- Source/ZMMO/Data/UI/UIStyleTypes.h | 1 + .../Game/UI/FrontEnd/UIServerCard_Base.cpp | 29 +- Source/ZMMO/Game/UI/Widgets/UIButton_Base.cpp | 442 +----------------- Source/ZMMO/Game/UI/Widgets/UIButton_Base.h | 219 ++------- Source/ZMMO/Game/UI/Widgets/UIPanel_Base.cpp | 92 ++-- Source/ZMMO/Game/UI/Widgets/UIPanel_Base.h | 49 +- 10 files changed, 175 insertions(+), 946 deletions(-) create mode 100644 Content/ZMMO/UI/Shared/UI_Button_Master_New.uasset diff --git a/Content/ZMMO/Data/UI/DT_UI_Styles.uasset b/Content/ZMMO/Data/UI/DT_UI_Styles.uasset index 2a65db144f63973efedc6d862e764ef6216aabf4..a85ab85ff1deee147588fea910bc8a15ce3fefb4 100644 GIT binary patch literal 47465 zcmeG_33yY*)*~Pwhyo7~7aj#3$l8{+l(s<99f~cKE{L*}G`VdfX%ezn3Zi9|=fb84 zC!Jd6;i-se8l z;r8@rzqGG3@l=_||<_CF?dePk!SWpbIo%P}h<=ba?5T8`W$~ zA)Pwqr*-R;BUvPaQ|i(!ygREW&LCBL!Xn3M#<_T|9VfckL_N`K}Hle zzOf;|oKDG=QEIn2Tp&(zWW|8IdK})5ikes+)?E*+lMTiZ#SWX>YH||O{Gw^MQdnV< z-EJxG?{-Vx>af{!OAIDknUmZtYVR$;o$VU)6LR-4mqFiIw}dE52(fFdRr zlk^)?cEOmFizCV1u9}CbJ1r_p>2jUwcKhbH5&~UOL?ZvV5043?$25dBQMM} z6iSwSyIpb^sr-n3qrT;}K+NKn$mw+zt^NT>S(KR$v%!+#%ymgtquD}M^?J(1+Gm+* zm1LrJ3c0uC52gNh(h*t*q}U=h})~H?TEZA)DtRSl;|Qzdbdtyo zUsZU?-Dx(tO32&8mTZ%Ibh&J$6z z=?9j{0~wWT9xaidmX;r3=nl8DgoOXl%*0rfE7=VW%BFO)#gb&Pml()_SBBPs5f^&7 zTj$KtM$H(=DJ4VuJcq$bCF-C|EhjCarS)u#ZaPLQ>3M7#VKZh$#>^Fif9}C%%s?4N zn^hiT=GuB=8Iu`2*X6K{kVwk`UD_}IoGNAf#$xgbE)kcU9|b6t!-AZVU)WhkE{>(>#`Xbno=SeM@ z190b8;GSEti@Cbvlg5g3UW@BiVSXx zi;TN$(*ZA1CA&j1QpvJW4Wu)*IigaDbZ){OFs)Rx(@9 z9~_O^u_W6+iCoZUH4Kzi*)4{0<|anZI-JbL*aImrzz8YjTG)2!}N4?_ohIzJKI zU>X|`Q=?Rho>y*{$beSKXTjyG)FmVU^XphUUId?`ijYxiD3-_vb;m3Z^M}%px+m=G z&5vETfqxN5F#>EL@ew`WR1oo0CM2Z0Ef$$S-|g%lO_wQEA_)ooZ1RvsF5L1I*p1Lt zmf7L3IaptDc{k`3eR*qvh+HDaF7Aj1^%bb(fRdT5#f;8J?!4rV{_7JG`WP%leI#>n zi3?Sz$?mt#D@2iJwn&Vc>6*QpZmUX-*TISB`rTA+hCO};1=9;<;>o* za#>qnctS!BH3Dih7O5b2q?6 zT?}W{-S`BoY8;S|K-c7)?A|0TWgl~S;6#(l85?s~^g9R+06oL*YXZIWw%E$Zg1Ux&tYEX4 z$m}<38o{u8(?TCJabD{+58s~?Y6ORxeQC`a=!zK@ThQ8cY59@|!g${3`e7S@pBYxW+ePks zXv#$JQyEq-$CvDk?F(&K^uTx;KK|k;7;vVc+~#&UiDOoF8O%OYqAPX8>lxUdwkqDU{;pFQEax7vzPLlK;Kzr zE3z+d)2Gqkiz!52?O$B+b+L>BP6GNmRBujm&~a}FC1W;hcc2XT3f|Y#;rKIk`@-1! z*{l*dTXSmz=)IrKQEIS|`{$=^2i=v60?WsAdA1+SQLe}~n82@)C5>-db|XODrG=8C ze^ItsGSc||Uxz>50AN|sJRK+PU3(1r>TiWmkaS+$rx#44zcttGD57y64Rz+e3d8k4 zQq85LGWTR6BgWCP+^`zIyr>0R&@iwhJ7Q-qWo>0mmihfe+s<==FNox^hU7cH`$T|y z)c(YCPiBF95T7Yf!(MqGh%kuFzTDh#G;o9C(U634Bl5!q-?yO9Y#IgAr6$v6G+4}| z4KB0I3IbrUEotm3p916%iLHyK)6wfeyEt28?s;IOT(*Epk=E_o=|N+e|0472?AZlu zWThywZspAePcfR*Tzg+_m>R3~#^WUH&Q@074}){2xf5G|25wCRrWF;zmSaxip&u}^ zG&HuE$c?+}jb%du*GJj^^!)wbGIDZRW_NbNO_gAm*)mX2pSM>v%myK1jc1~|nJa|` zf+4q9JfI0I-IOg)&9B=KM(lxP8(bx< zm$(j{S}8r?$SmuKDq&vBɢcVyy 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 diff --git a/Content/ZMMO/UI/Shared/UI_Button_Master_New.uasset b/Content/ZMMO/UI/Shared/UI_Button_Master_New.uasset new file mode 100644 index 0000000000000000000000000000000000000000..a2e21bace1ab4d8237e881172c97b546ec8ab558 GIT binary patch literal 45690 zcmeHQ31CcD_kXbqO6=4!Vok^@L9AJ1lZcRrT}(1B$uP;p%p}AX6s@f-Ek$czi&9io zQB-N`Yb`~!)mGJ3OVz%WCrdes<1oggq@TbOCuY9pPQxM8gUB|tH+x{7GuFUmG z!q#!a-J6vn*p%A!hfM0BS^x8~nrpxP{Bor;1ecj|*3Oc3a!x!)>4B5X|*N z{5zlYXtpM-Y@;`iO#DFIieTq_zk9z@kEso}jXS)4S+~b+oC(&&^yu*TuJ4DOu0HDx zqpE(brUVNf{PuTY-FG!vv%c!2oLZXGoe1`O+WY4&1;2T_!Vf;nf_8*?wjh{0U0&hp zbkVDS_wGHsdPjQ2rKt^~hDwRSStgS{BeA>MXc7&HG2#fqEhQYKzb}&Xff@Ls1wyj(Zk=fEvpxVKL(yCVY{9hu@oBF;xGyiiEh|t3C91D_48rc4Fu?> zsCzOs`t1L#25C<4ic2PK zbZV0*xQE_d4`Fym42s?aKlP~56%{qTyN3&H2HjLCu?ZHc(VFxIVg2C9^YEk~oi;Ti zUCb~ERoYy7sRD!O`e-$&qRA-iYg*$i2FgfI(;K>~O={smn)gU}c@P5`h3gt!61WU9 z7}Pnj27RVzFy#o5@1NB|q@*C7PCue6nZ%^kX9)Q>Ubs>R;1HeKXzV2p&(a!1V~pCQ z%@*Ue$-{($^|ZrlFa&)%md15oETBm#Oq_T45?z=Fj3b_Ofd?ty*ID}6cnt> z5;F}%Dm+W85e}EF+q;$$8ka*uEMrt&QdG>Xs36orZ|vrm@>2YS6u@T{P@ zTnRnUjalziO1(d=ECbRwP8agGjatF<-6LBx7_=HOF+$X)rkVJyaN_nW-_%Br9Fj<; zH>L4ALEC4|Hu#UZJ}fIEnVGW6-PZ@e7{Rez8KiCI+u%R9Mzf0O*HYVEG^yc0p&2Ga zj_}Rg%<*MVZ^$yH37&UqY8rq_oGC{q#^|H<8L6TH$~1=PGl+Gj6YYX~OW=%BjEWaW znuO4aUtVWA$T5j#9bAa<*yBQYA?l25wJ}zmA?gw%lZmOo#JPnkST{osjhu_v7D59o zRZr~Q8~5h^DhfcH-eA&Zqzctt8XSW2%MK_Utg~}522n_ln68O2dK$DJ-1Bu~5PgQx zWXPh?Jwm_e9jOgD8#&t2?rnZ$?CIG(31}h*BqwcLb-;NbLQ7F%j!@h1Ln;l8zEp&cdw(u?4L;YE z{7_6L2#XyF6oT{y->!mq_oXO|S!vY7X-A3O(f2OxzDKNYtua%l&WTnhi8^7){p|G^ z3tc5(49zCOs5uvc(S4}YL99W{)`}y{{bUL)8>#G8w{MSDGz$qKt!_008=)E|AB}zH z`=>{|9$JlowKQhC>XWlDdLuJZ^umtBDt|&Ph-{)p3aYVcQ<`unXz_S>3@RcsbO@^L zzVEOP0VXs;Z_ti{?sb+3n15pWFh(OUsf#B@{L(fH)=a)UA59YDjFYk@YBST92x7_9fkVekM(HHG|KQ`kX~67yIlH246^ji!PHQz z(+RzT*1in`hh~$ORm>8?g$E5cvVH)g*d`jpjAT(*z3j|7rZoy)CT+4(p`G?!o{Ysp zs5GI_u*uZwDAK31XipxZ(Mr)|#m!#zTRMQ)zI`9!j)mi_13NxKFce3M$-S#47lXU(shY zW-ME-xO(r6HgH{LZ7F=cwm>`v!VCm+35`7(ZZ$)gOSn`Vyc1(SGCeiUq#q^<8y;M( z4R$aY>P%{=Ug$c}CtiYCl(X_!n{H4}q){5Z4Ds}tTHVkaMWYdVgT^R?c*J}Fj-zRP zD2+Bg5RF2`1|^Pw<7jn`KFh?`3PZd%SA=4s#T1j^HRY8npc+lXP;HPrvwDaz46yF% zOtYE%-<&W3gQ~mQK+7QEc3$u5h>G3SBem&S>B51S)5##yT})4+IRb%aM#C*1f_DiN zOKVvQv*Rv}?T8RA0WID-?!)a};2V5CV(S*6S4!2Bus20K8XB~OWJ?kjL^55cs~N$Z za8aKwV)pvF>P7(sLEX&v#GB>1K^6>tK0gd;l6VUW1dMsd(e zCB=wk+}siy-eG82rc5#opARag&+g99XojW8E=21LKh&pYvFhYu6l0?G$!eW;6lp@A z0ZEOSY8vfTO*yZiQLI{{!IbDXGTDp}6Q|+Ht4uHC7yi-7xr^O1# zn#95mTz_FYb*fP~rEjoV35C*(!p9>@e*v`^M72&x`0`y(gsom;rjDWwxU+<5{qC1% zpgmQ_=uO&`9IRJ_vTq-s4f%VCMtzncS(Mk^qX*X63f=d@l1Qj<*G&gI_tNW4Qn(T- zihBa!LviYCQIkNEnWRu*%okU?n&JKR`t+c5eHQs#V$GRf!n$#!I5Aj1QmDPL-Xtg| zE;&t1R}1+&rjLLeZ18SQZG8ob#0_w_WWQOGHRkd8QD=|Qm^9*|LZcIH?adNHgaF@L zH^8)I1`rIY9TOld1BIk%bs9#l>DJwzW=v`HUGMtEM8=LzZxVVon)?Qn5l1&hp~sK? ze_@^IQ%s5pHFxQ$r3{=YCTml)6fM~Vv!VU0H(>NQlYyI4=&;w&1ft94ezVcszN{r% z6q&KSg?An?s#y35u7TZJf+6PWdjkTs@EY8h@jq*C+6D|$nn;V}P2Y@X=-4^DS}!XG z1TZ%!CKj(TC(_|91;M>&he(t9&z%mALHvxTRkpU`% z(TmJ5wed5HJveiq4hl|COe*jA+-v|=X=Y-fWs=WlMe&fN!8J`!d*9xO8ev zS1cxZHD^@l|K0@_^mzpiPXtZ#Wxug~j(Kxea2?_HcBTh5V73LL0UY#G`@^toh-I@P zlq=P#uC)6wZ(uMgh4({h)WKkpqN-`c$XN`FX%fl^*6C>=Y&@_jOhs7oULet4==gN= zA+`fm;K`>y!E}5g-0^JpIXE|0OO`ilONg#NJpleCuhWH6`VR(4t8lh5l0sf$l&|p7 z-g%YaO>8MCtt1DnbQ%O-G|#B@`vi}M>sXdT2#LoZ-t~Zo7JxEURC>R;09CNHkWjgE z=vxRCQNEZ)6B!}FIJy6e2tabM7o&c6f}*jtrOA%+dgo^^jW0$&GZ~lmW3R%y_S!d{|kqZJJUui z?{VN!D5H0z(jdJm-|GW4_BM(J$reJTD`T_a0@kMKG5E{**iT!*&5K#dfwI3^0t2(O zOwPfG!cH=DhMX(2<;okm%zHt%mR>xSW=Zts?Hob&c@kt_j?CGGfeNfSWCUIh540>u ztSWJ8i%A3ifg?wLA#@kCKsY<1QsG{RBc0=1Q z+WLE{RP2<_sLG-)jVhB~Wys1Q8n>` zt#~Vy*3{gnqLmdyA~FV%5mXyhBSf9f6E_Ut?%pS;S4?C~xU7WuH0lulaoj@3f~rg! zWkeeiCF_l}qSq#?vNAQa`GW>%O{=0fN@Q|W`Vn;OsG=HmD-~^OhFOL+vO#T>j40d9EOMlzYLV^Lfo`M)E7LxmvWg}$8CgGxPf;W_X_@hmtR|C_ z1ajZ{43(HoCrxTBs>v<-$uiS4o1xODq%g^pBT9iz`ehWQn4^+*k>ngiA4g}nDvI00zs$8jGA@kWj8I@D^#sVdjI)J-nsj zG*hLb&Ag5|uqqQgX;jiGgBYY)mBc~Ir9B(?mN>Tu@LJeeMNt0&TdFj5{|jVLen zqkAlAk+w*g@Ed&u{()Q`F6YQ-oUyb`&My$&F1cJB-aZHL&QpIMIe>SL@IG?@?=0c% zbO7%R;cayg??VUi_Be>Q*Fn5b9mE^qAl`ll@m_Wi?{f$7ra6d*(TG%(U%_twlF*99 zJLDkV5eMi-a4?ebm$9d+y z1i#v01CQ%~`{pUa<7K5@9uQn^E_hO*iF|{zk417Bk3cz+s4H_{*7yr#fHl3QG92`l z$>l*x24$G7tSEY%cO^Y9>cp0O`Okr#E%`vtmV65hwxlTxb40%T3Y}xTVNQb2L7(aJ z0PJF<8wXNR^t5tcN_wCJdN}O@y>Pma)|;ffxW7fw3y~Y!qK7yDdV6J@ThDeIVT>d6K)zNeP@4T5 z=(UjhQqrqTecPh<$rwhq*gr+d$LZZs$OrrNk$Z)FpULIRW933ISyA-%$Zf6Z^_StG zH(S=hhH)~mn5-yzugZNX>A_!Y$rm`j7(#M&QS|o6IM(!R$yaK^v(w}9JyzHc9}VuK z3!+1x3lK;t&7COa#r-Wxz6O|FDcPcjD$qNO35t?8-6_<3kE=*Y597}ky)>YW#6}Hx%+U zq8>k|i;ur>nL?>~#gbwOMbQh9yHL_|C73OGQO}Xyb%lJe9}ms2-(#7l+0TLA1Gz6H zJ=o6{z1O(o$ZhOpMd_2%`$Hig_R=^Dw6@=j1@9RtfxL<}pln~QSRc1wD#zHB_U-%L(W#acm(K{*kb(N4VDQGU2JHi7-mk_uqrM@TS z_Wn9Tubhl@ML`RDF@C03o*ok7+oIS_g(-0w%9^6&!@3AbNe{N<^x&VI-oI?nLtE?Q z2tCZLrRl0q7t~jlE-X8%(8cFn6_ruvOc(U*LKo^FV+Xtj zMJl5Y&_s^@fnSrZT6EQ>3w?l>dU6@G8_MMuXPo#hsWmpphtbq&z z9pHl&a?nE`XajoCNprdYLmSXl(ba-3^k0XrYKk)F2XOQU9IRn#D9YfI!yzlm;1@PP z8GORlC^w?3s$6!ZygFUz7d#;W2QeD-kWdHPA%~rm3HJDb^uh7LH|H01upQbpq6;(& zB~()TL)0k$yZm2-D&4{&!vuW(?}Uc=r-TrpLb-Cz<;qrYF7I5aVueaB^{Tp5uI%Dk zr*8FnZcSRcw`kJ5xrc9HTMzF}-p!l0iw@`%5*iU1(XvgC-Z5eEf#DIM=)|dFrAjW9 zT^d!b+9=esxo7ATr~EyF3yoeUp`??mP{PHjq>B@MVoA1hD(%D)7tN_e$x@}ulr2}@ zxdI_pHx;?}S-P%QX((w6{jD zqt~0XPBKg^?_9H1?K*XvHf!F(&E3zxP1|+>?L)eThJ{B&c8iPeozSOmzy8S@F(oxk zJJe{(${sN?XVgoRCQo_!m8sKazwzdrxo^#zzhK$&6)RV*Ui0qS&09X$`r)>ZwtxKT zzR&g_`23464}Np(_=#^%e)s(kXMR0hUaPAFGBc-GQQd&@U&-RpXdB*RhX znoa!9G`#`Qm}HI#Hu0$>GYe*s>j$A?NhdO1Nf)7ua3*cpG@-?R^Yvc$Vf#LwJHdU` z(u|&o*CJLM+8)Uly1l#l(#ifSf9U_+o@3FEYvj(q-Qc!U|7pLM{q*;d`*!Y{r^{XE zyYTeG7rOL6ztzL{R>aO>1Mb(Y^VY)mejN4Ek!ixaq3LPks+|}USmC=prQ9=9UjIG% z;pn^??~cBHZ19%x2dC-`M1py(+`rWa0XX zS96_(3%O?o-+w!A!r})Fyz<<)WaU;KI;!#;k5{Lk%Q>}k_s=a;Yajlp`SCt0@8=8q z+rRbl2HhJyMxICs^Vn6=)cDb`Ls`444jtTdnD~iXonLyP{A3NYmtImD8`6Z8%diY*Fpwk2+qtdtaD&sAhbJS+A`c-7q<@PW3+h)Yost zAHV(4oeCY+)k&JwcJ0)LBmUeyqR!#^!x{%&zw^f{S!K6u`R>(egMR20yr!OU{OBWP zddJ-D)AN94w05p{>yHiuKDx3p_vHn%haS}~*1g}>x9hR=s-0^Dm27_gT91YygO|T| zsoLeiHQtVze74`uO#%-^44l}h%;-_EWheCPvo*ch+3*vqj;(fUa-&A6{j=5mG*SedZoh@3RdT4Sn=p z!$~`<{F+m1jQ9KX9$%{c#)SKe4{rXX=H;FP)2jBpR$`d8{HY(;87f~~u&vp~>bHGo zw%ZXC6*uRNU)!wj7?@hE@}^eb-7GO9C+E_g&vX0l{`GK;OP@53s_nPiZ_tA?ZKE2L z`S$F|lQoY2mM>KPU~Ib%KLu{vvZdO`>N!0Z34<3e9Jup(_RFS&$(yJA@T+V620sPY zP+gCx=koceZrwX9TUqYQe(RQ1u6{ZE(?!(|En2(Tt#6|y8n;e2e*C4{oejx*#`zw< zQ-7rP;f$Zh7?)RXBJ>`A`26@`>u;=@+Gf|8FVo)(Y+LS1tD!M%T~2ig9`^e6vXlHf z)E_yt?#wL%!b^Auo)hQIFx}qkx8dOVh?jrWq_4UX+e5!K;j(YvO5I;Srsdb8UX1A5_WQ=}=d!Z{8s9AO(Ws*dYv#N;fBL>}GOo3F*ne7!^)o-6 z)?!HsJ6Gja_eXmi*!64F^q+zrUD!6GU8?}kOs7Xemz8~Q{4pcTqwT=F!^c+LtXe(6 zAT*tK>p^mjkTd80SbBfM+3v?xj~Wy0s%_cWY2a@S9L zckEnrW=h3xTF<%@u)6LWuYG#e?VHtIrVf1j&zQ3(a^1UpaII6Hn`=$IM~_wEdZ1Ii3A^P5g(aZ``T;=aom7m-k;j``-N4`+D}&<Db)(Gk z4TH9wx>jLDba2>m=iM)F+~qatTC;68V&)B*bJlBk-8RQ&9_(A9@}~9wT73WE+mCNO zy8Oq>n!aPgSAKEPd0Wq_W6PZB7ZurhWQYB2MqPY$XGqSOpWMq|Ej3uP`GMw(6~7Ew z6mWM#>X`%UMz0&WJockcKFV1+IwQ(dTRpyPU_h6JL&qdcJ-zSS%`KYtI+xLTe!_aM zNk;>|)t?ux4#-^b=iE}cjeGUeAL#6MqGQ0X-`D(9eeO%=4k51Bdgau)`&;{jrhk04 zYt*%pRsDB;cxK}Du#j<@cg9s;T&-!pj*0H?e)($kguCqy-*nwxS4c?7ZgsWaymPY+ zkt=Ajgs0eb5;#xgg?9!GzS#Fflj9lL9-mE)^r^IDh~V?U`L|Z%nhyImv)sa8KK{YG zuBqv6@37pu=hBu`+GX7T@wQj0hmUSorR1!NQCnO8@nfF<)X?fePr59+cb|JIuk+G; zVQlWg_|8p2drWv(`tYN%-SZygWmRbxP{V`Y)TnlUUflg!OE3M<_OtG7?nTw?-1L>X zKh!wq^G9UhweZ+n=Pk`&edNF7yN>-!e>CgV>$5&f?)CHfh_Vk(zS*-^zfO0q{xI|E z$q`!7Q(V=ElOS*+G5#w=OV7oT0#RVyWUOTefDz0dnX?p@9nYe(b$l@ zhxtPLHDe;CEiQSx0`~;>xfk;~ugVw3P~EhtkD}ckRX9Sck=dWh`mMZjO)+be2dk8( zSovYT@Y*T9mf*hO7IS%11nN&tBHwJA7-++5u+r&2M=LTHCOy>*_s^ODHtD z(!#*9+-lCbC2o_HE8dp{`)e$^yUoH5ha0aM*t%-Ky;Zp>14nsX?0;}(OzO2Q<&x6Y ztT;E#)cf$&f4vs^=(oJDKDwQFX!UpTc?o^KP0JU2-H!I@vcu;b(`1JaS!^AO?mBB09mauK2X$y#`-tHgbWdhtYL%^`3p}2gwsTpV0q53wu86z8nLjblXNs-)xhfYKO8XFYECbuj*J$B)$;RRppLTwTd3?1g})DL z{%R>tE4r*XjtPRY)mk0C>;$h3Yw*sbk)YDn-u$Xh2)!!6zJmufAZ)IoSBlKvFI2cv z@kKPK#8HB|rRCcTP=yl)a~1oF2fOkWKxJ=HGe~TFzt~Xvz8^z6eIEf1D%cB^(l;33 zjKjw-z^`3ve_wWJ5~dAiUxu-RPR(hn%l?(2H;GYdBi~C1C z{)PzYLbi}Xk184&G)ssPk_et8q!H9eEh@^bdeU7c;Q^XOxG93E*xC|u+h^sWjNDIx zkSudm7*g^%)9OJw$RKJ?bkrni&;sE)x5)K^j1x_oF|l@8LcE|6wA4ce_2fi{Y*H@~ zq`xH6qgDuIxWmZ;Nx~?B_dY}jqYsZ$$Oc)01=nH++qI?T-YpzGowX$&aw6VfUk&j( zlz4@`Gl?T7*~jeHb6r{Zs33Dz*wdUS1aZw~DrD_Vv@%I6pgNo|%E?%6q$6;VLWiqP zbeJl&wNQFGiFrThTVzsO`c}d?7ZO!Ev{8&KUKcI2W;~)FTNWE)wz3l){z^Sr?Ab^d zLytOUlY|+=-5&rB+WClekZHWn;%g zp33#)M6pxq(N>+aT<0R^7M&_fT)#?dDo3S-#4KC2{r^jf%%C%U6Zt`wTbQVNXlqHwqkIA6+f z#p9R7rgABGt!5YJ|LbQhS8LDUvyeoY$<`hOfVg5jmnEe{j zg%!hXxeOSu|5b(|(s6hamC<%6UG&inTQi|PlP>h7?DL!gjy_R`wJPsdLuD;p=rcpk zd0(&?=7?ar&;}RqIB%Q|=mG}X!|4KFkjEO1w!9DEf;R4f&+7nBr7N2*^Z^{yqc7Y8 zFP$#Hz$fmxZon`8@#w&<__T8;9}|eZSYctUX^F!;+AC`tDeC!rU9?Z|cx`{Rz*ia1 zDh=m@uZ3bs_d{v5f*p=!1z?Gj#o^VJwaxpr9ly91yxAI`uaUil1lm1CPz*>Q&#_)R zInl@Mk|m#2<}z|WJ(-m7(ZY~=G7jGr(PyDDgi97n-k3$6jlYf9dLOYqjd=ye0N<}X zE4SPiu%k$&-m}Tmv*_{dB+9Y#bfV3(q!;@b4P*=n4xv*RE~9ah9>LKPaf}M$btOu7_^qfx*N(_L|_|9^Cag(dGIB-8g1x z_&;{B8fN3->Kn^^6scp)Z8*_`EZkqE{C{=t$X5)u;$H`f$Jo6W+%;NuYlZ7t zIHYav!M?O(!P*NuoAzvH<3sDQ0E%pW1PNyFwp7Mm2fG^|2KONhKiUuZ5-m4}=oQ7W z`~MR=bN{z(XN)e5pd(%I@oIszbV$eCrha4{AF^d@+WYvE&9HlIO;-TH0?4AF1o5Xb z@?f%J7~AKD&_33OAR$Dnl|%Fj^XVbCD;!sNB#WWA+K6WR6!L1s5}bt~(t0{Xk)Ie9 z91VP}$D{SruJJgJ$`c>uYN)t+Z-->yYYgjEP&~yvtSzN;BFq9_HuU6USHqR%8k|q_ zl}Q%$sEpefulQHkKBeUvAGQ~BB7c!ExzF03nYjmmTf}=OiV9M@=Vgm{TJ0oL?2$B6 zcx7R)-rphqEwdGNx1ixfkI}jc-gLDW1_*;_B)HP3knT|9%gU~#A6HiAC*S+a_ifny z0M-k~Z7bKclW_y&dmot&D1e4Ui?{MI(?HAx2f*WWd}us#81LVQ#yW>_8op*4zGfP} zW+{BlGN*&jS|S&NrpP$4;5GyQ-+PJ?U7V)@o) z&0*f<*5lsw*IC|=*$KS;uUo#bUHr_RY2Pk>%o@yPmZVmB}Nb>2n%f|#eRCkGNknzJ`56hFgc{wiDPp#XeND7MWf{feCYV`lW|N73HG9)u|B5s+tn&;k!(NWa|yUt!OU*y5W# z7GmQ!KB)D-!E>IUhu|f}@AFS}$CnY{ofup!&60EI2MHA>R+9eUtsVx%t3IzwumT8p z&4|5ogeQ=Y_~@1^`0RG$ zX5GyY)>%P)*s~ea2Sz8-6YAp^Ss%Zc_3?B9Ahs3I!w@h-Ny(2r0se8iLQV&Nx9csg zR9QgTc5Ma+w%d)nbvHu@faHkH{wAj@QUFX%1@wT?h*W6ynJ2-`U!;NCHu%HB0*K&; zT5D2n{tXxTH&>*eafExLMkW2ci`@{8QTRk5oV~n`-lYX^;mPmcE)t^f^A+h8>Jh1s z)92A^#Fow~iC4D+3 zT&PdGOyG_6XJBF~7^bAu>#GuoX+^oD2m*Y%T-jKG6sjmI8!$D7$(3C89Qj^JQKRU9 zsaC#cYZA45V`JH&3Myj3q6*?9?MBrXsOy$;_l3-hd7k&caQ=Vm3IWcpu3)qRx>7NX z74)l_G7JMtX=UbDeQh-GwbKB8g{vzrPS)-Sfv4IUA?Ap!_*GEe88s0E75nR$3@#9A zk&Ong$uMi7*|htHjRr}I257_+LQ%QA#L`_}sy(u^1RQT(>-Cedl%q04hFMc)qY|n^ zdK$H3xObh%n%jtq_(~40M!Pn__ixy;3AWtqi!IDQxyrj@6hxLn_oeb5L6q z=?uT%Qsx7|){s=8^qHTD_UbD7#Qzrk71{k6Drs0*O|@G3oL5cvdR7n2v#g9y3k?Er zlj$itHGRe`VR_QC{y|h@pnKlCo=PhE99K)Nd0EXc(Aq$7+^N`82N?v>5>^JmbX3x? zens}B>lv~zN}CqBUi|#(w%~7P{~5aRtFI%^t$wQ+w-Wj(DR)+r^5bj!b$Mmy2Ql-T z?&&?Fe3yJ%uxuG0Sw8zi8bt2ogTIuNy-N`K3-94hVg@=;U|n-OCo15wm%#7f87H?U zjGxtgd-ABLRzo)FY{3>NIw&YZv|M9T2GrTDP;_Zsd~z8!EUxg?4Bu``k9?ihyt&qs z$zTN=q@ZG!p*8^{jq*>bsqo9(&cXEiw4yqLo<~L{l3fw(R!HsGU!1L`u+L8{C0_kR za?-OSQkpC0FzjCtnQ(r9#D%jPne-B#Fi_5svA}nP3jc(ye?!=sJ6ovbeyn!j>K~J5 zZ+b~plA92g;qwIEP5!py(Oz4RZ4aH?tIC7k!zUcD1qAbu-jybC4>&D{=qD%Mr_h)`M;=t0OwqVaNoVf>qtfT3P zhBNqWxIU=G+EG7;EE|_Pru?Q;QMO?J$-^1^Iv=dJ$a!w(=#6uh52#vax@ZeFOrb$@ zSp9_7dfn++eic2jE7BS^o&K6=Wt&55Vrz(sp7O=|*g&7>;i*}ym!0Wf4{D8dcqZLn zWGii~gY~p3=Bsej0GqGZv3?)HR?4Z29)SZXDDP1oQmvM0&W-6dp6c;DDfTkJg=VoY!A`|Uf2CcHwD-YZ6{Gu*aRA!T zIsO`0pBEVw(sr&aLwAKN1&WSn8-c*v)0+hZEF}ewaiNtrzPE_v5Akv13 zhgX2uPC8z$Tr20whV1l?;OL}K!@o)SpEF~2qhceXWj89~Nz?+0x7&?tQQcIx?&duLAjL=- z`zr_Zh>+eg&{kzIrbMJdvmNG3q(T)A`wAvgOan-TdU+yqu+P(B#5nqCaB`D^I{c3m zz6EjncL;CSftnHncKs0MaLf;z71a&GdVy}{5ddkvl(D~k%w+_R0mc2$^dJEvC?L42 ze>?90Kfg9@0(`uKLjAk?g!zO7g!y~>dk6W41hZ2iZ~G%2N0xTgUt}nv2fLF*Lt^14 zU;blcEC2judyBe6GD#Wx%i;&^OY&h4q(Wo3wFEEl2~QgthRFi+0Z)9{iF-Diw3TZR z5|ONXXO9ZotSI1d>se!g_9s%pX`xOoBbzIi!B3o`jC-E$Q-Se>XN#uE&_m_FJRh;c zHEc(N#r`weEQ=mNa5zrYGL6D&W*&-ue@DiP?{80?++W5oU$?)0-h~yxtKS{zzQE7p zomMjZ;pZimt#JzvUOFHp1vR`!l1^F+nm-0hJKt;d)m^?&b2Y&A=Sw@^b7VdgfGkVUex*3j!R!A z7^d;v%_a0*omH`6K^LD{Q4zr!3@ z&f2bPY;@30hFP-}iVlo&B_9^bAM#;Uyh6sane~vNN&~Vfx*}C5Ln7t_BxQt0Kw^n) z-N(u|E94v*Q&Byk{CKE!f?QEZhn!KuVu`yO#O@@+P{zd*>)B1d)5^I$^I{*4gbtME zP2@li6ety9jf)LmMN;aCMJ{Y&Y`$H>h4Ns~y{Jh6LOhoxtBFXiuQ zGLbm%vV1l(TkeJNSnwekuH=7w%dEt|!?*a%@Wj4lJ(c|D_!f5NEXXS!iu=dAm{nUK>S=w<9KjSXV*KM>Or?KO1jBI^l5+8=TnAQ1C032$C{S7|Wdd70Qd!wh zx%>~1@vK*!ZvhTqdl^C5XtoSPJuaTGWGsi9$udOQ$XAA;fq897Wi()oP(KBKDA_m; z9KV~pYpjG*U?;_T#0r^df^xPvkr7$O0wa}_JQcb5`F*Y5#gyd(LuZv<|5b8|w=XBp zdRr%VZIW|jj0Z0&u+<~w);v@h2Q*60av(6C+B1D&%g2L@b*$hMoAo@!ms8fOBff;U zUXk;iqxD8msSEx?qTq+CgST@MRLnz4Qe6kSkQkmJOQf%ckdnP^oZW2;TnH-Qn>NL4 zi47)af(zvk6WAx3pNQwd>}fc_9)xqi)$I@m7@{_ogwM(YcLj%da3LP-E*zeQ6Rc~v zHn_7o#0j)vV|sZS9tzESRaOXp5?@`21X3$0V}F&7-4c*VawQi6w``z8uRLynStK~7 z5|Q(T|yVd4h!7;)}&VOYW{ r)#*}_-usgKqPOm@9PioTv!h^@e-J}Oma>0t*R#~Y-EREc0Qmn0kK@hq literal 0 HcmV?d00001 diff --git a/Content/ZMMO/UI/Shared/UI_Panel_Master.uasset b/Content/ZMMO/UI/Shared/UI_Panel_Master.uasset index 2670a649d6e406fedc0d4c7bef3a674e10f385f3..0ea3506ad06d1a4c2d8252efd8f2dd0defb4bf39 100644 GIT binary patch literal 30990 zcmeHQ31C#!xjsX}7N8Ies~{Hy31IewC4i7+vOqRS0*Yd|$=pc>CNp7{grMveHR6UW zK3bm$_@s(eo**hdl~-u%)=DYGO4Xu*f=d;sRs{3@@7!}{ZYDD!tbN*^aOd9p|NlAv za{lw5|J*rq*|lu=e}2>0*f=YUv4}Q|ouV_!UkcXLjcmVU|M-sDokwe&?5!we&KsqS zbs*fhT{n;VF6X0mU*5*HFPuC4ns9dV^XZKDqwaset@Q6Tgkb46|LpCo{Qnuec}w@(>U&wrF=bbRnp-l+YmbEAG zvR;V{>rQtgMwFUUMoiW0n(EeKQesDOJY#>mX@8sGc&fz9$r$S&a1oQp*gNxdnEa%U zZ$z&yCct|JT&pd(UNGz)q+f|Q-dMsI;e@kZrrZhjBjhuO0F;WxEY_WNqF1F?7+{0ZA)aYww8M@(< zP6SM|+o#(sm72%Rel?`W+MfXAa;f!2E@zGA^3=23r$4lTFkP*fQ|WSg9Tt}T+mopv z^n)gA^(^sM;derda%$39mq{Jqu{j;A@vEPF+6UkayXtmN)#iF_F3nw_dTg~?iLGJ| zd##^sZVyi2tghA^o^)?{xm_!=IUJgW&5a$B*RvUNn$2w^8a8F{xN$IQxp`iV>ab{* zTt}wnwpBXFWmo^EtPqY?o};>To99;7*fns_EJdo#;bHm7XY74Lk#N~2zfze8Lzm~e zr>QQR3Z`#=ak!K>oTpW)74-$0W+`zNd0mxS@f^)QRkOO;y-(gWmK(_K)oNVCD%)$b zu-~`uTiS~nOPA2%dKz5S(=?YGhFaq3IHF0VlKL8rb&g#5ep^W3blO>LC(AMPWIEmW zI;dOt(+%x8(BpAdv&J3sH^C3e3u`r(%VyECyp9Uq6WzYP??zs`-lO?yA1R1=1tU6x ze5=yws%PIUT(yS=qYqH*ba`x!N){D))$b6~GW1?zul0HHVZ@&dr^D@WdC86Iod5ZA zoq|kE=6xi8E+c^R;|5^FOv~OgIv7}*CDU2qg*)Xs ztWLJGtlM``R!8XryGZp^vEQXVycpt`9q=AYu?Gt88dC5s`b>=U9j449=rc3S(}ZyO z#DgdL1;eDKl775Jw)Khc|EP2DR{Q#O%7KR_VpN&ADbrnEcU5k+TB&iT8WT6}f0Yo( z)YL4m-JapJQ{?!3Uttn_!Caclr-Kr0p2th^r|6wuK8DB_08dSwN?oQpD(zZX@mw#> zBxUglEW7lswbJ$AZpJG!yjovMdL)j#wG zMqU=~d=~ufvG>kE#%ztk33Zx<2Qaq0!}TTbd#(r{*Bh3;UW=CbO-9kFDxkTsxj$YV zGhn+9!p$Gq`Q{g45kxDyM}>YYb7?|}4)aU!=)RGY(6PC0-D^4F`-gf>!r-BhTjz9H z+$ky99!=Fv9n;M^U)AO1dF&d)y}59`iocy zI#qK!y{-yPq$aoA)Mq{{T!^>~Z;gZ|pADUE|s#E4VR)wYXdNS0iXQ1y7|+mhyx5#=mwe;z$YQ6S}y) z7F4FeK}skcD6!SB?Z?``ge69avxc22Te}?`N@>hiV&*L(KXbFbS8w1Ey);+h5nT}p z{+Np8$Ix8?UBKd&ICf)Z3V>7B3@E#OJcgTZmM)FAcSOhNj#1*$G&X10-KQ{n=^&Xw z!NW_O7_$_0*b`fC-JC@4I9gTA(EgZ+`VZu4)bsn0{;DSPy&ha*H@a4ue?)^j2{;o+^zu;#$IZ%rwTw06Z-1 z51s)?KJ2iX5k-myS^c8L8at*e{#=L?&V@J;9gHQiJm8=xJQ}&Y8l!bQM>a#?sRoq6 zutni9ggQ^jrKH|v#k_=0LfKA@#0?)F(NP($WTPiq+{t6`M<`(-)>7E60rIQfl

O10 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 delta 7477 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 V35d-#d%E{I12y2p_{8E@{|PdUBWC~r diff --git a/Source/ZMMO/Data/UI/UIStyleTokens.h b/Source/ZMMO/Data/UI/UIStyleTokens.h index 68ce067..07a9835 100644 --- a/Source/ZMMO/Data/UI/UIStyleTokens.h +++ b/Source/ZMMO/Data/UI/UIStyleTokens.h @@ -3,6 +3,7 @@ #include "CoreMinimal.h" #include "Fonts/SlateFontInfo.h" #include "Styling/SlateBrush.h" +#include "Styling/SlateTypes.h" #include "Layout/Margin.h" #include "Templates/SubclassOf.h" #include "Engine/Texture2D.h" @@ -195,117 +196,13 @@ struct ZMMO_API FUITextStyle }; // ===================================================================== -// FUIStyleButton — padrão multi-map (Hyper-style). 3 sub-mapas com a MESMA -// chave (nome da variante: "Primary", "Secondary", "Danger", "Ghost", ou -// customizada). UUIButton_Base.Variant é FName e busca em cada sub-mapa. -// O struct FUIStyleButtonVariant (composto, FLAT) permanece com a forma -// antiga — assim o hook BP_ApplyUIStyle dos WBP_Master continua funcional. +// FUIStyleButton — padrão Hyper "Buttons": 7 sub-mapas independentes +// indexados pela MESMA chave FName de variante (autor define livremente: +// "Default", "Square", "Topbar", "Home_Main_Menu", ...). UUIButton_Base.Variant +// é FName e busca o pedaço necessário em cada sub-mapa. ButtonStyle traz +// FButtonStyle do Slate completo (Normal/Hovered/Pressed/Disabled como +// FSlateBrush, Foregrounds, Padding, Sounds). // ===================================================================== -USTRUCT(BlueprintType) -struct ZMMO_API FUIStyleButtonBackground -{ - GENERATED_BODY() - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgNormal = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgHover = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgPressed = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgDisabled = FLinearColor(0.f, 0.f, 0.f, 0.42f); - - /** Textura opcional como background (Hyper-style). */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - TObjectPtr 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 -{ - GENERATED_BODY() - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgNormal = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgHover = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgPressed = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgDisabled = FLinearColor(0.f, 0.f, 0.f, 0.42f); - - 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") - FLinearColor TextColor = FLinearColor::White; - - 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) struct ZMMO_API FUIStyleButton { @@ -313,163 +210,103 @@ struct ZMMO_API FUIStyleButton FUIStyleButton() { - // ---- 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); + // Defaults "no draw" pra não pintar branco quando o brush estiver vazio + // (regra Hyper — ver feedback_slatebrush_empty_white). Autor preenche + // no DT_UI_Styles. + FButtonStyle DefaultStyle; + DefaultStyle.Normal.DrawAs = ESlateBrushDrawType::NoDrawType; + DefaultStyle.Hovered.DrawAs = ESlateBrushDrawType::NoDrawType; + DefaultStyle.Pressed.DrawAs = ESlateBrushDrawType::NoDrawType; + DefaultStyle.Disabled.DrawAs = ESlateBrushDrawType::NoDrawType; + ButtonStyle.Add(TEXT("Default"), DefaultStyle); - 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); + FSlateBrush DefaultStroke; + DefaultStroke.DrawAs = ESlateBrushDrawType::NoDrawType; + ButtonStroke.Add(TEXT("Default"), DefaultStroke); - 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); + ButtonFont.Add(TEXT("Default"), FSlateFontInfo()); - FUIStyleButtonBackground GhoBg; - GhoBg.BgNormal = FLinearColor::Transparent; - GhoBg.BgHover = FLinearColor(FColor(22, 32, 58, 128)); - GhoBg.BgPressed = FLinearColor::Transparent; - Backgrounds.Add(TEXT("Ghost"), GhoBg); + ButtonTextColorRegular.Add(TEXT("Default"), FLinearColor::White); + ButtonTextColorHovered.Add(TEXT("Default"), FLinearColor::White); + ButtonTextColorSelected.Add(TEXT("Default"), FLinearColor::White); - // ---- 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); + ButtonIconColor.Add(TEXT("Default"), FLinearColor::White); } - /** Cores/textura de fundo por variante (chave FName). */ + /** FButtonStyle do Slate por variante: Normal/Hovered/Pressed/Disabled + * como FSlateBrush completo + Foregrounds + Padding + Sounds. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - TMap Backgrounds; + TMap ButtonStyle; - /** Borda (cor, espessura, raio) por variante. */ + /** Stroke/contorno como brush independente (texturizável). Hoje o + * UUIButton_Base não consome este campo automaticamente — quem precisa + * de stroke decora o FSlateBrush dentro de ButtonStyle.Normal via + * OutlineSettings. Reservado pra evolução com 2 Borders empilhados. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - TMap Strokes; + TMap ButtonStroke; - /** Tipografia (cor de texto, CommonTextStyle) por variante. */ + /** Fonte do texto por variante. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - TMap Fonts; - - // Globais comuns às variantes: - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FMargin Padding = FMargin(22.f, 12.f); + TMap ButtonFont; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - float MinHeight = 48.f; + TMap ButtonTextColorRegular; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - float HoverScale = 1.02f; + TMap ButtonTextColorHovered; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - float PressedScale = 0.98f; + TMap ButtonTextColorSelected; - /** Tamanho por silhueta (Square/Rectangle/Topbar/Header). */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - TMap ShapeSizes; + TMap ButtonIconColor; }; /** - * Espelha 1:1 o Struct_UI_Style_Panels do Hyper: só os dois mapas de brush - * (fundo + contorno) indexados por EUIPanelTexture. É o que o UI_Panel_Master - * do Hyper expõe editável na instância (Details). EditAnywhere/ReadWrite para - * o designer ajustar brush por widget; o tema (DT) só faz seed/fallback. + * Override manual por instância (modo bUseTheme = false). 3 brushes diretos + * pros 3 Borders do painel — designer edita o brush bruto no Details. + * Default = NoDrawType pra não pintar branco quando vazio (regra Hyper). */ USTRUCT(BlueprintType) struct ZMMO_API FUIPanelBrushSet { GENERATED_BODY() - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel|Texture") - TMap PanelBackground; + FUIPanelBrushSet() + { + PanelBackground.DrawAs = ESlateBrushDrawType::NoDrawType; + PanelOutline.DrawAs = ESlateBrushDrawType::NoDrawType; + PanelOutlineEffect.DrawAs = ESlateBrushDrawType::NoDrawType; + } - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel|Texture") - TMap PanelOutline; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel|Brush") + FSlateBrush PanelBackground; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel|Brush") + FSlateBrush PanelOutline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel|Brush") + FSlateBrush PanelOutlineEffect; }; +/** + * Espelha 1:1 o Struct_UI_Style_Panels do Hyper, agora com 3 brushes por + * EUIPanelTexture: fundo, contorno e efeito sobre o contorno. Tudo + * data-driven via brush no DT_UI_Styles. + */ USTRUCT(BlueprintType) struct ZMMO_API FUIStylePanel { GENERATED_BODY() - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FLinearColor Bg = FLinearColor(FColor(12, 18, 34, 235)); // Panel - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FLinearColor BgRaised = FLinearColor(FColor(17, 24, 42)); // Panel2 - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FLinearColor BgSunken = FLinearColor(FColor(14, 20, 36)); // Panel3 - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FLinearColor BorderColor = FLinearColor(FColor(58, 75, 120)); - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FLinearColor BorderSoftColor = FLinearColor(FColor(58, 75, 120, 140)); - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - float CornerRadius = 12.f; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - float CornerRadiusSmall = 10.f; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - float BorderWidth = 1.f; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FMargin Padding = FMargin(30.f); - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FMargin PaddingSmall = FMargin(20.f); - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FLinearColor CardSelectedBorder = FLinearColor(FColor(201, 167, 90)); // Accent - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FLinearColor CardSelectedBg = FLinearColor(FColor(24, 32, 58)); - - /** - * Modo texturizado (espelha Struct_UI_Style_Panels do Hyper): brush por - * EUIPanelTexture. PanelBackground = fundo; PanelOutline = contorno. - * Se vazio p/ a chave, o painel usa o modo cor (RoundedBox) acima. - */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel|Texture") TMap PanelBackground; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel|Texture") TMap PanelOutline; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel|Texture") + TMap PanelOutlineEffect; }; USTRUCT(BlueprintType) diff --git a/Source/ZMMO/Data/UI/UIStyleTypes.h b/Source/ZMMO/Data/UI/UIStyleTypes.h index 826a9cd..ffcef81 100644 --- a/Source/ZMMO/Data/UI/UIStyleTypes.h +++ b/Source/ZMMO/Data/UI/UIStyleTypes.h @@ -66,6 +66,7 @@ enum class EUIPanelTexture : uint8 Rectangle_Vertical_03 UMETA(DisplayName = "Rectangle Vertical 03"), Rectangle_Vertical_04 UMETA(DisplayName = "Rectangle Vertical 04"), Rectangle_Square_01 UMETA(DisplayName = "Rectangle Square 01"), + Rectangle_Square_Gradient_01 UMETA(DisplayName = "Rectangle Square Gradient 01"), Vertical_Footer_01 UMETA(DisplayName = "Vertical Footer 01") }; diff --git a/Source/ZMMO/Game/UI/FrontEnd/UIServerCard_Base.cpp b/Source/ZMMO/Game/UI/FrontEnd/UIServerCard_Base.cpp index 8a357ac..bd87530 100644 --- a/Source/ZMMO/Game/UI/FrontEnd/UIServerCard_Base.cpp +++ b/Source/ZMMO/Game/UI/FrontEnd/UIServerCard_Base.cpp @@ -9,12 +9,11 @@ void UUIServerCard_Base::NativePreConstruct() { Super::NativePreConstruct(); - // Reaplica tema do botao antes de qualquer SetIsEnabled — garante que o - // background da variante "Primary" pinta tanto em Normal quanto Disabled. - if (Btn_Enter) - { - Btn_Enter->RefreshUIStyle(); - } + // TODO(ui-system): reativar quando UUIButton_Base voltar a ter RefreshUIStyle. + // if (Btn_Enter) + // { + // Btn_Enter->RefreshUIStyle(); + // } } void UUIServerCard_Base::NativeConstruct() @@ -25,10 +24,11 @@ void UUIServerCard_Base::NativeConstruct() Btn_Enter->OnClicked().AddUObject(this, &UUIServerCard_Base::HandleEnterClicked); bBound = true; } - if (Btn_Enter) - { - Btn_Enter->RefreshUIStyle(); - } + // TODO(ui-system): reativar quando UUIButton_Base voltar a ter RefreshUIStyle. + // if (Btn_Enter) + // { + // Btn_Enter->RefreshUIStyle(); + // } } void UUIServerCard_Base::NativeDestruct() @@ -69,10 +69,11 @@ void UUIServerCard_Base::SetFromEntry(const FZMMOWorldEntry& InEntry) // o usuario pode entrar no Lobby pra ver a lista de personagens, criar/ // deletar. O gate de "entrar no mundo" fica no proprio Lobby (botao // "Entrar no Servidor"), que checa World.State antes do handoff. - if (Btn_Enter) - { - Btn_Enter->RefreshUIStyle(); - } + // TODO(ui-system): reativar quando UUIButton_Base voltar a ter RefreshUIStyle. + // if (Btn_Enter) + // { + // Btn_Enter->RefreshUIStyle(); + // } } void UUIServerCard_Base::HandleEnterClicked() diff --git a/Source/ZMMO/Game/UI/Widgets/UIButton_Base.cpp b/Source/ZMMO/Game/UI/Widgets/UIButton_Base.cpp index 51bb889..d9c7d61 100644 --- a/Source/ZMMO/Game/UI/Widgets/UIButton_Base.cpp +++ b/Source/ZMMO/Game/UI/Widgets/UIButton_Base.cpp @@ -1,434 +1,28 @@ #include "UIButton_Base.h" -#include "ZMMOThemeSubsystem.h" -#include "Engine/GameInstance.h" -#include "Engine/World.h" -#include "TimerManager.h" -#include "Components/Border.h" -#include "Styling/SlateBrush.h" -#include "Components/TextBlock.h" -#include "Components/Image.h" -#include "Components/SizeBox.h" -#include "Components/ScaleBox.h" -#include "Components/OverlaySlot.h" -#include "CommonTextBlock.h" -#include "CommonActionWidget.h" +#include "UI/UIStyleRow.h" +#include "UI/UIStyleTokens.h" +#include "Engine/DataTable.h" -namespace +const FUIStyle* UUIButton_Base::GetActiveUIStyle(FName ThemeId) const { - /** Resolve o FUIStyle ativo; fallback p/ defaults (preview de designer). */ - const FUIStyle& ResolveActiveStyle(const UUserWidget* Widget, const FUIStyle& Fallback) + const UDataTable* DT = LoadObject(nullptr, + TEXT("/Game/ZMMO/Data/UI/DT_UI_Styles.DT_UI_Styles")); + if (!DT) { - if (Widget) - { - if (const UGameInstance* GI = Widget->GetGameInstance()) - { - if (const UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) - { - return Theme->GetActiveUIStyle(); - } - } - } - return Fallback; // sem GameInstance (design-time): Aurora Arcana default + return nullptr; } + + if (const FUIStyleRow* Row = DT->FindRow( + ThemeId, TEXT("UUIButton_Base::GetActiveUIStyle"), false)) + { + return &Row->Style; + } + return nullptr; } -FUIStyleButtonVariant UUIButton_Base::ResolveVariant(const FUIStyleButton& Button) const +const FUIStyleButton* UUIButton_Base::GetActiveButtonStyle(FName ThemeId) const { - // Compõe FLAT a partir dos 3 sub-mapas do tema (Backgrounds/Strokes/Fonts). - // Fallback inline → Defaults (constructor de FUIStyleButton popula - // Primary/Secondary/Danger/Ghost com defaults Aurora Arcana — GC-safe). - static const FUIStyleButton Defaults; - FUIStyleButtonVariant V; - - const FUIStyleButtonBackground* BG = Button.Backgrounds.Find(Variant); - if (!BG) { BG = Defaults.Backgrounds.Find(Variant); } - if (!BG) { BG = Defaults.Backgrounds.Find(TEXT("Primary")); } - if (BG) - { - V.BgNormal = BG->BgNormal; - V.BgHover = BG->BgHover; - V.BgPressed = BG->BgPressed; - V.BgDisabled = BG->BgDisabled; - V.BackgroundTexture = BG->BackgroundTexture; - V.BackgroundMargin = BG->BackgroundMargin; - } - - const FUIStyleButtonStroke* ST = Button.Strokes.Find(Variant); - if (!ST) { ST = Defaults.Strokes.Find(Variant); } - if (!ST) { ST = Defaults.Strokes.Find(TEXT("Primary")); } - if (ST) - { - V.BorderNormal = ST->BorderNormal; - V.BorderHover = ST->BorderHover; - V.BorderWidth = ST->BorderWidth; - V.CornerRadius = ST->CornerRadius; - } - - const FUIStyleButtonFont* F = Button.Fonts.Find(Variant); - if (!F) { F = Defaults.Fonts.Find(Variant); } - if (!F) { F = Defaults.Fonts.Find(TEXT("Primary")); } - if (F) - { - V.TextColor = F->TextColor; - V.TextStyle = F->TextStyle; - } - return V; -} - -TArray UUIButton_Base::GetVariantOptions() const -{ - TArray Options; - for (const TCHAR* N : { TEXT("Primary"), TEXT("Secondary"), TEXT("Danger"), TEXT("Ghost") }) - { - 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("UIButtonVariantOptions"), false)) - { - for (const TPair& P : Row->Style.Button.Backgrounds) { Options.AddUnique(P.Key.ToString()); } - for (const TPair& P : Row->Style.Button.Strokes) { Options.AddUnique(P.Key.ToString()); } - for (const TPair& P : Row->Style.Button.Fonts) { Options.AddUnique(P.Key.ToString()); } - } - } - return Options; -} - -// ---- Hyper "Set Button Text" ---- -void UUIButton_Base::SetButtonText(FText InText) -{ - if (!ButtonText) - { - return; - } - const FText Final = bUppercaseText - ? FText::FromString(InText.ToString().ToUpper()) - : InText; - ButtonText->SetText(Final); -} - -// ---- Hyper "Set Number Text" ---- -void UUIButton_Base::SetNumberText() -{ - if (!Number_Text) - { - return; - } - if (bShowNumberText) - { - Number_Text->SetText(FText::AsNumber(NumberTextAmount)); - Number_Text->SetVisibility(ESlateVisibility::Visible); - } - else - { - Number_Text->SetVisibility(ESlateVisibility::Hidden); - } -} - -// ---- Hyper "Set Icon" ---- -void UUIButton_Base::SetIcon() -{ - if (bUseIconInsteadOfText) - { - if (Image_Icon) - { - if (IconImage) - { - Image_Icon->SetBrushResourceObject(IconImage); - } - Image_Icon->SetVisibility(ESlateVisibility::Visible); - } - if (ButtonText) - { - ButtonText->SetVisibility(ESlateVisibility::Collapsed); - } - } - else - { - if (Image_Icon) - { - Image_Icon->SetVisibility(ESlateVisibility::Collapsed); - } - if (ButtonText) - { - ButtonText->SetVisibility(ESlateVisibility::Visible); - } - } -} - -// ---- Hyper "Set Sizebox Master Dimmensions" ---- -void UUIButton_Base::ApplySizeBox() -{ - if (bDoNotOverrideWithSizebox || !SizeBox_Master) - { - return; - } - SizeBox_Master->SetHeightOverride(InHeightOverride); - SizeBox_Master->SetWidthOverride(InWidthOverride); -} - -// ---- Hyper "Set InputAction Style" ---- -void UUIButton_Base::ApplyInputActionStyle() -{ - // InputActionWidget é privado em UCommonButtonBase (bind interno). Pega por - // nome — o widget do WBP chama-se "InputActionWidget" (bind do CommonUI). - UCommonActionWidget* IAW = Cast( - GetWidgetFromName(TEXT("InputActionWidget"))); - if (!IAW) - { - return; - } - - if (bShowInputActionWidget) - { - IAW->SetVisibility(ESlateVisibility::Visible); - IAW->SetIsEnabled(true); - } - else - { - IAW->SetVisibility(ESlateVisibility::Collapsed); - IAW->SetIsEnabled(false); - } - - if (UOverlaySlot* OS = Cast(IAW->Slot)) - { - OS->SetPadding(InputPadding); - OS->SetHorizontalAlignment(InputHorzAlignment); - OS->SetVerticalAlignment(InputVertAlignment); - } -} - -// ---- Hyper "Initialize Button" (orquestrador) ---- -void UUIButton_Base::InitializeButton() -{ - SetButtonText(ButtonTextValue); - SetNumberText(); - SetIcon(); - ApplySizeBox(); - RefreshUIStyle(); -} - -// ---- Hyper "Update Button Style" / "Set Brush" → nossa camada FUIStyle ---- -void UUIButton_Base::RefreshUIStyle() -{ - const FUIStyle Fallback; // defaults do struct = paleta Aurora Arcana - const FUIStyle& ActiveStyle = ResolveActiveStyle(this, Fallback); - const FUIStyleButtonVariant& VariantStyle = ResolveVariant(ActiveStyle.Button); - - // Fundo + borda no estado Normal (hover/pressed/disabled via NativeOn*). - ApplyVisualState(EUIButtonVisual::Normal); - - if (ButtonText) - { - // Tipografia: CommonTextStyle da variante (padrão Hyper/CommonUI). - if (VariantStyle.TextStyle) - { - ButtonText->SetStyle(VariantStyle.TextStyle); - } - // Cor: FUIStyle manda na cor por tema (sobrepõe o CommonTextStyle). - ButtonText->SetColorAndOpacity(FSlateColor(VariantStyle.TextColor)); - - // Alinhamento L/C/R dentro da largura do botão. - ButtonText->SetJustification(TextAlign); - } - - // Texto adapta ao tamanho do botão (ScaleToFit) ou usa tamanho fixo. - if (TextScaleBox) - { - TextScaleBox->SetStretch(bAutoSizeText ? EStretch::ScaleToFit : EStretch::None); - - // Posiciona o bloco de texto L/C/R dentro do botão (slot do Overlay). - if (UOverlaySlot* OS = Cast(TextScaleBox->Slot)) - { - EHorizontalAlignment H = HAlign_Center; - switch (TextAlign) - { - case ETextJustify::Left: H = HAlign_Left; break; - case ETextJustify::Right: H = HAlign_Right; break; - default: H = HAlign_Center; break; - } - OS->SetHorizontalAlignment(H); - OS->SetVerticalAlignment(VAlign_Center); - } - } - - BP_ApplyUIStyle(ActiveStyle.Button, VariantStyle); -} - -void UUIButton_Base::ApplyVisualState(EUIButtonVisual State) -{ - if (!Background) - { - return; - } - const FUIStyle Fallback; - const FUIStyle& AS = ResolveActiveStyle(this, Fallback); - const FUIStyleButtonVariant& V = ResolveVariant(AS.Button); - - FLinearColor Fill, Outline; - switch (State) - { - case EUIButtonVisual::Hovered: Fill = V.BgHover; Outline = V.BorderHover; break; - case EUIButtonVisual::Pressed: Fill = V.BgPressed; Outline = V.BorderHover; break; - case EUIButtonVisual::Disabled: Fill = V.BgDisabled; Outline = V.BorderNormal; break; - default: Fill = V.BgNormal; Outline = V.BorderNormal; break; - } - - FSlateBrush Brush = Background->Background; - Brush.TintColor = FSlateColor(Fill); - if (bRoundedBackground) - { - // CornerRadius/BorderWidth agora vêm da variante composta (Stroke). - const float R = V.CornerRadius; - Brush.DrawAs = ESlateBrushDrawType::RoundedBox; - Brush.OutlineSettings = FSlateBrushOutlineSettings( - FVector4(R, R, R, R), FSlateColor(Outline), V.BorderWidth); - Brush.OutlineSettings.RoundingType = ESlateBrushRoundingType::FixedRadius; - } - else - { - Brush.DrawAs = ESlateBrushDrawType::Box; - Brush.OutlineSettings = FSlateBrushOutlineSettings( - FVector4(0, 0, 0, 0), FSlateColor(Outline), V.BorderWidth); - } - Background->SetBrush(Brush); -} - -void UUIButton_Base::NativeOnHovered() -{ - Super::NativeOnHovered(); - ApplyVisualState(EUIButtonVisual::Hovered); -} - -void UUIButton_Base::NativeOnUnhovered() -{ - Super::NativeOnUnhovered(); - ApplyVisualState(EUIButtonVisual::Normal); -} - -void UUIButton_Base::NativeOnPressed() -{ - Super::NativeOnPressed(); - ApplyVisualState(EUIButtonVisual::Pressed); -} - -void UUIButton_Base::NativeOnReleased() -{ - Super::NativeOnReleased(); - ApplyVisualState(IsHovered() ? EUIButtonVisual::Hovered : EUIButtonVisual::Normal); -} - -void UUIButton_Base::NativeOnEnabled() -{ - Super::NativeOnEnabled(); - ApplyVisualState(EUIButtonVisual::Normal); -} - -void UUIButton_Base::NativeOnDisabled() -{ - Super::NativeOnDisabled(); - ApplyVisualState(EUIButtonVisual::Disabled); -} - -void UUIButton_Base::NativePreConstruct() -{ - Super::NativePreConstruct(); - InitializeButton(); - ApplyInputActionStyle(); -} - -void UUIButton_Base::NativeConstruct() -{ - Super::NativeConstruct(); - - if (!bThemeBound) - { - if (const UGameInstance* GI = GetGameInstance()) - { - if (UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) - { - Theme->OnThemeChanged.AddDynamic(this, &UUIButton_Base::HandleThemeChanged); - bThemeBound = true; - } - } - } - - InitializeButton(); - ApplyInputActionStyle(); - - if (bDeactivatedOnConstructWithTimeout && Timeout > 0.f) - { - StartDeactivateTimeout(); - } -} - -void UUIButton_Base::NativeDestruct() -{ - if (UWorld* World = GetWorld()) - { - World->GetTimerManager().ClearTimer(InactivityTimer); - World->GetTimerManager().ClearTimer(TimerTickHandle); - } - - if (bThemeBound) - { - if (const UGameInstance* GI = GetGameInstance()) - { - if (UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) - { - Theme->OnThemeChanged.RemoveDynamic(this, &UUIButton_Base::HandleThemeChanged); - } - } - bThemeBound = false; - } - - Super::NativeDestruct(); -} - -// ---- Hyper "Deactivate Button On Timeout" ---- -void UUIButton_Base::StartDeactivateTimeout() -{ - UWorld* World = GetWorld(); - if (!World) - { - return; - } - - SetIsEnabled(false); - TimeRemaining = Timeout; - - World->GetTimerManager().SetTimer( - InactivityTimer, this, &UUIButton_Base::HandleTimeoutFinished, Timeout, false); - - if (TimeUpdateFrequency > 0.f) - { - World->GetTimerManager().SetTimer( - TimerTickHandle, this, &UUIButton_Base::HandleTimerTick, - TimeUpdateFrequency, true); - } -} - -void UUIButton_Base::HandleTimeoutFinished() -{ - if (UWorld* World = GetWorld()) - { - World->GetTimerManager().ClearTimer(TimerTickHandle); - } - TimeRemaining = 0.f; - SetIsEnabled(true); - OnButtonActivatedByTimeout.Broadcast(); -} - -void UUIButton_Base::HandleTimerTick() -{ - TimeRemaining = FMath::Max(0.f, TimeRemaining - TimeUpdateFrequency); - OnButtonTimerUpdated.Broadcast(TimeRemaining); -} - -void UUIButton_Base::HandleThemeChanged(FName /*NewThemeId*/) -{ - RefreshUIStyle(); + const FUIStyle* UIStyle = GetActiveUIStyle(ThemeId); + return UIStyle ? &UIStyle->Button : nullptr; } diff --git a/Source/ZMMO/Game/UI/Widgets/UIButton_Base.h b/Source/ZMMO/Game/UI/Widgets/UIButton_Base.h index cdc0415..781c427 100644 --- a/Source/ZMMO/Game/UI/Widgets/UIButton_Base.h +++ b/Source/ZMMO/Game/UI/Widgets/UIButton_Base.h @@ -2,220 +2,55 @@ #include "CoreMinimal.h" #include "CommonButtonBase.h" -#include "Layout/Margin.h" -#include "Types/SlateEnums.h" -#include "UI/UIStyleTypes.h" -#include "UI/UIStyleTokens.h" #include "UIButton_Base.generated.h" -class UBorder; -class UTextBlock; -class UImage; class USizeBox; -class UScaleBox; +class UImage; +class UCanvasPanel; class UCommonTextBlock; -class UCommonActionWidget; +class UTextBlock; -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnUIButtonActivatedByTimeout); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnUIButtonTimerUpdated, float, TimeRemaining); +struct FUIStyle; +struct FUIStyleButton; /** - * Botão base do ZMMO — porte 1:1 do UI_Button_Master do projeto Hyper para C++ - * auto-configurável, trocando o Struct_Style_Button do Hyper pela camada de - * tema C++ (FUIStyle via UZMMOThemeSubsystem::GetActiveUIStyle()). - * - * Fundação: CommonUI (UCommonButtonBase) cuida de input/foco/click. - * Camada Abstract (padrão "_Abstract" do Hyper). WBP UI_Button_Base herda - * disto; UI_Button_Master herda do WBP. Os widgets visuais são opcionais - * (BindWidgetOptional) e nomeados como no Hyper. - * - * Auto-config: NativePreConstruct chama InitializeButton (text/number/icon/ - * sizebox/estilo) + ApplyInputActionStyle. Reage à troca de tema. + * Botão base do ZMMO (CommonUI). Hoje expõe BindWidgetOptional dos widgets + * que o WBP UI_Button_Master_New consome, e helpers pra ler o estilo ativo + * de DT_UI_Styles. A aplicação visual (pintar brushes/cores/fonte) entra + * depois — esta camada só prepara o acesso. */ UCLASS(Abstract, Blueprintable) class ZMMO_API UUIButton_Base : public UCommonButtonBase { GENERATED_BODY() -public: - // ---- Botão ---- - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button") - EUIButtonShape ButtonTypeSelection = EUIButtonShape::Rectangle; - - /** - * Variante visual do botão (dropdown vem de FUIStyle.Button.Backgrounds/ - * Strokes/Fonts no DT_UI_Styles + as 4 clássicas como fallback). - * Data-driven via FName, sem enum fixo (padrão Hyper-style). - */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button", - meta = (GetOptions = "GetVariantOptions")) - FName Variant = TEXT("Primary"); - - /** Opções do dropdown de Variant (data-driven do DT_UI_Styles). */ - UFUNCTION() - TArray GetVariantOptions() const; - - /** Hyper "Transform Policy = To Upper": força o texto em maiúsculas. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button") - bool bUppercaseText = true; - - // ---- Text Style ---- - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Text Style") - FText ButtonTextValue = FText::FromString(TEXT("Button Text")); - - /** Alinhamento do texto dentro do botão (esquerda/centro/direita). */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Text Style") - TEnumAsByte TextAlign = ETextJustify::Center; - - /** true = texto escala p/ caber no tamanho do botão (ScaleBox ScaleToFit); - * false = usa o tamanho fixo da fonte da variante. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Text Style") - bool bAutoSizeText = true; - - /** Arredonda o Background (RoundedBox) e desenha a borda na cor da - * variante. Desligado = caixa reta. Raio/largura vêm de FUIStyleButton. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button") - bool bRoundedBackground = true; - - // ---- Number Text ---- - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Number Text") - bool bShowNumberText = false; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Number Text") - int32 NumberTextAmount = 0; - - // ---- Icon ---- - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Icon") - bool bUseIconInsteadOfText = false; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Icon") - TObjectPtr IconImage = nullptr; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Icon") - EUIIconType IconTypeSelection = EUIIconType::None; - - // ---- Size ---- - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Size") - float InHeightOverride = 50.f; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Size") - float InWidthOverride = 150.f; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Size") - bool bDoNotOverrideWithSizebox = false; - - // ---- Input Action Style ---- - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Action Style") - bool bShowInputActionWidget = false; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Action Style") - FMargin InputPadding = FMargin(0.f, 0.f, 0.f, -22.f); - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Action Style") - TEnumAsByte InputHorzAlignment = HAlign_Center; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Action Style") - TEnumAsByte InputVertAlignment = VAlign_Bottom; - - // ---- Deactivate by Timer ---- - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Deactivate By Timer") - bool bDeactivatedOnConstructWithTimeout = false; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Deactivate By Timer") - float Timeout = 0.f; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Timer") - float TimeUpdateFrequency = 0.05f; - - UPROPERTY(BlueprintReadOnly, Category = "Timer") - float TimeRemaining = 0.f; - - UPROPERTY(BlueprintAssignable, Category = "Timer") - FOnUIButtonActivatedByTimeout OnButtonActivatedByTimeout; - - UPROPERTY(BlueprintAssignable, Category = "Timer") - FOnUIButtonTimerUpdated OnButtonTimerUpdated; - - // ---- API (porte 1:1 das funções do Hyper) ---- - UFUNCTION(BlueprintCallable, Category = "UI Button") - void SetButtonText(FText InText); - - UFUNCTION(BlueprintCallable, Category = "UI Button") - void SetNumberText(); - - UFUNCTION(BlueprintCallable, Category = "UI Button") - void SetIcon(); - - UFUNCTION(BlueprintCallable, Category = "UI Button") - void ApplySizeBox(); - - UFUNCTION(BlueprintCallable, Category = "UI Button") - void ApplyInputActionStyle(); - - /** Orquestrador (Hyper "Initialize Button"): aplica tudo + estilo do tema. */ - UFUNCTION(BlueprintCallable, Category = "UI Button") - void InitializeButton(); - - /** Re-resolve os tokens do tema ativo e reaplica (Hyper "Update Button Style"). */ - UFUNCTION(BlueprintCallable, Category = "UI Style") - void RefreshUIStyle(); - - /** Estado visual do botão (mapeia nos tokens BgNormal/Hover/Pressed/Disabled). */ - enum class EUIButtonVisual : uint8 { Normal, Hovered, Pressed, Disabled }; - - /** Pinta o Background (fill + borda) conforme o estado, re-resolvendo o tema. */ - void ApplyVisualState(EUIButtonVisual State); - protected: - virtual void NativePreConstruct() override; - virtual void NativeConstruct() override; - virtual void NativeDestruct() override; + // ---- Widgets BindWidgetOptional (espelham a árvore do UI_Button_Master_New) ---- + UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional)) + TObjectPtr SizeBox; - // Estados do UCommonButtonBase → repinta o Background pela variante. - virtual void NativeOnHovered() override; - virtual void NativeOnUnhovered() override; - virtual void NativeOnPressed() override; - virtual void NativeOnReleased() override; - virtual void NativeOnEnabled() override; - virtual void NativeOnDisabled() override; + UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional)) + TObjectPtr ImgStoke; - /** Hook opcional: o WBP pode estender/sobrescrever a aplicação visual. */ - UFUNCTION(BlueprintImplementableEvent, Category = "UI Style", - meta = (DisplayName = "Apply UI Style")) - void BP_ApplyUIStyle(const FUIStyleButton& Button, const FUIStyleButtonVariant& VariantStyle); + UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional)) + TObjectPtr CanvasPanel_Text; - FUIStyleButtonVariant ResolveVariant(const FUIStyleButton& Button) const; - - // ---- Widgets visuais opcionais (nomes espelham o Hyper) ---- - UPROPERTY(meta = (BindWidgetOptional)) - TObjectPtr SizeBox_Master; - - UPROPERTY(meta = (BindWidgetOptional)) + UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional)) TObjectPtr ButtonText; - /** Envolve o ButtonText: escala o texto p/ caber no botão (bAutoSizeText). */ - UPROPERTY(meta = (BindWidgetOptional)) - TObjectPtr TextScaleBox; - - UPROPERTY(meta = (BindWidgetOptional)) + UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional)) TObjectPtr Number_Text; - UPROPERTY(meta = (BindWidgetOptional)) - TObjectPtr Image_Icon; + UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional)) + TObjectPtr ImgIcon; - /** Fundo temável (nossa adição sobre o Hyper, p/ FUIStyle). */ - UPROPERTY(meta = (BindWidgetOptional)) - TObjectPtr Background; + // ---- Acesso ao estilo ativo (DT_UI_Styles) — C++ only ---- -private: - void StartDeactivateTimeout(); - void HandleTimeoutFinished(); - void HandleTimerTick(); + /** Carrega DT_UI_Styles e devolve o FUIStyle da row pedida (default "Default"). + * Retorna nullptr se a DT ou a row não existirem. Ponteiro válido enquanto + * a DT estiver carregada — uso típico é leitura imediata, sem armazenar. */ + const FUIStyle* GetActiveUIStyle(FName ThemeId = TEXT("Default")) const; - UFUNCTION() - void HandleThemeChanged(FName NewThemeId); - - bool bThemeBound = false; - FTimerHandle InactivityTimer; - FTimerHandle TimerTickHandle; + /** Atalho: devolve só o bloco Button do FUIStyle ativo. */ + const FUIStyleButton* GetActiveButtonStyle(FName ThemeId = TEXT("Default")) const; }; diff --git a/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.cpp b/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.cpp index 7ec2f4f..e840523 100644 --- a/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.cpp +++ b/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.cpp @@ -52,83 +52,43 @@ void UUIPanel_Base::RefreshUIStyle() const FUIStyle AS = ResolvePanelStyle(this, Fallback); const FUIStylePanel& P = AS.Panel; - // ---- Modo TEXTURA (padrão Hyper): brush por EUIPanelTexture ---- - // Fiel ao UI_Panel_Master do Hyper: só faz SetBrush nos dois Borders - // (Find no mapa por chave). Sem padding/visibility — a arte da textura - // já traz a moldura; o conteúdo é injetado pelo WBP filho. - // Seed dos mapas da instância a partir do tema quando vazios — assim o - // Details mostra Panel Background/Outline preenchidos e editáveis (igual - // ao Hyper, cujo `Panels` é alimentado pelo estilo). Edições do designer - // têm prioridade; o tema (P) é fallback por chave. - if (Panels.PanelBackground.Num() == 0) { Panels.PanelBackground = P.PanelBackground; } - if (Panels.PanelOutline.Num() == 0) { Panels.PanelOutline = P.PanelOutline; } + // bUseTheme = true -> brushes vêm da DT (lookup pelas 3 chaves). + // bUseTheme = false -> brushes vêm dos 3 FSlateBrush em Panels (direto). - const FSlateBrush* BgTex = nullptr; - if (PanelTexture != EUIPanelTexture::None) + static const FSlateBrush NoDrawBrush = [] { - BgTex = Panels.PanelBackground.Find(PanelTexture); - if (!BgTex) { BgTex = P.PanelBackground.Find(PanelTexture); } - } - if (BgTex) + FSlateBrush B; + B.DrawAs = ESlateBrushDrawType::NoDrawType; + return B; + }(); + + auto ResolveFromTheme = [](EUIPanelTexture Key, + const TMap& Map) -> const FSlateBrush* { - Background->SetBrush(*BgTex); - if (Outline) - { - // Hyper: Find sempre seguido de SetBrush. No Hyper a entrada de - // outline ausente é um brush TRANSPARENTE (alpha 0) — não some - // widget, só não desenha. Um FSlateBrush default desenha um - // retângulo BRANCO; o equivalente correto é DrawAs=NoDrawType. - const FSlateBrush* OlTex = Panels.PanelOutline.Find(PanelTexture); - if (!OlTex) { OlTex = P.PanelOutline.Find(PanelTexture); } - static const FSlateBrush NoDrawBrush = [] - { - FSlateBrush B; - B.DrawAs = ESlateBrushDrawType::NoDrawType; - return B; - }(); - Outline->SetBrush(OlTex ? *OlTex : NoDrawBrush); - } - BP_ApplyPanelStyle(P); - return; - } + return (Key == EUIPanelTexture::None) ? nullptr : Map.Find(Key); + }; + + const FSlateBrush* BgTex = bUseTheme + ? ResolveFromTheme(BackgroundKey, P.PanelBackground) + : &Panels.PanelBackground; + Background->SetBrush(BgTex ? *BgTex : NoDrawBrush); - // ---- Modo COR (RoundedBox) ---- if (Outline) { - Outline->SetVisibility(ESlateVisibility::Collapsed); + const FSlateBrush* OlTex = bUseTheme + ? ResolveFromTheme(OutlineKey, P.PanelOutline) + : &Panels.PanelOutline; + Outline->SetBrush(OlTex ? *OlTex : NoDrawBrush); } - FLinearColor Fill; - FLinearColor OutlineColor = P.BorderColor; - switch (PanelType) + if (OutlineEffect) { - case EUIPanelType::Secondary: Fill = P.BgRaised; break; - case EUIPanelType::Tertiary: Fill = P.BgSunken; break; - case EUIPanelType::Card: Fill = P.CardSelectedBg; OutlineColor = P.CardSelectedBorder; break; - case EUIPanelType::Primary: - case EUIPanelType::None: - default: Fill = P.Bg; break; + const FSlateBrush* OeTex = bUseTheme + ? ResolveFromTheme(OutlineEffectKey, P.PanelOutlineEffect) + : &Panels.PanelOutlineEffect; + OutlineEffect->SetBrush(OeTex ? *OeTex : NoDrawBrush); } - FSlateBrush Brush = Background->Background; - Brush.TintColor = FSlateColor(Fill); - if (bRoundedBackground) - { - const float R = bUseSmallPadding ? P.CornerRadiusSmall : P.CornerRadius; - Brush.DrawAs = ESlateBrushDrawType::RoundedBox; - Brush.OutlineSettings = FSlateBrushOutlineSettings( - FVector4(R, R, R, R), FSlateColor(OutlineColor), P.BorderWidth); - Brush.OutlineSettings.RoundingType = ESlateBrushRoundingType::FixedRadius; - } - else - { - Brush.DrawAs = ESlateBrushDrawType::Box; - Brush.OutlineSettings = FSlateBrushOutlineSettings( - FVector4(0, 0, 0, 0), FSlateColor(OutlineColor), P.BorderWidth); - } - Background->SetBrush(Brush); - Background->SetPadding(bUseSmallPadding ? P.PaddingSmall : P.Padding); - BP_ApplyPanelStyle(P); } diff --git a/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.h b/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.h index 843cdca..764218e 100644 --- a/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.h +++ b/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.h @@ -23,34 +23,31 @@ class ZMMO_API UUIPanel_Base : public UCommonUserWidget GENERATED_BODY() public: - /** Tipo do painel — escolhe Bg/BgRaised/BgSunken/Card de FUIStylePanel. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel") - EUIPanelType PanelType = EUIPanelType::Primary; - - /** Arredonda o Background (RoundedBox) com a borda do tema. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel") - bool bRoundedBackground = true; - - /** Usa o padding "pequeno" do tema (PaddingSmall) em vez de Padding. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel") - bool bUseSmallPadding = false; - /** - * Conjunto de textura (modo Hyper). Se != None E houver brush no mapa - * FUIStylePanel.PanelBackground p/ esta chave, usa o painel TEXTURIZADO - * (brush bg + outline). Senão, cai no modo COR (RoundedBox acima). + * true = lê brushes da DT_UI_Styles (FUIStylePanel) — tema centralizado. + * false = ignora a DT e usa apenas `Panels` (brushes manuais no Details). */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel") - EUIPanelTexture PanelTexture = EUIPanelTexture::None; + bool bUseTheme = true; - /** - * Mapas de brush por instância — espelha o `Panels` do UI_Panel_Master - * do Hyper (Details mostra Panel Background / Panel Outline editáveis). - * Tem prioridade. Se não houver brush p/ a chave aqui, cai no tema - * (DT_UI_Styles). Em NativePreConstruct é semeado a partir do tema - * quando vazio, então por padrão o tema manda — como no Hyper. - */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel") + // ---- Modo TEMA: escolhe a chave do mapa FUIStylePanel pra cada Border. ---- + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel", + meta = (EditCondition = "bUseTheme", EditConditionHides)) + EUIPanelTexture BackgroundKey = EUIPanelTexture::None; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel", + meta = (EditCondition = "bUseTheme", EditConditionHides)) + EUIPanelTexture OutlineKey = EUIPanelTexture::None; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel", + meta = (EditCondition = "bUseTheme", EditConditionHides)) + EUIPanelTexture OutlineEffectKey = EUIPanelTexture::None; + + // ---- Modo MANUAL: 3 brushes editáveis direto no Details. ---- + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel", + meta = (EditCondition = "!bUseTheme", EditConditionHides)) FUIPanelBrushSet Panels; /** Re-resolve os tokens do tema ativo e reaplica no Background. */ @@ -75,6 +72,10 @@ protected: UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Outline; + /** Efeito sobre o contorno (glow/embossing/etc.) — pintado acima do Outline. */ + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr OutlineEffect; + private: UFUNCTION() void HandleThemeChanged(FName NewThemeId);