render, types, ci: resolution-time dp with per-canvas density, bounded GLES image cache, clippy gate, backend capability matrix
Length::dp no longer collapses to absolute pixels at construction: the design value travels in a new LengthBase::Dp variant and the density multiplication happens when the length is resolved. Previously dp( n ) baked in whatever density() returned at view-build time, so correctness across output changes depended on the view being rebuilt after set_density and in that order; now a density change is picked up by the very next paint with no reconstruction. Length::resolve keeps its signature (process density), and the new Length::resolve_with_density takes an explicit factor. dp becomes const in the bargain. Density also becomes overridable per canvas, the first step towards surface-local responsive state. SoftwareCanvas and GlesCanvas carry a density: Option<f32> analogous to the layout_viewport introduced for sub-canvas fluid resolution: None means "use the process global", Canvas::set_density pins a local factor, and sub-canvases inherit it. All canvas-routed resolution honours it — geom_px / font_px for stock-widget design pixels, and the new Canvas::resolve_geom / resolve_font for explicit Length values, which every widget now uses in place of the raw l.resolve( canvas.viewport_layout(), EM ) pattern (row, column, wrap_grid, spacer, container, separator, button, text, rich_text, text_edit, list_item, vslider, image, and the container draw path). Overlay sizing keeps resolving against the main surface with the global density, which is what it describes. New tests cover explicit-density resolution, resolution-time application, the local-over-global override and sub-canvas inheritance. The GLES image texture cache is now bounded. It was content-keyed but unbounded and never evicted, so a stream of distinct buffers — a photo carousel, video thumbnails — grew GPU memory for the lifetime of the canvas. The cache now tracks an estimated byte total (RGBA8, w × h × 4) against a 32 MiB budget and evicts least-recently-drawn textures on insert; the most recent entry is never evicted, so a single texture larger than the whole budget still draws and simply owns the cache until replaced. Drop-time cleanup is unchanged: drain deletes whatever the map holds. CI gains a Clippy step (workspace, all targets, test-support, -D warnings) sharing the build cache of the test job, with make clippy mirroring the invocation locally and CONTRIBUTING listing it. Run make clippy locally before pushing the first time — the gate has not seen the tree yet and pre-existing lints will fail CI until addressed. docs/backends.md formalises the software/GLES capability matrix that was previously scattered across per-method rustdoc: parity set (fills, strokes, text, images, paths, path clips), graceful degradations on software (flat-fill gradients, no shadows, no backdrop blur, hard bottom edge), GPU-only features (external textures), the shared Oklab-fallback limitation, and the cross-backend blit panic. Linked from README, onboarding and architecture's known-gaps list, which now states the parity gaps explicitly. The dp/density prose in architecture.md, lib.rs and the Length rustdoc is updated for resolution-time semantics and the per-canvas override.
This commit is contained in:
@@ -122,19 +122,19 @@ impl<Msg: Clone> Column<Msg>
|
||||
#[ inline ]
|
||||
fn resolved_spacing( &self, canvas: &Canvas ) -> f32
|
||||
{
|
||||
self.spacing.resolve( canvas.viewport_layout(), Length::EM_BASE_DEFAULT )
|
||||
canvas.resolve_geom( self.spacing )
|
||||
}
|
||||
|
||||
#[ inline ]
|
||||
fn resolved_padding( &self, canvas: &Canvas ) -> f32
|
||||
{
|
||||
self.padding.resolve( canvas.viewport_layout(), Length::EM_BASE_DEFAULT )
|
||||
canvas.resolve_geom( self.padding )
|
||||
}
|
||||
|
||||
#[ inline ]
|
||||
fn resolved_max_width( &self, canvas: &Canvas ) -> Option<f32>
|
||||
{
|
||||
self.max_width.map( |l| l.resolve( canvas.viewport_layout(), Length::EM_BASE_DEFAULT ) )
|
||||
self.max_width.map( |l| canvas.resolve_geom( l ) )
|
||||
}
|
||||
|
||||
/// Report the intrinsic content width as preferred width instead of filling
|
||||
|
||||
@@ -92,13 +92,13 @@ impl<Msg: Clone> Row<Msg>
|
||||
#[ inline ]
|
||||
fn resolved_spacing( &self, canvas: &Canvas ) -> f32
|
||||
{
|
||||
self.spacing.resolve( canvas.viewport_layout(), Length::EM_BASE_DEFAULT )
|
||||
canvas.resolve_geom( self.spacing )
|
||||
}
|
||||
|
||||
#[ inline ]
|
||||
fn resolved_padding( &self, canvas: &Canvas ) -> f32
|
||||
{
|
||||
self.padding.resolve( canvas.viewport_layout(), Length::EM_BASE_DEFAULT )
|
||||
canvas.resolve_geom( self.padding )
|
||||
}
|
||||
|
||||
/// Push the content block to the right edge of the available width.
|
||||
|
||||
@@ -115,11 +115,9 @@ impl Spacer
|
||||
/// weighted by `weight`.
|
||||
pub fn preferred_size( &self, canvas: &Canvas ) -> ( f32, f32 )
|
||||
{
|
||||
let vp = canvas.viewport_layout();
|
||||
let em = Length::EM_BASE_DEFAULT;
|
||||
(
|
||||
self.fixed_width .map( |l| l.resolve( vp, em ) ).unwrap_or( 0.0 ),
|
||||
self.fixed_height.map( |l| l.resolve( vp, em ) ).unwrap_or( 0.0 ),
|
||||
self.fixed_width .map( |l| canvas.resolve_geom( l ) ).unwrap_or( 0.0 ),
|
||||
self.fixed_height.map( |l| canvas.resolve_geom( l ) ).unwrap_or( 0.0 ),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -128,12 +126,12 @@ impl Spacer
|
||||
/// layout only needs the main-axis size for one orientation.
|
||||
pub fn resolved_height( &self, canvas: &Canvas ) -> Option<f32>
|
||||
{
|
||||
self.fixed_height.map( |l| l.resolve( canvas.viewport_layout(), Length::EM_BASE_DEFAULT ) )
|
||||
self.fixed_height.map( |l| canvas.resolve_geom( l ) )
|
||||
}
|
||||
|
||||
pub fn resolved_width( &self, canvas: &Canvas ) -> Option<f32>
|
||||
{
|
||||
self.fixed_width.map( |l| l.resolve( canvas.viewport_layout(), Length::EM_BASE_DEFAULT ) )
|
||||
self.fixed_width.map( |l| canvas.resolve_geom( l ) )
|
||||
}
|
||||
|
||||
/// No-op — spacers are invisible.
|
||||
|
||||
@@ -116,7 +116,7 @@ impl<Msg: Clone> WrapGrid<Msg>
|
||||
{
|
||||
Some( m ) =>
|
||||
{
|
||||
let m = m.resolve( canvas.viewport_layout(), Length::EM_BASE_DEFAULT ).max( 1.0 );
|
||||
let m = canvas.resolve_geom( m ).max( 1.0 );
|
||||
let cols = ( ( ( inner_w + sx ) / ( m + sx ) ).floor() as usize ).max( 1 );
|
||||
match self.max_columns
|
||||
{
|
||||
@@ -130,12 +130,10 @@ impl<Msg: Clone> WrapGrid<Msg>
|
||||
|
||||
fn resolved( &self, canvas: &Canvas ) -> ( f32, f32, f32 )
|
||||
{
|
||||
let vp = canvas.viewport_layout();
|
||||
let em = Length::EM_BASE_DEFAULT;
|
||||
(
|
||||
self.spacing_x.resolve( vp, em ),
|
||||
self.spacing_y.resolve( vp, em ),
|
||||
self.padding.resolve( vp, em ),
|
||||
canvas.resolve_geom( self.spacing_x ),
|
||||
canvas.resolve_geom( self.spacing_y ),
|
||||
canvas.resolve_geom( self.padding ),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user