Bump to 0.2.0: SW/GLES paint parity, shared font resolution, FrameState refactor, docs and packaging fixes
Rendering parity (software ↔ GLES). The software backend now rounds glyph pen positions and image destinations to the nearest integer pixel, matching what the GLES backend already did; previously it truncated, so text and 1:1 images could land up to half a pixel off between the two backends and the bilinear sample read ~1 px softer than the source. Gradients and shadows are deliberately left unimplemented on the software backend, and the GLES multi-rect `glScissor` clip is left coarse on purpose: making it exact would need stencil bits the EGL config does not carry, or routing the partial-redraw path through the offscreen clip layer, which would break its `fill` / `clear_rects_transparent` scissor semantics. Adds software-backend pixel tests covering the snapping. Font resolution unification. The system-font candidate chain, `find_font_opt` and `load_default_font_bytes` lived in two copies (`render/helpers` and `gles_render/helpers`) that had already diverged — one resolved through `find_font_opt`, the other inlined the candidate loop — and now live once in `system_fonts`. The two per-backend `OnceLock` default-font caches and `primary_handle` collapse into a single `system_fonts::default_handle`. Module docs and the `font_registry` caller are updated accordingly. Shared image validation and rect inflation. `draw_image_data`'s dimension check and its one-line warning were byte-duplicated across both backends and are now `render::helpers::validate_rgba_dims`. The six manual symmetric `Rect`-inflate literals in the GLES primitives reuse the existing `Rect::expand`. FrameState and DrawCtx de-duplication. The eleven `SurfaceState` fields the draw pass owns and threads through `DrawCtx` — `widget_rects`, the cursor / selection maps, the scroll state, `accessible_extras`, `prev_focused` / `prev_hovered` / `prev_pressed` — move into a `FrameState` sub-struct. The per-frame `build_draw_ctx` / `commit_draw_ctx` helpers can then borrow `&mut ss.frame`, disjoint from `ss.canvas` and `ss.pool`, so the four frame paths (software / GLES × full / partial) replace their duplicated `DrawCtx` construction and write-back with a single helper call each. A whole-`SurfaceState` borrow could not express this (partial borrows do not cross function boundaries), which is why the helpers take the sub-struct. `content_dirty` stays on `SurfaceState` — it is an invalidation flag, not frame state — and is reset at the call site. rich_text tests. Adds the previously-missing `tests.rs` for the `RichText` widget: one hit rect per visual line a link spans (the widget's core invariant), the single-line and no-link cases, preferred-size growth with hard line breaks, and `map_msg` range preservation — all headless against a software `Canvas`, with line counts forced by `\n` so they do not depend on any system font's measured width. Documentation. Fills the rustdoc gaps on the embedder-facing surface: `core::UiSurface` accessors, the `egl_context` public API, the `GlesCanvas` methods, `theme::typography` and `theme::error`, and the `RichText` / `Text` builders; adds a crate-level "Rendering backends" overview. `CHANGELOG.md` is added (0.2.0 / 0.1.0), and `docs/widgets.md` / `docs/cookbook.md` gain `rich_text`, `external`, and the CPU-draw / path-clip / externally-laid-out-tree recipes. `debian/changelog` gets the 0.2.0-1 entry. Private intra-doc links to `system_fonts` are demoted to code spans so `cargo doc` is warning-free. Packaging. The `libltk-dev` registry crate shipped a `Cargo.toml` declaring the `lookup` bench while the `Makefile` install copied only `src/`, so Cargo refused to parse the manifest over a missing `benches/lookup.rs`; the install now ships `benches/` as well (the file alone satisfies the parse — criterion is a dev-dependency and is not resolved when the crate is consumed as a library). `Cargo.toml` is bumped to 0.2.0 to match the package version and the `ltk-0.2.0` registry directory.
This commit is contained in:
@@ -12,14 +12,14 @@
|
||||
//! elided (programs, VAO, default font all come from the parent).
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{ Arc, OnceLock };
|
||||
use std::sync::Arc;
|
||||
|
||||
use fontdue::{ Font, FontSettings, LineMetrics, Metrics };
|
||||
use fontdue::{ Font, LineMetrics, Metrics };
|
||||
use glow::HasContext;
|
||||
|
||||
use crate::theme::{ FontRegistry, FontStyle };
|
||||
|
||||
use super::helpers::{ alloc_fbo_tex, bytemuck_cast_slice, compile_program, compile_program_with_attribs, load_default_font_bytes };
|
||||
use super::helpers::{ alloc_fbo_tex, bytemuck_cast_slice, compile_program, compile_program_with_attribs };
|
||||
use super::shaders::
|
||||
{
|
||||
BACKDROP_BLUR_H_FRAG_SRC, BACKDROP_COMPOSITE_FRAG_SRC,
|
||||
@@ -35,34 +35,19 @@ use super::shaders::
|
||||
};
|
||||
use super::{ GlesCanvas, GlesVersion };
|
||||
|
||||
/// Process-wide cache of the GLES path's default font. Avoids
|
||||
/// re-reading + re-parsing the small Sora face on every surface
|
||||
/// bring-up. The fallback chain (Noto Sans / CJK / Devanagari / …)
|
||||
/// is owned by the crate-private system-fonts module and loaded
|
||||
/// lazily per codepoint, not per canvas.
|
||||
static DEFAULT_FONT_GLES: OnceLock<crate::system_fonts::FontHandle> = OnceLock::new();
|
||||
|
||||
fn default_handle_gles() -> crate::system_fonts::FontHandle
|
||||
{
|
||||
DEFAULT_FONT_GLES.get_or_init( ||
|
||||
{
|
||||
let bytes = load_default_font_bytes();
|
||||
let font = Font::from_bytes( bytes.as_slice(), FontSettings::default() )
|
||||
.expect( "bad font" );
|
||||
crate::system_fonts::FontHandle
|
||||
{
|
||||
font: Arc::new( font ),
|
||||
bytes: Arc::new( bytes ),
|
||||
face: 0,
|
||||
}
|
||||
} ).clone()
|
||||
}
|
||||
|
||||
impl GlesCanvas
|
||||
{
|
||||
/// Build a GPU canvas of `width × height` physical px on an already-current
|
||||
/// EGL/GLES context. Compiles every shader program, looks up its uniforms,
|
||||
/// uploads the shared quad geometry and the glyph atlas (format chosen per ES
|
||||
/// profile), then allocates the persistent shadow FBO and makes it the active
|
||||
/// draw target — every draw writes into the FBO, and [`Self::present`] is the
|
||||
/// only call that blits it to the default framebuffer. `dpi_scale` and
|
||||
/// `global_alpha` start at 1.0; the default font comes from the process-wide
|
||||
/// cached handle. Panics if the FBO is incomplete or a program fails to link.
|
||||
pub fn new( gl: Arc<glow::Context>, version: GlesVersion, width: u32, height: u32 ) -> Self
|
||||
{
|
||||
let font_handle = default_handle_gles();
|
||||
let font_handle = crate::system_fonts::default_handle();
|
||||
let font = font_handle.font.clone();
|
||||
let font_bytes = font_handle.bytes.clone();
|
||||
let font_face = font_handle.face;
|
||||
@@ -703,6 +688,7 @@ impl GlesCanvas
|
||||
}
|
||||
}
|
||||
|
||||
/// `( width, height )` of the FBO in physical px.
|
||||
pub fn size( &self ) -> ( u32, u32 ) { ( self.width, self.height ) }
|
||||
|
||||
/// Discard all cached gradient LUT textures. Call after a theme change
|
||||
@@ -723,14 +709,19 @@ impl GlesCanvas
|
||||
}
|
||||
}
|
||||
|
||||
/// DPI scale factor applied to font sizes before rasterisation.
|
||||
pub fn dpi_scale( &self ) -> f32 { self.dpi_scale }
|
||||
|
||||
/// Set the DPI scale factor applied to font sizes.
|
||||
pub fn set_dpi_scale( &mut self, s: f32 ) { self.dpi_scale = s; }
|
||||
|
||||
/// Global alpha multiplier applied to every draw (0.0 transparent, 1.0 opaque).
|
||||
pub fn global_alpha( &self ) -> f32 { self.global_alpha }
|
||||
|
||||
/// Set the global alpha multiplier applied to every draw.
|
||||
pub fn set_global_alpha( &mut self, a: f32 ) { self.global_alpha = a; }
|
||||
|
||||
/// The canvas default font, used when no specific face is resolved.
|
||||
pub fn font( &self ) -> &Font { &self.font }
|
||||
|
||||
/// Install a theme font registry so [`Self::font_for`] can resolve
|
||||
@@ -765,11 +756,15 @@ impl GlesCanvas
|
||||
crate::system_fonts::lookup( ch ).unwrap_or_else( || Arc::clone( &self.font ) )
|
||||
}
|
||||
|
||||
/// Glyph metrics for `ch` at `size` logical px, resolved through the fallback
|
||||
/// chain and pre-scaled by `dpi_scale`.
|
||||
pub fn font_metrics( &self, ch: char, size: f32 ) -> Metrics
|
||||
{
|
||||
self.font_for_char( ch ).metrics( ch, size * self.dpi_scale )
|
||||
}
|
||||
|
||||
/// Horizontal line metrics of the default font at `size` px (`None` if the
|
||||
/// font lacks them). Not pre-scaled by `dpi_scale`.
|
||||
pub fn font_line_metrics( &self, size: f32 ) -> Option<LineMetrics>
|
||||
{
|
||||
self.font.horizontal_line_metrics( size )
|
||||
|
||||
Reference in New Issue
Block a user