text_edit font sizing on Length; add Button::width and Text::line_height
Some checks failed
CI / build + test (push) Has been cancelled
CI / cargo audit (push) Has been cancelled

Unify TextEdit's font size with the button label. The button resolved its `font_size` Length in font space (`viewport_logical`, so the `× dpi_scale` at raster lands the right physical size), while `TextEdit::font_size` took a raw `f32` that the draw / hit-test paths treated as a logical size. A caller that resolved a Length against the physical surface and passed the result as that `f32` therefore double-counted `dpi_scale` and got a font that rendered too large. `TextEdit::font_size` now takes `impl Into<Length>` and is resolved against `viewport_logical` exactly like the button, so the two paths agree. The field stores `Option<Length>` (`None` follows the widget-scaling mode at the theme default), retiring the old `f32` sentinel (`0.0` = mode, negative = fluid design px). `font_size_fluid` becomes shorthand for `Length::fluid( n )`. The `Option<Length>` flows through the `WidgetHandlers::TextEdit` snapshot and `text_input_geometry` and is resolved at draw / hit-test time, both of which carry a canvas; the inner measure helpers (`wrapping`, `hit_test`) keep `f32` because they receive the already-resolved value. Backward compatible: `f32` call sites still compile via `f32: Into<Length>` (→ `Length::px`), with the same result as before.
Add `Button::width( impl Into<Length> )`. Text buttons size to their label plus padding; some layouts need a pinned width instead — a full-width or surface-proportional button. The new builder mirrors `height`: resolved in physical layout space, clamped to the available `max_width`, propagated through `map_msg`, and a no-op for icon buttons.
Add `Text::line_height( mult )`. Wrapped multi-line text used the font's declared leading (`new_line_size`), which is tight for some labels; the multiplier scales the gap between wrapped lines (`1.0`, the default, keeps the natural leading — every other `text` is unchanged — and a `0.5` floor keeps lines from overlapping). Applied uniformly in `preferred_size` and `draw` so the reported height and the drawn baselines stay consistent.
Tests: `button` gains a pinned-width and a size-to-content case; `text` gains a line-height default / clamp test and a check that doubling the line height doubles a wrapped block's reported height. Docs: `docs/widgets.md` `text` / `button` / `separator` sections updated for the new builders, and a `CHANGELOG.md` "Unreleased" section covering this batch alongside the responsive work already landed.
This commit is contained in:
2026-07-10 10:38:30 +02:00
parent ce893ac776
commit 8762ab9ce0
11 changed files with 176 additions and 62 deletions

View File

@@ -79,6 +79,11 @@ pub struct Button<Msg: Clone>
/// surface so it does not stay frozen while the rest of a fluid layout
/// grows. Resolved in physical layout space, like all geometry.
pub( crate ) height: Option<Length>,
/// Optional fixed width for text buttons. `None` sizes the button to its
/// label plus horizontal padding; a [`Length`] pins the width (e.g. to
/// make a full-width or surface-proportional button), clamped to the
/// available width. Resolved in physical layout space.
pub( crate ) width: Option<Length>,
/// Optional stable identifier for focus management.
pub( crate ) id: Option<WidgetId>,
/// Whether this button participates in keyboard focus (Tab). Default: `true`.
@@ -113,6 +118,7 @@ impl<Msg: Clone> Button<Msg>
icon_size: 0.0,
font_size: None,
height: None,
width: None,
id: None,
focusable: true,
cursor: None,
@@ -150,6 +156,7 @@ impl<Msg: Clone> Button<Msg>
icon_size: 0.0,
font_size: None,
height: None,
width: None,
id: None,
focusable: true,
cursor: None,
@@ -256,6 +263,16 @@ impl<Msg: Clone> Button<Msg>
self
}
/// Pin the button width for text buttons. Accepts logical `f32` pixels or
/// any [`Length`] (e.g. `Length::orient( 95.0, 25.0 )` for a
/// surface-proportional width). Without this the button sizes to its
/// label. Clamped to the available width. No-op for icon buttons.
pub fn width( mut self, w: impl Into<Length> ) -> Self
{
self.width = Some( w.into() );
self
}
/// Resolve the label font size against the canvas viewport, matching how
/// [`text`](crate::text) sizes its glyphs. An explicit override bypasses
/// the mode; the default follows the process [`crate::WidgetScaling`].
@@ -329,8 +346,15 @@ impl<Msg: Clone> Button<Msg>
{
ButtonContent::Text( label ) =>
{
let text_w = canvas.measure_text( label, self.label_font_size( canvas ) );
let w = (text_w + canvas.geom_px( theme::PAD_H ) * 2.0).min( max_width );
let w = match self.width
{
Some( l ) => l.resolve( canvas.viewport_layout(), Length::EM_BASE_DEFAULT ).min( max_width ),
None =>
{
let text_w = canvas.measure_text( label, self.label_font_size( canvas ) );
( text_w + canvas.geom_px( theme::PAD_H ) * 2.0 ).min( max_width )
}
};
( w, self.resolved_height( canvas ) )
}
ButtonContent::Icon { .. } =>
@@ -510,6 +534,7 @@ impl<Msg: Clone> Button<Msg>
icon_size: self.icon_size,
font_size: self.font_size,
height: self.height,
width: self.width,
id: self.id,
focusable: self.focusable,
cursor: self.cursor,