From 14572ebfb6ed335657b17108f7edb4e4d9035086 Mon Sep 17 00:00:00 2001 From: "Pedro M. de Echanove Pasquin" Date: Thu, 30 Jul 2026 15:23:52 +0200 Subject: [PATCH] =?UTF-8?q?render:=20sub-canvases=20inherit=20the=20root?= =?UTF-8?q?=20layout=20viewport=20for=20fluid=20resolution=20Content=20dra?= =?UTF-8?q?wn=20inside=20a=20scroll=20rendered=20smaller=20than=20identica?= =?UTF-8?q?l=20content=20outside=20it:=20the=20scroll=20viewport=20draws?= =?UTF-8?q?=20its=20child=20into=20a=20sub-canvas=20sized=20to=20the=20vie?= =?UTF-8?q?wport=20rect,=20and=20Canvas::viewport=5Flayout=20/=20viewport?= =?UTF-8?q?=5Flogical=20=E2=80=94=20the=20viewports=20that=20geom=5Fpx=20a?= =?UTF-8?q?nd=20font=5Fpx=20resolve=20fluid=20Lengths=20against=20?= =?UTF-8?q?=E2=80=94=20returned=20the=20canvas's=20own=20size.=20Inside=20?= =?UTF-8?q?a=20312=20px=20sub-canvas=20on=20a=20360=20px=20surface=20every?= =?UTF-8?q?=20fluid=20geometry=20(icon=20sizes,=20row=20heights,=20padding?= =?UTF-8?q?s)=20and=20font=20size=20resolved=20against=20312=20instead=20o?= =?UTF-8?q?f=20360,=20shrinking=20the=20scroll=20content=20by=20the=20widt?= =?UTF-8?q?h=20ratio,=20~13=20%=20in=20a=20phone-sized=20window=20with=202?= =?UTF-8?q?4=20px=20margins.=20First=20observed=20in=20Eydos=20Settings'?= =?UTF-8?q?=20Wi-Fi=20list,=20where=20the=20connected=20network=20row=20si?= =?UTF-8?q?ts=20outside=20the=20scroll=20and=20the=20rest=20inside:=20the?= =?UTF-8?q?=20same=20full-fan=20signal=20icon=20measured=2021x18=20px=20ou?= =?UTF-8?q?tside=20and=2018x16=20px=20inside.=20Add=20a=20layout=5Fviewpor?= =?UTF-8?q?t=20field=20to=20SoftwareCanvas=20and=20GlesCanvas,=20None=20on?= =?UTF-8?q?=20root=20canvases=20and=20set=20by=20sub=5Fcanvas=20to=20the?= =?UTF-8?q?=20parent's=20effective=20layout=20viewport,=20so=20nested=20su?= =?UTF-8?q?b-canvases=20keep=20propagating=20the=20root=20surface=20size.?= =?UTF-8?q?=20viewport=5Flayout=20and=20viewport=5Flogical=20consult=20the?= =?UTF-8?q?=20inherited=20value=20before=20falling=20back=20to=20the=20can?= =?UTF-8?q?vas=20size.=20Root=20canvases=20are=20untouched,=20and=20the=20?= =?UTF-8?q?GLES=20clip=20layers,=20which=20reuse=20sub=5Fcanvas,=20get=20t?= =?UTF-8?q?he=20same=20correction.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++++ src/gles_render/mod.rs | 6 ++++++ src/gles_render/setup.rs | 3 +++ src/render/mod.rs | 31 ++++++++++++++++++++++++++++--- src/render/setup.rs | 3 +++ 5 files changed, 44 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fcd784..589ae8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,10 @@ All notable changes to `ltk` are documented here. The format is based on [Keep a - **Renamed** `set_design_reference` / `design_reference` → `set_fluid_reference` / `fluid_reference`. **`Length::dp` changed meaning** — it used to be a surface-proportional value, and that behaviour now lives on `Length::fluid`; `dp` is the constant-physical-size unit. - **Widget struct fields are now `pub( crate )`** (configured through builders), except the value / state types apps read or construct (`Time`, `Date`, `ComboState`). +### Fixed + +- **Content inside a `scroll` no longer renders smaller than the rest of the surface.** Scroll viewports draw their child into a sub-canvas sized to the viewport rect, and fluid `Length` resolution (`Canvas::geom_px` / `font_px` — icon sizes, row heights, paddings, font sizes) resolved against that smaller canvas instead of the surface, shrinking everything inside any scroll by the width ratio (~13 % in a 360 px window with 24 px margins). Sub-canvases now inherit the root canvas's layout viewport (`viewport_layout` / `viewport_logical`), propagated through nested sub-canvases, so geometry resolves identically inside and outside offscreen content. + ## [0.2.0] — 2026-06-25 This release adds the primitives an embedder needs to drive ltk as the render backend for a retained, externally-owned widget tree (for example projecting an Android view hierarchy onto an ltk surface). Each is kept general rather than tied to one consumer. diff --git a/src/gles_render/mod.rs b/src/gles_render/mod.rs index 782272e..2114e51 100644 --- a/src/gles_render/mod.rs +++ b/src/gles_render/mod.rs @@ -151,6 +151,12 @@ pub struct GlesCanvas /// falls back to [`Self::font`]. pub font_registry: Option>, pub dpi_scale: f32, + /// Layout-resolution viewport inherited from the parent canvas. + /// `None` on a root canvas (resolve against own size); sub-canvases + /// carry the root surface size so fluid geometry and fonts resolve + /// the same inside offscreen content (scroll viewports, clip layers) + /// as outside it. + pub( crate ) layout_viewport: Option<( f32, f32 )>, pub global_alpha: f32, pub width: u32, pub height: u32, diff --git a/src/gles_render/setup.rs b/src/gles_render/setup.rs index 99b3744..273c404 100644 --- a/src/gles_render/setup.rs +++ b/src/gles_render/setup.rs @@ -336,6 +336,7 @@ impl GlesCanvas font_face, font_registry: None, dpi_scale: 1.0, + layout_viewport: None, global_alpha: 1.0, width, height, @@ -547,6 +548,8 @@ impl GlesCanvas font_face: self.font_face, font_registry: self.font_registry.as_ref().map( Arc::clone ), dpi_scale: self.dpi_scale, + layout_viewport: Some( self.layout_viewport.unwrap_or( + ( self.width as f32, self.height as f32 ) ) ), global_alpha: self.global_alpha, width, height, diff --git a/src/render/mod.rs b/src/render/mod.rs index a1c5e37..f5968a9 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -137,6 +137,12 @@ pub struct SoftwareCanvas pub font_registry: Option>, /// DPI scale factor applied to font sizes. pub dpi_scale: f32, + /// Layout-resolution viewport inherited from the parent canvas. + /// `None` on a root canvas (resolve against own size); sub-canvases + /// carry the root surface size so fluid geometry and fonts resolve + /// the same inside offscreen content (scroll viewports, clip layers) + /// as outside it. + pub( crate ) layout_viewport: Option<( f32, f32 )>, /// Global alpha multiplier for all drawing operations (0.0 = /// transparent, 1.0 = opaque). pub global_alpha: f32, @@ -216,13 +222,21 @@ impl Canvas /// ``` pub fn viewport_logical( &self ) -> ( f32, f32 ) { - let ( pw, ph ) = self.size(); + let ( pw, ph ) = match self.layout_viewport() + { + Some( ( w, h ) ) => ( w, h ), + None => + { + let ( w, h ) = self.size(); + ( w as f32, h as f32 ) + } + }; let scale = self.dpi_scale(); if scale > 0.0 { - ( pw as f32 / scale, ph as f32 / scale ) + ( pw / scale, ph / scale ) } else { - ( pw as f32, ph as f32 ) + ( pw, ph ) } } @@ -235,10 +249,21 @@ impl Canvas /// by `dpi_scale` at raster time, so they must NOT use this. pub fn viewport_layout( &self ) -> ( f32, f32 ) { + if let Some( v ) = self.layout_viewport() { return v; } let ( pw, ph ) = self.size(); ( pw as f32, ph as f32 ) } + /// Inherited layout viewport, when this is a sub-canvas. + fn layout_viewport( &self ) -> Option<( f32, f32 )> + { + match self + { + Canvas::Software( c ) => c.layout_viewport, + Canvas::Gles( c ) => c.layout_viewport, + } + } + /// Resolve a stock-widget **geometry** design pixel (height, padding, /// box size, gap…) through the process-wide [`crate::WidgetScaling`] /// mode, into a concrete physical-pixel value for the layout tree. diff --git a/src/render/setup.rs b/src/render/setup.rs index 697ae72..12ea198 100644 --- a/src/render/setup.rs +++ b/src/render/setup.rs @@ -36,6 +36,7 @@ impl SoftwareCanvas font_face: handle.face, font_registry: None, dpi_scale: 1.0, + layout_viewport: None, global_alpha: 1.0, glyph_cache: HashMap::new(), clip_mask: None, @@ -54,6 +55,8 @@ impl SoftwareCanvas font_face: self.font_face, font_registry: self.font_registry.as_ref().map( Arc::clone ), dpi_scale: self.dpi_scale, + layout_viewport: Some( self.layout_viewport.unwrap_or( + ( self.pixmap.width() as f32, self.pixmap.height() as f32 ) ) ), global_alpha: self.global_alpha, glyph_cache: HashMap::new(), clip_mask: None,