render: sub-canvases inherit the root layout viewport for fluid resolution
Content drawn inside a scroll rendered smaller than identical content outside it: the scroll viewport draws its child into a sub-canvas sized to the viewport rect, and Canvas::viewport_layout / viewport_logical — the viewports that geom_px and font_px resolve fluid Lengths against — returned the canvas's own size. Inside a 312 px sub-canvas on a 360 px surface every fluid geometry (icon sizes, row heights, paddings) and font size resolved against 312 instead of 360, shrinking the scroll content by the width ratio, ~13 % in a phone-sized window with 24 px margins. First observed in Eydos Settings' Wi-Fi list, where the connected network row sits outside the scroll and the rest inside: the same full-fan signal icon measured 21x18 px outside and 18x16 px inside. Add a layout_viewport field to SoftwareCanvas and GlesCanvas, None on root canvases and set by sub_canvas to the parent's effective layout viewport, so nested sub-canvases keep propagating the root surface size. viewport_layout and viewport_logical consult the inherited value before falling back to the canvas size. Root canvases are untouched, and the GLES clip layers, which reuse sub_canvas, get the same correction.
This commit is contained in:
@@ -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.
|
- **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`).
|
- **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
|
## [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.
|
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.
|
||||||
|
|||||||
@@ -151,6 +151,12 @@ pub struct GlesCanvas
|
|||||||
/// falls back to [`Self::font`].
|
/// falls back to [`Self::font`].
|
||||||
pub font_registry: Option<Arc<FontRegistry>>,
|
pub font_registry: Option<Arc<FontRegistry>>,
|
||||||
pub dpi_scale: f32,
|
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 global_alpha: f32,
|
||||||
pub width: u32,
|
pub width: u32,
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
|
|||||||
@@ -336,6 +336,7 @@ impl GlesCanvas
|
|||||||
font_face,
|
font_face,
|
||||||
font_registry: None,
|
font_registry: None,
|
||||||
dpi_scale: 1.0,
|
dpi_scale: 1.0,
|
||||||
|
layout_viewport: None,
|
||||||
global_alpha: 1.0,
|
global_alpha: 1.0,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
@@ -547,6 +548,8 @@ impl GlesCanvas
|
|||||||
font_face: self.font_face,
|
font_face: self.font_face,
|
||||||
font_registry: self.font_registry.as_ref().map( Arc::clone ),
|
font_registry: self.font_registry.as_ref().map( Arc::clone ),
|
||||||
dpi_scale: self.dpi_scale,
|
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,
|
global_alpha: self.global_alpha,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
|||||||
@@ -137,6 +137,12 @@ pub struct SoftwareCanvas
|
|||||||
pub font_registry: Option<Arc<FontRegistry>>,
|
pub font_registry: Option<Arc<FontRegistry>>,
|
||||||
/// DPI scale factor applied to font sizes.
|
/// DPI scale factor applied to font sizes.
|
||||||
pub dpi_scale: f32,
|
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 =
|
/// Global alpha multiplier for all drawing operations (0.0 =
|
||||||
/// transparent, 1.0 = opaque).
|
/// transparent, 1.0 = opaque).
|
||||||
pub global_alpha: f32,
|
pub global_alpha: f32,
|
||||||
@@ -216,13 +222,21 @@ impl Canvas
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn viewport_logical( &self ) -> ( f32, f32 )
|
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();
|
let scale = self.dpi_scale();
|
||||||
if scale > 0.0
|
if scale > 0.0
|
||||||
{
|
{
|
||||||
( pw as f32 / scale, ph as f32 / scale )
|
( pw / scale, ph / scale )
|
||||||
} else {
|
} 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.
|
/// by `dpi_scale` at raster time, so they must NOT use this.
|
||||||
pub fn viewport_layout( &self ) -> ( f32, f32 )
|
pub fn viewport_layout( &self ) -> ( f32, f32 )
|
||||||
{
|
{
|
||||||
|
if let Some( v ) = self.layout_viewport() { return v; }
|
||||||
let ( pw, ph ) = self.size();
|
let ( pw, ph ) = self.size();
|
||||||
( pw as f32, ph as f32 )
|
( 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,
|
/// Resolve a stock-widget **geometry** design pixel (height, padding,
|
||||||
/// box size, gap…) through the process-wide [`crate::WidgetScaling`]
|
/// box size, gap…) through the process-wide [`crate::WidgetScaling`]
|
||||||
/// mode, into a concrete physical-pixel value for the layout tree.
|
/// mode, into a concrete physical-pixel value for the layout tree.
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ impl SoftwareCanvas
|
|||||||
font_face: handle.face,
|
font_face: handle.face,
|
||||||
font_registry: None,
|
font_registry: None,
|
||||||
dpi_scale: 1.0,
|
dpi_scale: 1.0,
|
||||||
|
layout_viewport: None,
|
||||||
global_alpha: 1.0,
|
global_alpha: 1.0,
|
||||||
glyph_cache: HashMap::new(),
|
glyph_cache: HashMap::new(),
|
||||||
clip_mask: None,
|
clip_mask: None,
|
||||||
@@ -54,6 +55,8 @@ impl SoftwareCanvas
|
|||||||
font_face: self.font_face,
|
font_face: self.font_face,
|
||||||
font_registry: self.font_registry.as_ref().map( Arc::clone ),
|
font_registry: self.font_registry.as_ref().map( Arc::clone ),
|
||||||
dpi_scale: self.dpi_scale,
|
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,
|
global_alpha: self.global_alpha,
|
||||||
glyph_cache: HashMap::new(),
|
glyph_cache: HashMap::new(),
|
||||||
clip_mask: None,
|
clip_mask: None,
|
||||||
|
|||||||
Reference in New Issue
Block a user