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:
@@ -38,6 +38,7 @@ use std::sync::Arc;
|
||||
use crate::layout::column::column;
|
||||
use crate::layout::row::row;
|
||||
use crate::layout::spacer::spacer;
|
||||
use crate::types::Length;
|
||||
|
||||
use super::Element;
|
||||
|
||||
@@ -152,12 +153,12 @@ fn snap_step_delta( value: i32, step: i32, dir: i32 ) -> i32
|
||||
/// Time-of-day selector with up / down steppers per unit.
|
||||
pub struct TimePicker<Msg: Clone>
|
||||
{
|
||||
pub value: Time,
|
||||
pub on_change: Option<Arc<dyn Fn( Time ) -> Msg>>,
|
||||
pub minute_step: u8,
|
||||
pub second_step: u8,
|
||||
pub seconds: bool,
|
||||
pub twelve_hour: bool,
|
||||
pub( crate ) value: Time,
|
||||
pub( crate ) on_change: Option<Arc<dyn Fn( Time ) -> Msg>>,
|
||||
pub( crate ) minute_step: u8,
|
||||
pub( crate ) second_step: u8,
|
||||
pub( crate ) seconds: bool,
|
||||
pub( crate ) twelve_hour: bool,
|
||||
}
|
||||
|
||||
impl<Msg: Clone + 'static> TimePicker<Msg>
|
||||
@@ -305,7 +306,7 @@ impl<Msg: Clone + 'static> TimePicker<Msg>
|
||||
None =>
|
||||
{
|
||||
text( display )
|
||||
.size( theme::VAL_FS )
|
||||
.size( Length::widget( theme::VAL_FS ) )
|
||||
.color( theme::text() )
|
||||
.align_center()
|
||||
.into()
|
||||
@@ -317,7 +318,7 @@ impl<Msg: Clone + 'static> TimePicker<Msg>
|
||||
text_edit::<Msg>( "", display )
|
||||
.borderless( true )
|
||||
.fixed_width( 72.0 )
|
||||
.font_size( theme::VAL_FS )
|
||||
.font_size_fluid( theme::VAL_FS )
|
||||
.align( TextAlign::Center )
|
||||
.select_on_focus( true )
|
||||
.on_change( move |s: String|
|
||||
@@ -386,9 +387,9 @@ impl<Msg: Clone + 'static> TimePicker<Msg>
|
||||
);
|
||||
let minute_col = stepper( minute_field, minute_up, minute_dn );
|
||||
|
||||
let mut units = row::<Msg>().spacing( theme::SPACING )
|
||||
let mut units = row::<Msg>().spacing( Length::widget( theme::SPACING ) )
|
||||
.push( hour_col )
|
||||
.push( text( ":" ).size( theme::SEP_FS ).color( theme::text_muted() ) )
|
||||
.push( text( ":" ).size( Length::widget( theme::SEP_FS ) ).color( theme::text_muted() ) )
|
||||
.push( minute_col );
|
||||
|
||||
if self.seconds
|
||||
@@ -406,7 +407,7 @@ impl<Msg: Clone + 'static> TimePicker<Msg>
|
||||
);
|
||||
let second_col = stepper( second_field, second_up, second_dn );
|
||||
units = units
|
||||
.push( text( ":" ).size( theme::SEP_FS ).color( theme::text_muted() ) )
|
||||
.push( text( ":" ).size( Length::widget( theme::SEP_FS ) ).color( theme::text_muted() ) )
|
||||
.push( second_col );
|
||||
}
|
||||
|
||||
@@ -427,7 +428,7 @@ impl<Msg: Clone + 'static> TimePicker<Msg>
|
||||
// the whole hh:mm AM block inside the container. A flex
|
||||
// spacer here would defeat the auto-centre and pin AM/PM
|
||||
// to the right.
|
||||
units = units.push( spacer().width( theme::SPACING * 2.0 ) ).push( ampm );
|
||||
units = units.push( spacer().width( Length::widget( theme::SPACING * 2.0 ) ) ).push( ampm );
|
||||
}
|
||||
|
||||
// `Row::layout` centres a cluster horizontally when there are
|
||||
@@ -438,7 +439,7 @@ impl<Msg: Clone + 'static> TimePicker<Msg>
|
||||
// AM/PM), so they centre automatically inside the container.
|
||||
container::<Msg>( units )
|
||||
.background( theme::surface_alt() )
|
||||
.padding( theme::PADDING )
|
||||
.padding( Length::widget( theme::PADDING ) )
|
||||
.radius( theme::RADIUS )
|
||||
.into()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user