Skip to main content

slt/context/
core.rs

1/// The main rendering context passed to your closure each frame.
2///
3/// Provides all methods for building UI: text, containers, widgets, and event
4/// handling. You receive a `&mut Context` on every frame and describe what to
5/// render by calling its methods. SLT collects those calls, lays them out with
6/// flexbox, diffs against the previous frame, and flushes only changed cells.
7///
8/// # Example
9///
10/// ```no_run
11/// slt::run(|ui: &mut slt::Context| {
12///     if ui.key('q') { ui.quit(); }
13///     ui.text("Hello, world!").bold();
14/// });
15/// ```
16pub struct Context {
17    // NOTE: If you add a mutable per-frame field, also add it to ContextSnapshot in error_boundary_with
18    pub(crate) commands: Vec<Command>,
19    pub(crate) events: Vec<Event>,
20    pub(crate) consumed: Vec<bool>,
21    pub(crate) should_quit: bool,
22    pub(crate) area_width: u32,
23    pub(crate) area_height: u32,
24    pub(crate) tick: u64,
25    pub(crate) focus_index: usize,
26    pub(crate) focus_count: usize,
27    pub(crate) hook_states: Vec<Box<dyn std::any::Any>>,
28    pub(crate) hook_cursor: usize,
29    prev_focus_count: usize,
30    pub(crate) modal_focus_start: usize,
31    pub(crate) modal_focus_count: usize,
32    prev_modal_focus_start: usize,
33    prev_modal_focus_count: usize,
34    scroll_count: usize,
35    prev_scroll_infos: Vec<(u32, u32)>,
36    prev_scroll_rects: Vec<Rect>,
37    interaction_count: usize,
38    pub(crate) prev_hit_map: Vec<Rect>,
39    pub(crate) group_stack: Vec<String>,
40    pub(crate) prev_group_rects: Vec<(String, Rect)>,
41    group_count: usize,
42    prev_focus_groups: Vec<Option<String>>,
43    _prev_focus_rects: Vec<(usize, Rect)>,
44    mouse_pos: Option<(u32, u32)>,
45    click_pos: Option<(u32, u32)>,
46    last_text_idx: Option<usize>,
47    overlay_depth: usize,
48    pub(crate) modal_active: bool,
49    prev_modal_active: bool,
50    pub(crate) clipboard_text: Option<String>,
51    debug: bool,
52    theme: Theme,
53    pub(crate) dark_mode: bool,
54    pub(crate) is_real_terminal: bool,
55    pub(crate) deferred_draws: Vec<Option<RawDrawCallback>>,
56    pub(crate) notification_queue: Vec<(String, ToastLevel, u64)>,
57    pub(crate) pending_tooltips: Vec<PendingTooltip>,
58    pub(crate) text_color_stack: Vec<Option<Color>>,
59    scroll_lines_per_event: u32,
60}
61
62type RawDrawCallback = Box<dyn FnOnce(&mut crate::buffer::Buffer, Rect)>;
63
64pub(crate) struct PendingTooltip {
65    pub anchor_rect: Rect,
66    pub lines: Vec<String>,
67}
68
69struct ContextSnapshot {
70    cmd_count: usize,
71    last_text_idx: Option<usize>,
72    focus_count: usize,
73    interaction_count: usize,
74    scroll_count: usize,
75    group_count: usize,
76    group_stack_len: usize,
77    overlay_depth: usize,
78    modal_active: bool,
79    modal_focus_start: usize,
80    modal_focus_count: usize,
81    hook_cursor: usize,
82    hook_states_len: usize,
83    dark_mode: bool,
84    deferred_draws_len: usize,
85    notification_queue_len: usize,
86    pending_tooltips_len: usize,
87    text_color_stack_len: usize,
88}
89
90impl ContextSnapshot {
91    fn capture(ctx: &Context) -> Self {
92        Self {
93            cmd_count: ctx.commands.len(),
94            last_text_idx: ctx.last_text_idx,
95            focus_count: ctx.focus_count,
96            interaction_count: ctx.interaction_count,
97            scroll_count: ctx.scroll_count,
98            group_count: ctx.group_count,
99            group_stack_len: ctx.group_stack.len(),
100            overlay_depth: ctx.overlay_depth,
101            modal_active: ctx.modal_active,
102            modal_focus_start: ctx.modal_focus_start,
103            modal_focus_count: ctx.modal_focus_count,
104            hook_cursor: ctx.hook_cursor,
105            hook_states_len: ctx.hook_states.len(),
106            dark_mode: ctx.dark_mode,
107            deferred_draws_len: ctx.deferred_draws.len(),
108            notification_queue_len: ctx.notification_queue.len(),
109            pending_tooltips_len: ctx.pending_tooltips.len(),
110            text_color_stack_len: ctx.text_color_stack.len(),
111        }
112    }
113
114    fn restore(&self, ctx: &mut Context) {
115        ctx.commands.truncate(self.cmd_count);
116        ctx.last_text_idx = self.last_text_idx;
117        ctx.focus_count = self.focus_count;
118        ctx.interaction_count = self.interaction_count;
119        ctx.scroll_count = self.scroll_count;
120        ctx.group_count = self.group_count;
121        ctx.group_stack.truncate(self.group_stack_len);
122        ctx.overlay_depth = self.overlay_depth;
123        ctx.modal_active = self.modal_active;
124        ctx.modal_focus_start = self.modal_focus_start;
125        ctx.modal_focus_count = self.modal_focus_count;
126        ctx.hook_cursor = self.hook_cursor;
127        ctx.hook_states.truncate(self.hook_states_len);
128        ctx.dark_mode = self.dark_mode;
129        ctx.deferred_draws.truncate(self.deferred_draws_len);
130        ctx.notification_queue.truncate(self.notification_queue_len);
131        ctx.pending_tooltips.truncate(self.pending_tooltips_len);
132        ctx.text_color_stack.truncate(self.text_color_stack_len);
133    }
134}
135