Skip to main content

densa_ui/
context.rs

1//! What a widget can see and do while it measures, paints or handles an event.
2
3use densa_core::{Point, Rect, Size};
4use densa_paragraph::TextSystem;
5
6use crate::ui::State;
7use crate::{Action, ActionKind, Clipboard, Cursor, Style, Theme, WidgetId};
8
9/// What a widget gets while it measures itself or learns its size.
10#[derive(Debug)]
11pub struct LayoutCx<'a> {
12    pub(crate) text: &'a mut TextSystem,
13    pub(crate) theme: &'a Theme,
14    pub(crate) scale: f32,
15}
16
17impl LayoutCx<'_> {
18    /// The text system, to shape and wrap paragraphs.
19    pub fn text_system(&mut self) -> &mut TextSystem {
20        self.text
21    }
22
23    /// The theme.
24    pub fn theme(&self) -> &Theme {
25        self.theme
26    }
27
28    /// Physical pixels per logical pixel.
29    pub fn scale(&self) -> f32 {
30        self.scale
31    }
32
33    /// `logical` pixels in physical pixels.
34    pub fn px(&self, logical: f32) -> f32 {
35        logical * self.scale
36    }
37}
38
39/// What a widget gets while it paints.
40#[derive(Debug, Clone, Copy)]
41pub struct PaintCx<'a> {
42    pub(crate) state: &'a State,
43    pub(crate) id: WidgetId,
44}
45
46impl PaintCx<'_> {
47    /// The widget being painted.
48    pub fn id(&self) -> WidgetId {
49        self.id
50    }
51
52    /// Where the widget is, in physical pixels from the window's top-left corner.
53    pub fn rect(&self) -> Rect {
54        self.state.nodes[self.id.slot()].rect
55    }
56
57    /// The widget's rectangle inside its padding.
58    pub fn content_rect(&self) -> Rect {
59        self.state.nodes[self.id.slot()].content
60    }
61
62    /// How far the widget's children are scrolled.
63    pub fn scroll(&self) -> Point {
64        self.state.nodes[self.id.slot()].scroll
65    }
66
67    /// The size of what the widget's children cover, for scroll bars.
68    pub fn content_size(&self) -> Size {
69        self.state.nodes[self.id.slot()].content_size
70    }
71
72    /// Whether the pointer is over the widget or one of its children.
73    pub fn is_hovered(&self) -> bool {
74        self.state.nodes[self.id.slot()].hovered
75    }
76
77    /// Whether the widget has keyboard focus.
78    pub fn is_focused(&self) -> bool {
79        self.state.focus == Some(self.id)
80    }
81
82    /// Whether the widget handled a pointer press that has not been released yet.
83    pub fn is_pressed(&self) -> bool {
84        self.state.capture == Some(self.id)
85    }
86
87    /// Whether the pointer is over the widget itself, not one of its children.
88    pub fn is_hover_target(&self) -> bool {
89        self.state.hover == Some(self.id)
90    }
91
92    /// The widget's children, first painted first.
93    pub fn children(&self) -> &[WidgetId] {
94        &self.state.nodes[self.id.slot()].children
95    }
96
97    /// Where `id` is, if it is alive.
98    pub fn rect_of(&self, id: WidgetId) -> Option<Rect> {
99        self.state.node(id).map(|node| node.rect)
100    }
101
102    /// The theme.
103    pub fn theme(&self) -> &Theme {
104        &self.state.theme
105    }
106
107    /// Physical pixels per logical pixel.
108    pub fn scale(&self) -> f32 {
109        self.state.scale
110    }
111
112    /// `logical` pixels in physical pixels.
113    pub fn px(&self, logical: f32) -> f32 {
114        logical * self.state.scale
115    }
116}
117
118/// What a widget gets while it handles an event (or is updated by the application through
119/// [`Ui::update`](crate::Ui::update)): its state, and requests to the [`Ui`](crate::Ui).
120#[derive(Debug)]
121pub struct EventCx<'a> {
122    pub(crate) state: &'a mut State,
123    pub(crate) id: WidgetId,
124}
125
126impl EventCx<'_> {
127    /// The widget handling the event.
128    pub fn id(&self) -> WidgetId {
129        self.id
130    }
131
132    /// Where the widget is, in physical pixels from the window's top-left corner.
133    pub fn rect(&self) -> Rect {
134        self.state.nodes[self.id.slot()].rect
135    }
136
137    /// The widget's rectangle inside its padding.
138    pub fn content_rect(&self) -> Rect {
139        self.state.nodes[self.id.slot()].content
140    }
141
142    /// How far the widget's children are scrolled.
143    pub fn scroll(&self) -> Point {
144        self.state.nodes[self.id.slot()].scroll
145    }
146
147    /// The size of what the widget's children cover.
148    pub fn content_size(&self) -> Size {
149        self.state.nodes[self.id.slot()].content_size
150    }
151
152    /// Whether the pointer is over the widget or one of its children.
153    pub fn is_hovered(&self) -> bool {
154        self.state.nodes[self.id.slot()].hovered
155    }
156
157    /// Whether the widget has keyboard focus.
158    pub fn is_focused(&self) -> bool {
159        self.state.focus == Some(self.id)
160    }
161
162    /// Whether the widget handled a pointer press that has not been released yet.
163    pub fn is_pressed(&self) -> bool {
164        self.state.capture == Some(self.id)
165    }
166
167    /// The theme.
168    pub fn theme(&self) -> &Theme {
169        &self.state.theme
170    }
171
172    /// Physical pixels per logical pixel.
173    pub fn scale(&self) -> f32 {
174        self.state.scale
175    }
176
177    /// `logical` pixels in physical pixels.
178    pub fn px(&self, logical: f32) -> f32 {
179        logical * self.state.scale
180    }
181
182    /// The text system, to edit and re-lay out text.
183    pub fn text_system(&mut self) -> &mut TextSystem {
184        &mut self.state.text
185    }
186
187    /// Asks for the next frame to be painted.
188    pub fn request_paint(&mut self) {
189        self.state.needs_paint = true;
190    }
191
192    /// The clipboard, to copy to and paste from.
193    pub fn clipboard(&mut self) -> &mut dyn Clipboard {
194        self.state.clipboard.0.as_mut()
195    }
196
197    /// Asks for the widget to be measured again: its content changed size.
198    pub fn request_layout(&mut self) {
199        self.state.mark_dirty(self.id);
200    }
201
202    /// Asks for keyboard focus, given once the event has been handled.
203    pub fn request_focus(&mut self) {
204        self.state.requested_focus = Some(Some(self.id));
205    }
206
207    /// Gives keyboard focus up, if the widget has it.
208    pub fn release_focus(&mut self) {
209        if self.state.focus == Some(self.id) {
210            self.state.requested_focus = Some(None);
211        }
212    }
213
214    /// Stops receiving the pointer's moves and release after a press.
215    pub fn release_pointer(&mut self) {
216        if self.state.capture == Some(self.id) {
217            self.state.capture = None;
218        }
219    }
220
221    /// The cursor shown over the widget (and its children that set none); `None` for its
222    /// parent's.
223    pub fn set_cursor(&mut self, cursor: Option<Cursor>) {
224        self.state.nodes[self.id.slot()].cursor = cursor;
225    }
226
227    /// Where an input method should show its candidates while the widget has focus: the
228    /// caret's rectangle.
229    pub fn set_ime_area(&mut self, area: Option<Rect>) {
230        self.state.ime_area = area.map(|area| (self.id, area));
231    }
232
233    /// Reports `kind` to the application.
234    pub fn emit(&mut self, kind: ActionKind) {
235        let source = self.id;
236        self.state.actions.push_back(Action { source, kind });
237    }
238
239    /// Scrolls the widget's children by `offset` from their laid-out place, clamped so the
240    /// content stays in view. Returns the offset applied.
241    pub fn scroll_to(&mut self, offset: Point) -> Point {
242        self.state.scroll_to(self.id, offset)
243    }
244
245    /// The widget's children, first painted first.
246    pub fn children(&self) -> &[WidgetId] {
247        &self.state.nodes[self.id.slot()].children
248    }
249
250    /// Where `id` is, if it is alive.
251    pub fn rect_of(&self, id: WidgetId) -> Option<Rect> {
252        self.state.node(id).map(|node| node.rect)
253    }
254
255    /// The style of `id`, if it is alive.
256    pub fn style_of(&self, id: WidgetId) -> Option<&Style> {
257        self.state.node(id).map(|node| &node.style)
258    }
259
260    /// Whether keyboard focus is on `id` or a widget inside it.
261    pub fn focus_within(&self, id: WidgetId) -> bool {
262        self.state
263            .focus
264            .is_some_and(|focus| self.state.is_within(focus, id))
265    }
266
267    /// Changes the style of the widget itself or of a widget inside it (a splitter resizing
268    /// its panes, tabs showing one page); `false` for any other widget.
269    pub fn set_style(&mut self, id: WidgetId, style: Style) -> bool {
270        self.state.is_within(id, self.id) && self.state.set_style(id, style)
271    }
272}