azul_layout/lib.rs
1//! Layout crate for the Azul GUI framework.
2//!
3//! Provides the layout solver (`solver3`), text shaping (`text3`), font
4//! management (`font`), hit testing, page fragmentation, and widget support.
5//! Integrates with `azul-core` for DOM types and `azul-css` for style
6//! properties.
7
8#![doc(
9 html_logo_url = "https://raw.githubusercontent.com/maps4print/azul/master/assets/images/azul_logo_full_min.svg.png",
10 html_favicon_url = "https://raw.githubusercontent.com/maps4print/azul/master/assets/images/favicon.ico"
11)]
12// Lint policy: deny correctness/safety issues, warn on style (`clippy::all`).
13//
14// Crate-wide allows are intentionally limited to lints that are either
15// (a) pervasive AND feature-sensitive — an import/binding/field that is unused
16// under one feature set is live under another, so a per-site fix would
17// break a different feature build — or
18// (b) churny / newer-toolchain lints with little value in scoping.
19// Lints that fire in only a few, well-localized places are scoped with
20// `#[allow(...)]` on the specific `pub mod` declarations further down, so the
21// rest of the (hand-written) crate is actually checked.
22#![deny(unused_must_use)]
23#![warn(clippy::all)]
24// === "extreme lints" lockdown (2026-06-20) — maximal opt-in lint set ===
25// All clippy groups + opt-in rustc lints, warn-level so normal builds still
26// pass; the CI clippy job runs `-D warnings`, turning every one of these into
27// the outstanding-lint-failure report for Monday triage. NOT yet fixed.
28#![warn(
29 clippy::pedantic,
30 clippy::nursery,
31 clippy::cargo,
32 // missing_docs, // TODO(docs): re-enable as a dedicated final docs pass; disabled
33 // // for now so the cleanup focuses on code-quality lints, not doc debt.
34 missing_debug_implementations,
35 missing_copy_implementations,
36 unreachable_pub,
37 unused_qualifications,
38 unused_lifetimes,
39 unused_import_braces,
40 unused_macro_rules,
41 unused_crate_dependencies,
42 meta_variable_misuse,
43 trivial_casts,
44 trivial_numeric_casts,
45 elided_lifetimes_in_paths,
46 single_use_lifetimes,
47 variant_size_differences,
48 non_ascii_idents,
49 unsafe_op_in_unsafe_fn,
50 let_underscore_drop,
51)]
52#![allow(
53 // `unknown_lints` lets the two forward-compat lints below be listed even on
54 // the CI toolchain (1.88), where they are not yet known, without emitting an
55 // "unknown lint" warning of their own. They still apply on newer rustc.
56 unknown_lints,
57 mismatched_lifetime_syntaxes, // newer rustc; fires in macro-generated code
58 function_casts_as_integer, // newer rustc; widget callback pointer identity
59 // pervasive + feature-sensitive (unused under one feature, live under another):
60 unused_imports,
61 unused_variables,
62 unused_mut,
63 dead_code,
64 // design lint, pervasive across the layout solver / renderer:
65 clippy::too_many_arguments,
66 // churny / 3rd-party, low value to scope:
67 clippy::legacy_numeric_constants,
68 unexpected_cfgs, // web-lift diagnostic cfgs
69 deprecated, // image crate tiff encoder (only under `tiff`)
70 // transitive dependency-version dups not resolvable in azul's source —
71 // syn 1↔2 (proc-macro migration), heck/jni-sys/rustc-hash/rustls-webpki;
72 // re-audit when the dep tree aligns.
73 clippy::multiple_crate_versions,
74)]
75
76#[macro_use]
77extern crate alloc;
78extern crate core;
79
80// Dependencies kept for downstream/feature-plumbing use but not referenced
81// directly in this crate's source — marked intentionally linked so
82// unused_crate_dependencies stays quiet (the lint's own suggested fix).
83// `brotli-decompressor`: decompresses the codegen material_icons.ttf.br in azul-dll.
84#[cfg(feature = "icons")]
85use brotli_decompressor as _;
86// `lru`: reserved for the slippy-map tile cache (azul-dll widgets).
87use lru as _;
88// `unicode-normalization` / `xmlwriter`: pulled by text_layout / xml for the
89// shaping + SVG-writer paths consumed downstream.
90#[cfg(feature = "text_layout")]
91use unicode_normalization as _;
92#[cfg(feature = "xml")]
93use xmlwriter as _;
94
95/// Web-lift diagnostic marker: a volatile store of `val` to the absolute wasm
96/// linear-memory address `addr` (the 0x40000–0xF0000 free band the e2e harness
97/// peeks via `AzStartup_peekU32`).
98///
99/// Compiles to NOTHING without the `web_lift`
100/// feature — absolute-address stores would segfault native builds (macOS
101/// `__PAGEZERO` covers the low 4 GiB). All in-tree diagnostic markers MUST go
102/// through this helper rather than calling `core::ptr::write_volatile` on a
103/// literal address directly.
104///
105/// # Safety
106///
107/// With the `web_lift` feature enabled, `addr` must be a valid, writable wasm
108/// linear-memory address (within the 0x40000–0xF0000 diagnostic band). Without
109/// the feature this is a no-op and always safe.
110#[cfg(feature = "web_lift")]
111#[inline]
112pub unsafe fn az_mark(_addr: u32, _val: u32) {
113 // Volatile isn't const-callable, so this variant is a plain (non-const) fn.
114 core::ptr::write_volatile(_addr as usize as *mut u32, _val);
115}
116/// No-op `const` variant used when the `web_lift` feature is off.
117///
118/// # Safety
119///
120/// Always safe — this variant does nothing; the `unsafe` marker only exists to
121/// keep the signature identical to the `web_lift` variant so call sites compile
122/// unchanged under both features.
123#[cfg(not(feature = "web_lift"))]
124#[inline]
125pub const unsafe fn az_mark(_addr: u32, _val: u32) {}
126
127/// Read counterpart of [`az_mark`] (marker counters like `0x60758`).
128/// Returns 0 without the `web_lift` feature.
129///
130/// # Safety
131///
132/// With the `web_lift` feature enabled, `addr` must be a valid, readable wasm
133/// linear-memory address (within the 0x40000–0xF0000 diagnostic band). Without
134/// the feature this is a no-op that returns 0 and is always safe.
135#[cfg(feature = "web_lift")]
136#[inline]
137#[must_use] pub unsafe fn az_mark_read(_addr: u32) -> u32 {
138 // Volatile isn't const-callable, so this variant is a plain (non-const) fn.
139 core::ptr::read_volatile(_addr as usize as *const u32)
140}
141/// No-op `const` variant (returns 0) used when the `web_lift` feature is off.
142///
143/// # Safety
144///
145/// Always safe — returns 0 and touches nothing; the `unsafe` marker only exists
146/// to keep the signature identical to the `web_lift` variant.
147#[cfg(not(feature = "web_lift"))]
148#[inline]
149#[must_use] pub const unsafe fn az_mark_read(_addr: u32) -> u32 {
150 0
151}
152
153/// Font traits available regardless of text layout feature.
154pub mod font_traits;
155/// Optional probe instrumentation. With the `probe` feature off this
156/// is a tiny module of no-op stubs and pays zero cost.
157pub mod probe;
158/// Image decoding and encoding (wraps the `image` crate).
159#[cfg(feature = "image_decoding")]
160pub mod image;
161/// Scroll, hover, clipboard, cursor, and focus managers.
162#[cfg(feature = "text_layout")]
163// Scoped (was crate-wide): internal manager types exposed for tests.
164#[allow(private_interfaces)]
165pub mod managers;
166/// CSS layout solver: block, inline, flex, grid, and table formatting.
167#[cfg(feature = "text_layout")]
168// Scoped (was crate-wide): solver internals — intentional `drop(&_)` scope
169// markers, internal types exposed for tests, incremental-relayout assignments,
170// generated/parenthesized property code, and exhaustive generated matches.
171#[allow(
172 dropping_references,
173 private_interfaces,
174 unreachable_patterns,
175 unused_parens,
176 unused_doc_comments,
177 unused_assignments
178)]
179pub mod solver3;
180
181/// C-compatible string formatting via `strfmt`.
182#[cfg(feature = "strfmt")]
183pub mod fmt;
184#[cfg(feature = "strfmt")]
185pub use fmt::{FmtArg, FmtArgVec, FmtArgVecDestructor, FmtValue, fmt_string};
186
187/// Built-in widgets: button, text input, tabs, tree view, node graph, etc.
188#[cfg(feature = "widgets")]
189// Scoped (was crate-wide): incremental widget-state assignments and the
190// node_graph extern "C" fn that returns `()`.
191#[allow(unused_assignments, improper_ctypes_definitions)]
192pub mod widgets;
193
194/// Desktop platform helpers (file dialogs, notifications).
195#[cfg(feature = "extra")]
196pub mod desktop;
197
198/// ICU internationalization: date/time formatting, plurals, list formatting.
199#[cfg(any(
200 feature = "icu",
201 all(target_os = "macos", feature = "icu_macos"),
202 all(target_os = "windows", feature = "icu_windows"),
203))]
204pub mod icu;
205#[cfg(any(
206 feature = "icu",
207 all(target_os = "macos", feature = "icu_macos"),
208 all(target_os = "windows", feature = "icu_windows"),
209))]
210pub use icu::{
211 DateTimeFieldSet, FormatLength, IcuDate, IcuDateTime, IcuError,
212 IcuLocalizer, IcuLocalizerHandle, IcuResult, IcuStringVec, IcuTime,
213 LayoutCallbackInfoIcuExt, ListType, PluralCategory,
214};
215
216/// Project Fluent localization: message bundles, argument formatting, ZIP I/O.
217#[cfg(feature = "fluent")]
218pub mod fluent;
219#[cfg(feature = "fluent")]
220pub use fluent::{
221 check_fluent_syntax, check_fluent_syntax_bytes, create_fluent_zip,
222 create_fluent_zip_from_strings, export_to_zip, FluentError,
223 FluentLanguageInfo, FluentLanguageInfoVec, FluentLoadError, FluentLoadErrorVec,
224 FluentLocalizerHandle, FluentSyntaxCheckResult,
225 FluentZipLoadResult,
226};
227
228/// URL parsing (RFC 3986 compliant). Pure-Rust, always present (no TLS deps).
229/// URL types live in `azul_core::url`; re-exported so `azul_layout::url::*`
230/// keeps resolving. `Url::parse`/`join` are enabled via the `http` feature
231/// (which turns on `azul-core/url`).
232pub use azul_core::url;
233pub use azul_core::url::{Url, UrlParseError, ResultUrlUrlParseError};
234
235/// File system operations (C-compatible wrappers for `std::fs`).
236// Scoped (was crate-wide): `///` doc comments before `impl_vec!`/`impl_option!`
237// macro invocations, and an infallible inherent `FilePath::from_str` (returns
238// `Self`, so it cannot implement the fallible `FromStr` trait).
239#[allow(unused_doc_comments, clippy::should_implement_trait)]
240pub mod file;
241pub use file::{
242 dir_create, dir_create_all, dir_list, dir_delete, dir_delete_all,
243 file_append, file_copy, path_exists, file_metadata, file_read, file_read_string,
244 file_delete, file_rename, file_write, file_write_string,
245 path_canonicalize, path_extension, path_file_name, path_is_dir, path_is_file,
246 path_join, path_parent, temp_dir,
247 DirEntry, DirEntryVec, DirEntryVecDestructor, DirEntryVecDestructorType,
248 FileError, FileErrorKind, FileMetadata, FilePath, OptionFilePath,
249};
250
251/// HTTP client: GET/POST requests with pure-Rust TLS.
252///
253/// API surface always present (stub when off); ureq/rustls only pulled in with `http`.
254pub mod http;
255pub use http::{
256 download_bytes, download_bytes_with_config, http_get,
257 http_get_with_config, is_url_reachable, HttpError, HttpHeader,
258 HttpRequestConfig, HttpResponse, HttpResponseTooLargeError, HttpResult,
259 HttpStatusError,
260};
261
262/// JSON parsing and serialization for the C API.
263#[cfg(feature = "json")]
264pub mod json;
265#[cfg(feature = "json")]
266pub use json::{
267 json_parse, json_stringify,
268 Json, JsonInternal, JsonKeyValue, JsonKeyValueVec, JsonKeyValueVecDestructor, JsonKeyValueVecDestructorType,
269 JsonParseError, JsonType, JsonVec,
270 ResultJsonJsonParseError, OptionJson, OptionJsonVec, OptionJsonKeyValueVec,
271};
272
273/// ZIP file creation, extraction, and listing.
274#[cfg(feature = "zip")]
275pub mod zip;
276#[cfg(feature = "zip")]
277pub use zip::{
278 zip_create, zip_create_from_files, zip_extract_all, zip_list_contents,
279 ZipFile, ZipFileEntry, ZipFileEntryVec, ZipPathEntry, ZipPathEntryVec,
280 ZipReadConfig, ZipWriteConfig, ZipReadError, ZipWriteError,
281};
282
283/// Icon provider: resolves icons from Material Icons font, images, or ZIP packs.
284pub mod icon;
285pub use icon::{
286 // Resolver
287 default_icon_resolver,
288 // Data types for RefAny
289 ImageIconData, FontIconData,
290 // Helpers
291 register_image_icon,
292 register_font_icon,
293 register_icons_from_zip,
294 create_default_icon_provider,
295 register_material_icons,
296 register_embedded_material_icons,
297};
298// Re-export core icon types
299pub use azul_core::icon::{
300 IconProviderHandle, IconResolverCallbackType,
301 resolve_icons_in_styled_dom, OptionIconProviderHandle,
302};
303
304/// Callback handling for layout events (invocation, result processing).
305#[cfg(feature = "text_layout")]
306pub mod callbacks;
307/// CPU-based software rendering (no GPU required).
308#[cfg(feature = "cpurender")]
309// Scoped (was crate-wide): complex rasterizer signatures.
310#[allow(clippy::type_complexity)]
311pub mod cpurender;
312/// Glyph path and cell cache for CPU text rendering.
313#[cfg(feature = "cpurender")]
314pub mod glyph_cache;
315/// Default keyboard actions (copy, paste, select-all, undo, etc.).
316#[cfg(feature = "text_layout")]
317pub mod default_actions;
318/// Event determination: maps raw input to DOM node callbacks.
319#[cfg(feature = "text_layout")]
320pub mod event_determination;
321/// Font parsing, metrics extraction, and subsetting.
322#[cfg(feature = "text_layout")]
323// Scoped (was crate-wide): complex font-table signatures.
324#[allow(clippy::type_complexity)]
325pub mod font;
326
327/// Headless backend for CPU-only rendering without a display server.
328///
329/// Used with `AZUL_HEADLESS=1` for E2E testing, CI, and screenshot capture.
330#[cfg(feature = "text_layout")]
331pub mod headless;
332// Re-export allsorts types needed by printpdf
333#[cfg(feature = "text_layout")]
334pub use allsorts::subset::CmapTarget;
335#[cfg(feature = "text_layout")]
336pub use font::parsed::{
337 FontParseWarning, FontParseWarningSeverity, FontType, OwnedGlyph, ParsedFont, PdfFontMetrics,
338 SubsetFont,
339};
340// Re-export hyphenation for external crates (like printpdf)
341#[cfg(feature = "text_layout_hyphenation")]
342pub use hyphenation;
343/// Hit-testing: maps screen coordinates to DOM nodes.
344#[cfg(feature = "text_layout")]
345pub mod hit_test;
346/// Paged media: the `FragmentationContext` (continuous vs. paged) and page margins.
347/// The primitive types live in `azul_core::paged`; re-exported here so existing
348/// `azul_layout::paged::*` / `crate::paged::*` paths keep resolving.
349pub use azul_core::paged;
350/// Text shaping, line breaking (Knuth-Plass), and inline formatting.
351#[cfg(feature = "text_layout")]
352// Scoped (was crate-wide): internal types exposed for tests, a labelled
353// shaping loop, and complex shaping/cache signatures.
354#[allow(private_interfaces, unused_labels, clippy::type_complexity)]
355pub mod text3;
356/// Thread callback wrappers for the C API.
357#[cfg(feature = "text_layout")]
358pub mod thread;
359/// Timer callback wrappers for the C API.
360#[cfg(feature = "text_layout")]
361// Scoped (was crate-wide): hand-written `Ord`/`PartialOrd` on a timer type.
362#[allow(clippy::non_canonical_partial_ord_impl)]
363pub mod timer;
364/// Scroll physics timer for momentum-based smooth scrolling.
365#[cfg(feature = "text_layout")]
366pub mod scroll_timer;
367/// Window layout management: relayout, event processing, state sync.
368#[cfg(feature = "text_layout")]
369// Scoped (was crate-wide): parenthesized layout expressions.
370#[allow(unused_parens)]
371pub mod window;
372/// Window state types (keyboard, mouse, DPI, focus).
373#[cfg(feature = "text_layout")]
374pub mod window_state;
375/// XML and XHTML parsing for declarative UI definitions.
376#[cfg(feature = "xml")]
377// Scoped (was crate-wide): incremental parser-state assignments.
378#[allow(unused_assignments)]
379pub mod xml;
380
381// Export the main layout function and window management
382/// Canonical paged-media page margins (defined in [`paged`]).
383pub use paged::PageMargins;
384#[cfg(feature = "text_layout")]
385pub use hit_test::{CursorTypeHitTest, FullHitTest};
386#[cfg(feature = "text_layout")]
387pub use solver3::cache::LayoutCache as Solver3LayoutCache;
388#[cfg(feature = "text_layout")]
389pub use solver3::display_list::DisplayList as DisplayList3;
390#[cfg(feature = "text_layout")]
391pub use solver3::layout_document;
392#[cfg(feature = "text_layout")]
393pub use solver3::paged_layout::layout_document_paged;
394#[cfg(feature = "text_layout")]
395pub use solver3::{LayoutContext, LayoutError, Result as LayoutResult3};
396#[cfg(feature = "text_layout")]
397pub use text3::cache::{FontContext, FontManager, TextShapingCache};
398/// Backwards-compat alias for the old `TextLayoutCache` name.
399/// Will be dropped at the next API revision; new code should use
400/// [`TextShapingCache`] directly.
401#[cfg(feature = "text_layout")]
402pub use text3::cache::TextShapingCache as TextLayoutCache;
403#[cfg(feature = "font_async_registry")]
404pub use rust_fontconfig::registry::FcFontRegistry;
405#[cfg(feature = "text_layout")]
406pub use window::{CursorBlinkTimerAction, LayoutWindow, ScrollbarDragState, TooltipTimerAction};
407#[cfg(feature = "text_layout")]
408pub use managers::text_input::{PendingTextEdit, OptionPendingTextEdit};
409
410#[cfg(feature = "text_layout")]
411/// Parses raw font bytes into a [`FontRef`](azul_css::props::basic::FontRef)
412/// suitable for use in the layout system.
413// signature must match the `ParseFontFn = fn(LoadedFontSource) -> ...` callback type
414// (core/src/resources.rs) and the api.json export, so the owned param cannot become &.
415#[allow(clippy::needless_pass_by_value)]
416pub fn parse_font_fn(
417 source: azul_core::resources::LoadedFontSource,
418) -> Option<azul_css::props::basic::FontRef> {
419 use crate::font::parsed::ParsedFont;
420
421 ParsedFont::from_bytes(
422 source.data.as_ref(),
423 source.index as usize,
424 &mut Vec::new(), // Ignore warnings for now
425 )
426 .map(parsed_font_to_font_ref)
427}
428
429#[cfg(feature = "text_layout")]
430/// Wraps a [`ParsedFont`] in a [`FontRef`](azul_css::props::basic::FontRef),
431/// transferring ownership to the returned handle.
432pub fn parsed_font_to_font_ref(
433 parsed_font: ParsedFont,
434) -> azul_css::props::basic::FontRef {
435 use core::ffi::c_void;
436
437 extern "C" fn parsed_font_destructor(ptr: *mut c_void) {
438 unsafe {
439 drop(Box::from_raw(ptr.cast::<ParsedFont>()));
440 }
441 }
442
443 let boxed = Box::new(parsed_font);
444 let raw_ptr = Box::into_raw(boxed) as *const c_void;
445 azul_css::props::basic::FontRef::new(raw_ptr, parsed_font_destructor)
446}
447
448#[cfg(feature = "text_layout")]
449/// Recovers a reference to the [`ParsedFont`] stored inside a [`FontRef`](azul_css::props::basic::FontRef).
450///
451/// # Safety contract
452/// The `font_ref` must have been created by [`parsed_font_to_font_ref`],
453/// so that `font_ref.parsed` points to a valid `ParsedFont`.
454#[must_use] pub const fn font_ref_to_parsed_font(
455 font_ref: &azul_css::props::basic::FontRef,
456) -> &ParsedFont {
457 // SAFETY: `font_ref.parsed` was created by `parsed_font_to_font_ref`
458 // via `Box::into_raw`, so it points to a valid, aligned `ParsedFont`.
459 unsafe { &*font_ref.parsed.cast::<ParsedFont>() }
460}