From def4d8a962b36098ff04145a6257be5040aa378e Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:47:57 +0000 Subject: [PATCH 1/5] Testing: profiles for padding, padding color, post-creation configuration Adds support for configuring how padding in renders done by Masonry Testing is done at runtime. Also adds support for configuring the padding colour Additionally, this adds targeted methods for the archetypes of screenshots identified in https://github.com/linebender/xilem/pull/1457 --- masonry/src/widgets/button.rs | 2 +- masonry/src/widgets/text_input.rs | 2 +- masonry_testing/src/harness.rs | 140 +++++++++++++++++++++++++----- 3 files changed, 119 insertions(+), 25 deletions(-) diff --git a/masonry/src/widgets/button.rs b/masonry/src/widgets/button.rs index 52c9f2bc75..6c69b39468 100644 --- a/masonry/src/widgets/button.rs +++ b/masonry/src/widgets/button.rs @@ -333,7 +333,7 @@ mod tests { let window_size = Size::new(100.0, 40.0); let mut params = TestHarnessParams::DEFAULT; params.window_size = window_size; - params.root_padding = TestHarnessParams::ROOT_PADDING; + params.padding_pixels = TestHarnessParams::DEFAULT_PADDING_PIXELS; let mut harness = TestHarness::create_with(test_property_set(), widget, params); let button_id = harness.root_id(); diff --git a/masonry/src/widgets/text_input.rs b/masonry/src/widgets/text_input.rs index 81c124e75d..c435c3109b 100644 --- a/masonry/src/widgets/text_input.rs +++ b/masonry/src/widgets/text_input.rs @@ -350,7 +350,7 @@ mod tests { const HARNESS_PARAMS: TestHarnessParams = { let mut params = TestHarnessParams::DEFAULT; params.window_size = Size::new(150.0, 40.0); - params.root_padding = 15; + params.padding_pixels = 15; params }; diff --git a/masonry_testing/src/harness.rs b/masonry_testing/src/harness.rs index 0bcd05ab81..26cad785bd 100644 --- a/masonry_testing/src/harness.rs +++ b/masonry_testing/src/harness.rs @@ -140,7 +140,8 @@ pub struct TestHarness { vello_renderer: Option, mouse_state: PointerState, window_size: PhysicalSize, - root_padding: u32, + padding_pixels: u32, + padding_color: Color, background_color: Color, panic_on_rewrite_saturation: bool, screenshot_tolerance: u32, @@ -165,17 +166,16 @@ pub struct TestHarnessParams { pub background_color: Color, /// Extra padding added to screenshots in [`assert_render_snapshot`]. /// - /// Currently defaults to zero, but we plan to change this - /// to [`TestHarnessParams::ROOT_PADDING`] soon. - /// This is useful: + /// For full documentation on padding in screenshots, see [`TestHarness::set_render_padding`]. /// - /// 1) For individual widgets, as they will often be designed with content outside their - /// layout box (e.g. drop shadows, focus indicators). - /// 2) For full apps, as it allows (manual) validation that none of the app content is cut off by - /// the window border. + /// Defaults to [`TestHarnessParams::DEFAULT_PADDING_PIXELS`]. /// /// [`assert_render_snapshot`]: crate::assert_render_snapshot - pub root_padding: u32, + pub padding_pixels: u32, + /// The color to use for [padding added to screenshots](TestHarness::set_render_padding). + /// + /// Defaults to [`TestHarnessParams::DEFAULT_PADDING_COLOR`] + pub padding_color: Color, /// The maximum difference between two pixel channels before the harness will fail a screenshot test. /// Defaults to [`TestHarnessParams::DEFAULT_SCREENSHOT_TOLERANCE`]. pub screenshot_tolerance: u32, @@ -201,7 +201,7 @@ pub struct TestHarnessParams { /// This macro takes a test harness and a name, renders the current state of the app, /// and stores the rendered image to `/screenshots/.png`. /// This rendering will have extra padding which would not be present in a real app, -/// as documented in [`TestHarnessParams::root_padding`]. +/// as documented in [`TestHarness::set_render_padding`]. /// /// If a screenshot already exists, the rendered value is compared against this screenshot. /// The assert passes if both are equal; otherwise, a diff file is created. @@ -243,7 +243,8 @@ impl TestHarnessParams { pub const DEFAULT: Self = Self { window_size: Self::DEFAULT_SIZE, background_color: Self::DEFAULT_BACKGROUND_COLOR, - root_padding: 0, + padding_pixels: 0, + padding_color: Self::DEFAULT_PADDING_COLOR, screenshot_tolerance: Self::DEFAULT_SCREENSHOT_TOLERANCE, scale_factor: 1.0, panic_on_rewrite_saturation: true, @@ -256,13 +257,38 @@ impl TestHarnessParams { /// Default error tolerance for screenshot tests. pub const DEFAULT_SCREENSHOT_TOLERANCE: u32 = 16; - /// Default background color for tests. + ///
+ /// Default background color for tests.
pub const DEFAULT_BACKGROUND_COLOR: Color = Color::from_rgb8(0x29, 0x29, 0x29); - /// Recommended root padding for tests. + /// Recommended root padding for screenshot tests. + /// + /// This default is targeted for the most common kind of tests, which are + /// single-widget tests. + /// For these tests, the padding is present to validate that nothing + /// unexpected is drawn outside of the widget's bounds. + /// + /// We're in a transition period, meaning that this default value is currently zero. + /// We expect to change this value to [`TestHarnessParams::FUTURE_DEFAULT_PADDING_PIXELS`] soon. + /// + /// See [`TestHarness::set_render_padding`] for full documentation of Masonry Testing's padding. + pub const DEFAULT_PADDING_PIXELS: u32 = 0; + + /// The number of pixels which should be used for screenshot tests. + pub const FUTURE_DEFAULT_PADDING_PIXELS: u32 = 5; + + ///
+ /// The default color for padding in screenshot tests.
/// - /// To be set as [`TestHarnessParams::root_padding`] - pub const ROOT_PADDING: u32 = 5; + /// This default is targeted for the most common kind of tests, which are + /// single-widget tests. + /// For these tests, the padding is present to validate that nothing + /// unexpected is drawn outside of the widget's bounds. + /// As such, this color is chosen so that it's clear that it was not added by the + /// widget, and not clashing too harshly with the default background color. + /// + /// See [`TestHarness::set_render_padding`] for full documentation of Masonry Testing's padding. + pub const DEFAULT_PADDING_COLOR: Color = Color::from_rgba8(0xa6, 0xc8, 0xff, 0xff); /// One kibibyte. Used in [`TestHarnessParams::max_screenshot_size`]. pub const KIBIBYTE: u32 = 1024; @@ -361,7 +387,8 @@ impl TestHarness { mouse_state, window_size, background_color: params.background_color, - root_padding: params.root_padding, + padding_pixels: params.padding_pixels, + padding_color: params.padding_color, screenshot_tolerance: params.screenshot_tolerance, panic_on_rewrite_saturation: params.panic_on_rewrite_saturation, max_screenshot_size: params.max_screenshot_size, @@ -474,13 +501,83 @@ impl TestHarness { } // --- MARK: RENDER + + /// Configure the padding used for rendering, including [render snapshots][`assert_render_snapshot`]. + /// + /// The `padding_pixels` parameter is the physical pixels of padding in each direction, + /// i.e. the dimensions of the rendering will be the [window size](Self::window_size) + /// plus twice the padding pixels in each axis. + /// + /// The padding is intended for images saved using [`assert_render_snapshot`](crate::assert_render_snapshot), + /// but also applies to the image output by [`Self::render`]. + /// To configure the padding, you should call this function before a call to either of those. + /// Note that the padding the harness starts with can also be configured by setting the + /// [`TestHarnessParams::padding_pixels`] and [`TestHarnessParams::padding_color`] the harness is created with. + /// + /// This padding is used for several purposes, which can each be configured in different ways: + /// + /// + /// - Screenshots of applications, for which you should call [`use_page_image_padding`](Self::use_page_image_padding). + /// This is applicable for both integration tests and "hero images" for documentation. + /// - Detecting unwanted overdraw in widgets. The harness is configured for this by default; see + /// [`DEFAULT_PADDING_COLOR`](TestHarnessParams::DEFAULT_PADDING_COLOR). + /// - Validating the intentional "overdrawn" content of a widget, such as its focus indicator or box shadow. + /// For screenshot tests of this kind, you should call [`use_widget_overdraw_padding`](Self::use_widget_overdraw_padding). + /// - Screenshots of widgets for documentation. The tests which create these should + /// call [`use_widget_image_padding`](Self::use_widget_image_padding). + /// + /// [`assert_render_snapshot`]: crate::assert_render_snapshot + pub fn set_render_padding(&mut self, padding_pixels: u32, color: Color) { + self.padding_pixels = padding_pixels; + self.padding_color = color; + } + + /// Set the padding to be suitable for images in documentation of a widget. + /// + /// This padding is designed to allow widgets to be seen in-context, so the padding + /// is slightly larger than the default. + /// The padding area will be the same color as the background colour. + /// + /// This is a pre-configured wrapper around [`set_render_padding`](Self::set_render_padding). + pub fn use_widget_image_padding(&mut self) { + // TODO: Maybe we want like 6 pixels vertically and 8 horizontally? + // TODO: Do we also want a black border beyond the padding - see also `use_page_image_padding`. + self.set_render_padding(8, Color::TRANSPARENT); + } + + /// Set the padding to be used for tests of intentional widget overdraw, + /// i.e. where a widget is intended to draw outside of its bounds. + /// + /// This can be used for tests of focus indicators or box shadows. + /// + /// This is a pre-configured wrapper around [`set_render_padding`](Self::set_render_padding). + pub fn use_widget_overdraw_padding(&mut self) { + self.set_render_padding( + TestHarnessParams::FUTURE_DEFAULT_PADDING_PIXELS, + Color::TRANSPARENT, + ); + } + + /// Set the padding to be suitable for rendering a full page for testing. + /// + /// When testing an application, you want your screenshot tests to be as + /// representative of the app's content as possible. + /// As such, the padding added by this method is minimal; it is only being used + /// to provide a border to delineate where the page ends. + /// + /// This is a pre-configured wrapper around [`set_render_padding`](Self::set_render_padding). + pub fn use_page_image_padding(&mut self) { + self.set_render_padding(1, Color::BLACK); + } + // TODO - We add way too many dependencies in this code // TODO - Should be async? /// Renders the window into an image and updates the `accesskit_consumer` tree. /// /// The returned image contains a bitmap (an array of pixels) as an 8-bits-per-channel RGB image. - /// The returned image has padding of the [`TestHarnessParams::root_padding`] this harness - /// was created with on all sides. + /// The returned image has padding based on this harness's current padding parameters. + /// See [`set_render_padding`](Self::set_render_padding) for full details. /// This padded area is currently indicated with a different background color. // TODO: There are some users of this function which just use it assert that `paint`/`compose` doesn't crash. // Those could avoid actually performing a real render. @@ -521,7 +618,7 @@ impl TestHarness { let (width, height) = (self.window_size.width, self.window_size.height); - let padding = self.root_padding; + let padding = self.padding_pixels; // Avoid having a zero-sized image let width = width.max(1) + padding * 2; let height = height.max(1) + padding * 2; @@ -552,9 +649,6 @@ impl TestHarness { let scene = if padding != 0 { let mut scene = Scene::new(); - // 25% opacity of 50% grey provides a border of where the actual widget content is. - // Alternatively, maybe we should use a stronger color here? - let padding_color = Color::from_rgba8(127, 127, 127, 64); // We draw the border first, so that any content is above the background color. for [x0, y0, x1, y1] in [ [0, 0, padding, height], // Left edge @@ -565,7 +659,7 @@ impl TestHarness { scene.fill( Fill::EvenOdd, Affine::IDENTITY, - padding_color, + self.padding_color, None, &Rect::new(x0 as f64, y0 as f64, x1 as f64, y1 as f64), ); From 236d5216f9172a2ff2acb318337d23a95fb493a3 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:51:51 +0000 Subject: [PATCH 2/5] Add a way to use the new testing method --- masonry_testing/src/harness.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/masonry_testing/src/harness.rs b/masonry_testing/src/harness.rs index 26cad785bd..f908e428c7 100644 --- a/masonry_testing/src/harness.rs +++ b/masonry_testing/src/harness.rs @@ -571,6 +571,16 @@ impl TestHarness { self.set_render_padding(1, Color::BLACK); } + /// Set the padding to use the upcoming default padding. + /// + /// This is a pre-configured wrapper around [`set_render_padding`](Self::set_render_padding). + pub fn use_future_default_padding(&mut self) { + self.set_render_padding( + TestHarnessParams::FUTURE_DEFAULT_PADDING_PIXELS, + TestHarnessParams::DEFAULT_PADDING_COLOR, + ); + } + // TODO - We add way too many dependencies in this code // TODO - Should be async? /// Renders the window into an image and updates the `accesskit_consumer` tree. From 4b9240f9de7c21e2c1c2d266496b10dc50eaf80b Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:52:38 +0000 Subject: [PATCH 3/5] Fixup screenshots --- masonry/screenshots/button_hello.png | Bin 648 -> 648 bytes masonry/screenshots/text_input_clip.png | Bin 1521 -> 1521 bytes .../screenshots/text_input_cursor_blink.png | Bin 1743 -> 1749 bytes masonry/screenshots/text_input_outline.png | Bin 1370 -> 1370 bytes .../screenshots/text_input_placeholder.png | Bin 1013 -> 1017 bytes masonry/screenshots/text_input_selection.png | Bin 1746 -> 1753 bytes .../text_input_selection_unfocused.png | Bin 1540 -> 1540 bytes masonry/src/widgets/button.rs | 2 +- 8 files changed, 1 insertion(+), 1 deletion(-) diff --git a/masonry/screenshots/button_hello.png b/masonry/screenshots/button_hello.png index e44653a73ab2780f78d923ca2f8fd98190593560..71bd96e6fb01382f9f449732bdc86d4d474ffc75 100644 GIT binary patch delta 24 gcmeBR?O>gt$-L~u|BX80j4UO0>|SqnXLMu)0CLs{^8f$< delta 24 gcmeBR?O>gt$!u?Lzfnh=k>&U4_X{?=GdeN?0AG6u9{>OV diff --git a/masonry/screenshots/text_input_clip.png b/masonry/screenshots/text_input_clip.png index 1fa18ff7661f0a83748fbdc4ae7f44c0a4dc0765..55d5a7ef067edf9ed7c00885652eef65efa4ad90 100644 GIT binary patch delta 24 gcmey!{gHcuCiAir|2OLRFtPMri0Ro}!&J%)0EwLnKL7v# delta 24 fcmey!{gHcuCbPZ0{YD)hCYCKW&1{=%m`a%eX`%0$&1(a8QJUB9WyKe}sdg2nR(tD8fO_ z7&um~TD4})n*ICtU%q_#?%lih7&AU#6U>5TFb?)jBOG{uPN!>cZ;y_SUb1A#9L9_f z*aWj+8H|H{(+CFu#A6mMTErK9b_Fbhaj&m8IL={-F? z56=(|!r>7PUtizK%F6Na@$T;KsHiB+goTCK?e@UHK#VtRf7oEPT4Q5lM@B}R4`SuY zl@G&FUS3{YTwG94Fg7-Z6r~X}Iy#z`mUi*tMM;tx8XA0jd@#9e*)m+{@9!_p$;!%V zYikP)4b^Ur!^hxwCs%InvyGe7^ZmA@QlqfMZbz;ge9capT4(!ckRKrR5=H9Z(|Rh>wpK z^SG^Eub)4E{?o#dUf}{_bH+b89NFTt6UM*{y<&H!KQKPF(13fsmv0boAS;OZf@TY# zafbsL2uVeZqYujli@JHY3l7`_cNX(khwWK)e`a?$B(u7w#w^C495Y7dcrP47&YrAuu#TX1mj)51~KX1#Pn82qn3 z_`yX3-s0lWf0tf1t7#P`0f#6(q9`DFxWj>>;CwXVjv53U+9ObIP)FS1IHWUMY-)3# ze}V!Ol>0t%z=gxg#&MEakyZ?0x?*> ze!a(ShxJb5h~ zSa-m)+S*#!3`enG*|KE|=IiR}Fo{7?QBh}S=e&9IFiuQN?C?Bw7$L`Eie$l(=|A-q5*&6LuL(*t$QWCNv-U(qIvxMq|s_~Cj98~2}_2X z_Zw1>frhlHSAkoTz=1CE%&*F;-%1#Nb;^X6rLe^kc2ELggCq9943BVNS)@0)Y8K8x z9YKRq+GfRt9~K%=%kay1^HLV8e~ISiW=WFp-2&K;nV6UuBnFDT?W%=ycv$$NG6A%zkQBi^0VjWacQlh~T5fL#sI5^=__R@75TG!aT+(XA z7%8OVh7DMB58Jcr&HVTIGlu9n`0sPVK{yBp;UFCRRF-fs_)Rbd;b0IB!a+C)2jL(b zgoAJ}2nU03FbD^Oa4`NmIJgA^!{ZhV+`NV1aq|{#`^50L?GrbQV|d&!j$2JKJZ?3~ zO=>CbU`!DXif~YbgCZOhF5#dE2Sqq2if~YbgCZOh;h+cyML4LLufG8_N)K8S=nJy| O0000q)MG3{GEw?r$ zZCaqTCd5$M(hHzauUJqD6scf9(Q>P>$VDna5wIdqz)PW8z=qw~o!yz;o&Dsu_Ms`sE?om{E8&ps||7yIo=r50g{-yY-f7TfJH=U-;6 zs#6KxXMYlLhtri&C3oJoBVP22?UhW^yWdOt#czx{#bIBZwp<%>NH(o?zz+Kn!IdqF zmQpU?8gpVx{Yggfz&WdaZt+DMB5!3&6&=bwvxZyjsg%7Lj+HA{4h#%rW@fHmzdkuR zS(2p1ix(qaT3XuD(t-iFxp3ja)R_ZtsH&Qfkbkgt?b`hO{Km#c9ge=fK0iOdkdTnB zuCAJz8c%RckvT#RnC^@_tGm>Q9O;|0vQ5=?9-iRfWUKP~JtrLfsL0%V$8qU~U5Df5 zsPp{-^V`QHpaB2eGsnj#%=aYi(=tnVw$#|8)?Zfz9Gc5jcTIich?IEJVt2T9W|`bG zM}Nb0)zGctGbEQy920#^@>)1xMsIKL{Q2|6F1!p6561xC79y9xIi!uvn>RlZj;g9E zJr5!4fD!>me0;o!7B3!1&lgGp_kw zzFEM5tRUhA%{D;e4hJ$2l8P8dAC=5DZGY?D2{>>OTv^1g4?D8ztnP4_tlGXBs~CTB z!V;O|y>Jv37P=l)PEHQu(C*#4v5RkpFn%H&kcbQKgMxwt9C2}RBEEe2a=YCg931?# zaFn$vS8mxSmH7{Tn4kf#xY+gIW0b5~T7@j&&`S@B0+NS294HE|M-_M6Ea1>@fq!y? zI^qt;5rfrc*P8oO6ri9?ukJa@%F3FWnlNxzYHBJTN?u;xbAe;ks#Th%p}ssV9H%ZR z?KdaG0WV?iYllo?7w^4Q)lhV%35Tp`_@_zNoF*LDUe7WCk--t?U86JVsP|?LG>j-5 zc&)|mqD70uVB^M(vMi^hq@bCA(|^wlhhA3D>WZ=gp+Aj`j1=+g>}(y5p`jsnIN&0T zhyB8sFVD#67|@6Sg0IiYA{bv}#!ZporwK>T9}ZL&-Ryw9Q^A4!q{9(<$h7}wE1F!4 z{nCwSZcv`Q77oli;8|^LEo_FPn6PZywhi&Rx;g|gC@L!I?Ce~yU;)O7iGPXx{r$)R z*qtUEXkw5gcI?=JhE(rM(3#fP*P{gnBDlK_2WB+jPkhO&!?At8$tY`;oeG-A&re$v zyDQQc|MB5fKqFI5_y& zsqB@TcDT}a*8x6+d1z5fT5W$Co!HK_E8=aQ0uD@SFh(s9yXbRojDI+BO?0fHll24# zw!^hnOpHrf6^xNWI&Rs4Mfb2HyWYw_pFg8S&%r;R6Ar>bI0y&f;J31bgW@m2D8fMz z4#Gh=2nXRH9E5{#P=tdb92DW82nY4w!NDaMD341paPbz(=PG^qdYDc z$E7AIk4sH*ky?r?7&udegCZOh;h+cyMK~zJK@kp$A{-Rqpa=&=I4HtF5e{nh>mMOK Vi$-h%qWk~=002ovPDHLkV1j?*N@xH8 diff --git a/masonry/screenshots/text_input_outline.png b/masonry/screenshots/text_input_outline.png index 6e28b89d8f456a8b3b0a7cd9a494fba0e87c1f6a..201946b6c95f5530edefbb703cd7c20bcfcabb00 100644 GIT binary patch delta 24 gcmcb`b&G3)CiAir|2OKWGO?^o(fhtRl!=uY0D#*GJ^%m! delta 24 gcmcb`b&G3)CbPZ0{YD*CCYHH7SdMKDWnyIp0A?HqdjJ3c diff --git a/masonry/screenshots/text_input_placeholder.png b/masonry/screenshots/text_input_placeholder.png index 5ec70e594aa88f592e2de2ead5672bfd304ecf71..b616426be69305a75e7753d1b6119dc7945005ee 100644 GIT binary patch delta 976 zcmV;>126pb2l)q(B!6yDOjJdt$p0Q59xW{`YinzHd3lM6iC zGBQ$9Qha=TG&D3qLPB_Wcq%F?eSLj@e}79$OIKG{8yg!{RaHnxNRpD0jg5_2Sy_8~ zdtP2%8X6j7V`F!BcXf4jF)=Y+U0rEuX<=buSXfwXZEa3YPJn=ba&mHRZf<&ddS+&3 zH#awojEq`ZS~)~SL?Iy|Z*Omhhlfv3Pl}3)WMpJ;aBzZxf-f&GCMG66K0btmge)v9 zmX?+~J3A#MB_JRmD=RCLVF4I_OiWCen3z#fSr`}?KtMn>H8qcqkCBm)Q&Ur!nVA+A z7E)4MjgdE_0006;Nkl-%Q!3i(JI8{~C0e5Y^Ju37t)&&aGB{{|y=4k|B7QF&g)Q9b z5a^A0jA$4r{}Tl$yGT4%bApzS5BBP1Og` zsW#9CUb55Dad(E#+i-szZS@xB@A2$OZS<1kI--?HM{AWU(P!#^9`JWf?pUaGmS{iy zLGVY>#uA(!7ea0sdm6qSOF+io#n}PE~0O%EecAjQdY_}-Y0htiI!-ImS~BVXo;3+iN3K< yy*K|+-TLE;n?ESB{zy(nr9ucHgb>1-{s3yViwU%ib;SS+0000WYH8nLrKtPF!iCtM@Np1jz&gCA0HnpD=UG4fip8RARr)RWo3wnh%hiPR8&-j zg@u)sm1k#XN=izUl$4N=kUTs*J3BjsgoJc-bYNg$K0ZDtCx0h#adB*HY-ng`L_|bV zQc^N9GFVtxG&D3_U0ry1cxh>AeSLjbS64AHF&i5jDk>^fRaKIbl1NBMV`F0)8X8$y zS&fa2dwY9cUS4;1cXf4jW@ctfOG{y4VQp<~ZfFtga8@Q0006-NklCpK^TDX^%+DF5l~SO zRIvB!z4v~nXT^>UL5kAk&%exWym*-K&9LU)-?#nt%w#i}%xefCgb+dqVNa=aHdk!2 zkzy{JP8s#3k2CoaHd4xGj?+ec*~|$xbCSs#_2u%|PCjSUS1e&WrJ_+^6E@T&(GvZi zN0%#TXMd+OD^rCw7bZs)OX_1JlsLz<>jHY+E5N`4B*SIrdI4xrO2wvpFTmZ7$*I)# zy`gK5ZbiEi-Mf*P@a)bXp$#h!6x0+jatDHep5|8PjE+91tF!w2QnZ7m8GoTc(Vw>f z-n~U@K3jx=h~G<>dz^ym1_q-ZGaB}t|3t&D!+(RuL_6C6!u?6Kz#CXwIox5`-AiH{ zAf*;BqC+*&Y;86qdQS}j^r#)Q{ns3vb={ri)(G66M%(_4^?N*hQX9QoxsGUM(a~D< zN_2~Q4E$YFA(}j0AUw)w5d2}Z)i^Cmdq!<^;%7s4x>b?1o>h>RB-26e2{YP>#DiuNqA%qb2^an$BoUkuj#?Sx& N002ovPDHLkV1i;nte*e? diff --git a/masonry/screenshots/text_input_selection.png b/masonry/screenshots/text_input_selection.png index df0f79108d2f522b647a78d4d9b9b8dcc6b3d67b..3dbd6f880d1cec227a04593e2087415000e9dd96 100644 GIT binary patch delta 1737 zcmV;)1~&Q94cQHlB!8euL_t(|+U=a{PZU=e#`{-z*P5t_mq;|xm{>q)MG3{GEw?r$ zZCaqTCd5$M(hHzauUJqD6scf9(Q>P>$VHT(2v`v);H6M4V8iarUS@W8_LJZ2gD0J; z%OV;{Vf(y!KAbtsoLT1i?R(y{yE!kNx%PmfCe%xO1Qg+*2!97fI4HtF5e|xQP=tdb z92DW82nR(G4vKJ4goBzfaI9LjYR#H82M!##diCo4`}ZF(W_-XVm<7vV9PFD$IB)}l z!O+&$78Mn>WXX~_j2R!W31-1E7zg{N5e@)|+bmkNh!1^s1uTPcux}dSK!O5>eAcru zU>xk5MmR8maep?Mz4Hs~n?^Vge9E&oU?1UN5RMleFZ*nJCCxa)RezzqwY8OS5DvmY zIQ~~~R8>{ECW^(27tfh;w6wJD?(Ro>2nXTt2*<{a8!IX*#(&1fy1Kd|BO}oh8XD?wI06C! z(B80NgQ6%gF)_o#!+O41xpL*BaFmsm6%`fb=jV@(jv__r#EguLq^71`zI@qaGS%1D z`}p{vbJ?9M5csECC>a~wqw2OWA#1gZx#CNyxShMZ+ zsPivbtAFfJg7%t3zr*QDue5aBwxhrBg6-7|(|bQi`rtQ4oZ_@EPnoX`IwYIc(r<_T z=)uTlMN2NT+!}R4DL-4#x&N$HzqaV24LxsXN#*UzJz2voc2`)u8II-4m-qMgXJllo zUAs0ZDM^x~g$oy=zoew3xw#n)aC83r`N=Z};D1n6H9kIm_3G7md3g;D4LTgXy}iD^ zzQMu4ot>T4)zzNhm?U!q?>F5Ub5?b#;n~u+XDl{V+i_@|gA*;v@AsT=^y5Og=Z@pj z4Z9A<%@OAZ`SLqQC7=NR+%v}~$K`vH_F1VpEK6$WR_m^-0uIgPs=cPZd00v~VYWM5 zJAX1w?wO$j;6df7-Qc7Y^~w5ZX_L10ni__kn?d0*=_&Skb?9=~BDh z9uySxtZ=(v-b=rc80fh)4_~s!EP+Xymnjv;Fr>1_$z!4oApA z)4pG~|4c31pQ<33@@aQk6F3NlcTHuk7+YZ5q6MV|dteeF9F+Fzfxpkyg%wuK%t zfv|yLM?ktqIIt|j3&5O(bAK?8pg<{URdC@)1v2I`yclm@%3?Lq)YOD`Gk#kD`_U5} z9gT#6I#j18F)v;5m}bu>lER@;-$U4U6Q18=g#4BagM*ge|dR1hQ&IlxVTt< zvf<(30|NsSE@iLWw8NF&yAJRn%tMJ<++zFF=!8zUDdOEc2^?6|pnr|IKpdjZy)o>> zn5bAqCF=<@zS1c$6ic~P5Xt`BbOw*^?wESO(Ps=aQKvGuV5eHpa{o{)+;{SUrjU4@U8x0duwYe;UFA@gK)f5 za8y-Qxt^pYOP0)?vT|~A3!V%8H7L^7HdYM@NyObYezEMp9E#uUxreGMVb@>wSED z5L~`|IZpKR^ApEpW@h5ahJ=LZ7su`dINr;VntJVHvUaJ@o>*cLmiX>59&fhY8Fl_` z)+#%cpnrWP5qCIU>6LQF9XsNM7j3U)nBMH@>a^whphL20E&X=bj|i@8 zQMBYT`Sz$2Tgopof(Oo7^>d3Z*${arODbfUrH0W^j_V)Vv`UVFF zcXoDGS66$2W0K4fe86;f%vsf`hG$FPot15>w)5~f2Pa#UKkhr>=qH8dp1Y3AH|;tc zw?>>F=9}L=CIJQb=bkw}Jz>5tX`h!`!m^}>Znf@)D&WvuuG;JBTSugXlNP(fwKLP? zo_{&&Z>WYY6`vugbo`j8W0Kdx0W*4fdgjfWCwAdwSXdYa__h$a1kNFCY~H;2sc=+Q zR_b{OSqGE|IO5{sL>!kj8jTAUEO=Hp(#pqy*qUw>-XL3iamo^qZd4tvw1>vW6qs?% z5Aw_c4rB!p&u_8;8h1F5fsjZT! zS!ug95e|3>d*3)@61#Zst*VBiGetOLMZ-T$xaJh$!1iXQ35W~~JMS5tkw?8ZbD&{F z;lOJxb{8&OC;I+IXM~41b>`u|Q6$t%lL_~y$XJuvSPj+x{&>arA z2;*VDFy`wsGCBq{B7orAv$6=r6`FBVWcVq<(fy|bl|?t(fA3^)AV2AFgd8&M|J8~n z7h~TvBbpnOC$EJA^A31cQ&R(*;V337+qP{(ytcL$K@18D3p+YG=FgvxaeqQWLSJ7W zasYOx2nU)NB#9k6cAz2E`x11fb#--Ufq@9_u0L7KXuzNNl39mi`+k#A)+#y_G>>1N zwkUR2{LkY)VajmlzlIcKpdoGSQQ_7kaG;Ak_nZ3q_a=8F~-l9FcUGG&G2=KaA5M%-rg=r()sh}ak@Ch-`~Hyyd0OsJgB(1SbwtN;o$=V z15cdFUcF_9E4}v|;6s>)7PYv=_LtF#?M#~@-sVZ*z@!Fa)B>@KK7aS-uoKrr$0|Bm zPjFy6Tw}$=xVS~Z7%8OvwjEe>4LP#vto-x&b4v6a{PQ{CARL5)a1aiDD@!;i{t}EL z92DUo9E5{#5DvmYI0y$tI4HtF5e|xQQ2!qsT!Mk}xC8?iZ=pOc-oj;{D38lNaltss z Date: Mon, 10 Nov 2025 13:34:47 +0000 Subject: [PATCH 4/5] Add use_no_padding, enforce width for overdraw --- masonry_testing/src/harness.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/masonry_testing/src/harness.rs b/masonry_testing/src/harness.rs index f908e428c7..b62b9ec548 100644 --- a/masonry_testing/src/harness.rs +++ b/masonry_testing/src/harness.rs @@ -265,6 +265,8 @@ impl TestHarnessParams { /// /// This default is targeted for the most common kind of tests, which are /// single-widget tests. + // TODO: Is it true that single-widget is the most common? Maybe we need (even...) more constructors, + // like TestHarness::for_page/TestHarness::for_widget? /// For these tests, the padding is present to validate that nothing /// unexpected is drawn outside of the widget's bounds. /// @@ -547,16 +549,13 @@ impl TestHarness { } /// Set the padding to be used for tests of intentional widget overdraw, - /// i.e. where a widget is intended to draw outside of its bounds. + /// i.e. where a widget is intended to draw up to `width` pixels outside of its bounds. /// /// This can be used for tests of focus indicators or box shadows. /// /// This is a pre-configured wrapper around [`set_render_padding`](Self::set_render_padding). - pub fn use_widget_overdraw_padding(&mut self) { - self.set_render_padding( - TestHarnessParams::FUTURE_DEFAULT_PADDING_PIXELS, - Color::TRANSPARENT, - ); + pub fn use_widget_overdraw_padding(&mut self, width: u32) { + self.set_render_padding(width, Color::TRANSPARENT); } /// Set the padding to be suitable for rendering a full page for testing. @@ -581,6 +580,13 @@ impl TestHarness { ); } + /// Set the harness to not use padding in renders. + /// + /// This is a pre-configured wrapper around [`set_render_padding`](Self::set_render_padding). + pub fn use_no_padding(&mut self) { + self.set_render_padding(0, TestHarnessParams::DEFAULT_PADDING_COLOR); + } + // TODO - We add way too many dependencies in this code // TODO - Should be async? /// Renders the window into an image and updates the `accesskit_consumer` tree. From 5f6783d936128437ba474ccd2756056ceacff615 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Mon, 10 Nov 2025 13:34:59 +0000 Subject: [PATCH 5/5] Port button and text_input to this --- masonry/screenshots/button_set_properties.png | Bin 1884 -> 1908 bytes masonry/screenshots/text_input_clip.png | Bin 1521 -> 1504 bytes .../screenshots/text_input_cursor_blink.png | Bin 1749 -> 1655 bytes masonry/screenshots/text_input_outline.png | Bin 1370 -> 1358 bytes .../screenshots/text_input_placeholder.png | Bin 1017 -> 1010 bytes masonry/screenshots/text_input_selection.png | Bin 1753 -> 1657 bytes .../text_input_selection_unfocused.png | Bin 1540 -> 1526 bytes masonry/src/widgets/button.rs | 11 +++++++---- masonry/src/widgets/text_input.rs | 3 ++- 9 files changed, 9 insertions(+), 5 deletions(-) diff --git a/masonry/screenshots/button_set_properties.png b/masonry/screenshots/button_set_properties.png index 1d19961687a04a1816c41b183070e196e657ade4..ba7d525310f789311a67a5b30799f143ff295471 100644 GIT binary patch literal 1908 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 JPDHLkV1o6hzHI;i delta 952 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 B{GI>+ literal 1749 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 QX#fBK07*qoM6N<$f-=B)M*si- literal 1370 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 FV1j%#K575} diff --git a/masonry/screenshots/text_input_selection.png b/masonry/screenshots/text_input_selection.png index 3dbd6f880d1cec227a04593e2087415000e9dd96..e47eee3985587b59b991ba6e20d9709ec8723a4e 100644 GIT binary patch literal 1657 zcmV-<28Q{GP)001%w0ssI2R0Bsc000I+NklWT%WK#>Xt6fIk2 zkwuiC2w2v#7b<0GnD^einYYZFpZLvw$D6(?0}K#NhUqyuKiqrfzB@DL^Uk?v-kWz< z+-!S7>c+Z)TtI+H2{0)ECUptG+qZAuxpU{)vu7VYdNe&f{e;Bj1(*N}$N&!boB+n4 z%F4>7rlzp4u#Foxt|W1J0Vco#GJpd&bD;OMndEfX@kF1UpU#^4D?{ zfCD}!fRWHCGcVs<4SY@jL&#IhSAdTI6JYn1S3C~CYB0LER^M%IY-}XJ1egF5;3Wet zEiJXpCL1nP*x39Rkcxq~@y}dm&G!&M=z(7@1eSCb--n)0NBuNnw z5o2Rxdfac@w(VKKg@uK=xw%=9&j92U0vLRCdDpI8adB}x&#zmz4)*-~ z{QCNOG{FAOojdbKFaXQ49336KW5ja8Xa(Vcekgfr@y~{Yiny+S=n5G=ZRqd zGu-1TYiX+-oW_55Q?$t1(erkeR~w{1Cas|INsiFA`_zZCc_Az0X zc$WqYhjw*!ty;B;b-^PjCis`v~9t`}aEqTvAe^=L1APNHf4uQBll}X^lqX znl)<{3)oO(hk7uHgZ7ct^m<1PE@{ z4!g(;7VY3kJ77$LnVG#~L`|tMI|Am++Nm-#Ykzjd6q4rJfU~o+ZO=Sa^<{t~BO{r8)22;|qWJmwEf#P=qx5i)LHbWW`pHfdp~kviCycyV zODqx@U_Ff^!6Jwq0VBcMo|nkW0t2kS1yVP1vLoONm1c{g)pg5A=Dx`;zXlf+6x7z% z&N?z7Apu`3BO~L50N%QFtEOql)r$pu?Y`7BFcUC%0MQ%gIMzk{nPm+Le1U*PNyAGs z=3F3P=$k1VF!zmFM~&9di>@8PD1wlL5nimjcI{f$*t2JkD2nm%@mO3zYsrB16piwY zrDzChu8@!rW=~B`)x9%3JnRS`3>Dk@+^ zBPS=PrKM%{>eXn+#KiRU^dJ_|T_9jA>Il{&M~Y3@2-0Xl{U^H zc5_SzeE2kH6t&_O2?f!Y*G-aQi~iZ}Ym~A>Pm3(jMUhq8CBx(M07hkW>o@uJ?>V%; zxglZ+&#t!wp676u^o^-LNpk{5xsDHj{Eson$yl)S8zmh0akhYU0UySt0Ygyb*45P^ zs&H=-2w@2i4@cmlw$jgl*w|RK@%IB%$buk%%>n_VsBUg~V{zyz286JQboOhSN32r$V~0n_87ZvOZvJpk_J4}jAa4sO1M z18u_M=9{q4HcD>3jS_8yMmyb+5@1pSOiF-BU%vhW!V$5qN%Asm00000NkvXXu0mjf D6zL7A literal 1753 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 delta 881 zcmV-%1CIRm3xo_WiBL{Q4GJ0x0000DNk~Le0002A0000+2m=5B08&X#5&!@KsZdN* zMW)FA|NsB7DWd{^K%?AV0009PNkl+I-Kf)=Ih>gbelG$-$91ud(!L(b8EoT3Y~t}X>fbKOeNx|H1< z&FO&_rbp45`iXzlFpAa{bPZwp0&2B)0{kGuVW_@0Yeawhih~ zsVUj;L9z>8R~0?67Im+!;n_o zKWTl@sjSR&0HI}JK~i!rx^L2fJNNCjmDHi*G;U~IEF#avCfX&EnY54B`k3$#LAyI6`icZxvIueGCI($K6LEYkYv{{F)gyqLL=FSc`T!$|0SJu&^39(O zM&BqyRnv(BqLZsN^0h-hlUperN`?Zn6$`pQ*cI!~SqfzII-3`SqS zhN_HzNC2Y1+*S>3ZiuSX(DX(XUDkEn7w11qBW!GUmm@=P&7qTG(}T5 zG)4bUXq|lO)yYS7om^k&