1use densa_core::{Point, Rect, Size};
4use densa_paragraph::TextSystem;
5
6use crate::ui::State;
7use crate::{Action, ActionKind, Cursor, Style, Theme, WidgetId};
8
9#[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 pub fn text_system(&mut self) -> &mut TextSystem {
20 self.text
21 }
22
23 pub fn theme(&self) -> &Theme {
25 self.theme
26 }
27
28 pub fn scale(&self) -> f32 {
30 self.scale
31 }
32
33 pub fn px(&self, logical: f32) -> f32 {
35 logical * self.scale
36 }
37}
38
39#[derive(Debug, Clone, Copy)]
41pub struct PaintCx<'a> {
42 pub(crate) state: &'a State,
43 pub(crate) id: WidgetId,
44}
45
46impl PaintCx<'_> {
47 pub fn id(&self) -> WidgetId {
49 self.id
50 }
51
52 pub fn rect(&self) -> Rect {
54 self.state.nodes[self.id.slot()].rect
55 }
56
57 pub fn content_rect(&self) -> Rect {
59 self.state.nodes[self.id.slot()].content
60 }
61
62 pub fn scroll(&self) -> Point {
64 self.state.nodes[self.id.slot()].scroll
65 }
66
67 pub fn content_size(&self) -> Size {
69 self.state.nodes[self.id.slot()].content_size
70 }
71
72 pub fn is_hovered(&self) -> bool {
74 self.state.nodes[self.id.slot()].hovered
75 }
76
77 pub fn is_focused(&self) -> bool {
79 self.state.focus == Some(self.id)
80 }
81
82 pub fn is_pressed(&self) -> bool {
84 self.state.capture == Some(self.id)
85 }
86
87 pub fn is_hover_target(&self) -> bool {
89 self.state.hover == Some(self.id)
90 }
91
92 pub fn children(&self) -> &[WidgetId] {
94 &self.state.nodes[self.id.slot()].children
95 }
96
97 pub fn rect_of(&self, id: WidgetId) -> Option<Rect> {
99 self.state.node(id).map(|node| node.rect)
100 }
101
102 pub fn theme(&self) -> &Theme {
104 &self.state.theme
105 }
106
107 pub fn scale(&self) -> f32 {
109 self.state.scale
110 }
111
112 pub fn px(&self, logical: f32) -> f32 {
114 logical * self.state.scale
115 }
116}
117
118#[derive(Debug)]
121pub struct EventCx<'a> {
122 pub(crate) state: &'a mut State,
123 pub(crate) id: WidgetId,
124}
125
126impl EventCx<'_> {
127 pub fn id(&self) -> WidgetId {
129 self.id
130 }
131
132 pub fn rect(&self) -> Rect {
134 self.state.nodes[self.id.slot()].rect
135 }
136
137 pub fn content_rect(&self) -> Rect {
139 self.state.nodes[self.id.slot()].content
140 }
141
142 pub fn scroll(&self) -> Point {
144 self.state.nodes[self.id.slot()].scroll
145 }
146
147 pub fn content_size(&self) -> Size {
149 self.state.nodes[self.id.slot()].content_size
150 }
151
152 pub fn is_hovered(&self) -> bool {
154 self.state.nodes[self.id.slot()].hovered
155 }
156
157 pub fn is_focused(&self) -> bool {
159 self.state.focus == Some(self.id)
160 }
161
162 pub fn is_pressed(&self) -> bool {
164 self.state.capture == Some(self.id)
165 }
166
167 pub fn theme(&self) -> &Theme {
169 &self.state.theme
170 }
171
172 pub fn scale(&self) -> f32 {
174 self.state.scale
175 }
176
177 pub fn px(&self, logical: f32) -> f32 {
179 logical * self.state.scale
180 }
181
182 pub fn text_system(&mut self) -> &mut TextSystem {
184 &mut self.state.text
185 }
186
187 pub fn request_paint(&mut self) {
189 self.state.needs_paint = true;
190 }
191
192 pub fn request_layout(&mut self) {
194 self.state.mark_dirty(self.id);
195 }
196
197 pub fn request_focus(&mut self) {
199 self.state.requested_focus = Some(Some(self.id));
200 }
201
202 pub fn release_focus(&mut self) {
204 if self.state.focus == Some(self.id) {
205 self.state.requested_focus = Some(None);
206 }
207 }
208
209 pub fn release_pointer(&mut self) {
211 if self.state.capture == Some(self.id) {
212 self.state.capture = None;
213 }
214 }
215
216 pub fn set_cursor(&mut self, cursor: Option<Cursor>) {
219 self.state.nodes[self.id.slot()].cursor = cursor;
220 }
221
222 pub fn set_ime_area(&mut self, area: Option<Rect>) {
225 self.state.ime_area = area.map(|area| (self.id, area));
226 }
227
228 pub fn emit(&mut self, kind: ActionKind) {
230 let source = self.id;
231 self.state.actions.push_back(Action { source, kind });
232 }
233
234 pub fn scroll_to(&mut self, offset: Point) -> Point {
237 self.state.scroll_to(self.id, offset)
238 }
239
240 pub fn children(&self) -> &[WidgetId] {
242 &self.state.nodes[self.id.slot()].children
243 }
244
245 pub fn rect_of(&self, id: WidgetId) -> Option<Rect> {
247 self.state.node(id).map(|node| node.rect)
248 }
249
250 pub fn style_of(&self, id: WidgetId) -> Option<&Style> {
252 self.state.node(id).map(|node| &node.style)
253 }
254
255 pub fn focus_within(&self, id: WidgetId) -> bool {
257 self.state
258 .focus
259 .is_some_and(|focus| self.state.is_within(focus, id))
260 }
261
262 pub fn set_style(&mut self, id: WidgetId, style: Style) -> bool {
265 self.state.is_within(id, self.id) && self.state.set_style(id, style)
266 }
267}