docs overhaul, orientation API, fluid-sizing fixes, examples made honest
CI / build + test (push) Has been cancelled
CI / cargo audit (push) Has been cancelled

Documentation pass: every claim in docs/ and the meta files was audited against the source and the drift fixed — around ninety corrections. CONTRIBUTING and the CI workflow now run cargo test with --features test-support (the gated test_support module made both the documented commands and the CI build fail to compile), make example becomes make examples, make doctest-md and the debhelper requirement of make clean are documented, and patch shape asks for a CHANGELOG entry. theming.md loses the nonexistent surface.backdrop, gains the real gradient defaults (linear-rgb, oklab), the six slot variants including typography, the ten-field palette, a truthful effects-consumer table, the ThemePreference/from_hour API and a responsive-sizing note; the stale docstrings in src/theme that fed the drift are fixed too. architecture.md's "Known gaps" section is rewritten against reality (multi-touch slots, xdg-activation, a11y live regions and SetValue/Increment/Decrement are implemented), gains a module map, subsurfaces and window-lifecycle coverage, and correct crustace/loginmanager paths. widgets.md fixes the ten factual errors (stateless spinner, toast/combo via overlays(), tooltip hover contract, row has no max_width, scroll axes, multiline text_edit, dialog panic wording) and now states the column() 16 px default padding — the recurring ambush — plus row's differing 0 default and dialog's max_width. onboarding, README and cookbook get the remaining sweep: build/test instructions, complete example lists, img_widget, clipping-parity honesty, ~30 Hz software cap, read_rgba_pixels signature, tab indentation in snippets, and rustdoc-style links that rendered literally are gone everywhere. CHANGELOG is restructured per Keep a Changelog with the missing entries (window_resizable, claims_raw_touch, Row::align_top/fill_height, caret fixes, dependency pins) and the pad_v Added/Changed contradiction resolved.
New adaptive-layout API: ltk::orientation() with the Orientation enum, backed by viewport_size()/set_viewport_size — the runtime records the main surface's physical dimensions on every configure, before App::on_resize, so view() can branch a layout on portrait vs landscape without hand-tracking resizes. The portrait rule matches Length::orient (square counts as portrait); embedders driving core::UiSurface call set_viewport_size themselves. Documented in the crate root's responsive-design section and architecture.md.
Fluid-vs-fixed sizing fixes in widgets, all the same disease — fluid content inside a fixed-pixel box. TextEdit::fixed_width takes impl Into<Length> (f32 call sites keep compiling as px) and the time picker's digit fields move to Length::fluid( 72.0 ), matching their fluid font so digits can no longer outgrow the box. Dialog::max_width takes impl Into<Length> with a Length::fluid( 480.0 ) default so the card scales with the stock buttons inside it, and the card's interior no longer stacks the column() default 16 px padding on top of CARD_PADDING — that double inset squeezed the action row until its buttons clipped on narrow windows. App::on_pointer_axis now triggers a view rebuild and repaint; previously state mutated in the hook did not paint until the next unrelated event.
Examples reworked to be honest demos: responsive's mode/density controls become stock buttons in a grid/column so they follow the modes they demonstrate instead of overflowing; dialog's openers stack vertically, and the example gains the app-level ESC handler so the ESC chain closes an open dialog first and quits second; widgets' tab strip now switches real per-tab pages; carousel gains pointer/touch drag through the horizontal-swipe hooks (crustace's pager pattern), one-tile-per-detent mouse wheel, and snap math driven by the real surface width from on_resize instead of a hardcoded 800; clip_path arranges its cells by ltk::orientation() and sizes them from the counter-axis of the flow.
This commit is contained in:
2026-07-30 19:28:26 +02:00
parent 14572ebfb6
commit 1fd697aa6d
33 changed files with 1131 additions and 661 deletions
+1 -1
View File
@@ -104,7 +104,7 @@ pub fn window_controls() -> WindowControlsSpec
default_window_controls( Palette::from_slots( &mode.slots ) )
}
/// The eight canonical palette slots of the active mode projected as a
/// The ten canonical palette slots of the active mode projected as a
/// [`Palette`] struct. This is a one-call shortcut equivalent to
/// `Palette::from_slots(&active_document().mode(active_mode()).slots)`,
/// covering the common case where a widget needs `text_primary` /
+3 -3
View File
@@ -312,9 +312,9 @@ pub fn tint_symbolic( rgba: &[u8], tint: Color ) -> Vec<u8>
/// Process-wide cache of rasterised theme icons, keyed by (absolute path on
/// disk, target longest-edge size in physical pixels). Entries are produced
/// by [`icon_rgba`] and never invalidated — the key embeds the absolute path
/// and the icon files are read-only on disk, so a `set_active_document`
/// switch produces fresh keys rather than serving stale data.
/// by [`icon_rgba`]. The key embeds the absolute path and the icon files
/// are read-only on disk, so entries never go stale; [`clear_svg_cache`]
/// empties the map on `set_active_document` purely to drop dead memory.
static SVG_CACHE: Mutex<Option<HashMap<( PathBuf, u32 ), ( Arc<Vec<u8>>, u32, u32 )>>>
= Mutex::new( None );
+11 -9
View File
@@ -16,10 +16,10 @@
//! /usr/share/ltk/themes/<id>/ (system overlay, lower priority)
//! ~/.local/share/ltk/themes/<id>/ (user overlay, higher priority)
//! theme.json
//! background-light.png
//! background-dark.png
//! fonts/
//! Sora-Regular.ttf
//! branding/{light,dark}/ wallpaper, lockscreen, logos
//! icons/apps/ per-application icons
//! icons/catalogue/{filled,line}/ symbolic glyph catalogue
//! cursors/ + cursor.theme consumed by the compositor
//! ```
//!
//! Paths inside `theme.json` are interpreted relative to the theme's
@@ -35,11 +35,13 @@
//! [`palette()`], …) cover the common patterns without going through the
//! full document.
//!
//! There is **no in-code fallback**: if `ensure_active` cannot locate the
//! `default` theme in any search path, the process aborts with a message
//! pointing at the `ltk-theme-default` Debian package (it `Provides:
//! ltk-theme`) or at the `LTK_THEMES_DIR` environment variable for
//! development installations.
//! When `ensure_active` cannot locate the `default` theme in any search
//! path, an embedded B/W fallback document is installed instead and the
//! process keeps running: [`is_fallback_active`] flips on, the draw path
//! stamps a red warning banner, and stderr points at the
//! `ltk-theme-default` Debian package (it `Provides: ltk-theme`) or at
//! the `LTK_THEMES_DIR` environment variable for development
//! installations.
use std::sync::{ Arc, OnceLock };
+4 -3
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
//! The eight-slot semantic [`Palette`] every widget speaks in terms of, plus
//! The ten-slot semantic [`Palette`] every widget speaks in terms of, plus
//! the derived [`WindowControlsSpec`] fallback used when a theme document
//! omits the explicit `window_controls` block.
@@ -46,10 +46,11 @@ pub struct Palette
impl Palette
{
/// Project a [`SlotStore`] onto the eight canonical palette fields.
/// Project a [`SlotStore`] onto the ten canonical palette fields.
/// Slot ids are the ones declared in the default theme JSON
/// (`bg-page`, `surface`, `surface-alt`, `text-primary`,
/// `text-secondary`, `accent`, `divider`, `icon`). Missing slots
/// `text-secondary`, `accent`, `divider`, `icon`, `danger`,
/// `danger-bg`). Missing slots
/// fall back to a documented sensible default so downstream widgets
/// never see uninitialised colours. Used by [`crate::theme::palette()`] and
/// [`crate::theme::window_controls`].
+2 -2
View File
@@ -63,8 +63,8 @@ pub enum Slot
Paint { value: Paint, meta: Metadata },
/// An ordered stack of outer shadows, typically an elevation level.
Shadows { value: Vec<Shadow>, meta: Metadata },
/// A composite surface: fill, outer shadows (ref or inline), inset
/// shadows and an optional backdrop.
/// A composite surface: fill, outer shadows (ref or inline) and
/// inset shadows.
Surface { value: Surface, meta: Metadata },
/// A resolved text style (family, weight, size, line-height, …).
TextStyle { value: TextStyle, meta: Metadata },