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/// Event abstraction over crossterm.
22pub mod events;
23/// Fuzzy matching logic.
24pub mod fuzzy;
25/// Terminal image protocol encoding.
26pub mod image;
27/// Keybindings manager and default action maps.
28pub mod keybindings;
29/// Clipboard / kill-ring for editors.
30pub mod kill_ring;
31/// Constraint-based layout engine.
32pub mod layout;
33/// Differential terminal renderer.
34pub mod renderer;
35/// Terminal trait and test double.
36pub mod terminal;
37/// Beam Design Language theme system.
38pub mod theme;
39/// TUI runtime and focus management.
40pub mod tui;
41/// Undo / redo stacks.
42pub mod undo_stack;
43/// ANSI-aware text wrapping and width measurement.
44pub mod utils;
45/// Word-boundary navigation helpers.
46pub mod word_navigation;
47
48pub use crossterm::event::KeyEvent;
49pub use events::{
50 Event,
51 Key,
52 Modifiers,
53 matches_key,
54};
55pub use keybindings::{
56 KeybindingsManager,
57 default_bindings,
58};
59pub use renderer::{
60 InputResult,
61 RenderError,
62 RenderStrategy,
63 Rendered,
64 Renderer,
65};
66pub use terminal::{
67 Terminal,
68 TestTerminal,
69};
70pub use tui::{
71 Anchor,
72 Overlay,
73 OverlayConstraints,
74 OverlayPosition,
75 TUI,
76};
77
78/// A UI element that can be rendered and respond to input.
79///
80/// All visible elements in a TUI application implement this trait. The
81/// framework calls [`render`](Component::render) on every frame and
82/// [`handle_input`](Component::handle_input) when the focused component should
83/// process an event.
84///
85/// Components that can receive focus should also implement [`Focusable`].
86pub trait Component {
87 /// Render this component into lines of text at the given width.
88 ///
89 /// The returned [`Rendered`] must satisfy the invariant that every line's
90 /// visible width is ≤ `width`.
91 fn render(&self, width: u16) -> Result<Rendered, RenderError>;
92
93 /// Render this component into a specific rectangular area.
94 ///
95 /// The default implementation delegates to [`render`](Component::render)
96 /// with the rect's width, ignoring height bounds. Components that want
97 /// to be layout-aware (e.g. clip to height, scroll, center vertically)
98 /// should override this.
99 fn render_rect(&self, rect: crate::layout::Rect) -> Result<Rendered, RenderError> {
100 self.render(rect.width)
101 }
102
103 /// Handle an input event (key press, resize, mouse, etc.).
104 ///
105 /// The default implementation ignores all events. Override this to add
106 /// interactivity.
107 fn handle_input(&mut self, _event: &events::Event) -> InputResult {
108 InputResult::Ignored
109 }
110
111 /// Returns `true` if this component wants to receive
112 /// `KeyEventKind::Release` events in addition to `Press` / `Repeat`.
113 ///
114 /// Most components should leave this as `false`.
115 fn wants_key_release(&self) -> bool {
116 false
117 }
118
119 /// Cast this component to a [`Focusable`] reference, if supported.
120 fn as_focusable(&self) -> Option<&dyn Focusable> {
121 None
122 }
123
124 /// Cast this component to a mutable [`Focusable`] reference, if supported.
125 fn as_focusable_mut(&mut self) -> Option<&mut dyn Focusable> {
126 None
127 }
128}
129
130/// Extension of [`Component`] for elements that can receive keyboard focus.
131///
132/// Focus is managed by [`TUI`]; only the focused component receives input
133/// events.
134pub trait Focusable: Component {
135 /// Returns `true` when this component currently has focus.
136 fn focused(&self) -> bool;
137
138 /// Set or clear the focused state.
139 fn set_focused(&mut self, focused: bool);
140}