photon_ui/lib.rs
1#![doc = include_str!("../README.md")]
2#![deny(dead_code)]
3#![deny(unused)]
4#![deny(unused_mut)]
5#![deny(clippy::missing_safety_doc)]
6#![deny(clippy::undocumented_unsafe_blocks)]
7#![cfg_attr(not(test), deny(clippy::expect_used))]
8#![cfg_attr(not(test), deny(clippy::unwrap_used))]
9// for @siennathesane's sanity and to make it clear the scope of error handling. and because it's
10// super fucking subtle and i'll miss it in code reviews sorry not sorry
11#![deny(clippy::question_mark_used)]
12// just keeps syntax consistent
13#![deny(clippy::needless_borrow)]
14// personal preference.
15#![allow(bindings_with_variant_name)]
16
17/// Fuzzy autocomplete engine.
18pub mod autocomplete;
19/// UI components (text, input, editor, table, etc.).
20pub mod components;
21/// Layer compositor.
22pub mod compositor;
23/// Event abstraction over crossterm.
24pub mod events;
25/// Fuzzy matching logic.
26pub mod fuzzy;
27/// Terminal image protocol encoding.
28pub mod image;
29/// Keybindings manager and default action maps.
30pub mod keybindings;
31/// Clipboard / kill-ring for editors.
32pub mod kill_ring;
33/// Full-terminal-size stacking surfaces.
34pub mod layer;
35/// Constraint-based layout engine.
36pub mod layout;
37/// Differential terminal renderer.
38pub mod renderer;
39/// Terminal trait and test double.
40pub mod terminal;
41/// Beam Design Language theme system.
42pub mod theme;
43/// TUI runtime and focus management.
44pub mod tui;
45/// Undo / redo stacks.
46pub mod undo_stack;
47/// ANSI-aware text wrapping and width measurement.
48pub mod utils;
49/// Word-boundary navigation helpers.
50pub mod word_navigation;
51
52pub use crossterm::event::KeyEvent;
53pub use events::{
54 Event,
55 Key,
56 Modifiers,
57 matches_key,
58};
59pub use keybindings::{
60 KeybindingsManager,
61 default_bindings,
62};
63pub use layer::{
64 Layer,
65 Shadow,
66};
67pub use renderer::{
68 InputResult,
69 RenderError,
70 RenderStrategy,
71 Rendered,
72 Renderer,
73};
74pub use terminal::{
75 Terminal,
76 TestTerminal,
77};
78pub use tui::{
79 Anchor,
80 Overlay,
81 OverlayConstraints,
82 OverlayPosition,
83 TUI,
84};
85
86/// A UI element that can be rendered and respond to input.
87///
88/// All visible elements in a TUI application implement this trait. The
89/// framework calls [`render`](Component::render) on every frame and
90/// [`handle_input`](Component::handle_input) when the focused component should
91/// process an event.
92///
93/// Components that can receive focus should also implement [`Focusable`].
94pub trait Component {
95 /// Render this component into lines of text at the given width.
96 ///
97 /// The returned [`Rendered`] must satisfy the invariant that every line's
98 /// visible width is ≤ `width`.
99 fn render(&self, width: u16) -> Result<Rendered, RenderError>;
100
101 /// Render this component into a specific rectangular area.
102 ///
103 /// The default implementation delegates to [`render`](Component::render)
104 /// with the rect's width, ignoring height bounds. Components that want
105 /// to be layout-aware (e.g. clip to height, scroll, center vertically)
106 /// should override this.
107 fn render_rect(&self, rect: crate::layout::Rect) -> Result<Rendered, RenderError> {
108 self.render(rect.width)
109 }
110
111 /// Handle an input event (key press, resize, mouse, etc.).
112 ///
113 /// The default implementation ignores all events. Override this to add
114 /// interactivity.
115 fn handle_input(&mut self, _event: &events::Event) -> InputResult {
116 InputResult::Ignored
117 }
118
119 /// Returns `true` if this component wants to receive
120 /// `KeyEventKind::Release` events in addition to `Press` / `Repeat`.
121 ///
122 /// Most components should leave this as `false`.
123 fn wants_key_release(&self) -> bool {
124 false
125 }
126
127 /// Cast this component to a [`Focusable`] reference, if supported.
128 fn as_focusable(&self) -> Option<&dyn Focusable> {
129 None
130 }
131
132 /// Cast this component to a mutable [`Focusable`] reference, if supported.
133 fn as_focusable_mut(&mut self) -> Option<&mut dyn Focusable> {
134 None
135 }
136}
137
138/// Extension of [`Component`] for elements that can receive keyboard focus.
139///
140/// Focus is managed by [`TUI`]; only the focused component receives input
141/// events.
142pub trait Focusable: Component {
143 /// Returns `true` when this component currently has focus.
144 fn focused(&self) -> bool;
145
146 /// Set or clear the focused state.
147 fn set_focused(&mut self, focused: bool);
148}