responsive fluid/physical scaling, widget-API stabilization, and perf guardrails
Responsive scaling. ltk now offers two first-class ways to size a UI so it adapts across screens, chosen per process via `WidgetScaling { Fluid, Physical }` (`set_widget_scaling` / `widget_scaling`, default `Fluid`). Fluid sizing (`Length::fluid( px )`) makes a design pixel a proportion of the surface's smaller side, calibrated against a reference width (`set_fluid_reference` / `fluid_reference`, 412 px default) and bounded by `FLUID_MIN` / `FLUID_MAX`; physical sizing (`Length::dp( px )`) is a constant-physical-size pixel scaled by display density (`set_density` / `density`). `Length` gains `orient( portrait, landscape )` — resolve one value in portrait, another in landscape — plus `widget( px )`, which picks fluid or dp per the active mode. Canvas exposes `geom_px` (geometry, resolved in physical layout space) and `font_px` (font size, bridging logical / physical per mode) so widgets and apps share one resolution path. Note the rename: `set_design_reference` / `design_reference` became `set_fluid_reference` / `fluid_reference`, and `Length::dp` changed meaning — the old surface-proportional behaviour now lives on `Length::fluid`.
Widgets. Every stock widget resolves its default geometry and font through the widget-scaling mode instead of frozen pixels, so a whole UI scales coherently without per-call units. New size builders where they were missing: `button` gains `font_size` / `height`, `text_edit` gains `height` / `font_size_fluid`, `separator` gains `pad_v`, and assorted widgets accept a `Length` where they previously took only `f32`.
Overlays. `OverlaySpec::size` is now `( Length, Length )` instead of `( u32, u32 )`, resolved against the main surface when the overlay is materialized, so overlays can scale with the display; `Length::px( … )` reproduces the old fixed sizing.
API stabilization (toward 1.0). Widget struct fields are now `pub( crate )` — they are configured through builders, not field access — except the value / state types apps genuinely read or construct (`Time`, `Date`, `ComboState`), which stay public. The internal `test_support` helpers move behind a `test-support` Cargo feature (off by default, so third-party builds never see them; ltk's own `make test` enables it). `Separator` drops its `0.0`-means-mode sentinel for `Option<Length>`, so an explicit `pad_v( 0.0 )` is a real flush divider distinct from the mode-following default.
Performance guardrails. Opt-in diagnostics via `LTK_PERF_WARN=1` warn about stuck animations, sustained software-render animation, and low `poll_interval`; software-rendered animation is capped near 30 Hz to spare CPU on machines that fall back off EGL. Apps can override the cap with `App::cap_software_animation`.
Docs and build. The two scaling modes are documented in README, onboarding and architecture, with the earlier gradient / backdrop doc drift cleaned up. The Makefile now ships the `locales/` directory into the packaged crate (fixing i18n keys rendering raw for downstreams), builds the new `responsive` example, and runs tests with `--features test-support`.
This commit is contained in:
@@ -43,17 +43,17 @@ use crate::widget::Element;
|
||||
/// ```
|
||||
pub struct Column<Msg: Clone>
|
||||
{
|
||||
pub children: Vec<Element<Msg>>,
|
||||
pub( crate ) children: Vec<Element<Msg>>,
|
||||
/// Vertical gap between children. Stored as [`Length`] so a `Vmin(2.0)`
|
||||
/// or `Em(0.5)` gap scales with the viewport instead of freezing at a
|
||||
/// px constant.
|
||||
pub spacing: Length,
|
||||
pub( crate ) spacing: Length,
|
||||
/// Padding on all sides. Same [`Length`] semantics as `spacing`.
|
||||
pub padding: Length,
|
||||
pub align_center_x: bool,
|
||||
pub center_y: bool,
|
||||
pub max_width: Option<Length>,
|
||||
pub fit_content: bool,
|
||||
pub( crate ) padding: Length,
|
||||
pub( crate ) align_center_x: bool,
|
||||
pub( crate ) center_y: bool,
|
||||
pub( crate ) max_width: Option<Length>,
|
||||
pub( crate ) fit_content: bool,
|
||||
}
|
||||
|
||||
impl<Msg: Clone> Column<Msg>
|
||||
|
||||
@@ -41,12 +41,12 @@ use crate::widget::Element;
|
||||
/// ```
|
||||
pub struct Row<Msg: Clone>
|
||||
{
|
||||
pub children: Vec<Element<Msg>>,
|
||||
pub( crate ) children: Vec<Element<Msg>>,
|
||||
/// Horizontal gap between children. [`Length`]; default `8.0` px.
|
||||
pub spacing: Length,
|
||||
pub( crate ) spacing: Length,
|
||||
/// Padding on all sides. [`Length`]; default `0.0` px.
|
||||
pub padding: Length,
|
||||
pub align_right: bool,
|
||||
pub( crate ) padding: Length,
|
||||
pub( crate ) align_right: bool,
|
||||
}
|
||||
|
||||
impl<Msg: Clone> Row<Msg>
|
||||
|
||||
@@ -70,15 +70,15 @@ use crate::widget::Element;
|
||||
pub struct Spacer
|
||||
{
|
||||
/// Relative weight of this spacer (default 1).
|
||||
pub weight: u32,
|
||||
pub( crate ) weight: u32,
|
||||
/// Fixed height (overrides flexible behavior in a column). Accepts any
|
||||
/// [`Length`] — pass an `f32`/`i32`/`u32` for the px case (kept for
|
||||
/// backward compatibility with existing call sites), or
|
||||
/// `Length::vmin( … )` etc. for viewport-relative gaps.
|
||||
pub fixed_height: Option<Length>,
|
||||
pub( crate ) fixed_height: Option<Length>,
|
||||
/// Fixed width (overrides flexible behavior in a row). Same length-type
|
||||
/// semantics as [`Self::fixed_height`].
|
||||
pub fixed_width: Option<Length>,
|
||||
pub( crate ) fixed_width: Option<Length>,
|
||||
}
|
||||
|
||||
impl Spacer
|
||||
@@ -222,4 +222,23 @@ mod tests
|
||||
assert_eq!( s.resolved_height( &canvas ), None );
|
||||
assert_eq!( s.resolved_width( &canvas ), None );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn builder_stores_weight_and_fixed_dimensions()
|
||||
{
|
||||
let d = spacer();
|
||||
assert_eq!( d.weight, 1 );
|
||||
assert!( d.fixed_height.is_none() );
|
||||
assert!( d.fixed_width.is_none() );
|
||||
|
||||
let h = spacer().height( 24.0 );
|
||||
assert_eq!( h.fixed_height, Some( Length::px( 24.0 ) ) );
|
||||
assert!( h.fixed_width.is_none() );
|
||||
|
||||
let w = spacer().width( 12.0 );
|
||||
assert_eq!( w.fixed_width, Some( Length::px( 12.0 ) ) );
|
||||
assert!( w.fixed_height.is_none() );
|
||||
|
||||
assert_eq!( spacer().weight( 5 ).weight, 5 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,10 +56,10 @@ pub struct Stack<Msg: Clone>
|
||||
/// `Option<Rect>` overrides alignment/sizing with an exact rect (see
|
||||
/// [`push_placed`](Self::push_placed)); the 8th clips the child's draw to a
|
||||
/// rect (Android clipChildren — see [`push_placed_clipped`](Self::push_placed_clipped)).
|
||||
pub children: Vec<( Element<Msg>, HAlign, VAlign, f32, f32, f32, Option<Rect>, Option<Rect> )>,
|
||||
pub( crate ) children: Vec<( Element<Msg>, HAlign, VAlign, f32, f32, f32, Option<Rect>, Option<Rect> )>,
|
||||
/// When `true`, [`preferred_size`](Self::preferred_size) reports the max of
|
||||
/// children's intrinsic widths and heights instead of `(max_width, tallest)`.
|
||||
pub fit_content: bool,
|
||||
pub( crate ) fit_content: bool,
|
||||
}
|
||||
|
||||
impl<Msg: Clone> Stack<Msg>
|
||||
|
||||
@@ -32,18 +32,18 @@ use crate::widget::Element;
|
||||
pub struct WrapGrid<Msg: Clone>
|
||||
{
|
||||
/// Child widgets laid out in row-major order.
|
||||
pub children: Vec<Element<Msg>>,
|
||||
pub( crate ) children: Vec<Element<Msg>>,
|
||||
/// Number of columns per row.
|
||||
pub columns: usize,
|
||||
pub( crate ) columns: usize,
|
||||
/// Horizontal gap between cells.
|
||||
pub spacing_x: Length,
|
||||
pub( crate ) spacing_x: Length,
|
||||
/// Vertical gap between rows.
|
||||
pub spacing_y: Length,
|
||||
pub( crate ) spacing_y: Length,
|
||||
/// Padding on all sides.
|
||||
pub padding: Length,
|
||||
pub( crate ) padding: Length,
|
||||
/// When `true`, a partial last row is centred horizontally within
|
||||
/// the grid's content rect instead of being left-aligned.
|
||||
pub centre_last_row: bool,
|
||||
pub( crate ) centre_last_row: bool,
|
||||
}
|
||||
|
||||
impl<Msg: Clone> WrapGrid<Msg>
|
||||
|
||||
Reference in New Issue
Block a user