On a page with forge/web an ltk app had a main surface, overlays and subsurfaces, but none of what a shell such as crustace needs from the compositor: fullscreen did nothing, the app heard nothing of the other windows, an activation token it asked for never came, forge/web was never told when a launched window had shown, the shell could not speak its protocol, and the app had no way to read its own files in a browser. Each now goes through the app's connection to forge/web, in the shape of the Wayland protocol it stands for. Fullscreen. On a page with forge/web, `App::fullscreen` and `App::start_fullscreen` send a `set_fullscreen` request for the main window, as `xdg_toplevel.set_fullscreen` does, instead of doing nothing; alone on a page the canvas still takes the browser's fullscreen. `examples/web_hello.rs` gains a button that toggles it. The desktop's windows. On connecting, the backend asks for the list of toplevels, and forge/web's `toplevel_opened` and `toplevel_closed` events become `ToplevelEvent`s for `App::on_toplevel_event`, as `ext-foreign-toplevel-list-v1` does on Wayland, so a shell's dock knows which applications have windows open. Activation. The tokens an app asks for with `App::take_activation_requests` are issued before it draws: forge/web's, through its `activation_token` request, or empty without a desktop, as on Wayland without xdg-activation, and handed to `App::on_activation_token` with their messages processed. A browser app that launched others through tokens waited for one that never came. Each surface's first presented frame is reported with a `presented` request (`WebSurfaceState::presented`), so forge/web knows when a window it launched has shown and can report it activated, which dismisses crustace's splash. The shell protocol. `platform/web/shell.rs` carries the JSON a shell speaks to forge over its socket over the app's connection to forge/web: `ltk::web::shell_send( json )` sends it as a `shell` request, and each `shell` event's message reaches the listener `ltk::web::set_shell_listener` gives, as JSON. What is sent before the app has connected is kept and sent once it has, so a shell can tell forge/web its theme while it starts. An overlay's role now carries its `App::overlay_namespace`, as its layer-shell namespace does on Wayland, so forge/web can tell a shell's overlays apart. The cursor over every surface. The CSS `cursor` was set on the main surface's element only, so over an overlay the pointer kept the page's arrow. `WebSurfaceState` keeps the element that takes its input, the shape moves to that of the surface the pointer enters, and `WebPlatform::cursor` keeps the last one asked for. An app's own files. `ltk::vfs`, through which ltk reads its themes, fonts and keyboard layouts, is public: `read`, `read_to_string`, `read_dir`, `is_file` and `is_dir` read the file system natively and, in a browser, the files the page loaded into memory. `ltk::web::load_tree( base, root ).await` loads any tree served with a `MANIFEST` under `root`, and `load_resources` is that into `/ltk` with the directory made the resource directory. `ltk::time::Instant` is public too, `std`'s natively and `web_time`'s on wasm32, where `std::time::Instant::now` panics, so an app that builds for both measures time with ltk's clock. wasm-bindgen-cli. `scripts/wasm-bindgen-cli.sh TOOLS_DIR` builds wasm-bindgen-cli into `TOOLS_DIR/bin` at the version of the `wasm-bindgen` crate ltk pins in its `Cargo.toml`, from its sources with a lock resolved for Debian's rustc, which is what `make web-deps` did inline and now runs. Since `make install` copies `scripts/` into the registry, a project that builds for the browser against the installed ltk, such as forge/web or crustace, gets the same CLI without a checkout of ltk. The README and docs/architecture.md say so. Images named by their caller. Every draw through `draw_image_data` fingerprints its pixels to find them in the image caches, which for a terminal means hashing every glyph of every frame. `Canvas::draw_image_keyed( key, rgba, w, h, dest, opacity )` is `draw_image_data` with `key` naming the pixels in place of their fingerprint: every draw with the same key must pass the same pixels, which are read only when the image is not cached. Both backends' caches are keyed by `ImageId`, `Content` for a fingerprint and `Keyed` for a caller's key, so a key never names an image drawn by content even when it equals its fingerprint. `GlesCanvas` and `SoftwareCanvas` gain `draw_image_keyed`, and their `draw_image_data` and it share one private `draw_image`. Tests check that a keyed image draws as its pixels do and that a key equal to a fingerprint is another image. A held button is not also tapped. A button with `on_long_press` and no `on_drag_start` stayed a tap after its hold fired, so releasing it sent its `on_press` as well. `GestureState::long_press_msg_fired` is set when the hold fires `long_press_msg` and suppresses the press message on release; a test covers it. `CHANGELOG.md` describes these changes.
627 lines
26 KiB
Rust
Executable File
627 lines
26 KiB
Rust
Executable File
// SPDX-License-Identifier: LGPL-2.1-only
|
||
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
|
||
|
||
// Inside `unsafe fn` bodies, every unsafe op must still be wrapped in
|
||
// its own `unsafe { ... }` block. Without this lint the compiler treats
|
||
// the whole body as one implicit `unsafe` scope and a SAFETY: comment
|
||
// per call site becomes impossible to enforce — the kind of drift that
|
||
// hides UB in renderer code where every glow::* call is `unsafe fn`.
|
||
#![ deny( unsafe_op_in_unsafe_fn ) ]
|
||
// Without a backend there is no runtime to use the helpers it shares with the core.
|
||
#![ cfg_attr( not( backend ), allow( dead_code, unused_imports ) ) ]
|
||
// Wayland drives the whole runtime, so its build is where dead code shows; a backend that drives part of it would warn about the rest.
|
||
#![ cfg_attr( all( backend, not( feature = "wayland" ) ), allow( dead_code ) ) ]
|
||
// Drawing and damage passes take the geometry and state of a surface as plain arguments, and caches are keyed by tuples; splitting either up for the lint would only add types. The quick start keeps its `fn main` to show how an app starts.
|
||
#![ allow( clippy::too_many_arguments, clippy::type_complexity, clippy::needless_doctest_main ) ]
|
||
|
||
//! # ltk — Liberux ToolKit
|
||
//!
|
||
//! A lightweight Wayland UI toolkit built on top of
|
||
//! [smithay-client-toolkit](https://crates.io/crates/smithay-client-toolkit),
|
||
//! [tiny-skia](https://crates.io/crates/tiny-skia) and
|
||
//! [rustybuzz](https://crates.io/crates/rustybuzz).
|
||
//!
|
||
//! ltk is the UI toolkit for the Liberux desktop. The Liberux compositor (Forge)
|
||
//! handles window management, decorations, and positioning — ltk focuses on
|
||
//! rendering the content of each Wayland surface.
|
||
//!
|
||
//! `ltk` is also a public library for third-party developers building native
|
||
//! Wayland applications. If you are approaching the crate through `cargo doc`,
|
||
//! the API is grouped conceptually into three navigation modules:
|
||
//!
|
||
//! - [`window`] — the basic application window path most apps should start with
|
||
//! - [`shell`] — layer-shell and overlay APIs for shell-like surfaces
|
||
//! - [`runtime`] — advanced runtime hooks, invalidation, channels, and
|
||
//! runtime-free embedding via [`core::UiSurface`]
|
||
//!
|
||
//! ## Quick start
|
||
//!
|
||
//! ```rust,no_run
|
||
//! use ltk::{App, Element, column, text, button, spacer, Color, ButtonVariant};
|
||
//!
|
||
//! #[ derive( Clone ) ]
|
||
//! enum Msg { Quit }
|
||
//!
|
||
//! struct MyApp;
|
||
//!
|
||
//! impl App for MyApp
|
||
//! {
|
||
//! type Message = Msg;
|
||
//!
|
||
//! fn app_id( &self ) -> &str { "net.example.Hello" }
|
||
//!
|
||
//! // Nothing worth restoring in a one-button app.
|
||
//! fn save_state( &self ) -> Option<Vec<u8>> { None }
|
||
//! fn restore_state( &mut self, _state: Vec<u8> ) {}
|
||
//!
|
||
//! fn view( &self ) -> Element<Msg>
|
||
//! {
|
||
//! column()
|
||
//! .push( text( "Hello, ltk!" ).size( 32.0 ).color( Color::WHITE ) )
|
||
//! .push( spacer() )
|
||
//! .push( button( "Quit" ).on_press( Msg::Quit ) )
|
||
//! .into()
|
||
//! }
|
||
//!
|
||
//! fn update( &mut self, msg: Msg )
|
||
//! {
|
||
//! match msg { Msg::Quit => std::process::exit( 0 ) }
|
||
//! }
|
||
//! }
|
||
//!
|
||
//! fn main() { ltk::run( MyApp ); }
|
||
//! ```
|
||
//!
|
||
//! ## Architecture
|
||
//!
|
||
//! - **[`App`]** trait — implement this to define your application.
|
||
//! - **[`Element`]** enum — the widget tree returned by [`App::view`].
|
||
//! - Widgets, layouts and primitive types are listed below in their own
|
||
//! sidebar sections; the [`widgets`], [`layouts`] and [`types`] modules
|
||
//! are concept-oriented landing pages that `cargo doc` exposes for the
|
||
//! same set, grouped by category.
|
||
//!
|
||
//! ## Rendering backends
|
||
//!
|
||
//! All drawing goes through a single [`Canvas`], which is one of two interchangeable backends exposing the same API. The GLES backend (`GlesCanvas`) is the default when an EGL/OpenGL ES context is available and renders on the GPU; the software backend (`SoftwareCanvas`) rasterises on the CPU with tiny-skia and is the fallback when there is no GL context (and what offscreen/preview rendering uses). The two are kept at visual parity for the common primitives, with a few backend-specific traits documented per method (e.g. multi-rect [`Canvas::set_clip_rects`] is an exact mask on software but a bounding-box scissor on GLES, and [`Canvas::is_software`] lets a caller branch on a real path clip vs a bounding rect). [`run()`] selects the backend automatically; [`core::UiSurface`] lets an embedder force one.
|
||
//!
|
||
//! ## Widgets
|
||
//!
|
||
//! The interactive and decorative leaves of the [`Element`] tree:
|
||
//!
|
||
//! - **Buttons / activations** — [`button()`], [`icon_button`],
|
||
//! [`pressable`], [`window_button`], [`list_item()`].
|
||
//! - **Stateful binary controls** — [`toggle()`], [`checkbox()`],
|
||
//! [`radio()`].
|
||
//! - **Continuous controls** — [`slider()`], [`vslider()`],
|
||
//! [`progress_bar()`], [`pattern_grid()`].
|
||
//! - **Text** — [`text()`], [`text_edit()`].
|
||
//! - **Decoration** — [`container()`], [`separator()`], [`img_widget()`].
|
||
//! - **Clipping wrappers** — [`scroll()`], [`viewport()`], [`flex()`].
|
||
//! - **Modal overlays** — [`dialog()`] (centered confirmation card with
|
||
//! optional title, subtitle, custom body and action row; built-in
|
||
//! scrim, ESC-to-cancel, and tap-outside-to-dismiss for the
|
||
//! non-modal variant).
|
||
//!
|
||
//! See [`widgets`] for the grouped landing page and
|
||
//! `docs/widgets.md` for the per-widget catalogue.
|
||
//!
|
||
//! ## Layouts
|
||
//!
|
||
//! Composable arrangers for [`Element`] trees:
|
||
//!
|
||
//! - [`column()`] — vertical flow.
|
||
//! - [`row()`] — horizontal flow.
|
||
//! - [`stack()`] — z-order overlay with per-child alignment.
|
||
//! - [`grid()`] — fixed-column-count wrapping grid; [`grid_min_cell()`]
|
||
//! derives the column count from the width instead.
|
||
//! - [`spacer()`] — invisible flexible filler.
|
||
//!
|
||
//! See [`layouts`] for the grouped landing page.
|
||
//!
|
||
//! ## Types
|
||
//!
|
||
//! Geometry and primitive values that flow through every builder:
|
||
//!
|
||
//! - [`Color`], [`Rect`], [`Point`], [`Size`], [`Corners`], [`WidgetId`].
|
||
//! - [`Length`] — a size/distance that may be absolute pixels
|
||
//! ([`LengthBase::Px`]), relative to the surface viewport
|
||
//! ([`LengthBase::Vw`] / [`LengthBase::Vh`] / [`LengthBase::Vmin`] /
|
||
//! [`LengthBase::Vmax`] / [`LengthBase::Orient`]), to the root font
|
||
//! size ([`LengthBase::Em`]) or to the pixel density
|
||
//! ([`LengthBase::Dp`]). Every setter that takes a size, padding,
|
||
//! spacing or font height now accepts `impl Into<Length>`, so legacy
|
||
//! `.size( 24.0 )` keeps working while new code can write
|
||
//! `.size( Length::vmin( 4.0 ).clamp( 16.0, 32.0 ) )` for a typeface
|
||
//! that scales with the screen. See **Responsive design** below.
|
||
//!
|
||
//! See [`types`] for the full module with `//!` description.
|
||
//!
|
||
//! ## Responsive design: one UI from phone to desktop
|
||
//!
|
||
//! A core goal of ltk is that a **single view** reads coherently across
|
||
//! a portrait phone, a tablet and a landscape desktop window — no
|
||
//! per-target forks, no media-query soup. The mechanism is **fluid
|
||
//! sizing**: express sizes as a fraction of the surface so the whole
|
||
//! design breathes with the screen, instead of freezing at one pixel
|
||
//! size that only looks right on one device.
|
||
//!
|
||
//! ### The core rule — fluid, but clamped
|
||
//!
|
||
//! Default to `Length::vmin( pct ).clamp( lo, hi )` for every font size,
|
||
//! padding, spacing and spacer:
|
||
//!
|
||
//! - the percentage tracks the surface's **smaller** side, so portrait
|
||
//! and landscape stay coherent — an element keeps the same fraction of
|
||
//! the narrow axis whichever way the device is held;
|
||
//! - the px `clamp` bounds the fluid range so the design never collapses
|
||
//! on a watch-sized surface nor balloons on a 4K monitor.
|
||
//!
|
||
//! The clamp is not optional polish: it is what turns *pure* proportional
|
||
//! sizing (fragile at the extremes) into *bounded* proportional sizing
|
||
//! (robust everywhere). Treat "always clamp a fluid value" as the rule,
|
||
//! not the exception.
|
||
//!
|
||
//! ### Orientation-dependent proportion — [`Length::orient`]
|
||
//!
|
||
//! Sometimes the right *proportion* differs by orientation, not merely
|
||
//! the reference axis. A logo may want 40 % of the width in portrait —
|
||
//! there is vertical room to spare — but only 5 % of the height in
|
||
//! landscape, where vertical room is scarce.
|
||
//! [`Length::orient`]`( portrait, landscape )` expresses exactly that:
|
||
//! `portrait` % of the **width** when the surface is portrait,
|
||
//! `landscape` % of the **height** when it is landscape (the short side
|
||
//! of each orientation, but with its own proportion).
|
||
//!
|
||
//! When the *structure* of the layout should change with the
|
||
//! orientation — a row of panels in landscape, the same panels stacked
|
||
//! in portrait — branch the view on [`orientation()`] (backed by
|
||
//! [`viewport_size()`], recorded by the runtime on every configure and
|
||
//! sharing `orient`'s square-counts-as-portrait rule). See
|
||
//! `examples/clip_path.rs`.
|
||
//!
|
||
//! For images, pair it with
|
||
//! [`Image::short_side`](widget::image::Image::short_side), which sizes
|
||
//! the image along the screen's short side and lets the other axis follow
|
||
//! the source aspect ratio:
|
||
//!
|
||
//! ```rust,no_run
|
||
//! # use std::sync::Arc;
|
||
//! # use ltk::{ img_widget, Length, Element };
|
||
//! # #[ derive( Clone ) ] enum Msg {}
|
||
//! # fn _ex( rgba: Arc<Vec<u8>>, w: u32, h: u32 ) -> Element<Msg> {
|
||
//! // 40 % of the width in portrait, 5 % of the height in landscape.
|
||
//! img_widget( rgba, w, h ).short_side( Length::orient( 40.0, 5.0 ) ).into()
|
||
//! # }
|
||
//! ```
|
||
//!
|
||
//! ### When constant *physical* size matters instead
|
||
//!
|
||
//! Fluid units scale with the screen's pixels, not with real-world
|
||
//! millimetres — and legibility is a function of physical (angular) size,
|
||
//! not of what fraction of the screen a glyph fills. When a size must stay
|
||
//! a **constant physical size** across very different displays, use the
|
||
//! other mode: [`Length::dp`] (a density-independent pixel — `n ×` the
|
||
//! pixel density, applied when the value is resolved — the mainstream
|
||
//! HiDPI unit), or [`LengthBase::Em`] for text
|
||
//! relative to the root font size. The pre-calibrated
|
||
//! [`theme::typography`] scale
|
||
//! ([`theme::typography::h0`]…[`theme::typography::body_xs`]) is built on
|
||
//! clamped `vmin`, so it stays fluid while respecting a readable px floor
|
||
//! and ceiling — a good default for running text.
|
||
//!
|
||
//! ### The two modes, and choosing per app
|
||
//!
|
||
//! ltk offers both strategies as first-class citizens and lets the app
|
||
//! pick — per value, or process-wide for every stock widget:
|
||
//!
|
||
//! - **Fluid** — [`Length::fluid`] and the raw `vmin` / `vmax` / `vw` /
|
||
//! `vh` / `orient` units. Surface-proportional; tracks the short side
|
||
//! (width in portrait, height in landscape). Best for full-screen system
|
||
//! surfaces on known hardware (lock screen, greeter, splash, kiosk,
|
||
//! launcher).
|
||
//! - **Physical** — [`Length::dp`] plus [`set_density`] / [`density`].
|
||
//! Constant real-world size, HiDPI-aware. Best for conventional windowed
|
||
//! apps across an open-ended device set.
|
||
//!
|
||
//! Stock widgets carry one design pixel per dimension and resolve it
|
||
//! through the process-wide [`WidgetScaling`] mode
|
||
//! ([`set_widget_scaling`]): [`WidgetScaling::Fluid`] (the default) reads
|
||
//! it as [`Length::fluid`], [`WidgetScaling::Physical`] as [`Length::dp`].
|
||
//! An explicit [`Length`] on a widget always overrides the mode.
|
||
//!
|
||
//! ### Structural changes — branch in `view()`
|
||
//!
|
||
//! When the layout *structure* must change (sidebar → bottom tabs, two
|
||
//! columns → one) rather than merely resize, branch in `view()` on
|
||
//! `surface_width` / `surface_height`. Keep this for genuine
|
||
//! restructuring; [`Length`] covers all pure sizing.
|
||
//!
|
||
//! ## Runtime-free embedding
|
||
//!
|
||
//! Use [`core::UiSurface`] when you need ltk's layout, drawing and
|
||
//! hit-testing without [`run()`] — typically for compositor-side
|
||
//! decorations, embedding ltk widgets in another render loop, or
|
||
//! offscreen rendering / previews.
|
||
//!
|
||
//! ## Licence and third-party assets
|
||
//!
|
||
//! `ltk` itself is distributed under `LGPL-2.1-only`. The default
|
||
//! theme bundles two third-party asset sets that travel under their
|
||
//! own licences and must be credited when the toolkit (or a binary
|
||
//! that embeds the default theme) is redistributed:
|
||
//!
|
||
//! - **Symbolic icons** under `themes/default/icons/catalogue/` —
|
||
//! Streamline's *Core Line Free* set, [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/),
|
||
//! © Streamline. Some files modified for the symbolic-tinting
|
||
//! pipeline; details in `themes/default/icons/catalogue/LICENSE.md`.
|
||
//! Upstream: <https://www.streamlinehq.com/icons/core-line-free>.
|
||
//! - **Sora Regular** (`src/theme/fallback/Sora-Regular.otf`) — the
|
||
//! embedded font fallback, [SIL OFL 1.1](https://scripts.sil.org/OFL),
|
||
//! © The Sora Project Authors, Jonathan Barnbrook, Julián Moncada.
|
||
//! - **Pointer cursors** under `themes/default/cursors/` — GNOME's
|
||
//! *Adwaita* cursor theme (the cursors GNOME Shell ships), bundled
|
||
//! verbatim, © the GNOME Project. Offered upstream under
|
||
//! [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/) *or*
|
||
//! [LGPL 3](https://www.gnu.org/licenses/lgpl-3.0.html), and
|
||
//! [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) for
|
||
//! the newer assets; any one option satisfies the licence. See
|
||
//! `themes/default/cursors/README.md` (what the set is and how it is
|
||
//! used) and `themes/default/cursors/LICENSE.md` (attribution).
|
||
//! Upstream: <https://gitlab.gnome.org/GNOME/adwaita-icon-theme>.
|
||
//!
|
||
//! The remaining artwork in the default theme (wallpapers, lockscreens,
|
||
//! launcher logo, brand-mark variants, per-application icons) is
|
||
//! original to Liberux Labs and travels under the toolkit's own
|
||
//! `LGPL-2.1-only` licence. The full Debian-style declaration lives in
|
||
//! `debian/copyright` of the source tree.
|
||
|
||
// Load YAML locale files from `ltk/locales/*.yaml`. The `t!()` macro is
|
||
// re-exported (see below) so consuming applications can also use it; their
|
||
// own `i18n!()` invocations merge into the same runtime registry. The
|
||
// fallback locale is English — built-in widget strings always have an
|
||
// English entry.
|
||
rust_i18n::i18n!( "locales", fallback = "en" );
|
||
|
||
/// Serialises tests across modules that read or mutate the process-wide
|
||
/// density / [`WidgetScaling`] globals, so parallel test threads never
|
||
/// observe each other's transient state.
|
||
#[ cfg( test ) ]
|
||
pub( crate ) static TEST_GLOBALS_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new( () );
|
||
|
||
pub mod types;
|
||
pub mod font_face;
|
||
pub mod osk;
|
||
pub( crate ) mod render;
|
||
pub( crate ) mod system_fonts;
|
||
pub( crate ) mod text_shaping;
|
||
pub( crate ) mod a11y;
|
||
pub( crate ) mod widget;
|
||
pub( crate ) mod layout;
|
||
pub( crate ) mod app;
|
||
pub( crate ) mod channel;
|
||
pub mod theme;
|
||
pub mod wallpaper;
|
||
pub mod chassis;
|
||
pub mod nav_stack;
|
||
|
||
pub( crate ) mod tree;
|
||
pub( crate ) mod draw;
|
||
#[ cfg( backend ) ]
|
||
pub( crate ) mod input;
|
||
#[ cfg( backend ) ]
|
||
pub( crate ) mod event_loop;
|
||
#[ cfg( backend ) ]
|
||
pub( crate ) mod platform;
|
||
#[ cfg( feature = "wayland" ) ]
|
||
pub( crate ) mod protocol;
|
||
#[ cfg( feature = "wayland" ) ]
|
||
pub( crate ) mod session_state;
|
||
pub( crate ) mod secure_mem;
|
||
pub( crate ) mod resources;
|
||
pub mod time;
|
||
pub mod vfs;
|
||
pub mod gles_render;
|
||
#[ cfg( not( target_arch = "wasm32" ) ) ]
|
||
pub mod egl_context;
|
||
pub mod core;
|
||
|
||
pub use app::
|
||
{
|
||
Anchor, App, BackAtRoot, BackResponse, ChannelSender, ShellMode, Layer, OverlayId, OverlaySpec,
|
||
SubsurfaceId, SubsurfaceSpec, SubsurfaceParent,
|
||
InvalidationScope, MouseButton, MouseButtonEvent, RawKey, SelectionKind, SurfaceTarget, ToplevelEvent,
|
||
};
|
||
pub use theme::
|
||
{
|
||
Palette, ThemeMode, ThemePreference, ThemeError,
|
||
ThemeDocument, Mode, SlotStore,
|
||
WallpaperSpec, WallpaperFit, LauncherSpec, WindowControlsSpec,
|
||
active_document, active_mode, active_theme_id,
|
||
is_fallback_active,
|
||
set_active_document, set_active_mode,
|
||
tint_symbolic,
|
||
};
|
||
pub use theme::{ color as theme_color, color_or as theme_color_or };
|
||
pub use theme::{ paint as theme_paint, shadows as theme_shadows };
|
||
pub use theme::{ surface as theme_surface, text_style as theme_text_style };
|
||
pub use theme::resolve_surface as theme_resolve_surface;
|
||
pub use theme::palette as theme_palette;
|
||
pub use theme::window_controls as theme_window_controls;
|
||
pub use theme::wallpaper as theme_wallpaper;
|
||
pub use theme::lockscreen as theme_lockscreen;
|
||
pub use theme::app_icon as theme_app_icon;
|
||
pub use theme::app_default_icon as theme_app_default_icon;
|
||
pub use theme::launcher_icon as theme_launcher_icon;
|
||
pub use theme::logo as theme_logo;
|
||
pub use theme::logo_square as theme_logo_square;
|
||
pub use theme::logo_horizontal as theme_logo_horizontal;
|
||
pub use theme::branding_asset as theme_branding_asset;
|
||
pub use theme::branding_raster as theme_branding_raster;
|
||
pub use theme::branding_image as theme_branding_image;
|
||
pub use theme::{ decode_svg_bytes, icon_path as theme_icon_path, icon_rgba as theme_icon_rgba };
|
||
pub use gles_render::{ BorrowedGlesTexture, GlesVersion };
|
||
pub use render::is_software_render;
|
||
pub use render::Canvas;
|
||
pub use font_face::{ ColorGlyph, FontFace, LineMetrics, Metrics, OutlineBounds };
|
||
pub use osk::Purpose as InputPurpose;
|
||
pub use wallpaper::{ WallpaperBundle, ImageData };
|
||
pub use nav_stack::NavStack;
|
||
pub use chassis::{ set_default_theme, theme_logo_rgba, theme_icon_tinted, branding_bundle_or_solid, wallpaper_bundle_or_solid, backdrop };
|
||
pub use types::{ Color, CompositeMode, Corners, CursorShape, Length, LengthBase, PathCmd, Point, Rect, Size, WidgetId };
|
||
pub use types::{ WidgetScaling, FLUID_MIN, FLUID_MAX };
|
||
pub use types::{ fluid_reference, set_fluid_reference };
|
||
pub use types::{ density, set_density };
|
||
pub use types::{ set_text_scale, text_scale };
|
||
pub use types::{ widget_scaling, set_widget_scaling };
|
||
pub use types::{ Orientation, orientation, viewport_size, set_viewport_size };
|
||
pub use types::{ output_logical_size, set_output_logical_size };
|
||
pub use widget::{ Element, button, icon_button, text_edit, image as img_widget, text, container };
|
||
pub use text_shaping::{ caret_offsets, caret_offsets_with_font, line_metrics, measure_text };
|
||
pub use widget::rich_text::{ rich_text, RichText, LinkSpan };
|
||
pub use widget::button::ButtonVariant;
|
||
pub use widget::slider::{ Slider, slider, SliderAxis };
|
||
pub use widget::vslider::{ VSlider, vslider };
|
||
pub use widget::pattern_grid::{ PatternGrid, pattern_grid };
|
||
pub use widget::text::{ TextAlign, WRAP_PAD as TEXT_WRAP_PAD };
|
||
pub use widget::toggle::{ Toggle, toggle };
|
||
pub use widget::separator::{ Separator, separator };
|
||
pub use widget::progress_bar::{ ProgressBar, progress_bar };
|
||
pub use widget::checkbox::{ Checkbox, checkbox };
|
||
pub use widget::radio::{ Radio, radio };
|
||
pub use widget::list_item::{ ListItem, list_item };
|
||
pub use widget::window_button::{ WindowButton, WindowButtonKind, window_button, window_controls };
|
||
pub use widget::pressable::{ Pressable, pressable };
|
||
pub use widget::flex::{ Flex, flex };
|
||
pub use widget::combo::{ Combo, ComboState, combo };
|
||
pub use widget::spinner::{ Spinner, spinner };
|
||
pub use widget::tab_bar::{ TabBar, tabs };
|
||
pub use widget::anchored_overlay::{ anchored, AnchoredOverlay };
|
||
pub use widget::tab_strip::{ TabItem, TabLayout, TabMotion, TabOverview, TabStrip, TabStripColors, tab, tab_overview, tab_strip };
|
||
pub use widget::toast::{ Toast, toast };
|
||
pub use widget::tooltip::{ Tooltip, tooltip };
|
||
pub use widget::notebook::{ Notebook, NotebookPage, notebook };
|
||
pub use widget::external::{ External, ExternalSource };
|
||
pub use widget::external as widget_external;
|
||
pub use widget::date_picker::
|
||
{
|
||
Date, DatePicker, Locale as DateLocale, date_picker,
|
||
is_leap_year, days_in_month, day_of_week, add_months,
|
||
};
|
||
pub use widget::time_picker::{ Time, TimePicker, time_picker };
|
||
pub use widget::color_picker::
|
||
{
|
||
ColorPicker, color_picker, color_to_hex, parse_hex,
|
||
};
|
||
pub use widget::dialog::{ Dialog, dialog };
|
||
pub use layout::spacer::{ Spacer, spacer };
|
||
pub use layout::column::{ Column, column };
|
||
pub use layout::row::{ Row, row };
|
||
pub use layout::stack::{ Stack, stack, HAlign, VAlign };
|
||
// push_aligned_margin is available as a method on Stack — no separate re-export needed.
|
||
pub use layout::wrap_grid::{ WrapGrid, grid, grid_min_cell };
|
||
pub use widget::scroll::{ scroll, ScrollAxis };
|
||
pub use widget::viewport::{ Viewport, viewport };
|
||
pub use widget::carousel::{ Carousel, carousel };
|
||
#[ cfg( backend ) ]
|
||
pub use app::{ run, try_run, RunError };
|
||
#[ cfg( feature = "android" ) ]
|
||
pub use platform::android::{ keep_running, set_android_app, AndroidApp };
|
||
|
||
/// What only the browser backend offers.
|
||
#[ cfg( feature = "web" ) ]
|
||
pub mod web
|
||
{
|
||
pub use crate::platform::web::{ load_resources, load_tree, set_canvas, set_shell_listener, shell_send };
|
||
}
|
||
pub use resources::{ resource_dir, set_resource_dir };
|
||
pub use xkeysym::Keysym;
|
||
|
||
/// Widgets — the interactive and decorative leaves of the [`Element`]
|
||
/// tree.
|
||
///
|
||
/// Concept-oriented sidebar entry: every widget the toolkit ships is also
|
||
/// available at the crate root (`ltk::button`, `ltk::toggle`, …); this
|
||
/// module groups them so `cargo doc` shows a single landing page when you
|
||
/// are looking for "what controls can I draw".
|
||
///
|
||
/// See [`docs/widgets.md`](https://github.com/liberux/ltk/blob/master/docs/widgets.md)
|
||
/// for the per-widget catalogue with usage notes and minimal examples.
|
||
pub mod widgets
|
||
{
|
||
pub use crate::
|
||
{
|
||
// Buttons / activations
|
||
button, icon_button,
|
||
Pressable, pressable,
|
||
WindowButton, WindowButtonKind, window_button, window_controls,
|
||
ListItem, list_item,
|
||
// Stateful binary controls
|
||
Toggle, toggle,
|
||
Checkbox, checkbox,
|
||
Radio, radio,
|
||
// Continuous controls
|
||
Slider, slider, SliderAxis,
|
||
VSlider, vslider,
|
||
PatternGrid, pattern_grid,
|
||
ProgressBar, progress_bar,
|
||
// Composite picker
|
||
Combo, ComboState, combo,
|
||
// Activity / hint indicators
|
||
Spinner, spinner,
|
||
// Segmented selector + paginated tabs
|
||
TabBar, tabs,
|
||
Notebook, NotebookPage, notebook,
|
||
// Date / time / color pickers
|
||
Date, DatePicker, DateLocale, date_picker,
|
||
Time, TimePicker, time_picker,
|
||
ColorPicker, color_picker, color_to_hex, parse_hex,
|
||
// Transient overlays (return OverlaySpec)
|
||
Toast, toast,
|
||
Tooltip, tooltip,
|
||
// Modal / non-modal centered overlays
|
||
Dialog, dialog,
|
||
// Text input and display
|
||
text, text_edit, TextAlign,
|
||
// Decoration and chrome
|
||
container, Separator, separator,
|
||
img_widget,
|
||
// Clipping wrappers
|
||
scroll,
|
||
Viewport, viewport,
|
||
Flex, flex,
|
||
// Button styling token
|
||
ButtonVariant,
|
||
};
|
||
}
|
||
|
||
/// Layouts — composable arrangers for [`Element`] trees.
|
||
///
|
||
/// Concept-oriented sidebar entry. Each layout has a free constructor
|
||
/// (`column()`, `row()`, …) and a builder-style API for spacing, padding
|
||
/// and alignment. Layouts and [widgets] share the same `Element<Msg>`
|
||
/// tree.
|
||
pub mod layouts
|
||
{
|
||
pub use crate::
|
||
{
|
||
Column, column,
|
||
Row, row,
|
||
Stack, stack, HAlign, VAlign,
|
||
WrapGrid, grid, grid_min_cell,
|
||
Spacer, spacer,
|
||
};
|
||
}
|
||
|
||
/// Basic application-window API.
|
||
///
|
||
/// Start here if you are building a normal Wayland client window.
|
||
///
|
||
/// This module is documentation-first: it re-exports the common entry points
|
||
/// that most applications need so `cargo doc` presents a smaller and more
|
||
/// approachable surface before the user gets into overlays, gestures, and
|
||
/// shell-specific features.
|
||
///
|
||
/// The default path is:
|
||
///
|
||
/// 1. implement [`App`]
|
||
/// 2. return an [`Element`] tree from [`App::view`]
|
||
/// 3. mutate state in [`App::update`]
|
||
/// 4. start the event loop with [`run`]
|
||
///
|
||
/// If you are looking for layer-shell, overlays, or advanced runtime hooks,
|
||
/// move on to [`crate::shell`] or [`crate::runtime`].
|
||
pub mod window
|
||
{
|
||
pub use crate::
|
||
{
|
||
App, ButtonVariant, Color, Element, Keysym, Point, Rect, Size,
|
||
BackAtRoot, BackResponse, NavStack,
|
||
Column, Row, Stack, WrapGrid, Spacer,
|
||
button, icon_button, text, text_edit, img_widget,
|
||
container, checkbox, radio, toggle, separator,
|
||
progress_bar, list_item, slider, vslider, scroll, viewport,
|
||
column, row, stack, grid, grid_min_cell, spacer,
|
||
TextAlign, SliderAxis,
|
||
};
|
||
#[ cfg( backend ) ]
|
||
pub use crate::run;
|
||
}
|
||
|
||
/// Shell and layer-surface API.
|
||
///
|
||
/// Use this module when you are building shell-like surfaces rather than a
|
||
/// plain application window:
|
||
///
|
||
/// - panels
|
||
/// - docks
|
||
/// - homescreens
|
||
/// - greeters
|
||
/// - lock screens
|
||
/// - transient overlays
|
||
///
|
||
/// These items are also available at the crate root; this module exists so
|
||
/// `cargo doc` exposes a concept-oriented entry point for layer-shell users.
|
||
pub mod shell
|
||
{
|
||
pub use crate::
|
||
{
|
||
Anchor, App, Layer, OverlayId, OverlaySpec, ShellMode,
|
||
SurfaceTarget, InvalidationScope,
|
||
WindowButton, WindowButtonKind, window_button, window_controls,
|
||
};
|
||
}
|
||
|
||
/// Advanced runtime and embedding API.
|
||
///
|
||
/// This module groups the hooks that are useful once the basic app-window flow
|
||
/// is no longer enough:
|
||
///
|
||
/// - external wakeups via [`ChannelSender`]
|
||
/// - redraw narrowing via [`InvalidationScope`]
|
||
/// - surface-level invalidation targets via [`SurfaceTarget`]
|
||
/// - runtime-free embedding with [`crate::core::UiSurface`]
|
||
/// - direct theme/runtime state access
|
||
///
|
||
/// Most applications do not need to start here.
|
||
pub mod runtime
|
||
{
|
||
pub use crate::
|
||
{
|
||
ChannelSender, InvalidationScope, SurfaceTarget,
|
||
Palette, ThemeDocument, ThemeError, ThemeMode, ThemePreference,
|
||
WallpaperBundle, ImageData,
|
||
active_document, active_mode, active_theme_id,
|
||
is_fallback_active,
|
||
set_active_document, set_active_mode,
|
||
theme_color, theme_color_or, theme_paint, theme_palette,
|
||
theme_resolve_surface, theme_shadows, theme_surface, theme_text_style,
|
||
theme_wallpaper, theme_lockscreen, theme_window_controls,
|
||
theme_branding_asset, theme_branding_raster, theme_branding_image,
|
||
tint_symbolic,
|
||
};
|
||
pub use crate::core::{ RenderOptions, RenderOutput, UiSurface };
|
||
}
|
||
|
||
/// Internal helpers re-exported for the integration tests under `tests/` and
|
||
/// the criterion benches under `benches/`. The items themselves are `pub` but
|
||
/// live inside `pub(crate)` modules, so the only path that reaches them from
|
||
/// outside the crate is `ltk::test_support::*` — which is exactly what test
|
||
/// crates (and benches, which are also external) need.
|
||
///
|
||
/// **Not part of the stable public API.** Anything in here may change between
|
||
/// patch releases without notice. Hidden from generated docs via
|
||
/// `#[doc(hidden)]`, and gated behind the `test-support` feature so it is
|
||
/// absent from third-party builds entirely (ltk's own `make test` enables it).
|
||
#[ cfg( feature = "test-support" ) ]
|
||
#[ doc( hidden ) ]
|
||
pub mod test_support
|
||
{
|
||
pub use crate::tree::{ find_widget_at, find_widget, find_handlers, next_focusable_index };
|
||
pub use crate::widget::{ LaidOutWidget, WidgetHandlers };
|
||
pub use crate::widget::slider::{ value_from_x_in_rect, value_from_pos_in_rect, thumb_design_px, SliderAxis };
|
||
pub use crate::widget::vslider::value_from_y_in_rect;
|
||
#[ cfg( backend ) ]
|
||
pub use crate::event_loop::diff_overlay_ids;
|
||
}
|