1use super::{
4 Action, Dock, FocusStyle, HAlign, StateFlags, Stretch, Style, StylePatch, Track, Units, VAlign,
5 Widget, WidgetKind,
6};
7use yog_gfx::{Color, Rect};
8
9use crate::layout::{Align, FlexDir};
10
11impl Widget {
12 pub(crate) fn new(kind: WidgetKind) -> Self {
13 let flex_dir = if let WidgetKind::Panel(dir) = &kind {
14 *dir
15 } else {
16 FlexDir::Column
17 };
18 Self {
19 kind,
20 id: None,
21 style: Style::default(),
22 flex_dir,
23 children: Vec::new(),
24 on_click: None,
25 enabled: true,
26 focused: false,
27 action: None,
28 visible: true,
29 state: StateFlags::NONE,
30 states: None,
31 tooltip: None,
32 }
33 }
34
35 pub fn tooltip(mut self, text: impl Into<String>) -> Self {
37 self.set_tooltip(text);
38 self
39 }
40 pub fn set_tooltip(&mut self, text: impl Into<String>) {
42 let t = text.into();
43 self.tooltip = (!t.is_empty()).then_some(t);
44 }
45
46 pub fn action(mut self, a: Action) -> Self {
48 self.action = Some(a);
49 self
50 }
51 pub fn visible(mut self, v: bool) -> Self {
53 self.visible = v;
54 self
55 }
56 pub fn state_style(mut self, when: StateFlags, patch: StylePatch) -> Self {
59 self.states
60 .get_or_insert_with(Default::default)
61 .rules
62 .push((when, patch));
63 self
64 }
65
66 pub fn get_path(&self, path: &[u16]) -> Option<&Widget> {
69 let mut w = self;
70 for &i in path {
71 w = w.children.get(i as usize)?;
72 }
73 Some(w)
74 }
75 pub fn get_path_mut(&mut self, path: &[u16]) -> Option<&mut Widget> {
77 let mut w = self;
78 for &i in path {
79 w = w.children.get_mut(i as usize)?;
80 }
81 Some(w)
82 }
83
84 pub fn is_interactive(&self) -> bool {
86 self.action.is_some() || self.on_click.is_some()
87 }
88
89 pub fn set_state_inherited(&mut self, mask: StateFlags, on: bool) {
93 self.state.set(mask, on);
94 for c in &mut self.children {
95 if !c.is_interactive() {
96 c.set_state_inherited(mask, on);
97 }
98 }
99 }
100
101 pub fn clear_state_deep(&mut self, mask: StateFlags) {
103 self.state.remove(mask);
104 for c in &mut self.children {
105 c.clear_state_deep(mask);
106 }
107 }
108
109 pub fn effective_style(&self) -> std::borrow::Cow<'_, Style> {
112 let mut state = self.state;
113 if !self.enabled {
114 state.insert(StateFlags::DISABLED);
115 }
116 self.effective_style_in(state)
117 }
118
119 pub fn effective_style_in(&self, state: StateFlags) -> std::borrow::Cow<'_, Style> {
121 let Some(states) = &self.states else {
122 return std::borrow::Cow::Borrowed(&self.style);
123 };
124 let mut matching = states
125 .rules
126 .iter()
127 .filter(|(when, _)| !when.is_empty() && state.contains(*when))
128 .peekable();
129 if matching.peek().is_none() {
130 return std::borrow::Cow::Borrowed(&self.style);
131 }
132 let mut s = self.style.clone();
133 for (_, patch) in matching {
134 patch.apply(&mut s);
135 }
136 std::borrow::Cow::Owned(s)
137 }
138
139 pub fn id(mut self, id: impl Into<String>) -> Self {
140 self.id = Some(id.into());
141 self
142 }
143 pub fn on_click(mut self, ev: impl Into<String>) -> Self {
144 self.on_click = Some(ev.into());
145 self
146 }
147 pub fn child(mut self, w: Widget) -> Self {
148 self.children.push(w);
149 self
150 }
151
152 pub fn w(mut self, w: impl Into<Units>) -> Self {
155 self.style.w = w.into();
156 self
157 }
158 pub fn h(mut self, h: impl Into<Units>) -> Self {
159 self.style.h = h.into();
160 self
161 }
162 pub fn min_w(mut self, v: f32) -> Self {
163 self.style.min_w = v;
164 self
165 }
166 pub fn min_h(mut self, v: f32) -> Self {
167 self.style.min_h = v;
168 self
169 }
170 pub fn flex(mut self, v: f32) -> Self {
171 self.style.flex = v;
172 self
173 }
174 pub fn flex_dir(mut self, v: FlexDir) -> Self {
175 self.flex_dir = v;
176 self
177 }
178 pub fn gap(mut self, v: f32) -> Self {
179 self.style.gap = v;
180 self
181 }
182 pub fn bg(mut self, v: Color) -> Self {
183 self.style.bg = v;
184 self
185 }
186 pub fn bg_image(mut self, source: impl Into<String>) -> Self {
188 self.style.bg_image = source.into();
189 self
190 }
191 pub fn color(mut self, v: Color) -> Self {
192 self.style.color = v;
193 self
194 }
195 pub fn align(mut self, v: Align) -> Self {
196 self.style.align = v;
197 self
198 }
199 pub fn font_scale(mut self, v: f32) -> Self {
200 self.style.font_scale = v;
201 self
202 }
203 pub fn shadow(mut self, v: bool) -> Self {
204 self.style.text_shadow = v;
205 self
206 }
207 pub fn no_wrap(mut self) -> Self {
208 self.style.no_wrap = true;
209 self
210 }
211 pub fn focus_style(mut self, v: FocusStyle) -> Self {
212 self.style.focus_style = v;
213 self
214 }
215 pub fn focus_color(mut self, v: Color) -> Self {
216 self.style.focus_color = v;
217 self
218 }
219 pub fn dock(mut self, v: Dock) -> Self {
220 self.style.dock = v;
221 self
222 }
223 pub fn enabled(mut self, v: bool) -> Self {
224 self.enabled = v;
225 self
226 }
227 pub fn focused(mut self, v: bool) -> Self {
228 self.focused = v;
229 self
230 }
231 pub fn at(mut self, x: f32, y: f32) -> Self {
233 self.style.pos = Some((x, y));
234 self
235 }
236 pub fn ellipsis(mut self) -> Self {
238 self.style.no_wrap = true;
239 self.style.ellipsis = true;
240 self
241 }
242 pub fn clip(mut self, v: bool) -> Self {
244 self.style.clip = v;
245 self
246 }
247 pub fn scroll_y(mut self, v: f32) -> Self {
249 self.style.scroll_y = v;
250 self
251 }
252 pub fn scrollbar(mut self, v: bool) -> Self {
254 self.style.scrollbar = v;
255 self
256 }
257 pub fn font(mut self, font: impl Into<String>) -> Self {
259 self.style.font = font.into();
260 self
261 }
262 pub fn placeholder(mut self, text: impl Into<String>) -> Self {
264 if let WidgetKind::TextBox(t) = &mut self.kind {
265 t.placeholder = text.into();
266 }
267 self
268 }
269 pub fn max_length(mut self, n: usize) -> Self {
271 if let WidgetKind::TextBox(t) = &mut self.kind {
272 t.max_length = n;
273 }
274 self
275 }
276 pub fn on_change(mut self, ev: impl Into<String>) -> Self {
278 if let WidgetKind::TextBox(t) = &mut self.kind {
279 t.on_change = Some(ev.into());
280 }
281 self
282 }
283 pub fn on_submit(mut self, ev: impl Into<String>) -> Self {
285 if let WidgetKind::TextBox(t) = &mut self.kind {
286 t.on_submit = Some(ev.into());
287 }
288 self
289 }
290 pub fn stretch(mut self, stretch: Stretch) -> Self {
292 if let WidgetKind::Image(i) = &mut self.kind {
293 i.stretch = stretch;
294 }
295 self
296 }
297 pub fn region(mut self, region: Rect) -> Self {
299 if let WidgetKind::Image(i) = &mut self.kind {
300 i.region = Some(region);
301 }
302 self
303 }
304 pub fn tint(mut self, tint: Color) -> Self {
306 if let WidgetKind::Image(i) = &mut self.kind {
307 i.tint = tint;
308 }
309 self
310 }
311 pub fn children(mut self, children: impl IntoIterator<Item = Widget>) -> Self {
313 self.children.extend(children);
314 self
315 }
316
317 pub fn find_mut(&mut self, id: &str) -> Option<&mut Widget> {
321 if self.id.as_deref() == Some(id) {
322 return Some(self);
323 }
324 self.children.iter_mut().find_map(|c| c.find_mut(id))
325 }
326
327 pub fn set_text(&mut self, text: impl Into<String>) -> bool {
330 match &mut self.kind {
331 WidgetKind::Label(t) | WidgetKind::Button(t) => {
332 *t = text.into();
333 true
334 }
335 WidgetKind::TextBox(t) => {
336 t.text = text.into();
337 true
338 }
339 _ => false,
340 }
341 }
342
343 pub fn with_text(mut self, text: impl Into<String>) -> Self {
345 self.set_text(text);
346 self
347 }
348
349 pub fn with_source(mut self, source: impl Into<String>) -> Self {
351 match &mut self.kind {
352 WidgetKind::Image(i) => i.source = source.into(),
353 WidgetKind::Surface(id) => *id = source.into(),
354 WidgetKind::ItemSlot(id) | WidgetKind::InvSlot { item_id: id, .. } => {
355 *id = source.into()
356 }
357 _ => {}
358 }
359 self
360 }
361
362 pub fn with_count(mut self, n: u32) -> Self {
364 if let WidgetKind::InvSlot { count, .. } = &mut self.kind {
365 *count = n;
366 }
367 self
368 }
369
370 pub fn padding(mut self, top: f32, right: f32, bottom: f32, left: f32) -> Self {
371 self.style.pad = [top, right, bottom, left];
372 self
373 }
374 pub fn margin(mut self, top: f32, right: f32, bottom: f32, left: f32) -> Self {
375 self.style.margin = [top, right, bottom, left];
376 self
377 }
378
379 pub fn cell(mut self, row: u16, col: u16) -> Self {
382 self.style.cell.row = row;
383 self.style.cell.col = col;
384 self
385 }
386 pub fn span(mut self, rows: u16, cols: u16) -> Self {
389 self.style.cell.row_span = rows.max(1);
390 self.style.cell.col_span = cols.max(1);
391 self
392 }
393 pub fn halign(mut self, v: HAlign) -> Self {
395 self.style.halign = Some(v);
396 self
397 }
398 pub fn valign(mut self, v: VAlign) -> Self {
400 self.style.valign = Some(v);
401 self
402 }
403
404 pub fn rows(mut self, rows: impl IntoIterator<Item = impl Into<Track>>) -> Self {
406 if let WidgetKind::Grid(g) = &mut self.kind {
407 g.rows = rows.into_iter().map(Into::into).collect();
408 }
409 self
410 }
411 pub fn cols(mut self, cols: impl IntoIterator<Item = impl Into<Track>>) -> Self {
413 if let WidgetKind::Grid(g) = &mut self.kind {
414 g.cols = cols.into_iter().map(Into::into).collect();
415 }
416 self
417 }
418 pub fn row_spacing(mut self, v: f32) -> Self {
420 if let WidgetKind::Grid(g) = &mut self.kind {
421 g.row_spacing = v;
422 }
423 self
424 }
425 pub fn col_spacing(mut self, v: f32) -> Self {
427 if let WidgetKind::Grid(g) = &mut self.kind {
428 g.col_spacing = v;
429 }
430 self
431 }
432}