From 4247613bb0e3ab729642b37885e65d2f034f8e7b Mon Sep 17 00:00:00 2001 From: "Pedro M. de Echanove Pasquin" Date: Sat, 1 Aug 2026 12:07:56 +0200 Subject: [PATCH] =?UTF-8?q?widget/toggle,=20widget/radio:=20`height()`=20b?= =?UTF-8?q?uilder=20to=20override=20the=20theme=20row=20height,=20floored?= =?UTF-8?q?=20at=20the=20control's=20visual=20so=20it=20never=20clips=20To?= =?UTF-8?q?ggle=20and=20Radio=20report=20a=20fixed=20preferred=20height=20?= =?UTF-8?q?of=2048=20design=20px=20(theme::HEIGHT),=20which=20the=20fluid?= =?UTF-8?q?=20widget-scaling=20mode=20inflates=20to=2072=20px=20on=20large?= =?UTF-8?q?=20surfaces.=20That=20makes=20dense=20settings-style=20lists=20?= =?UTF-8?q?=E2=80=94=20several=20rows=20of=20label=20+=20control=20?= =?UTF-8?q?=E2=80=94=20the=20dominant=20consumer=20of=20vertical=20space?= =?UTF-8?q?=20on=20short=20landscape=20windows,=20with=20no=20way=20for=20?= =?UTF-8?q?the=20application=20to=20trade=20the=20built-in=20touch-target?= =?UTF-8?q?=20generosity=20for=20row=20density.=20Both=20widgets=20gain=20?= =?UTF-8?q?a=20`height(=20impl=20Into=20)`=20builder=20storing=20a?= =?UTF-8?q?n=20optional=20override=20that=20`preferred=5Fsize()`=20resolve?= =?UTF-8?q?s=20through=20`canvas.resolve=5Fgeom()`,=20so=20callers=20can?= =?UTF-8?q?=20pass=20viewport-relative=20lengths=20(e.g.=20`Length::vh(=20?= =?UTF-8?q?5.0=20).clamp(=2030.0,=2048.0=20)`)=20and=20have=20the=20row=20?= =?UTF-8?q?height=20track=20the=20surface.=20The=20resolved=20value=20is?= =?UTF-8?q?=20floored=20at=20the=20control's=20visual=20size=20=E2=80=94?= =?UTF-8?q?=20the=20track=20height=20for=20Toggle,=20the=20outer=20circle?= =?UTF-8?q?=20for=20Radio=20=E2=80=94=20so=20the=20pill=20or=20ring=20neve?= =?UTF-8?q?r=20clips=20regardless=20of=20how=20aggressive=20the=20override?= =?UTF-8?q?=20is;=20the=20visual=20itself=20keeps=20its=20theme=20size=20a?= =?UTF-8?q?nd=20stays=20vertically=20centred=20in=20whatever=20rect=20the?= =?UTF-8?q?=20layout=20assigns,=20exactly=20as=20before.=20Without=20the?= =?UTF-8?q?=20builder=20both=20widgets=20behave=20identically=20to=20the?= =?UTF-8?q?=20previous=20fixed-height=20code.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/widget/radio/mod.rs | 25 ++++++++++++++++++++++--- src/widget/toggle/mod.rs | 25 ++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/widget/radio/mod.rs b/src/widget/radio/mod.rs index 4a3002b..809c4fb 100644 --- a/src/widget/radio/mod.rs +++ b/src/widget/radio/mod.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-2.1-only // Copyright (C) 2026 Liberux Labs, S. L. -use crate::types::{ Rect, WidgetId }; +use crate::types::{ Length, Rect, WidgetId }; use crate::render::Canvas; use super::Element; @@ -48,6 +48,8 @@ pub struct Radio pub( crate ) label: Option, /// Optional stable identifier for focus management. pub( crate ) id: Option, + /// Optional override of the theme row height. + pub( crate ) height: Option, } impl Radio @@ -55,7 +57,7 @@ impl Radio /// Create a radio in the given state, with no label and no callback. pub fn new( selected: bool ) -> Self { - Self { selected, on_select: None, label: None, id: None } + Self { selected, on_select: None, label: None, id: None, height: None } } /// Set the message emitted when this option is picked. The @@ -81,6 +83,17 @@ impl Radio self } + /// Override the preferred height (default: the theme row height, + /// [`theme::HEIGHT`] design px). Accepts any [`Length`] so dense + /// layouts can tie the row height to the viewport. The resolved + /// value is floored at the outer circle size so the ring never + /// clips; the circle keeps its theme size and stays centred. + pub fn height( mut self, h: impl Into ) -> Self + { + self.height = Some( h.into() ); + self + } + pub fn preferred_size( &self, max_width: f32, canvas: &Canvas ) -> (f32, f32) { let outer = canvas.geom_px( theme::OUTER_SIZE ); @@ -91,7 +104,12 @@ impl Radio } else { outer.min( max_width ) }; - ( w, canvas.geom_px( theme::HEIGHT ) ) + let h = match self.height + { + Some( l ) => canvas.resolve_geom( l ).max( outer ), + None => canvas.geom_px( theme::HEIGHT ), + }; + ( w, h ) } /// Focus ring on the outer circle extends `FOCUS_W + 2 + FOCUS_W/2 ≈ 6.5 px` @@ -159,6 +177,7 @@ impl Radio on_select: self.on_select.map( |m| ( *f )( m ) ), label: self.label, id: self.id, + height: self.height, } } } diff --git a/src/widget/toggle/mod.rs b/src/widget/toggle/mod.rs index 7c8890f..5e17be3 100644 --- a/src/widget/toggle/mod.rs +++ b/src/widget/toggle/mod.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-2.1-only // Copyright (C) 2026 Liberux Labs, S. L. -use crate::types::{ Rect, WidgetId }; +use crate::types::{ Length, Rect, WidgetId }; use crate::render::Canvas; use super::Element; @@ -46,6 +46,8 @@ pub struct Toggle pub( crate ) label: Option, /// Optional stable identifier for focus management. pub( crate ) id: Option, + /// Optional override of the theme row height. + pub( crate ) height: Option, } impl Toggle @@ -56,7 +58,7 @@ impl Toggle /// widget tree, otherwise the toggle is decorative. pub fn new( value: bool ) -> Self { - Self { value, on_toggle: None, label: None, id: None } + Self { value, on_toggle: None, label: None, id: None, height: None } } /// Set the message emitted when the toggle is activated (tap, Enter or @@ -85,6 +87,17 @@ impl Toggle self } + /// Override the preferred height (default: the theme row height, + /// [`theme::HEIGHT`] design px). Accepts any [`Length`] so dense + /// layouts can tie the row height to the viewport. The resolved + /// value is floored at the track height so the pill never clips; + /// the track keeps its theme size and stays vertically centred. + pub fn height( mut self, h: impl Into ) -> Self + { + self.height = Some( h.into() ); + self + } + /// Return the preferred `(width, height)` given available `max_width`. /// /// Width is `track_width` for an unlabelled toggle, or @@ -100,7 +113,12 @@ impl Toggle } else { track_w.min( max_width ) }; - ( w, canvas.geom_px( theme::HEIGHT ) ) + let h = match self.height + { + Some( l ) => canvas.resolve_geom( l ).max( canvas.geom_px( theme::TRACK_H ) ), + None => canvas.geom_px( theme::HEIGHT ), + }; + ( w, h ) } /// Bounding box of everything painted at `rect` across all states. The @@ -179,6 +197,7 @@ impl Toggle on_toggle: self.on_toggle.map( |m| ( *f )( m ) ), label: self.label, id: self.id, + height: self.height, } } }