1use 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#[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 clipboard(&mut self) -> &mut dyn Clipboard {
194 self.state.clipboard.0.as_mut()
195 }
196
197 pub fn request_layout(&mut self) {
199 self.state.mark_dirty(self.id);
200 }
201
202 pub fn request_focus(&mut self) {
204 self.state.requested_focus = Some(Some(self.id));
205 }
206
207 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 pub fn release_pointer(&mut self) {
216 if self.state.capture == Some(self.id) {
217 self.state.capture = None;
218 }
219 }
220
221 pub fn set_cursor(&mut self, cursor: Option<Cursor>) {
224 self.state.nodes[self.id.slot()].cursor = cursor;
225 }
226
227 pub fn set_ime_area(&mut self, area: Option<Rect>) {
230 self.state.ime_area = area.map(|area| (self.id, area));
231 }
232
233 pub fn emit(&mut self, kind: ActionKind) {
235 let source = self.id;
236 self.state.actions.push_back(Action { source, kind });
237 }
238
239 pub fn scroll_to(&mut self, offset: Point) -> Point {
242 self.state.scroll_to(self.id, offset)
243 }
244
245 pub fn children(&self) -> &[WidgetId] {
247 &self.state.nodes[self.id.slot()].children
248 }
249
250 pub fn rect_of(&self, id: WidgetId) -> Option<Rect> {
252 self.state.node(id).map(|node| node.rect)
253 }
254
255 pub fn style_of(&self, id: WidgetId) -> Option<&Style> {
257 self.state.node(id).map(|node| &node.style)
258 }
259
260 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 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}