Skip to main content

densa_ui/
lib.rs

1//! The widget layer of densa: a retained tree of widgets, laid out with flexbox, painted
2//! onto a `densa_draw::Canvas`, and driven by pointer, keyboard and input method events.
3//!
4//! A [`Ui`] owns the widgets. The application adds them under the [root](Ui::root) with a
5//! [`Style`], then a window layer (such as `densa-window`) calls [`Ui::paint`] each frame and
6//! hands it input with [`Ui::pointer`], [`Ui::key`] and [`Ui::ime`]. Widgets report what
7//! the user did as [`Action`]s, read with [`Ui::actions`]. Nothing here depends on a window
8//! system or a GPU, so an interface runs, and is tested, headless.
9//!
10//! Layout runs only when a style, a widget's content or the viewport changes; painting,
11//! hit-testing and delivering events run every frame and do not allocate.
12//!
13//! Widgets also describe themselves to assistive technology ([`Widget::accessibility`]):
14//! [`Ui::access_tree`] is what a screen reader reads, and [`Ui::access_action`] does what it
15//! asks. Both are platform-neutral; `densa-window` connects them to the operating system.
16//!
17//! ```
18//! use densa_core::{Point, Size};
19//! use densa_paragraph::TextSystem;
20//! use densa_ui::{
21//!     ActionKind, Event, EventCx, Handled, PointerButton, PointerEvent, PointerKind, Style, Ui,
22//!     Widget,
23//! };
24//!
25//! /// Reports a click when pressed.
26//! struct Clickable;
27//!
28//! impl Widget for Clickable {
29//!     fn event(&mut self, cx: &mut EventCx<'_>, event: &Event<'_>) -> Handled {
30//!         match event {
31//!             Event::Pointer(p) if matches!(p.kind, PointerKind::Down(_)) => {
32//!                 cx.emit(ActionKind::Clicked);
33//!                 Handled::Yes
34//!             }
35//!             _ => Handled::No,
36//!         }
37//!     }
38//! }
39//!
40//! let mut ui = Ui::new(TextSystem::new()?);
41//! ui.resize(Size::new(400.0, 300.0));
42//! let button = ui.add(ui.root(), Clickable, Style::row().size(120.0, 32.0))?;
43//!
44//! let press = PointerKind::Down(PointerButton::Primary);
45//! ui.pointer(PointerEvent::new(press, Point::new(10.0, 10.0)));
46//! let clicked: Vec<_> = ui.actions().map(|action| action.source).collect();
47//! assert_eq!(clicked, [button]);
48//! # Ok::<(), Box<dyn std::error::Error>>(())
49//! ```
50
51mod access;
52mod access_tree;
53mod context;
54mod dispatch;
55mod event;
56mod id;
57mod style;
58mod theme;
59mod ui;
60mod widget;
61
62pub use access::{AccessAction, AccessId, AccessNode, AccessRequest, AccessTree, Role, MAX_PART};
63pub use context::{EventCx, LayoutCx, PaintCx};
64pub use event::{
65    Cursor, Event, Ime, Key, KeyEvent, Modifiers, NamedKey, PointerButton, PointerEvent,
66    PointerKind, ScrollDelta,
67};
68pub use id::WidgetId;
69pub use style::{Align, Direction, Edges, Justify, Length, Overflow, Position, Style};
70pub use theme::Theme;
71pub use ui::{Ui, UiError};
72pub use widget::{Action, ActionKind, Available, Constraints, Handled, Widget};