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//! Popups (menus, completion lists) open above every other widget with [`Ui::open_popup`],
14//! placed next to an [`Anchor`] and kept inside the viewport; a press outside closes them. A
15//! secondary press or the context menu key reports [`ActionKind::ContextMenu`], where an
16//! application opens its context menu.
17//!
18//! Widgets copy and paste through the Ui's [`Clipboard`] ([`EventCx::clipboard`]): a
19//! [`MemoryClipboard`] until the window gives it the system's.
20//!
21//! Widgets also describe themselves to assistive technology ([`Widget::accessibility`]):
22//! [`Ui::access_tree`] is what a screen reader reads, and [`Ui::access_action`] does what it
23//! asks. Both are platform-neutral; `densa-window` connects them to the operating system.
24//!
25//! ```
26//! use densa_core::{Point, Size};
27//! use densa_paragraph::TextSystem;
28//! use densa_ui::{
29//!     ActionKind, Event, EventCx, Handled, PointerButton, PointerEvent, PointerKind, Style, Ui,
30//!     Widget,
31//! };
32//!
33//! /// Reports a click when pressed.
34//! struct Clickable;
35//!
36//! impl Widget for Clickable {
37//!     fn event(&mut self, cx: &mut EventCx<'_>, event: &Event<'_>) -> Handled {
38//!         match event {
39//!             Event::Pointer(p) if matches!(p.kind, PointerKind::Down(_)) => {
40//!                 cx.emit(ActionKind::Clicked);
41//!                 Handled::Yes
42//!             }
43//!             _ => Handled::No,
44//!         }
45//!     }
46//! }
47//!
48//! let mut ui = Ui::new(TextSystem::new()?);
49//! ui.resize(Size::new(400.0, 300.0));
50//! let button = ui.add(ui.root(), Clickable, Style::row().size(120.0, 32.0))?;
51//!
52//! let press = PointerKind::Down(PointerButton::Primary);
53//! ui.pointer(PointerEvent::new(press, Point::new(10.0, 10.0)));
54//! let clicked: Vec<_> = ui.actions().map(|action| action.source).collect();
55//! assert_eq!(clicked, [button]);
56//! # Ok::<(), Box<dyn std::error::Error>>(())
57//! ```
58
59mod access;
60mod access_tree;
61mod clipboard;
62mod context;
63mod dispatch;
64mod event;
65mod id;
66mod popup;
67mod style;
68mod theme;
69mod ui;
70mod widget;
71
72pub use access::{AccessAction, AccessId, AccessNode, AccessRequest, AccessTree, Role, MAX_PART};
73pub use clipboard::{Clipboard, ClipboardError, MemoryClipboard};
74pub use context::{EventCx, LayoutCx, PaintCx};
75pub use event::{
76    Cursor, Event, Ime, Key, KeyEvent, Modifiers, NamedKey, PointerButton, PointerEvent,
77    PointerKind, ScrollDelta,
78};
79pub use id::WidgetId;
80pub use popup::Anchor;
81pub use style::{Align, Direction, Edges, Justify, Length, Overflow, Position, Style};
82pub use theme::Theme;
83pub use ui::{Ui, UiError};
84pub use widget::{Action, ActionKind, Available, Constraints, Handled, Widget};