1use embedded_graphics_core::pixelcolor::Rgb565;
2
3#[cfg(not(feature = "std"))]
4use crate::math::F32Ext as _;
5use crate::{
6 geometry::Rect,
7 image::{ImageFit, ImageRef, ReelPlayer},
8 render::TextAlign,
9 style::{Style, WidgetStyle},
10 widget::WidgetId,
11 widgets::{ChartMode, KeyboardLayout, NotificationLevel, SurfaceState, WidgetKind},
12};
13
14use super::*;
15
16impl<'a, const NODES: usize, const EVENTS: usize, const DIRTY: usize>
17 GuiContext<'a, NODES, EVENTS, DIRTY>
18{
19 pub fn add_panel<S>(&mut self, rect: Rect, style: S) -> Result<WidgetId, GuiError>
20 where
21 S: Into<WidgetStyle>,
22 {
23 self.add_widget(rect, WidgetKind::Panel, style)
24 }
25
26 pub fn add_themed_panel(&mut self, rect: Rect) -> Result<WidgetId, GuiError> {
27 self.add_panel(rect, self.theme.panel)
28 }
29
30 pub fn add_label<S>(
31 &mut self,
32 rect: Rect,
33 text: &'a str,
34 style: S,
35 ) -> Result<WidgetId, GuiError>
36 where
37 S: Into<WidgetStyle>,
38 {
39 self.add_widget(rect, WidgetKind::Label(text), style)
40 }
41
42 pub fn add_themed_label(&mut self, rect: Rect, text: &'a str) -> Result<WidgetId, GuiError> {
43 self.add_label(rect, text, self.theme.label)
44 }
45
46 pub fn add_button<S>(
47 &mut self,
48 rect: Rect,
49 text: &'a str,
50 style: S,
51 ) -> Result<WidgetId, GuiError>
52 where
53 S: Into<WidgetStyle>,
54 {
55 let id = self.add_widget(rect, WidgetKind::Button(text), style)?;
56 self.ensure_focus();
57 Ok(id)
58 }
59
60 pub fn add_themed_button(&mut self, rect: Rect, text: &'a str) -> Result<WidgetId, GuiError> {
61 self.add_button(rect, text, self.theme.button)
62 }
63
64 #[cfg(feature = "rich-widgets")]
65 pub fn add_progress_bar<S>(
66 &mut self,
67 rect: Rect,
68 value: f32,
69 style: S,
70 ) -> Result<WidgetId, GuiError>
71 where
72 S: Into<WidgetStyle>,
73 {
74 self.add_widget(
75 rect,
76 WidgetKind::ProgressBar {
77 value: value.clamp(0.0, 1.0),
78 },
79 style,
80 )
81 }
82
83 #[cfg(feature = "rich-widgets")]
84 pub fn add_themed_progress_bar(
85 &mut self,
86 rect: Rect,
87 value: f32,
88 ) -> Result<WidgetId, GuiError> {
89 self.add_progress_bar(rect, value, self.theme.progress)
90 }
91
92 #[cfg(feature = "rich-widgets")]
93 pub fn add_toggle<S>(
94 &mut self,
95 rect: Rect,
96 label: &'a str,
97 on: bool,
98 style: S,
99 ) -> Result<WidgetId, GuiError>
100 where
101 S: Into<WidgetStyle>,
102 {
103 let id = self.add_widget(rect, WidgetKind::Toggle { label, on }, style)?;
104 self.ensure_focus();
105 Ok(id)
106 }
107
108 #[cfg(feature = "rich-widgets")]
109 pub fn add_themed_toggle(
110 &mut self,
111 rect: Rect,
112 label: &'a str,
113 on: bool,
114 ) -> Result<WidgetId, GuiError> {
115 self.add_toggle(rect, label, on, self.theme.toggle)
116 }
117
118 #[cfg(feature = "rich-widgets")]
119 pub fn add_checkbox<S>(
120 &mut self,
121 rect: Rect,
122 label: &'a str,
123 checked: bool,
124 style: S,
125 ) -> Result<WidgetId, GuiError>
126 where
127 S: Into<WidgetStyle>,
128 {
129 let id = self.add_widget(rect, WidgetKind::Checkbox { label, checked }, style)?;
130 self.ensure_focus();
131 Ok(id)
132 }
133
134 #[cfg(feature = "rich-widgets")]
135 pub fn add_themed_checkbox(
136 &mut self,
137 rect: Rect,
138 label: &'a str,
139 checked: bool,
140 ) -> Result<WidgetId, GuiError> {
141 self.add_checkbox(rect, label, checked, self.theme.checkbox)
142 }
143
144 #[cfg(feature = "rich-widgets")]
145 pub fn add_slider<S>(
146 &mut self,
147 rect: Rect,
148 value: f32,
149 min: f32,
150 max: f32,
151 style: S,
152 ) -> Result<WidgetId, GuiError>
153 where
154 S: Into<WidgetStyle>,
155 {
156 let value = value.clamp(min.min(max), min.max(max));
157 let id = self.add_widget(rect, WidgetKind::Slider { value, min, max }, style)?;
158 self.ensure_focus();
159 Ok(id)
160 }
161
162 #[cfg(feature = "rich-widgets")]
163 pub fn add_themed_slider(
164 &mut self,
165 rect: Rect,
166 value: f32,
167 min: f32,
168 max: f32,
169 ) -> Result<WidgetId, GuiError> {
170 self.add_slider(rect, value, min, max, self.theme.slider)
171 }
172
173 #[cfg(feature = "rich-widgets")]
174 pub fn add_value_label<S>(
175 &mut self,
176 rect: Rect,
177 label: &'a str,
178 value: i32,
179 style: S,
180 ) -> Result<WidgetId, GuiError>
181 where
182 S: Into<WidgetStyle>,
183 {
184 self.add_widget(rect, WidgetKind::ValueLabel { label, value }, style)
185 }
186
187 #[cfg(feature = "rich-widgets")]
188 pub fn add_themed_value_label(
189 &mut self,
190 rect: Rect,
191 label: &'a str,
192 value: i32,
193 ) -> Result<WidgetId, GuiError> {
194 self.add_value_label(rect, label, value, self.theme.value_label)
195 }
196
197 #[cfg(feature = "rich-widgets")]
198 pub fn add_icon_button<S>(
199 &mut self,
200 rect: Rect,
201 icon: char,
202 label: &'a str,
203 style: S,
204 ) -> Result<WidgetId, GuiError>
205 where
206 S: Into<WidgetStyle>,
207 {
208 let id = self.add_widget(rect, WidgetKind::IconButton { icon, label }, style)?;
209 self.ensure_focus();
210 Ok(id)
211 }
212
213 #[cfg(feature = "rich-widgets")]
214 pub fn add_themed_icon_button(
215 &mut self,
216 rect: Rect,
217 icon: char,
218 label: &'a str,
219 ) -> Result<WidgetId, GuiError> {
220 self.add_icon_button(rect, icon, label, self.theme.icon_button)
221 }
222
223 #[cfg(feature = "rich-widgets")]
224 pub fn add_list<S>(
225 &mut self,
226 rect: Rect,
227 items: &'a [&'a str],
228 selected: usize,
229 visible_rows: usize,
230 style: S,
231 ) -> Result<WidgetId, GuiError>
232 where
233 S: Into<WidgetStyle>,
234 {
235 let selected = selected.min(items.len().saturating_sub(1));
236 let id = self.add_widget(
237 rect,
238 WidgetKind::List {
239 items,
240 selected,
241 offset: selected,
242 visible_rows: visible_rows.max(1),
243 },
244 style,
245 )?;
246 self.ensure_focus();
247 Ok(id)
248 }
249
250 #[cfg(feature = "rich-widgets")]
251 pub fn add_feed_timeline<S>(
252 &mut self,
253 rect: Rect,
254 items: &'a [&'a str],
255 selected: usize,
256 visible_rows: usize,
257 expanded: bool,
258 style: S,
259 ) -> Result<WidgetId, GuiError>
260 where
261 S: Into<WidgetStyle>,
262 {
263 let selected = selected.min(items.len().saturating_sub(1));
264 let id = self.add_widget(
265 rect,
266 WidgetKind::FeedTimeline {
267 items,
268 selected,
269 offset: selected,
270 visible_rows: visible_rows.max(1),
271 expanded,
272 },
273 style,
274 )?;
275 self.ensure_focus();
276 Ok(id)
277 }
278
279 #[cfg(feature = "rich-widgets")]
280 pub fn add_themed_list(
281 &mut self,
282 rect: Rect,
283 items: &'a [&'a str],
284 selected: usize,
285 visible_rows: usize,
286 ) -> Result<WidgetId, GuiError> {
287 self.add_list(rect, items, selected, visible_rows, self.theme.list)
288 }
289
290 #[cfg(feature = "rich-widgets")]
291 pub fn add_scroll_view<S>(
292 &mut self,
293 rect: Rect,
294 offset_y: i32,
295 content_h: u32,
296 style: S,
297 ) -> Result<WidgetId, GuiError>
298 where
299 S: Into<WidgetStyle>,
300 {
301 let id = self.add_widget(
302 rect,
303 WidgetKind::ScrollView {
304 offset_y,
305 content_h,
306 },
307 style,
308 )?;
309 self.ensure_focus();
310 Ok(id)
311 }
312
313 #[cfg(feature = "rich-widgets")]
314 pub fn add_themed_scroll_view(
315 &mut self,
316 rect: Rect,
317 offset_y: i32,
318 content_h: u32,
319 ) -> Result<WidgetId, GuiError> {
320 self.add_scroll_view(rect, offset_y, content_h, self.theme.list)
321 }
322
323 #[cfg(feature = "rich-widgets")]
324 pub fn add_tabs<S>(
325 &mut self,
326 rect: Rect,
327 labels: &'a [&'a str],
328 selected: usize,
329 style: S,
330 ) -> Result<WidgetId, GuiError>
331 where
332 S: Into<WidgetStyle>,
333 {
334 let selected = selected.min(labels.len().saturating_sub(1));
335 let id = self.add_widget(rect, WidgetKind::Tabs { labels, selected }, style)?;
336 self.ensure_focus();
337 Ok(id)
338 }
339
340 #[cfg(feature = "rich-widgets")]
341 pub fn add_themed_tabs(
342 &mut self,
343 rect: Rect,
344 labels: &'a [&'a str],
345 selected: usize,
346 ) -> Result<WidgetId, GuiError> {
347 self.add_tabs(rect, labels, selected, self.theme.tabs)
348 }
349
350 #[cfg(feature = "rich-widgets")]
351 pub fn add_dialog<S>(
352 &mut self,
353 rect: Rect,
354 title: &'a str,
355 body: &'a str,
356 style: S,
357 ) -> Result<WidgetId, GuiError>
358 where
359 S: Into<WidgetStyle>,
360 {
361 self.add_widget(rect, WidgetKind::Dialog { title, body }, style)
362 }
363
364 #[cfg(feature = "rich-widgets")]
365 pub fn add_themed_dialog(
366 &mut self,
367 rect: Rect,
368 title: &'a str,
369 body: &'a str,
370 ) -> Result<WidgetId, GuiError> {
371 self.add_dialog(rect, title, body, self.theme.dialog)
372 }
373
374 #[cfg(feature = "rich-widgets")]
375 pub fn add_toast<S>(
376 &mut self,
377 rect: Rect,
378 text: &'a str,
379 ttl_ms: u32,
380 style: S,
381 ) -> Result<WidgetId, GuiError>
382 where
383 S: Into<WidgetStyle>,
384 {
385 self.add_widget(rect, WidgetKind::Toast { text, ttl_ms }, style)
386 }
387
388 #[cfg(feature = "rich-widgets")]
389 pub fn add_themed_toast(
390 &mut self,
391 rect: Rect,
392 text: &'a str,
393 ttl_ms: u32,
394 ) -> Result<WidgetId, GuiError> {
395 self.add_toast(rect, text, ttl_ms, self.theme.toast)
396 }
397
398 #[cfg(feature = "rich-widgets")]
399 pub fn add_meter<S>(
400 &mut self,
401 rect: Rect,
402 value: f32,
403 min: f32,
404 max: f32,
405 style: S,
406 ) -> Result<WidgetId, GuiError>
407 where
408 S: Into<WidgetStyle>,
409 {
410 self.add_widget(rect, WidgetKind::Meter { value, min, max }, style)
411 }
412
413 #[cfg(feature = "rich-widgets")]
414 pub fn add_themed_meter(
415 &mut self,
416 rect: Rect,
417 value: f32,
418 min: f32,
419 max: f32,
420 ) -> Result<WidgetId, GuiError> {
421 self.add_meter(rect, value, min, max, self.theme.meter)
422 }
423
424 #[allow(clippy::too_many_arguments)]
425 #[cfg(feature = "rich-widgets")]
426 pub fn add_arc_gauge<S>(
427 &mut self,
428 rect: Rect,
429 value: f32,
430 min: f32,
431 max: f32,
432 start_deg: i32,
433 end_deg: i32,
434 thickness: u8,
435 antialias: bool,
436 style: S,
437 ) -> Result<WidgetId, GuiError>
438 where
439 S: Into<WidgetStyle>,
440 {
441 self.add_widget(
442 rect,
443 WidgetKind::ArcGauge {
444 value,
445 min,
446 max,
447 start_deg,
448 end_deg,
449 thickness: thickness.max(1),
450 antialias,
451 major_ticks: 6,
452 minor_ticks: 2,
453 show_value: false,
454 },
455 style,
456 )
457 }
458
459 #[cfg(feature = "rich-widgets")]
460 pub fn add_gauge<S>(
461 &mut self,
462 rect: Rect,
463 value: f32,
464 min: f32,
465 max: f32,
466 style: S,
467 ) -> Result<WidgetId, GuiError>
468 where
469 S: Into<WidgetStyle>,
470 {
471 self.add_widget(
472 rect,
473 WidgetKind::Gauge {
474 value,
475 min,
476 max,
477 major_ticks: 6,
478 minor_ticks: 2,
479 show_value: false,
480 },
481 style,
482 )
483 }
484
485 #[allow(clippy::too_many_arguments)]
486 pub fn add_sweeping_arc<S>(
487 &mut self,
488 rect: Rect,
489 progress: f32,
490 arc_radius: u32,
491 frame_inset: u16,
492 corner_radius: u8,
493 bg_color: Rgb565,
494 arc_color: Rgb565,
495 frame_color: Rgb565,
496 style: S,
497 ) -> Result<WidgetId, GuiError>
498 where
499 S: Into<WidgetStyle>,
500 {
501 self.add_widget(
502 rect,
503 WidgetKind::SweepingArc {
504 progress: progress.clamp(0.0, 1.0),
505 arc_radius,
506 frame_inset,
507 corner_radius,
508 bg_color,
509 arc_color,
510 frame_color,
511 },
512 style,
513 )
514 }
515
516 #[allow(clippy::too_many_arguments)]
517 #[cfg(feature = "rich-widgets")]
518 pub fn add_gauge_needle<S>(
519 &mut self,
520 rect: Rect,
521 value: f32,
522 min: f32,
523 max: f32,
524 start_deg: i32,
525 end_deg: i32,
526 style: S,
527 ) -> Result<WidgetId, GuiError>
528 where
529 S: Into<WidgetStyle>,
530 {
531 self.add_widget(
532 rect,
533 WidgetKind::GaugeNeedle {
534 value,
535 min,
536 max,
537 start_deg,
538 end_deg,
539 },
540 style,
541 )
542 }
543
544 #[cfg(feature = "rich-widgets")]
545 pub fn add_chart<S>(
546 &mut self,
547 rect: Rect,
548 values: &'a [f32],
549 min: f32,
550 max: f32,
551 style: S,
552 ) -> Result<WidgetId, GuiError>
553 where
554 S: Into<WidgetStyle>,
555 {
556 self.add_widget(
557 rect,
558 WidgetKind::Chart {
559 values,
560 min,
561 max,
562 thickness: 1,
563 fill_under: false,
564 markers: false,
565 mode: ChartMode::Line,
566 show_grid: false,
567 show_axes: false,
568 show_labels: false,
569 },
570 style,
571 )
572 }
573
574 #[cfg(feature = "rich-widgets")]
575 pub fn set_chart_style(
576 &mut self,
577 id: WidgetId,
578 thickness: u8,
579 fill_under: bool,
580 markers: bool,
581 ) -> Result<(), GuiError> {
582 let rect = self.absolute_rect(id).ok_or(GuiError::NotFound)?;
583 let node = self.node_mut(id).ok_or(GuiError::NotFound)?;
584 match node.kind {
585 WidgetKind::Chart {
586 thickness: ref mut t,
587 fill_under: ref mut fill,
588 markers: ref mut mark,
589 ..
590 } => {
591 *t = thickness.max(1);
592 *fill = fill_under;
593 *mark = markers;
594 self.dirty.add(rect)?;
595 Ok(())
596 }
597 _ => Err(GuiError::NotFound),
598 }
599 }
600
601 #[cfg(feature = "rich-widgets")]
602 pub fn set_chart_decoration(
603 &mut self,
604 id: WidgetId,
605 mode: ChartMode,
606 show_grid: bool,
607 show_axes: bool,
608 show_labels: bool,
609 ) -> Result<(), GuiError> {
610 let rect = self.absolute_rect(id).ok_or(GuiError::NotFound)?;
611 let node = self.node_mut(id).ok_or(GuiError::NotFound)?;
612 match node.kind {
613 WidgetKind::Chart {
614 mode: ref mut chart_mode,
615 show_grid: ref mut grid,
616 show_axes: ref mut axes,
617 show_labels: ref mut labels,
618 ..
619 } => {
620 *chart_mode = mode;
621 *grid = show_grid;
622 *axes = show_axes;
623 *labels = show_labels;
624 self.dirty.add(rect)?;
625 Ok(())
626 }
627 _ => Err(GuiError::NotFound),
628 }
629 }
630
631 #[cfg(feature = "rich-widgets")]
632 pub fn add_spinner<S>(&mut self, rect: Rect, phase: f32, style: S) -> Result<WidgetId, GuiError>
633 where
634 S: Into<WidgetStyle>,
635 {
636 self.add_widget(rect, WidgetKind::Spinner { phase }, style)
637 }
638
639 #[cfg(feature = "rich-widgets")]
640 pub fn add_dropdown<S>(
641 &mut self,
642 rect: Rect,
643 items: &'a [&'a str],
644 selected: usize,
645 style: S,
646 ) -> Result<WidgetId, GuiError>
647 where
648 S: Into<WidgetStyle>,
649 {
650 let selected = selected.min(items.len().saturating_sub(1));
651 let id = self.add_widget(
652 rect,
653 WidgetKind::Dropdown {
654 items,
655 selected,
656 open: false,
657 },
658 style,
659 )?;
660 self.ensure_focus();
661 Ok(id)
662 }
663
664 #[cfg(feature = "rich-widgets")]
665 pub fn add_roller<S>(
666 &mut self,
667 rect: Rect,
668 items: &'a [&'a str],
669 selected: usize,
670 style: S,
671 ) -> Result<WidgetId, GuiError>
672 where
673 S: Into<WidgetStyle>,
674 {
675 let selected = selected.min(items.len().saturating_sub(1));
676 let id = self.add_widget(rect, WidgetKind::Roller { items, selected }, style)?;
677 self.ensure_focus();
678 Ok(id)
679 }
680
681 #[cfg(feature = "rich-widgets")]
682 pub fn add_table<S>(
683 &mut self,
684 rect: Rect,
685 rows: &'a [&'a [&'a str]],
686 style: S,
687 ) -> Result<WidgetId, GuiError>
688 where
689 S: Into<WidgetStyle>,
690 {
691 self.add_widget(
692 rect,
693 WidgetKind::Table {
694 rows,
695 separators: true,
696 cell_padding: 1,
697 align: TextAlign::Left,
698 },
699 style,
700 )
701 }
702
703 #[cfg(feature = "rich-widgets")]
704 pub fn set_table_style(
705 &mut self,
706 id: WidgetId,
707 separators: bool,
708 cell_padding: u8,
709 align: TextAlign,
710 ) -> Result<(), GuiError> {
711 let rect = self.absolute_rect(id).ok_or(GuiError::NotFound)?;
712 let node = self.node_mut(id).ok_or(GuiError::NotFound)?;
713 match node.kind {
714 WidgetKind::Table {
715 separators: ref mut cell_sep,
716 cell_padding: ref mut pad,
717 align: ref mut table_align,
718 ..
719 } => {
720 *cell_sep = separators;
721 *pad = cell_padding.min(6);
722 *table_align = align;
723 self.dirty.add(rect)?;
724 Ok(())
725 }
726 _ => Err(GuiError::NotFound),
727 }
728 }
729
730 #[cfg(feature = "rich-widgets")]
731 pub fn add_textarea<S>(
732 &mut self,
733 rect: Rect,
734 text: &'a str,
735 placeholder: &'a str,
736 style: S,
737 ) -> Result<WidgetId, GuiError>
738 where
739 S: Into<WidgetStyle>,
740 {
741 let cursor = text.chars().count();
742 let (text_buf, text_len) = textarea_storage_from_str(text);
743 let id = self.add_widget(
744 rect,
745 WidgetKind::TextArea {
746 text_buf,
747 text_len,
748 cursor,
749 placeholder,
750 selection: None,
751 cursor_visible: true,
752 read_only: false,
753 single_line: false,
754 accept_newline: true,
755 },
756 style,
757 )?;
758 self.ensure_focus();
759 Ok(id)
760 }
761
762 #[cfg(feature = "rich-widgets")]
763 pub fn add_keyboard<S>(
764 &mut self,
765 rect: Rect,
766 keys: &'a [char],
767 cols: u8,
768 target: Option<WidgetId>,
769 style: S,
770 ) -> Result<WidgetId, GuiError>
771 where
772 S: Into<WidgetStyle>,
773 {
774 self.add_keyboard_with_alt(rect, keys, None, cols, target, style)
775 }
776
777 #[cfg(feature = "rich-widgets")]
778 pub fn add_keyboard_with_alt<S>(
779 &mut self,
780 rect: Rect,
781 keys: &'a [char],
782 alt_keys: Option<&'a [char]>,
783 cols: u8,
784 target: Option<WidgetId>,
785 style: S,
786 ) -> Result<WidgetId, GuiError>
787 where
788 S: Into<WidgetStyle>,
789 {
790 let id = self.add_widget(
791 rect,
792 WidgetKind::Keyboard {
793 keys,
794 selected: 0,
795 cols: cols.max(1),
796 alt_keys,
797 layout: KeyboardLayout::Normal,
798 target,
799 },
800 style,
801 )?;
802 self.ensure_focus();
803 Ok(id)
804 }
805
806 pub fn add_image<S>(
807 &mut self,
808 rect: Rect,
809 image: ImageRef<'a>,
810 fit: ImageFit,
811 style: S,
812 ) -> Result<WidgetId, GuiError>
813 where
814 S: Into<WidgetStyle>,
815 {
816 self.add_widget(rect, WidgetKind::Image { image, fit }, style)
817 }
818
819 #[cfg(feature = "rich-widgets")]
820 pub fn add_peek_reveal<S>(
821 &mut self,
822 rect: Rect,
823 icon: ImageRef<'a>,
824 title: &'a str,
825 subtitle: &'a str,
826 style: S,
827 ) -> Result<WidgetId, GuiError>
828 where
829 S: Into<WidgetStyle>,
830 {
831 self.add_widget(
832 rect,
833 WidgetKind::PeekReveal {
834 icon,
835 title,
836 subtitle,
837 progress: 0.0,
838 },
839 style,
840 )
841 }
842
843 #[cfg(feature = "rich-widgets")]
844 pub fn add_glance_tile<S>(
845 &mut self,
846 rect: Rect,
847 icon: char,
848 title: &'a str,
849 subtitle: &'a str,
850 style: S,
851 ) -> Result<WidgetId, GuiError>
852 where
853 S: Into<WidgetStyle>,
854 {
855 let id = self.add_widget(
856 rect,
857 WidgetKind::GlanceTile {
858 icon,
859 title,
860 subtitle,
861 highlighted: false,
862 },
863 style,
864 )?;
865 self.ensure_focus();
866 Ok(id)
867 }
868
869 #[cfg(feature = "rich-widgets")]
870 pub fn add_card_deck<S>(
871 &mut self,
872 rect: Rect,
873 titles: &'a [&'a str],
874 selected: usize,
875 style: S,
876 ) -> Result<WidgetId, GuiError>
877 where
878 S: Into<WidgetStyle>,
879 {
880 self.add_widget(
881 rect,
882 WidgetKind::CardDeck {
883 titles,
884 selected: selected.min(titles.len().saturating_sub(1)),
885 },
886 style,
887 )
888 }
889
890 #[cfg(feature = "rich-widgets")]
891 pub fn add_reel<S>(
892 &mut self,
893 rect: Rect,
894 player: ReelPlayer<'a>,
895 fit: ImageFit,
896 style: S,
897 ) -> Result<WidgetId, GuiError>
898 where
899 S: Into<WidgetStyle>,
900 {
901 self.add_widget(rect, WidgetKind::Reel { player, fit }, style)
902 }
903
904 #[cfg(feature = "rich-widgets")]
905 pub fn add_state_surface<S>(
906 &mut self,
907 rect: Rect,
908 state: SurfaceState,
909 title: &'a str,
910 message: &'a str,
911 action: Option<&'a str>,
912 style: S,
913 ) -> Result<WidgetId, GuiError>
914 where
915 S: Into<WidgetStyle>,
916 {
917 self.add_widget(
918 rect,
919 WidgetKind::StateSurface {
920 state,
921 title,
922 message,
923 action,
924 busy_phase: 0.0,
925 },
926 style,
927 )
928 }
929
930 #[cfg(feature = "rich-widgets")]
931 pub fn add_heads_up_banner<S>(
932 &mut self,
933 rect: Rect,
934 level: NotificationLevel,
935 text: &'a str,
936 ttl_ms: u32,
937 style: S,
938 ) -> Result<WidgetId, GuiError>
939 where
940 S: Into<WidgetStyle>,
941 {
942 self.add_widget(
943 rect,
944 WidgetKind::HeadsUpBanner {
945 level,
946 text,
947 ttl_ms,
948 },
949 style,
950 )
951 }
952
953 #[allow(clippy::too_many_arguments)]
954 #[cfg(feature = "rich-widgets")]
955 pub fn add_notification_action_sheet<S>(
956 &mut self,
957 rect: Rect,
958 level: NotificationLevel,
959 title: &'a str,
960 body: &'a str,
961 actions: &'a [&'a str],
962 selected: usize,
963 open: bool,
964 style: S,
965 ) -> Result<WidgetId, GuiError>
966 where
967 S: Into<WidgetStyle>,
968 {
969 self.add_widget(
970 rect,
971 WidgetKind::NotificationActionSheet {
972 level,
973 title,
974 body,
975 actions,
976 selected: selected.min(actions.len().saturating_sub(1)),
977 open,
978 },
979 style,
980 )
981 }
982
983 pub fn add_border<S>(&mut self, rect: Rect, style: S) -> Result<WidgetId, GuiError>
984 where
985 S: Into<WidgetStyle>,
986 {
987 self.add_widget(rect, WidgetKind::Border, style)
988 }
989
990 pub fn add_spacer(&mut self, rect: Rect) -> Result<WidgetId, GuiError> {
991 self.add_widget(rect, WidgetKind::Spacer, Style::default())
992 }
993
994 #[cfg(feature = "rich-widgets")]
995 pub fn add_menu<S>(
996 &mut self,
997 rect: Rect,
998 items: &'a [&'a str],
999 selected: usize,
1000 style: S,
1001 ) -> Result<WidgetId, GuiError>
1002 where
1003 S: Into<WidgetStyle>,
1004 {
1005 let selected = selected.min(items.len().saturating_sub(1));
1006 let id = self.add_widget(rect, WidgetKind::Menu { items, selected }, style)?;
1007 self.ensure_focus();
1008 Ok(id)
1009 }
1010}