From 87060b77491190f146b90bb0c45fe2e09f9e52be Mon Sep 17 00:00:00 2001 From: Muhammad Ragib Hasin Date: Sun, 26 Apr 2026 09:42:12 +0600 Subject: [PATCH 1/5] Make Scrollbar collapse when not hovered --- masonry/src/widgets/scroll_bar.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/masonry/src/widgets/scroll_bar.rs b/masonry/src/widgets/scroll_bar.rs index f6b3f86a3..9f7816e0f 100644 --- a/masonry/src/widgets/scroll_bar.rs +++ b/masonry/src/widgets/scroll_bar.rs @@ -46,6 +46,7 @@ pub struct ScrollBar { pub(crate) portal_size: f64, pub(crate) content_size: f64, grab_anchor: Option, + expanded: bool, } // --- MARK: BUILDERS @@ -62,6 +63,7 @@ impl ScrollBar { portal_size, content_size, grab_anchor: None, + expanded: false, } } } @@ -338,12 +340,14 @@ impl Widget for ScrollBar { fn register_children(&mut self, _ctx: &mut RegisterCtx<'_>) {} - fn update( - &mut self, - _ctx: &mut UpdateCtx<'_>, - _props: &mut PropertiesMut<'_>, - _event: &Update, - ) { + fn update(&mut self, ctx: &mut UpdateCtx<'_>, _props: &mut PropertiesMut<'_>, event: &Update) { + match event { + Update::HoveredChanged(received) => { + self.expanded = *received; + ctx.request_paint_only(); + } + _ => {} + } } fn measure( @@ -384,12 +388,20 @@ impl Widget for ScrollBar { let edge_width = theme::SCROLLBAR_EDGE_WIDTH; let cursor_padding = theme::SCROLLBAR_PAD; let cursor_min_length = theme::SCROLLBAR_MIN_SIZE; + let scrollbar_width = theme::SCROLLBAR_WIDTH; let size = ctx.content_box_size(); - let (inset_x, inset_y) = self.axis.pack_xy(0.0, cursor_padding); + let inset_start = cursor_padding + + if self.expanded { + 0. + } else { + scrollbar_width / 2. + }; + let (inset_x0, inset_y0) = self.axis.pack_xy(0.0, inset_start); + let (inset_x1, inset_y1) = self.axis.pack_xy(0.0, cursor_padding); let cursor_rect = self .cursor_rect(size, cursor_min_length) - .inset((-inset_x, -inset_y)) + .inset((-inset_x0, -inset_y0, -inset_x1, -inset_y1)) .to_rounded_rect(radius); painter.fill(cursor_rect, theme::SCROLLBAR_COLOR).draw(); From 577e380e74a87019ae0c7d8570c595459a82915d Mon Sep 17 00:00:00 2001 From: Muhammad Ragib Hasin Date: Sun, 26 Apr 2026 11:13:26 +0600 Subject: [PATCH 2/5] Fix tests --- masonry/screenshots/scrollbar_bottom.png | Bin 213 -> 164 bytes masonry/screenshots/scrollbar_default.png | Bin 218 -> 174 bytes masonry/screenshots/scrollbar_down.png | Bin 192 -> 160 bytes masonry/screenshots/scrollbar_horizontal.png | Bin 200 -> 166 bytes .../scrollbar_horizontal_hovered.png | Bin 0 -> 200 bytes masonry/screenshots/scrollbar_hovered.png | Bin 0 -> 218 bytes masonry/src/widgets/scroll_bar.rs | 6 ++++++ 7 files changed, 6 insertions(+) create mode 100644 masonry/screenshots/scrollbar_horizontal_hovered.png create mode 100644 masonry/screenshots/scrollbar_hovered.png diff --git a/masonry/screenshots/scrollbar_bottom.png b/masonry/screenshots/scrollbar_bottom.png index bf2b4b76ffcc1ebc169592f1280b3ebd93ac5fc6..adf42f412d293d83c64f2a357fb31135399f4974 100644 GIT binary patch delta 136 zcmcc0xP)yP2upLuh`Y zkF^4fck1zZ3Mo{_?uC``WWh zvR-}XVwQQIG@W_6Mtf&ET<0gF`$lZ4i6+ypxqEi)Go5~EZeZ7P?YB#d>{#AwW&dP6 Xv-DA!)N>zq1|aZs^>bP0l+XkKn#fP; diff --git a/masonry/screenshots/scrollbar_default.png b/masonry/screenshots/scrollbar_default.png index c23e07dcec08c1834572d6aa1979dcdfba900bb5..fe787c28624f57f7cc153bfdb473dd7cca731847 100644 GIT binary patch delta 146 zcmcb`xQ=mxN`0xPi(^Q|t+%Ha3N|~4xL(}yS;Ax%-+_zle4gC5xx2P~us1F&yxD0R z9#bCYIVmweSN>D<`t#4ju0GS-bT9OKmjXu<7=7Z8T(>IwrPAe}HqWoPZjwDd&2Cbm lkG`h>iz7OEvfKRr3f@B}(r4~v;AUW8@O1TaS?83{1OPxGJ4*ln delta 190 zcmZ3-c#Cm@O8r7l7srr_TW?P<falM%PTv#B)!HGjGYXOT)$_mEL*=7P;oLH|I zoV=a#WL1A;X}`Vv<|Hn|wv`6U`)`)0Xv$9Be?`$Vh->TaFM*M7?xyaU606+mv@k$J zgsau*XTGFg>hlMBXV>NA-&q?_i(^Q|t+&?=1rI0)FdV$$>D6BR U(|t7MG6N8Jy85}Sb4q9e0M+*}{{R30 delta 164 zcmZ3$cz|(&N`1Shi(^Q|t+&?~^0qjLxCY*ebPxE#(Bw3sg-e6YnN`GreamNaN#ioN zGitNvJnygH{ySv7vjB@DGMZ4Y{&&y1;&|t_zcu%Z<+V^xuq diff --git a/masonry/screenshots/scrollbar_horizontal.png b/masonry/screenshots/scrollbar_horizontal.png index 9a2daebc48ceb209c12641b2aa364f972760cbaf..6a37404a38a3dddc46725696ee2895f3d42c06a9 100644 GIT binary patch delta 138 zcmX@XxQuavN`0=Ui(^Q|t+!V;@;U^Fv|ZfI7q~jtA)SBjC9Zt&sawC~EZ8}5Pr*K3 zzoPf@XWh931ST9`B)cpmIi52`(|uL#?AHxbv~TV#Q}BB|HQju_0zR<6@y@M3yoX+V SQ@h9j1fH&bF6)?`5}E*QhcuP| delta 172 zcmZ3+c!F_)N`1eli(^Q|t+!YAavpM!Xn44$cu}h`htf7HXSWT(k9ek)MJl_7on)E$ z@WCbrg$c?9Ezj>hXnen%%X%f(!o^(og#idWUHx3vIVCg! E07be&jsO4v diff --git a/masonry/screenshots/scrollbar_horizontal_hovered.png b/masonry/screenshots/scrollbar_horizontal_hovered.png new file mode 100644 index 0000000000000000000000000000000000000000..9a2daebc48ceb209c12641b2aa364f972760cbaf GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^CxF<914uB~Se>>7QvIGTjv*Dd-d^3ydB{Pc;o+L% zMXkadO53cQ-8KY2;+a+!sq7kdl4a(@2b&xeCMXxQJiq&(@%?fx>y=y&YwFj%E3}-( zWO>E+_WE_d_vW`cysG!H?7g2YIYDjNp0;qw31+Ec_UZzQx1QZ(y zoAPe0as9@!xa&K&njc-W>dd_mdKI;Vst08d?2 Apa1{> literal 0 HcmV?d00001 diff --git a/masonry/src/widgets/scroll_bar.rs b/masonry/src/widgets/scroll_bar.rs index 9f7816e0f..d5a88f7ae 100644 --- a/masonry/src/widgets/scroll_bar.rs +++ b/masonry/src/widgets/scroll_bar.rs @@ -493,6 +493,9 @@ mod tests { assert_render_snapshot!(harness, "scrollbar_default"); + harness.mouse_move((5., 50.)); + assert_render_snapshot!(harness, "scrollbar_hovered"); + assert!(harness.pop_action_erased().is_none()); harness.mouse_click_on(scrollbar_id, None); @@ -520,6 +523,9 @@ mod tests { assert_render_snapshot!(harness, "scrollbar_horizontal"); + harness.mouse_move((50., 5.)); + assert_render_snapshot!(harness, "scrollbar_horizontal_hovered"); + assert!(harness.pop_action_erased().is_none()); harness.mouse_click_on(scrollbar_id, None); From efb86fd70abf175f66bc7d36b4a19f12e5d68641 Mon Sep 17 00:00:00 2001 From: Muhammad Ragib Hasin Date: Sun, 26 Apr 2026 15:34:31 +0600 Subject: [PATCH 3/5] Fix Portal tests --- .../portal_button_list_no_scroll.png | Bin 2230 -> 2143 bytes .../portal_button_list_scroll_to_item_13.png | Bin 2509 -> 2415 bytes .../portal_button_list_scroll_to_item_3.png | Bin 2418 -> 2328 bytes .../portal_button_list_scrolled.png | Bin 2418 -> 2328 bytes .../portal_scrolled_button_into_view.png | Bin 1383 -> 1315 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/masonry/screenshots/portal_button_list_no_scroll.png b/masonry/screenshots/portal_button_list_no_scroll.png index 75c491540128067891d081bbb8d09a1d729a222e..c06bc7ec836b6b3bed407e8b9a4fbff56ecf9420 100644 GIT binary patch literal 2143 zcmZ{l3sh3+8ivWiftoBer;Eo~|-PfyRSTf(WSsr&cuM?^$>Nl9r%#kuqbXM$H&KGVh+LKj*gCqefy9I1VZg9K0dy< zxEP64zoPhjK~obyCnv9}>hhU0#YIKMXU~?*&CNwd9*l~LPE1Uu(PDYL#_8$l-rh&U z!z1nO9Z#P=%gf6*GlN-K**H2nIXEEf>>O-u?JX> z8H#)c9$Pg1^y;@5@7*zrbt)C{m&8ObPwygw$dd0}x&Jgqi1Y>Aw)*-sQCs&?pn-JA zzC>RT8BLZw`u_Haaw5<`HpKC1kYr8NTvc*am9$D~V8 zwN9z^7V;+0#?~EoVmjmrZ@y(u{`TE+;0ey$Qo*2b_LDsM?1{>N0LnbD=?nx8Lg50W zVT`^x-0Y)D*Y%5Opr$lPs0>SzH@(j-N8qW@Tz=}Y1t2s-s@4&hcEiD%f{TR`&u{2dCH|jL%40p=} z?v3?We_v^BtIJmk&+#t^S*W`Q0WsGeL#5WU3@&YSwK&HC;7lW#fV+8ecJ-~;Zm?XXyqe(iCd`_ z#q)D6WPSCgHmBMCPyV_&B92JuPZe+2p!@Fx!1-Twnl%2RwsNx^+B>v2*7~E(N1M|X z+23R|A2Ws#A4C{+XG084I2aGU(Ib-{IWUJ6P8OqUbyCbta;|oQ1(&T1(WXU4eOg;e zVzpr*+N)uqNn7ZXmyIhQOuyUQ9?}@nKaZd^WStuTvmh++&V2Rbo<2{YL#dZj6uaHe zo6+Mb@b_~=Q1>4pySn_L(m`{u{sbYoH!1WYB!MTeokb={s_N}Y@}tqR1v`vZXzbx3 z>)}1_Kbp~ZR|PXP;^Ewu9nfSNTzf6Enx2kZeDPET>d+=r%Ql&jygjEnBo%*m%3xN% zZjRI-d8{IEO@-wM1W?k85Lh#DU5AG4Fu-Mm!FX(gZ)f$C}z*y^- z-$s%X(y)bmWw{;ZQ{w}kVENP%%tH;ThShaZuL~x%Zg{6SdU1Gh_>F8el<{A}{>`d z9nUk#8GZU{ST(RFXar$qzu(5-i@VP6+B$n)@RML$dgNxEKjKxdn!e`Cwsgbv3%KzI zYyNk3{CI-u`O(}9z=A}BdK%+X#5qU;E2+Qjow1puTv$gK{YME;uqcQd4%**NVw;fU zJX+(1xx?+dGy`*vKMgI9ehvIGC+~Mw+l)Zn*=BTl9)%lFQ3TeXBm~nHul!euPAr$( zl8Qsq!52a4wJZOX`kzCOl29eoe-)5z)a2-lOh6)?W_ZQtqNBzT9_Q`C!-lhd2W*kq AmjD0& delta 2098 zcmYk4dpy(oAIFEW>GEqtb)phFve`mWSewnZYzT9UIU{%F5<+h0i=TUxRjBy2U$RQI zjbZ(wsB|*-CWb{jjL9XpbK-Y6^4sc?_z{BGV0)a|P1K@Dv{reA= zmX-(v5}EAy_H7#qW#;L5W_*0Spx`o@>;M4iCr;QbE-uPsQ$(VDN=mAU$q_uBh{53e z{R6$dX(Ey6`STYM5mEE=!2H##jMuMQ?d>UTZEXOM5f*lCaBygSeSK|h?f7x)sHh7W z8UGv`8;g%mluD)b^$n@1sdw(&F*7qGkw~Vdrh$Qha5%iItZaIEx}u_jNF;vx^y%Wo zgsG`1KR-VPgF&a$$H%4F*}3oE_cu4UBqt}MP$(N4Oi4)z9*+n3{HL9rUAeh=eSLkt zzVy-2QAF(Xio}M0KW0Tg_Rx}!GXGdbQIWse}iHS)p)`OQXU!6KddHAq8 zARs6wC-=-5pRTU%+S=Ow{(+}Y|1&W$K_s5?^E=zn0PqC@frp1zaWNw{_M(@UcU)XN z0)dptWN+TQIeRuRBqR)vCm@mXe-3tVa56VP78De6^r)qk6&8gux3I7}cI-G3dF13t zJ2)J*yu6IX5)U6n7#YE_Sdy3LBoPEsJ3uDj+%JjeMmtZtxoV}4sX$<0Z5N!ombNxG z95`kcelR=3`obgUyH^Th`~xb5`CCYVhOQb*nX!n$*y+jo(UXrCREKwf)SK>JUfirZ zy;^1&SvTnYk@QUO7n8`k0Bb?ggb#q;dzmQRSRp!L%GHC*vu{N-=cvRZF|ef#f$d0g z|E@RHc%=T7FC>GnBt{r_YI0dM=1K9C8_>yaz8QV_OA@_Udy1zNI^*uq5dE05{QM%R zqO;2CJugGcE(T>1dp}sknEVqw=6*Uz8LFpnO1yI0m4WtMY4(L^Z{;8(+{$sm&)d^d z&w+%>0q2#2PT}?znL!Te?pt3E^`*+hlRf$E4F;e6T2!d*i2_zFQ`+*YksBbJDLJ}1 zC7ZQrE&@?;#rIBP2|lzCYKZFu(;G(a>Q<&&6k6#mhMLs~ z>Vo0YK>b({Y`dP5k#jsbZhdLz?p{dxVUSh-NRrPrx1#O4886_T}cV985!o=`QxxtU6E@Na+VVXbbwmpfKAXOgSqC9N?eJ zuq281tOc3et_|C-`|_T}rFyj;%N(51CPi5=U?q9NTV`<@UZniaEm zoL~8n?Rm|tBcZhj(E@qPF{am?BJfC9pIO;)-5&hJtZ099V;(a6y`0;x){m?)=`JhFvB6sUtEZI*n3%7$=IF zbE=M_J=Mc;>L>TkB2@F^SdsJj*V{JQ3*uOxT}wxwXc@jM;Xg~X$pPobvXHC1nS4FB zu(p-(XP<2f{K9GUO$19gmn+vgW#Xm%4duX7t(HE1K&Y2QdM6qH-=d_fh`!F&COQ+B z5Rua}4YONkM1TF`7L}&sBNqGY<$L}Yi6{`QtcVJ>;P7%>um)I8TxGcb(Dt{jqE|N! zz=1V83&}K767$tfq6+B$M+Q~1QM36ulEfr2LxgnQ-+xo0vonCQUTCIXCR9I9;RJ8I z#IZWO#LOG_KDpNTE_A_h`l`eIo1Vpe3d~=HR2;?aZ_0u?sF_pO#^N2|uno zGpn&LVa2g+-r|6&dK`ZbDBbnAISd0CM5)_IZ64jJ~z&!Skx}D{RiMgMZ(>eGXUY8}1PAbQH3dTD^{Rknf#*0bzveCQ#koF@VyZ Ws)1h;(xCH-`H_jI2^_qC=Klah;lMut diff --git a/masonry/screenshots/portal_button_list_scroll_to_item_13.png b/masonry/screenshots/portal_button_list_scroll_to_item_13.png index bbe3932f7fa66bd9f4a5b36b77efb35bbcab60b1..6319d828c65b2ea1285ad66ef4d0102517e36d7a 100644 GIT binary patch delta 2083 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 diff --git a/masonry/screenshots/portal_button_list_scroll_to_item_3.png b/masonry/screenshots/portal_button_list_scroll_to_item_3.png index fd73e8bf1b8fd2d5f3bb97869c4ed4c87802dd37..7b09e0d85dbe97b82263c2936589640e637e515a 100644 GIT binary patch delta 2040 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 Bv<(0N diff --git a/masonry/screenshots/portal_button_list_scrolled.png b/masonry/screenshots/portal_button_list_scrolled.png index fd73e8bf1b8fd2d5f3bb97869c4ed4c87802dd37..7b09e0d85dbe97b82263c2936589640e637e515a 100644 GIT binary patch delta 2040 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 Bv<(0N diff --git a/masonry/screenshots/portal_scrolled_button_into_view.png b/masonry/screenshots/portal_scrolled_button_into_view.png index da95ffb49b6c85b93460f3e860ec7139da1aceec..93d61f49f4857daa4f98c89a0480e240d5c9d149 100644 GIT binary patch delta 1176 zcmaiyX*k;l0Eb(%&Wv8#vPV1fREtWGv~Fq=(i|jn1XbpU+uSNzN&j=ixkwx#&XmNp zEg6aw)u5_cT2B*3s3(alC8@L8hiP|x?3cYCeqY}YzgMo#R;L>r)+@e%13(}%6BDpR zB9+VK0>Oa0yB89PURzUexjcXWz=eecDD+B01ABdaeQ9Y43^p$>XWH1FMeE`UX7S$I}x} zqZLb~QkhI95{Ym)cQ%_Nkx0a1ad0p}B$|wi0}>`CCJG9EHZ_H;tjHT1n=CD@YisMq z#>R$+hdVpF#>by0BqTy0SK8a#U0t!m!;hVu;N|5Nbb2ua0wIw|2L}fP0)axI1Ox=y z*x1~^e;&EER#vtyE=a?f z8L>j42nh*AAW);DPnVaM0U%>wU=WRV^YFl9FmAcIdB(<|Qgan^;Kl99fJ28=1Sn@G zue8}Wvr`^topf?!GF@N)lV?;^)lM$_bJKqm^%<&Wg8hS{Q;lHDsz;gJUjJ^eI0LZXfiVIQ2t++SW5QmH0pbX>XqkdD{=jcly)gHOTV;R|9>l^nvW-Fg5-E z8|d-U=6#ZwBJi^fA5rB}UJjzsVMUC;V(Jmxl{{Twuo^lTJ(hp@_d zJ#so~gZX7irzy^_M9~EsLJJVDtVKb&UO+3?6x9W@7w^$vdh$M5o8z9N!T14>yc2r8 zy1EJeMHmHy#ZEro$E6~vWls#m*HbU>cq?!oZ2n?c@X3X zw=-};V$BPV$3Xkw#2I{C9<^cQ5^(> zR%$_?jgOXtqe4ZO_uy*)a`{n(SpTN*NP0{^1VC>0p69f2;vS=S0`o47qdIOS3&?zU z6>b^S@(ZX9Zi@(u!*2QMYZ8mTsr(*a{JMZX(#$p9WsqU&K~IW2X*c*`xdwGLm){3h zD(Qc|7xI5(oNmY@DI`_8{jpj@!dJXlcX-y$zL=Kufz delta 1245 zcmaLLeK^wz0KoC-#YG9d%-nTK<>{1dEQVa&?M*gmV^*qZSedIETRFCrpP85G!eq@` z3|osc3yp_e42@LeY$ptjQSQv4a(a1G*Z=q2eg67BU#azswQ#qEcb*FoFd8Q^GX>9| zKkw;z=l^OfYrS~!r?Rs0 z{QMgo9i1}St%?dM8jU5BDemt3V`9z~78c3nx3O6Nwl>Yk$OwgUoXL!nNXoObbFN>{ zHy8}5seB%fKRP;EQBgTGG!z%dK6Wguv{W)TH`mzMG&u=O8jVJ&R7NBQ>-Bm#96_g_ zjf|x0^}m;vmOXy_I5w80)oOct`&cY?e}DhgtJjc7pIf)&FJ8>RU>+(}V|;u+bH3h=|hZbSM-?D9pQZ1%SihY&N^8sfop6B_wcoJYH2*6%vUg5C~pgUMVRl z-(9|(j7FoOQ0U^~A|4;;>-(*jmyfsi0S<=~6cohg^NBzSFfa%Jcp{Og zrKQEy)y-@+=R;Px1C4!uGEP}{fs?8D224>h&-Too5v~$o_4_hj-1N2={0Ff=vo>PAVRUxZU|2hDo`WDt zG7lVBTWX%))OQ!qDq~T`OYi#{K02X{(U!dpJjd3|BPD0`8zR?XAf0v@?rF~dlZYDm zBe$Ag?3ZfQI=31HI9^#vo&DyH-I7wJ(8y#OMd)v>8ShP|_m&@b;jsS(d|t@T`})Cw zqs$y-nNzA-J~kGlF{W zD5Pwu#Zx+KODO$|^9}T#O=&BRtNjnxaqepx?GdhoL-1|7;tP;EAE8asKp@?D^UqX! zeYHNN>m^-nbwZGU0anDT7f(l9aZfFM>9gsRO$vExPr29l5I!E1tbNv!Wc&8-i%40V z=T5{4tB1YIwuSCLc&tgklOp}I4^-!8!;zd3p|@oqeYZZ_9*T}S-ldhN;hi(m6n)EI z-Bq!MH7naqJCKnKi`_}rJK1JSnb0fw5NGq-V5_Xvl~|hVZXgziu-jX#YTn6rc;dMK zDF(4sQke8wz0K5U0B9l$g22JfiH)~@2YVEyf~3CYX1#0?y4*ZC#Smib=IgvQ6CKom zuoaxEEH0)dVPn#?Wjo_^fC^FsN{?$_Jl^BX^Zj1%2IC@g7?%H7Ya6u@Bt@b_$d_!t z-tsAZM;H~bO82O)$6Gm$Nu~q5Cr|p_g0`4B=qSvfe&|8$hwnCYTE}GR$9=l@+@ioka2MDtA;FLc&_B-LBxX` sVtB31cAt$pLrTsrGSjC1-HFIZz@i$wq#%fvZRDWO@VKK`B|0MYA5 Date: Wed, 29 Apr 2026 06:13:09 +0600 Subject: [PATCH 4/5] Use ctx methods for hover check and grab handle --- masonry/src/widgets/scroll_bar.rs | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/masonry/src/widgets/scroll_bar.rs b/masonry/src/widgets/scroll_bar.rs index d5a88f7ae..9760b6fac 100644 --- a/masonry/src/widgets/scroll_bar.rs +++ b/masonry/src/widgets/scroll_bar.rs @@ -9,7 +9,7 @@ use crate::core::keyboard::{Key, KeyState, NamedKey}; use crate::core::{ AccessCtx, AccessEvent, AllowRawMut, ChildrenIds, EventCtx, LayoutCtx, MeasureCtx, NoAction, PaintCtx, PointerButtonEvent, PointerEvent, PointerUpdate, PropertiesMut, PropertiesRef, - RegisterCtx, TextEvent, Update, UpdateCtx, Widget, WidgetId, WidgetMut, + RegisterCtx, TextEvent, UpdateCtx, Widget, WidgetId, WidgetMut, }; use crate::imaging::Painter; use crate::kurbo::{Axis, Point, Rect, Size, Stroke}; @@ -46,7 +46,6 @@ pub struct ScrollBar { pub(crate) portal_size: f64, pub(crate) content_size: f64, grab_anchor: Option, - expanded: bool, } // --- MARK: BUILDERS @@ -63,7 +62,6 @@ impl ScrollBar { portal_size, content_size, grab_anchor: None, - expanded: false, } } } @@ -340,16 +338,6 @@ impl Widget for ScrollBar { fn register_children(&mut self, _ctx: &mut RegisterCtx<'_>) {} - fn update(&mut self, ctx: &mut UpdateCtx<'_>, _props: &mut PropertiesMut<'_>, event: &Update) { - match event { - Update::HoveredChanged(received) => { - self.expanded = *received; - ctx.request_paint_only(); - } - _ => {} - } - } - fn measure( &mut self, _ctx: &mut MeasureCtx<'_>, @@ -391,12 +379,11 @@ impl Widget for ScrollBar { let scrollbar_width = theme::SCROLLBAR_WIDTH; let size = ctx.content_box_size(); - let inset_start = cursor_padding - + if self.expanded { - 0. - } else { - scrollbar_width / 2. - }; + let inset_start = if ctx.is_hovered() || self.grab_anchor.is_some() { + cursor_padding + } else { + cursor_padding + scrollbar_width / 2. + }; let (inset_x0, inset_y0) = self.axis.pack_xy(0.0, inset_start); let (inset_x1, inset_y1) = self.axis.pack_xy(0.0, cursor_padding); let cursor_rect = self From c5201e63c33e42fb0f2ff5cb523564ad1ca49fb1 Mon Sep 17 00:00:00 2001 From: Muhammad Ragib Hasin Date: Sun, 26 Apr 2026 11:13:26 +0600 Subject: [PATCH 5/5] Make Scrollbar auto-hideable --- .../portal_button_list_mouse_jiggle.png | Bin 0 -> 2143 bytes .../portal_button_list_no_scroll.png | Bin 2143 -> 1964 bytes .../portal_scrolled_button_into_view.png | Bin 1315 -> 1166 bytes masonry/src/widgets/portal.rs | 47 ++++++++++++++++++ masonry/src/widgets/scroll_bar.rs | 46 +++++++++++++++-- masonry/src/widgets/variable_label.rs | 10 ++++ 6 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 masonry/screenshots/portal_button_list_mouse_jiggle.png diff --git a/masonry/screenshots/portal_button_list_mouse_jiggle.png b/masonry/screenshots/portal_button_list_mouse_jiggle.png new file mode 100644 index 0000000000000000000000000000000000000000..c06bc7ec836b6b3bed407e8b9a4fbff56ecf9420 GIT binary patch literal 2143 zcmZ{l3sh3+8ivWiftoBer;Eo~|-PfyRSTf(WSsr&cuM?^$>Nl9r%#kuqbXM$H&KGVh+LKj*gCqefy9I1VZg9K0dy< zxEP64zoPhjK~obyCnv9}>hhU0#YIKMXU~?*&CNwd9*l~LPE1Uu(PDYL#_8$l-rh&U z!z1nO9Z#P=%gf6*GlN-K**H2nIXEEf>>O-u?JX> z8H#)c9$Pg1^y;@5@7*zrbt)C{m&8ObPwygw$dd0}x&Jgqi1Y>Aw)*-sQCs&?pn-JA zzC>RT8BLZw`u_Haaw5<`HpKC1kYr8NTvc*am9$D~V8 zwN9z^7V;+0#?~EoVmjmrZ@y(u{`TE+;0ey$Qo*2b_LDsM?1{>N0LnbD=?nx8Lg50W zVT`^x-0Y)D*Y%5Opr$lPs0>SzH@(j-N8qW@Tz=}Y1t2s-s@4&hcEiD%f{TR`&u{2dCH|jL%40p=} z?v3?We_v^BtIJmk&+#t^S*W`Q0WsGeL#5WU3@&YSwK&HC;7lW#fV+8ecJ-~;Zm?XXyqe(iCd`_ z#q)D6WPSCgHmBMCPyV_&B92JuPZe+2p!@Fx!1-Twnl%2RwsNx^+B>v2*7~E(N1M|X z+23R|A2Ws#A4C{+XG084I2aGU(Ib-{IWUJ6P8OqUbyCbta;|oQ1(&T1(WXU4eOg;e zVzpr*+N)uqNn7ZXmyIhQOuyUQ9?}@nKaZd^WStuTvmh++&V2Rbo<2{YL#dZj6uaHe zo6+Mb@b_~=Q1>4pySn_L(m`{u{sbYoH!1WYB!MTeokb={s_N}Y@}tqR1v`vZXzbx3 z>)}1_Kbp~ZR|PXP;^Ewu9nfSNTzf6Enx2kZeDPET>d+=r%Ql&jygjEnBo%*m%3xN% zZjRI-d8{IEO@-wM1W?k85Lh#DU5AG4Fu-Mm!FX(gZ)f$C}z*y^- z-$s%X(y)bmWw{;ZQ{w}kVENP%%tH;ThShaZuL~x%Zg{6SdU1Gh_>F8el<{A}{>`d z9nUk#8GZU{ST(RFXar$qzu(5-i@VP6+B$n)@RML$dgNxEKjKxdn!e`Cwsgbv3%KzI zYyNk3{CI-u`O(}9z=A}BdK%+X#5qU;E2+Qjow1puTv$gK{YME;uqcQd4%**NVw;fU zJX+(1xx?+dGy`*vKMgI9ehvIGC+~Mw+l)Zn*=BTl9)%lFQ3TeXBm~nHul!euPAr$( zl8Qsq!52a4wJZOX`kzCOl29eoe-)5z)a2-lOh6)?W_ZQtqNBzT9_Q`C!-lhd2W*kq AmjD0& literal 0 HcmV?d00001 diff --git a/masonry/screenshots/portal_button_list_no_scroll.png b/masonry/screenshots/portal_button_list_no_scroll.png index c06bc7ec836b6b3bed407e8b9a4fbff56ecf9420..d0fb071918e7e6d4ef1acb411eb1f9c47576e986 100644 GIT binary patch literal 1964 zcmaJ>c{r478=vzv%GQaxTy;vF_b|y|Mz%86hMBR8$=Jpb2~CW$mnE4mYhw6{I$>}S z%05Q65Qni08T-T;>)2xy%93y1I{o>5&vm`eeO>qSF2DD;+;LWxMu$aDib5ce!zRWU zJOuI+6}*HG2s+iSZC@dfe@R%G+hE~vxV}E_+&Kk#dByehbp-{auC5^hp{%ZcQ9}cT zM5>}t`q!^}sH&=)nOV5Gxf>W@!owpxJZ`wUx+y9mcsyQfYpcJ1poxh&5~*TqYwzOX zhQ%7q%*<3*zaAJEEH5u75bT&tW>ppA>Q&bdA3j{WcD=isWpD2o9UYUDlpGvH$c�`V zYiMYel$4H-kB^Lu78Skh?d{dqH%Lu=YHn_c#}l-)bS_*#EiNvWmX>k36RWGM6&025 z-;X&uIwdEk-i%&1U!Yv7Mb= zVq#+T^e#C$IX`-o7!neut*sXo74zmzV@F44fB(ST++09Fkg2J~{QUgn_r%wzj6H zht|~8j)=H#Vqz8_|8Qz*ib_pxYisxO3$U|uz+iBEK0iPIg@OV?Q4xtioF@?M2L}i5 z-VL3dowcyI{OVP4V`Gz=+65gQ165TGeSNIREFT7e2-%rn&^C8C%j3i=-M-T8zTsPq z@6RZ-WBgLD%c;1vop{`QK3dGFc0%&@LZV6!3g%~=orC9aUc{cckE`qd%BpuhGP$1_ z#L3v7+ai7>y1lU>*=L)=xv!)DaO{?fe`cR;UQQ)`689qc{&tGcft1G^KC1Dyfj|FP zNYMUbU{g%plJ?5CNhp#s%XDMes9IN<|K)KL6<4wrdY7~c*MwYQ8CE{7q`os!u+drFq$cu@r32&{CZD9`4? zuOUfs_RrPD1fBOO2!0Fz7g@hIbp;c49CjF;&u#c6zN0VcSLgapu@nlxm5brF4UBXz zccCMGyVrG8fLa10@eO^w=^_EIOnff;RQANO3)|yUN2A)qgMxJaSlyE1LE=H?oCLi` zdaRt&(i0O9Bd>|IU%)r~!vWR)Ybqnz%o7<*I}SsnQBK$N^|S9S7XTy?zg^x&EMtZN zz-#izVdrHm122fBWD+?tC7U};(>?^5KjBjfz-#kXruIU=u2={<$9pnA>cX(q^xqe> zGbCZU)pQHi){!synK?wx&u7g-pnPH~KoO<@6d)E5o+98&Q$j{L(#_J%F73IjT-N-t z${Rnx3u+VC2T$hv=lhjT9y+Aycp#=1d|X&2RTPruoG*$_&KQx%`q6u;b? z6NR?N$XtoI9Ln#me5)HA)lLQuQcsA znY`WCNaHL;@<(MH2O8&>SA4r*(-gcn|0NNBlSvd3@yjFog_SK~dJ`=u&>D6s@~YNW zLq%_JEVlxb5Gn~3cu*oKCFJoy%MeePCwx#_jx0yU(Vk+#G5ceIeeeWF!_hqY?)cuf z^O~4d14;bOMA^0?IqJaOBtRvRBGoCTftC?kSRGLWu^r=jKt%%b$oX2yU@e~ZJ_>vH zgwd8-0VJ2uGdiQrQ69Wz+pdX>HYt)Rt+j04v8ie$K|H=86V%o}bI+XJD62wIP?YAM z|2MW1;eTMK|N6ZON09``g_@I@+_MlM(wEw+( zPj`znkp)D3H|BMA!gdK;ynf4GtX+??Jo;)+ofElL)l`FUQI^qQ!)^UT4a}~*XoQ%3@-3TFp z@+JRL KOH7G@3+2CdG?T#q literal 2143 zcmZ{l3sh3+8ivWiftoBer;Eo~|-PfyRSTf(WSsr&cuM?^$>Nl9r%#kuqbXM$H&KGVh+LKj*gCqefy9I1VZg9K0dy< zxEP64zoPhjK~obyCnv9}>hhU0#YIKMXU~?*&CNwd9*l~LPE1Uu(PDYL#_8$l-rh&U z!z1nO9Z#P=%gf6*GlN-K**H2nIXEEf>>O-u?JX> z8H#)c9$Pg1^y;@5@7*zrbt)C{m&8ObPwygw$dd0}x&Jgqi1Y>Aw)*-sQCs&?pn-JA zzC>RT8BLZw`u_Haaw5<`HpKC1kYr8NTvc*am9$D~V8 zwN9z^7V;+0#?~EoVmjmrZ@y(u{`TE+;0ey$Qo*2b_LDsM?1{>N0LnbD=?nx8Lg50W zVT`^x-0Y)D*Y%5Opr$lPs0>SzH@(j-N8qW@Tz=}Y1t2s-s@4&hcEiD%f{TR`&u{2dCH|jL%40p=} z?v3?We_v^BtIJmk&+#t^S*W`Q0WsGeL#5WU3@&YSwK&HC;7lW#fV+8ecJ-~;Zm?XXyqe(iCd`_ z#q)D6WPSCgHmBMCPyV_&B92JuPZe+2p!@Fx!1-Twnl%2RwsNx^+B>v2*7~E(N1M|X z+23R|A2Ws#A4C{+XG084I2aGU(Ib-{IWUJ6P8OqUbyCbta;|oQ1(&T1(WXU4eOg;e zVzpr*+N)uqNn7ZXmyIhQOuyUQ9?}@nKaZd^WStuTvmh++&V2Rbo<2{YL#dZj6uaHe zo6+Mb@b_~=Q1>4pySn_L(m`{u{sbYoH!1WYB!MTeokb={s_N}Y@}tqR1v`vZXzbx3 z>)}1_Kbp~ZR|PXP;^Ewu9nfSNTzf6Enx2kZeDPET>d+=r%Ql&jygjEnBo%*m%3xN% zZjRI-d8{IEO@-wM1W?k85Lh#DU5AG4Fu-Mm!FX(gZ)f$C}z*y^- z-$s%X(y)bmWw{;ZQ{w}kVENP%%tH;ThShaZuL~x%Zg{6SdU1Gh_>F8el<{A}{>`d z9nUk#8GZU{ST(RFXar$qzu(5-i@VP6+B$n)@RML$dgNxEKjKxdn!e`Cwsgbv3%KzI zYyNk3{CI-u`O(}9z=A}BdK%+X#5qU;E2+Qjow1puTv$gK{YME;uqcQd4%**NVw;fU zJX+(1xx?+dGy`*vKMgI9ehvIGC+~Mw+l)Zn*=BTl9)%lFQ3TeXBm~nHul!euPAr$( zl8Qsq!52a4wJZOX`kzCOl29eoe-)5z)a2-lOh6)?W_ZQtqNBzT9_Q`C!-lhd2W*kq AmjD0& diff --git a/masonry/screenshots/portal_scrolled_button_into_view.png b/masonry/screenshots/portal_scrolled_button_into_view.png index 93d61f49f4857daa4f98c89a0480e240d5c9d149..b91bc77208da940a6f8071679102589c47298fb4 100644 GIT binary patch delta 1146 zcmV-=1cm#f3XTboBYy$uP)t-sDJdyGKSn1fD)RF3P*74^TU|XpKf=Ppsi~^n-Q9$Q zhRw~+l$4c4MMtu-v#_wSadC6;@$o7uEjTzk*VovGhl$qK*5BXYhlhwuOHE2jO@DuZ z<>lqb$jL@VNn&DTH8nWv>+5T4ZAnQ>@bK_CIXqxsVtjmlJAXSqU0q*6K|@+vT#%5F z)z#I~($bZcmc+!xc6NAueSfjBu`w|;LPA8Lp`)dxrZzS>=H}-vE-|#UwQg>3#KgqB zyuEXCb*&aBB*>rSvZEbI#pP*x7 zWiKx>L_|iuzQ4i2!c|pRn3$QSrl-rx%blH`XlQDvsHw5BvVnnvf`WrEFf*j2r8YJ> znwp$)a&)=5y5r;H(b3YDmX}9IN@iwgOiWJi@9&nDn18yuyDcp+Nc%zrVoe=jSUcEi5cADJd*u zWM(TXE~lrcySuzRJVGrkFElhaH8nXsJwrJ;KQ1mZDk?5AGBrOxMJOmMDSqrb0007D zNklE{o{8u>}XLv2<_V|`Y3LWUDVRJC7v*<2|}dt0PCU2ZcZu5?X&)xj|< zr0+|AU&p8!66$IA7Q3;*35R|4pCNHZxn9-Cw6AMKmoLc-*^rs89XTtlkUVM77&k-m zzPCxmMurntWbFD4che`CrhB_PuUrj>q<^f-Iy@MX6_O&248Ji$HW^|4P7Kkty6)ai zh4tLKn;8m+Xe91PZOwz3kkQ_^W=MYNR_7e{p^sAftdId?s7IFZ?9{aihvbw=MQQh~ zkX-4BwY63mWs=;^2x;xe-L~EM9il?xt1RxWigGI?`AqkR{DKlIWYNIj<1DusGIO@) z-1*APx# M07*qoM6N<$f*3zFjsO4v delta 1297 zcmaixX;_j60Dzfu&0P0od#pL1T8aY3mlqx}L5M>IQL;Sd0h;E#O|77B9*EiyQZY1@ zytNG{UX*00Sy>)EL7wFUXi6S=bv-)E?b*M6?fvooeSf^UTPqC(M?gIjs+%_s3WcK4 zcr!DL)zwuhmFDLcU~X=SM&q2F(H0g61Oh;z(9X_S8ygfHZe?w4hr^wUi%VEpS+THy zTUsKR%&?J>Q7$)ocz75Fv$C{=tJNBvPNz_GdU|>j2&B2Wd5NUK-#>72auNWnD=I1% z7Z+c>dIbmJ#|sNZD3lYM4Tgn<$flarGH00;^S85$ZYE-q0jmCv3jr>3Uv z+-Z-GPqei~`S|$AWHOb$)C@o{jxtn6lfzEC78s;Q|B4?ka5SHG~ZaQgI_ z(o%72Yuk$#FJfZiMn*=;%PZ(~A1^ODpMOoG(P*_=l}bgScvejJ?wBofJDu{Jg~7z_r7!wCooM4?c(Z{Ma+C@j{MrKKfD$CF7(DW^`6e0}|w zmzRk|a%5ywd%N7m#y&kg!_LkDha-%Rj?K@{hlDWkcw%qw>`oeX*4>S z?9Sz8L7^~vd&k<^Iyor6+t?^=YHBVh5Dp9sW@KbeOiVaAVe0E|b$8zf0GrLtO}Si7 zrBVkkJr96D^b|x_7w?quH{(OJLoNq1wOZpd{(BDS=^N~s{CDYp zY-3LZoDB4_GtJnAG5B8ryYg$-SzczwSam_f)dTJ~LiAH-zccdC89~47LLfiKW?kj) z0C&durX;dTw0{nYM$P#pGub)3*~GR=I4yPe*bI~C?^dPv6Y#KWV!z2Bo!LCQLwgTb zcV_3<9@@L;sY&VH{wTv-fHC+!Lx7gh$jw_CW@U~T2e-b3&)6K22f?|G=TldtX%c++ zs(~=YnWFKXkZaR5X4rD)7Tmx zW1^F58_{d%qx~BV?6=IbH*VBmzYXQUJUpHctW%N*yn={hXH9KPu2uuh;4_}3vXLQ7 zUSOSdXpYA(p*1^+qM{~C)e*;|E+n!#oWrvHbbjJs#hZlqCVZdO<@d!*KH4=R*ikp= zH*TC)-1^wC-h! { scrollbar_horizontal_visible: bool, scrollbar_vertical: WidgetPod, scrollbar_vertical_visible: bool, + nanos_since_last_pointer_move: Option, } // --- MARK: BUILDERS @@ -72,6 +76,7 @@ impl Portal { scrollbar_horizontal_visible: false, scrollbar_vertical: WidgetPod::new(ScrollBar::new(Axis::Vertical, 0.0, 0.0)), scrollbar_vertical_visible: false, + nanos_since_last_pointer_move: None, } } @@ -441,6 +446,19 @@ impl Widget for Portal { ctx.set_handled(); }; } + PointerEvent::Move(_) => { + let f = |mut bar: WidgetMut<'_, ScrollBar>| { + if bar.widget.opacity.value() == 0. { + bar.widget.opacity.move_to(1., SCROLLBAR_ANIM_OVER_MILLIS); + bar.ctx.request_anim_frame(); + } + }; + ctx.mutate_child_later(&mut self.scrollbar_horizontal, f); + ctx.mutate_child_later(&mut self.scrollbar_vertical, f); + + self.nanos_since_last_pointer_move = Some(0); + ctx.request_anim_frame(); + } _ => (), } @@ -620,6 +638,31 @@ impl Widget for Portal { } } + fn on_anim_frame( + &mut self, + ctx: &mut UpdateCtx<'_>, + _props: &mut PropertiesMut<'_>, + interval: u64, + ) { + if let Some(mut since_last_move) = self.nanos_since_last_pointer_move.take() { + since_last_move += interval; + + // TODO: make this configurable or move to theme + const VISIBILITY_TIMEOUT: u64 = 400_000_000; + if since_last_move >= VISIBILITY_TIMEOUT { + let f = |mut bar: WidgetMut<'_, ScrollBar>| { + bar.widget.opacity.move_to(0., SCROLLBAR_ANIM_OVER_MILLIS); + bar.ctx.request_anim_frame(); + }; + ctx.mutate_child_later(&mut self.scrollbar_horizontal, f); + ctx.mutate_child_later(&mut self.scrollbar_vertical, f); + } else { + self.nanos_since_last_pointer_move = Some(since_last_move); + ctx.request_anim_frame(); + } + } + } + fn register_children(&mut self, ctx: &mut RegisterCtx<'_>) { ctx.register_child(&mut self.child); ctx.register_child(&mut self.scrollbar_horizontal); @@ -927,6 +970,10 @@ mod tests { assert_render_snapshot!(harness, "portal_button_list_no_scroll"); + harness.mouse_move((200., 200.)); + harness.animate_ms(300); + assert_render_snapshot!(harness, "portal_button_list_mouse_jiggle"); + harness.edit_root_widget(|mut portal| { Portal::set_viewport_pos(&mut portal, Point::new(0.0, 130.0)) }); diff --git a/masonry/src/widgets/scroll_bar.rs b/masonry/src/widgets/scroll_bar.rs index 9760b6fac..4305c90b4 100644 --- a/masonry/src/widgets/scroll_bar.rs +++ b/masonry/src/widgets/scroll_bar.rs @@ -11,10 +11,11 @@ use crate::core::{ PaintCtx, PointerButtonEvent, PointerEvent, PointerUpdate, PropertiesMut, PropertiesRef, RegisterCtx, TextEvent, UpdateCtx, Widget, WidgetId, WidgetMut, }; -use crate::imaging::Painter; +use crate::imaging::{Composite, GroupRef, Painter}; use crate::kurbo::{Axis, Point, Rect, Size, Stroke}; use crate::layout::LenReq; use crate::theme; +use crate::widgets::AnimatedF32; // TODO // - Fade scrollbars? Find out how Linux/macOS/Windows do it @@ -45,6 +46,7 @@ pub struct ScrollBar { pub(crate) moved: bool, pub(crate) portal_size: f64, pub(crate) content_size: f64, + pub(crate) opacity: AnimatedF32, grab_anchor: Option, } @@ -61,6 +63,7 @@ impl ScrollBar { moved: false, portal_size, content_size, + opacity: AnimatedF32::stable(0.), grab_anchor: None, } } @@ -336,6 +339,21 @@ impl Widget for ScrollBar { } } + fn on_anim_frame( + &mut self, + ctx: &mut UpdateCtx<'_>, + _props: &mut PropertiesMut<'_>, + interval: u64, + ) { + let millis = (interval as f64 * 1e-6) as f32; + let result = self.opacity.advance(millis); + ctx.request_paint_only(); + + if !result.is_completed() { + ctx.request_anim_frame(); + } + } + fn register_children(&mut self, _ctx: &mut RegisterCtx<'_>) {} fn measure( @@ -372,6 +390,14 @@ impl Widget for ScrollBar { _props: &PropertiesRef<'_>, painter: &mut Painter<'_>, ) { + if self.opacity.value() != 1. { + painter.push_fill_clip(ctx.border_box()); + painter.push_group(GroupRef::new().with_composite(Composite::new( + crate::peniko::BlendMode::default(), + self.opacity.value(), + ))); + } + let radius = theme::SCROLLBAR_RADIUS; let edge_width = theme::SCROLLBAR_EDGE_WIDTH; let cursor_padding = theme::SCROLLBAR_PAD; @@ -399,6 +425,11 @@ impl Widget for ScrollBar { theme::SCROLLBAR_BORDER_COLOR, ) .draw(); + + if self.opacity.value() != 1. { + painter.pop_group(); + painter.pop_clip(); + } } fn accessibility_role(&self) -> Role { @@ -472,8 +503,9 @@ mod tests { #[test] fn simple_scrollbar() { - let widget = NewWidget::new(ScrollBar::new(Axis::Vertical, 200.0, 600.0)) - .with_props(Dimensions::FIT); + let mut widget = ScrollBar::new(Axis::Vertical, 200.0, 600.0); + widget.opacity.move_to(1., 0.); + let widget = NewWidget::new(widget).with_props(Dimensions::FIT); let mut harness = TestHarness::create_with_size(test_property_set(), widget, (50, 200)); let scrollbar_id = harness.root_id(); @@ -483,6 +515,9 @@ mod tests { harness.mouse_move((5., 50.)); assert_render_snapshot!(harness, "scrollbar_hovered"); + harness.mouse_move((5., 50.)); + assert_render_snapshot!(harness, "scrollbar_hovered"); + assert!(harness.pop_action_erased().is_none()); harness.mouse_click_on(scrollbar_id, None); @@ -502,8 +537,9 @@ mod tests { #[test] fn horizontal_scrollbar() { - let widget = NewWidget::new(ScrollBar::new(Axis::Horizontal, 200.0, 600.0)) - .with_props(Dimensions::FIT); + let mut widget = ScrollBar::new(Axis::Horizontal, 200.0, 600.0); + widget.opacity.move_to(1., 0.); + let widget = NewWidget::new(widget).with_props(Dimensions::FIT); let mut harness = TestHarness::create_with_size(test_property_set(), widget, (200, 50)); let scrollbar_id = harness.root_id(); diff --git a/masonry/src/widgets/variable_label.rs b/masonry/src/widgets/variable_label.rs index 610f1157d..b9b94a25f 100644 --- a/masonry/src/widgets/variable_label.rs +++ b/masonry/src/widgets/variable_label.rs @@ -96,6 +96,16 @@ impl AnimatedF32 { AnimationStatus::Ongoing } } + + /// Returns the target value. + pub fn target(&self) -> f32 { + self.target + } + + /// Returns the current value. + pub fn value(&self) -> f32 { + self.value + } } /// The status an animation can be in.