1use std::collections::HashMap;
2use std::ops::RangeInclusive;
3use std::sync::Arc;
4
5use crate::context::Context;
6use crate::element::{AccentColor, ElementDecl, ElementKind, ElementMeta, PlotSeries, Value};
7
8pub struct Response {
10 clicked: bool,
11 changed: bool,
12}
13
14impl Response {
15 pub fn clicked(&self) -> bool {
17 self.clicked
18 }
19
20 pub fn changed(&self) -> bool {
22 self.changed
23 }
24}
25
26pub(crate) trait WidgetSink {
31 fn make_id(&mut self, label: &str) -> String;
32 fn declare(&mut self, decl: ElementDecl);
33 fn consume_edit(&mut self, id: &str) -> Option<Value>;
34 fn window_name(&self) -> Arc<str>;
35 fn record_child(&mut self, id: String);
36}
37
38fn widget_slider(sink: &mut impl WidgetSink, label: &str, value: &mut f32, range: &RangeInclusive<f32>) -> Response {
41 let id = sink.make_id(label);
42 let (clicked, changed) = if let Some(Value::Float(v)) = sink.consume_edit(&id) {
43 let new = v as f32;
44 let changed = *value != new;
45 *value = new;
46 (true, changed)
47 } else {
48 (false, false)
49 };
50 sink.record_child(id.clone());
51 sink.declare(ElementDecl {
52 id,
53 kind: ElementKind::Slider,
54 label: label.to_string(),
55 value: Value::Float(*value as f64),
56 meta: ElementMeta {
57 min: Some(*range.start() as f64),
58 max: Some(*range.end() as f64),
59 step: Some(0.01),
60 ..Default::default()
61 },
62 window: sink.window_name(),
63 });
64 Response { clicked, changed }
65}
66
67fn widget_slider_f64(sink: &mut impl WidgetSink, label: &str, value: &mut f64, range: &RangeInclusive<f64>) -> Response {
68 let id = sink.make_id(label);
69 let (clicked, changed) = if let Some(Value::Float(v)) = sink.consume_edit(&id) {
70 let changed = *value != v;
71 *value = v;
72 (true, changed)
73 } else {
74 (false, false)
75 };
76 sink.record_child(id.clone());
77 sink.declare(ElementDecl {
78 id,
79 kind: ElementKind::Slider,
80 label: label.to_string(),
81 value: Value::Float(*value),
82 meta: ElementMeta {
83 min: Some(*range.start()),
84 max: Some(*range.end()),
85 step: Some(0.001),
86 ..Default::default()
87 },
88 window: sink.window_name(),
89 });
90 Response { clicked, changed }
91}
92
93fn widget_slider_int(sink: &mut impl WidgetSink, label: &str, value: &mut i32, range: &RangeInclusive<i32>) -> Response {
94 let id = sink.make_id(label);
95 let (clicked, changed) = if let Some(Value::Int(v)) = sink.consume_edit(&id) {
96 let new = v as i32;
97 let changed = *value != new;
98 *value = new;
99 (true, changed)
100 } else {
101 (false, false)
102 };
103 sink.record_child(id.clone());
104 sink.declare(ElementDecl {
105 id,
106 kind: ElementKind::Slider,
107 label: label.to_string(),
108 value: Value::Int(*value as i64),
109 meta: ElementMeta {
110 min: Some(*range.start() as f64),
111 max: Some(*range.end() as f64),
112 step: Some(1.0),
113 ..Default::default()
114 },
115 window: sink.window_name(),
116 });
117 Response { clicked, changed }
118}
119
120fn widget_slider_uint(sink: &mut impl WidgetSink, label: &str, value: &mut u32, range: &RangeInclusive<u32>) -> Response {
121 let id = sink.make_id(label);
122 let (clicked, changed) = if let Some(Value::Int(v)) = sink.consume_edit(&id) {
123 let new = v as u32;
124 let changed = *value != new;
125 *value = new;
126 (true, changed)
127 } else {
128 (false, false)
129 };
130 sink.record_child(id.clone());
131 sink.declare(ElementDecl {
132 id,
133 kind: ElementKind::Slider,
134 label: label.to_string(),
135 value: Value::Int(*value as i64),
136 meta: ElementMeta {
137 min: Some(*range.start() as f64),
138 max: Some(*range.end() as f64),
139 step: Some(1.0),
140 ..Default::default()
141 },
142 window: sink.window_name(),
143 });
144 Response { clicked, changed }
145}
146
147fn widget_checkbox(sink: &mut impl WidgetSink, label: &str, value: &mut bool) -> Response {
148 let id = sink.make_id(label);
149 let (clicked, changed) = if let Some(Value::Bool(v)) = sink.consume_edit(&id) {
150 let changed = *value != v;
151 *value = v;
152 (true, changed)
153 } else {
154 (false, false)
155 };
156 sink.record_child(id.clone());
157 sink.declare(ElementDecl {
158 id,
159 kind: ElementKind::Checkbox,
160 label: label.to_string(),
161 value: Value::Bool(*value),
162 meta: ElementMeta::default(),
163 window: sink.window_name(),
164 });
165 Response { clicked, changed }
166}
167
168fn widget_color3(sink: &mut impl WidgetSink, label: &str, value: &mut [f32; 3]) -> Response {
169 let id = sink.make_id(label);
170 let (clicked, changed) = if let Some(Value::Color3(c)) = sink.consume_edit(&id) {
171 let changed = *value != c;
172 *value = c;
173 (true, changed)
174 } else {
175 (false, false)
176 };
177 sink.record_child(id.clone());
178 sink.declare(ElementDecl {
179 id,
180 kind: ElementKind::ColorPicker3,
181 label: label.to_string(),
182 value: Value::Color3(*value),
183 meta: ElementMeta::default(),
184 window: sink.window_name(),
185 });
186 Response { clicked, changed }
187}
188
189fn widget_color4(sink: &mut impl WidgetSink, label: &str, value: &mut [f32; 4]) -> Response {
190 let id = sink.make_id(label);
191 let (clicked, changed) = if let Some(Value::Color4(c)) = sink.consume_edit(&id) {
192 let changed = *value != c;
193 *value = c;
194 (true, changed)
195 } else {
196 (false, false)
197 };
198 sink.record_child(id.clone());
199 sink.declare(ElementDecl {
200 id,
201 kind: ElementKind::ColorPicker4,
202 label: label.to_string(),
203 value: Value::Color4(*value),
204 meta: ElementMeta::default(),
205 window: sink.window_name(),
206 });
207 Response { clicked, changed }
208}
209
210fn widget_text_input(sink: &mut impl WidgetSink, label: &str, value: &mut String) -> Response {
211 let id = sink.make_id(label);
212 let (clicked, changed) = if let Some(Value::String(s)) = sink.consume_edit(&id) {
213 let changed = *value != s;
214 *value = s;
215 (true, changed)
216 } else {
217 (false, false)
218 };
219 sink.record_child(id.clone());
220 sink.declare(ElementDecl {
221 id,
222 kind: ElementKind::TextInput,
223 label: label.to_string(),
224 value: Value::String(value.clone()),
225 meta: ElementMeta::default(),
226 window: sink.window_name(),
227 });
228 Response { clicked, changed }
229}
230
231fn widget_dropdown(sink: &mut impl WidgetSink, label: &str, selected: &mut usize, options: &[&str]) -> Response {
232 let id = sink.make_id(label);
233 let (clicked, changed) = if let Some(Value::Enum { selected: s, .. }) = sink.consume_edit(&id) {
234 let changed = *selected != s;
235 *selected = s;
236 (true, changed)
237 } else {
238 (false, false)
239 };
240 sink.record_child(id.clone());
241 sink.declare(ElementDecl {
242 id,
243 kind: ElementKind::Dropdown,
244 label: label.to_string(),
245 value: Value::Enum {
246 selected: *selected,
247 options: options.iter().map(|s| s.to_string()).collect(),
248 },
249 meta: ElementMeta::default(),
250 window: sink.window_name(),
251 });
252 Response { clicked, changed }
253}
254
255fn widget_button(sink: &mut impl WidgetSink, label: &str) -> Response {
256 let id = sink.make_id(label);
257 let clicked = matches!(sink.consume_edit(&id), Some(Value::Button(true)));
258 sink.record_child(id.clone());
259 sink.declare(ElementDecl {
260 id,
261 kind: ElementKind::Button,
262 label: label.to_string(),
263 value: Value::Button(false),
264 meta: ElementMeta::default(),
265 window: sink.window_name(),
266 });
267 Response { clicked, changed: clicked }
268}
269
270fn widget_button_compact(sink: &mut impl WidgetSink, label: &str) -> Response {
271 let id = sink.make_id(label);
272 let clicked = matches!(sink.consume_edit(&id), Some(Value::Button(true)));
273 sink.record_child(id.clone());
274 sink.declare(ElementDecl {
275 id,
276 kind: ElementKind::ButtonCompact,
277 label: label.to_string(),
278 value: Value::Button(false),
279 meta: ElementMeta::default(),
280 window: sink.window_name(),
281 });
282 Response { clicked, changed: clicked }
283}
284
285fn widget_label(sink: &mut impl WidgetSink, text: &str) {
286 let id = sink.make_id("__label");
287 sink.record_child(id.clone());
288 sink.declare(ElementDecl {
289 id,
290 kind: ElementKind::Label,
291 label: String::new(),
292 value: Value::String(text.to_string()),
293 meta: ElementMeta::default(),
294 window: sink.window_name(),
295 });
296}
297
298fn widget_kv(sink: &mut impl WidgetSink, label: &str, value: &str) {
299 let id = sink.make_id(label);
300 sink.record_child(id.clone());
301 sink.declare(ElementDecl {
302 id,
303 kind: ElementKind::KeyValue,
304 label: label.to_string(),
305 value: Value::String(value.to_string()),
306 meta: ElementMeta::default(),
307 window: sink.window_name(),
308 });
309}
310
311fn widget_progress_bar(sink: &mut impl WidgetSink, label: &str, value: f64, accent: AccentColor, subtitle: Option<&str>) {
312 let id = sink.make_id(label);
313 sink.record_child(id.clone());
314 sink.declare(ElementDecl {
315 id,
316 kind: ElementKind::ProgressBar,
317 label: label.to_string(),
318 value: Value::Progress(value.clamp(0.0, 1.0)),
319 meta: ElementMeta {
320 accent: Some(accent),
321 subtitle: subtitle.map(|s| s.to_string()),
322 ..Default::default()
323 },
324 window: sink.window_name(),
325 });
326}
327
328fn widget_stat(sink: &mut impl WidgetSink, label: &str, value: &str, subvalue: Option<&str>, accent: AccentColor) {
329 let id = sink.make_id(label);
330 sink.record_child(id.clone());
331 sink.declare(ElementDecl {
332 id,
333 kind: ElementKind::Stat,
334 label: label.to_string(),
335 value: Value::StatValue {
336 value: value.to_string(),
337 subvalue: subvalue.map(|s| s.to_string()),
338 },
339 meta: ElementMeta {
340 accent: Some(accent),
341 ..Default::default()
342 },
343 window: sink.window_name(),
344 });
345}
346
347fn widget_status(
348 sink: &mut impl WidgetSink,
349 label: &str,
350 active: bool,
351 active_text: Option<&str>,
352 inactive_text: Option<&str>,
353 active_color: AccentColor,
354 inactive_color: AccentColor,
355) {
356 let id = sink.make_id(label);
357 sink.record_child(id.clone());
358 sink.declare(ElementDecl {
359 id,
360 kind: ElementKind::Status,
361 label: label.to_string(),
362 value: Value::StatusValue {
363 active,
364 active_text: active_text.map(|s| s.to_string()),
365 inactive_text: inactive_text.map(|s| s.to_string()),
366 active_color: Some(active_color.as_str().to_string()),
367 inactive_color: Some(inactive_color.as_str().to_string()),
368 },
369 meta: ElementMeta::default(),
370 window: sink.window_name(),
371 });
372}
373
374fn widget_mini_chart(sink: &mut impl WidgetSink, label: &str, values: &[f32], unit: Option<&str>, accent: AccentColor) {
375 let id = sink.make_id(label);
376 sink.record_child(id.clone());
377 sink.declare(ElementDecl {
378 id,
379 kind: ElementKind::MiniChart,
380 label: label.to_string(),
381 value: Value::ChartValue {
382 values: values.to_vec(),
383 current: values.last().copied(),
384 unit: unit.map(|s| s.to_string()),
385 },
386 meta: ElementMeta {
387 accent: Some(accent),
388 ..Default::default()
389 },
390 window: sink.window_name(),
391 });
392}
393
394fn widget_plot(
395 sink: &mut impl WidgetSink,
396 label: &str,
397 series: &[(&str, &[f32], AccentColor)],
398 x_label: Option<&str>,
399 y_label: Option<&str>,
400) {
401 let id = sink.make_id(label);
402 let plot_series: Vec<PlotSeries> = series
403 .iter()
404 .map(|(name, values, color)| PlotSeries {
405 name: name.to_string(),
406 values: values.to_vec(),
407 color: color.as_str().to_string(),
408 })
409 .collect();
410 sink.record_child(id.clone());
411 sink.declare(ElementDecl {
412 id,
413 kind: ElementKind::Plot,
414 label: label.to_string(),
415 value: Value::PlotValue {
416 series: plot_series,
417 x_label: x_label.map(|s| s.to_string()),
418 y_label: y_label.map(|s| s.to_string()),
419 },
420 meta: ElementMeta::default(),
421 window: sink.window_name(),
422 });
423}
424
425fn make_label_id(prefix: &str, label: &str, label_counts: &mut HashMap<String, usize>) -> String {
428 let count = label_counts.entry(label.to_string()).or_insert(0);
429 let id = if *count == 0 {
430 format!("{prefix}::{label}")
431 } else {
432 format!("{prefix}::{label}#{count}")
433 };
434 *count += 1;
435 id
436}
437
438pub struct Window<'a> {
442 name: Arc<str>,
443 ctx: &'a mut Context,
444 label_counts: HashMap<String, usize>,
445}
446
447impl<'a> WidgetSink for Window<'a> {
448 fn make_id(&mut self, label: &str) -> String {
449 make_label_id(&self.name, label, &mut self.label_counts)
450 }
451
452 fn declare(&mut self, decl: ElementDecl) {
453 self.ctx.declare(decl);
454 }
455
456 fn consume_edit(&mut self, id: &str) -> Option<Value> {
457 self.ctx.consume_edit(id)
458 }
459
460 fn window_name(&self) -> Arc<str> {
461 self.name.clone()
462 }
463
464 fn record_child(&mut self, _id: String) {
465 }
467}
468
469impl<'a> Window<'a> {
470 pub(crate) fn new(name: String, ctx: &'a mut Context) -> Self {
471 Self {
472 name: Arc::from(name.as_str()),
473 ctx,
474 label_counts: HashMap::new(),
475 }
476 }
477
478 pub fn slider(&mut self, label: &str, value: &mut f32, range: RangeInclusive<f32>) -> Response {
479 widget_slider(self, label, value, &range)
480 }
481
482 pub fn slider_f64(&mut self, label: &str, value: &mut f64, range: RangeInclusive<f64>) -> Response {
483 widget_slider_f64(self, label, value, &range)
484 }
485
486 pub fn slider_int(&mut self, label: &str, value: &mut i32, range: RangeInclusive<i32>) -> Response {
487 widget_slider_int(self, label, value, &range)
488 }
489
490 pub fn slider_uint(&mut self, label: &str, value: &mut u32, range: RangeInclusive<u32>) -> Response {
491 widget_slider_uint(self, label, value, &range)
492 }
493
494 pub fn checkbox(&mut self, label: &str, value: &mut bool) -> Response {
495 widget_checkbox(self, label, value)
496 }
497
498 pub fn color_picker(&mut self, label: &str, value: &mut [f32; 3]) -> Response {
499 widget_color3(self, label, value)
500 }
501
502 pub fn color_picker4(&mut self, label: &str, value: &mut [f32; 4]) -> Response {
503 widget_color4(self, label, value)
504 }
505
506 pub fn text_input(&mut self, label: &str, value: &mut String) -> Response {
507 widget_text_input(self, label, value)
508 }
509
510 pub fn dropdown(&mut self, label: &str, selected: &mut usize, options: &[&str]) -> Response {
511 widget_dropdown(self, label, selected, options)
512 }
513
514 pub fn button(&mut self, label: &str) -> Response {
515 widget_button(self, label)
516 }
517
518 pub fn label(&mut self, text: &str) {
519 widget_label(self, text);
520 }
521
522 pub fn kv(&mut self, label: &str, value: &str) {
523 widget_kv(self, label, value);
524 }
525
526 pub fn kv_value(&mut self, label: &str, value: &mut String) -> Response {
527 let id = self.make_id(label);
528 let (clicked, changed) = if let Some(Value::String(v)) = self.ctx.consume_edit(&id) {
529 let changed = *value != v;
530 *value = v;
531 (true, changed)
532 } else {
533 (false, false)
534 };
535 self.ctx.declare(ElementDecl {
536 id,
537 kind: ElementKind::KeyValue,
538 label: label.to_string(),
539 value: Value::String(value.clone()),
540 meta: ElementMeta::default(),
541 window: self.name.clone(),
542 });
543 Response { clicked, changed }
544 }
545
546 pub fn button_compact(&mut self, label: &str) -> Response {
547 widget_button_compact(self, label)
548 }
549
550 pub fn horizontal<F>(&mut self, f: F)
551 where
552 F: FnOnce(&mut Horizontal<'_, 'a>),
553 {
554 let h_id = self.make_id("__horiz");
555 let mut horiz = Horizontal::new(h_id, self);
556 f(&mut horiz);
557 horiz.finish();
558 }
559
560 pub fn separator(&mut self) {
561 let id = self.make_id("__sep");
562 self.ctx.declare(ElementDecl {
563 id,
564 kind: ElementKind::Separator,
565 label: String::new(),
566 value: Value::Bool(false),
567 meta: ElementMeta::default(),
568 window: self.name.clone(),
569 });
570 }
571
572 pub fn section(&mut self, title: &str) {
573 let id = self.make_id(title);
574 self.ctx.declare(ElementDecl {
575 id,
576 kind: ElementKind::Section,
577 label: title.to_string(),
578 value: Value::String(title.to_string()),
579 meta: ElementMeta::default(),
580 window: self.name.clone(),
581 });
582 }
583
584 pub fn progress_bar(&mut self, label: &str, value: f64, accent: AccentColor) {
585 widget_progress_bar(self, label, value, accent, None);
586 }
587
588 pub fn progress_bar_with_subtitle(&mut self, label: &str, value: f64, accent: AccentColor, subtitle: &str) {
589 widget_progress_bar(self, label, value, accent, Some(subtitle));
590 }
591
592 pub fn stat(&mut self, label: &str, value: &str, subvalue: Option<&str>, accent: AccentColor) {
593 widget_stat(self, label, value, subvalue, accent);
594 }
595
596 pub fn status(
597 &mut self,
598 label: &str,
599 active: bool,
600 active_text: Option<&str>,
601 inactive_text: Option<&str>,
602 active_color: AccentColor,
603 inactive_color: AccentColor,
604 ) {
605 widget_status(self, label, active, active_text, inactive_text, active_color, inactive_color);
606 }
607
608 pub fn mini_chart(&mut self, label: &str, values: &[f32], unit: Option<&str>, accent: AccentColor) {
609 widget_mini_chart(self, label, values, unit, accent);
610 }
611
612 pub fn set_accent(&mut self, accent: AccentColor) {
613 let id = self.make_id(&format!("__accent_{}", accent.as_str()));
614 self.ctx.declare(ElementDecl {
615 id,
616 kind: ElementKind::Label,
617 label: String::new(),
618 value: Value::String(String::new()),
619 meta: ElementMeta {
620 accent: Some(accent),
621 ..Default::default()
622 },
623 window: self.name.clone(),
624 });
625 }
626
627 pub fn grid<F>(&mut self, cols: usize, f: F)
628 where
629 F: FnOnce(&mut Grid<'_, 'a>),
630 {
631 let grid_id = self.make_id("__grid");
632 let mut grid = Grid::new(grid_id, self, cols);
633 f(&mut grid);
634 grid.finish();
635 }
636
637 pub fn plot(
638 &mut self,
639 label: &str,
640 series: &[(&str, &[f32], AccentColor)],
641 x_label: Option<&str>,
642 y_label: Option<&str>,
643 ) {
644 widget_plot(self, label, series, x_label, y_label);
645 }
646}
647
648pub struct Horizontal<'a, 'ctx> {
652 id: String,
653 window: &'a mut Window<'ctx>,
654 children: Vec<String>,
655 label_counts: HashMap<String, usize>,
656}
657
658impl<'a, 'ctx> WidgetSink for Horizontal<'a, 'ctx> {
659 fn make_id(&mut self, label: &str) -> String {
660 make_label_id(&self.id, label, &mut self.label_counts)
661 }
662
663 fn declare(&mut self, decl: ElementDecl) {
664 self.window.ctx.declare(decl);
665 }
666
667 fn consume_edit(&mut self, id: &str) -> Option<Value> {
668 self.window.ctx.consume_edit(id)
669 }
670
671 fn window_name(&self) -> Arc<str> {
672 self.window.name.clone()
673 }
674
675 fn record_child(&mut self, id: String) {
676 self.children.push(id);
677 }
678}
679
680impl<'a, 'ctx> Horizontal<'a, 'ctx> {
681 fn new(id: String, window: &'a mut Window<'ctx>) -> Self {
682 Self {
683 id,
684 window,
685 children: Vec::new(),
686 label_counts: HashMap::new(),
687 }
688 }
689
690 fn finish(self) {
691 self.window.ctx.declare(ElementDecl {
692 id: self.id,
693 kind: ElementKind::Horizontal,
694 label: String::new(),
695 value: Value::GridValue {
696 cols: self.children.len(),
697 children: self.children,
698 },
699 meta: ElementMeta::default(),
700 window: self.window.name.clone(),
701 });
702 }
703
704 pub fn button(&mut self, label: &str) -> Response {
705 let id = self.make_id(label);
706 let clicked = matches!(self.window.ctx.consume_edit(&id), Some(Value::Button(true)));
707 self.children.push(id.clone());
708 self.window.ctx.declare(ElementDecl {
709 id,
710 kind: ElementKind::ButtonInline,
711 label: label.to_string(),
712 value: Value::Button(false),
713 meta: ElementMeta::default(),
714 window: self.window.name.clone(),
715 });
716 Response { clicked, changed: clicked }
717 }
718
719 pub fn label(&mut self, text: &str) {
720 widget_label(self, text);
721 }
722
723 pub fn kv(&mut self, label: &str, value: &str) {
724 widget_kv(self, label, value);
725 }
726
727 pub fn text_input(&mut self, label: &str, value: &mut String) -> Response {
728 widget_text_input(self, label, value)
729 }
730
731 pub fn text_input_inline(&mut self, placeholder: &str, value: &mut String) -> Response {
732 let id = self.make_id(placeholder);
733 let (clicked, changed) = if let Some(Value::String(s)) = self.window.ctx.consume_edit(&id) {
734 let changed = *value != s;
735 *value = s;
736 (true, changed)
737 } else {
738 (false, false)
739 };
740 self.children.push(id.clone());
741 self.window.ctx.declare(ElementDecl {
742 id,
743 kind: ElementKind::TextInputInline,
744 label: placeholder.to_string(),
745 value: Value::String(value.clone()),
746 meta: ElementMeta::default(),
747 window: self.window.name.clone(),
748 });
749 Response { clicked, changed }
750 }
751
752 pub fn slider(&mut self, label: &str, value: &mut f32, range: RangeInclusive<f32>) -> Response {
753 widget_slider(self, label, value, &range)
754 }
755
756 pub fn slider_f64(&mut self, label: &str, value: &mut f64, range: RangeInclusive<f64>) -> Response {
757 widget_slider_f64(self, label, value, &range)
758 }
759
760 pub fn slider_int(&mut self, label: &str, value: &mut i32, range: RangeInclusive<i32>) -> Response {
761 widget_slider_int(self, label, value, &range)
762 }
763
764 pub fn slider_uint(&mut self, label: &str, value: &mut u32, range: RangeInclusive<u32>) -> Response {
765 widget_slider_uint(self, label, value, &range)
766 }
767}
768
769pub struct Grid<'a, 'ctx> {
773 id: String,
774 window: &'a mut Window<'ctx>,
775 cols: usize,
776 children: Vec<String>,
777 label_counts: HashMap<String, usize>,
778}
779
780impl<'a, 'ctx> WidgetSink for Grid<'a, 'ctx> {
781 fn make_id(&mut self, label: &str) -> String {
782 make_label_id(&self.id, label, &mut self.label_counts)
783 }
784
785 fn declare(&mut self, decl: ElementDecl) {
786 self.window.ctx.declare(decl);
787 }
788
789 fn consume_edit(&mut self, id: &str) -> Option<Value> {
790 self.window.ctx.consume_edit(id)
791 }
792
793 fn window_name(&self) -> Arc<str> {
794 self.window.name.clone()
795 }
796
797 fn record_child(&mut self, id: String) {
798 self.children.push(id);
799 }
800}
801
802impl<'a, 'ctx> Grid<'a, 'ctx> {
803 fn new(id: String, window: &'a mut Window<'ctx>, cols: usize) -> Self {
804 Self {
805 id,
806 window,
807 cols,
808 children: Vec::new(),
809 label_counts: HashMap::new(),
810 }
811 }
812
813 fn finish(self) {
814 self.window.ctx.declare(ElementDecl {
815 id: self.id,
816 kind: ElementKind::Grid,
817 label: String::new(),
818 value: Value::GridValue {
819 cols: self.cols,
820 children: self.children,
821 },
822 meta: ElementMeta::default(),
823 window: self.window.name.clone(),
824 });
825 }
826
827 pub fn slider(&mut self, label: &str, value: &mut f32, range: RangeInclusive<f32>) -> Response {
828 widget_slider(self, label, value, &range)
829 }
830
831 pub fn slider_f64(&mut self, label: &str, value: &mut f64, range: RangeInclusive<f64>) -> Response {
832 widget_slider_f64(self, label, value, &range)
833 }
834
835 pub fn slider_int(&mut self, label: &str, value: &mut i32, range: RangeInclusive<i32>) -> Response {
836 widget_slider_int(self, label, value, &range)
837 }
838
839 pub fn slider_uint(&mut self, label: &str, value: &mut u32, range: RangeInclusive<u32>) -> Response {
840 widget_slider_uint(self, label, value, &range)
841 }
842
843 pub fn checkbox(&mut self, label: &str, value: &mut bool) -> Response {
844 widget_checkbox(self, label, value)
845 }
846
847 pub fn color_picker(&mut self, label: &str, value: &mut [f32; 3]) -> Response {
848 widget_color3(self, label, value)
849 }
850
851 pub fn color_picker4(&mut self, label: &str, value: &mut [f32; 4]) -> Response {
852 widget_color4(self, label, value)
853 }
854
855 pub fn text_input(&mut self, label: &str, value: &mut String) -> Response {
856 widget_text_input(self, label, value)
857 }
858
859 pub fn dropdown(&mut self, label: &str, selected: &mut usize, options: &[&str]) -> Response {
860 widget_dropdown(self, label, selected, options)
861 }
862
863 pub fn button(&mut self, label: &str) -> Response {
864 widget_button(self, label)
865 }
866
867 pub fn label(&mut self, text: &str) {
868 widget_label(self, text);
869 }
870
871 pub fn progress_bar(&mut self, label: &str, value: f64, accent: AccentColor) {
872 widget_progress_bar(self, label, value, accent, None);
873 }
874
875 pub fn progress_bar_with_subtitle(&mut self, label: &str, value: f64, accent: AccentColor, subtitle: &str) {
876 widget_progress_bar(self, label, value, accent, Some(subtitle));
877 }
878
879 pub fn stat(&mut self, label: &str, value: &str, subvalue: Option<&str>, accent: AccentColor) {
880 widget_stat(self, label, value, subvalue, accent);
881 }
882
883 pub fn status(
884 &mut self,
885 label: &str,
886 active: bool,
887 active_text: Option<&str>,
888 inactive_text: Option<&str>,
889 active_color: AccentColor,
890 inactive_color: AccentColor,
891 ) {
892 widget_status(self, label, active, active_text, inactive_text, active_color, inactive_color);
893 }
894
895 pub fn mini_chart(&mut self, label: &str, values: &[f32], unit: Option<&str>, accent: AccentColor) {
896 widget_mini_chart(self, label, values, unit, accent);
897 }
898
899 pub fn plot(
900 &mut self,
901 label: &str,
902 series: &[(&str, &[f32], AccentColor)],
903 x_label: Option<&str>,
904 y_label: Option<&str>,
905 ) {
906 widget_plot(self, label, series, x_label, y_label);
907 }
908
909 pub fn button_compact(&mut self, label: &str) -> Response {
910 widget_button_compact(self, label)
911 }
912
913 pub fn kv(&mut self, label: &str, value: &str) {
914 widget_kv(self, label, value);
915 }
916
917 pub fn separator(&mut self) {
918 let id = self.make_id("__sep");
919 self.children.push(id.clone());
920 self.window.ctx.declare(ElementDecl {
921 id,
922 kind: ElementKind::Separator,
923 label: String::new(),
924 value: Value::Bool(false),
925 meta: ElementMeta::default(),
926 window: self.window.name.clone(),
927 });
928 }
929
930 pub fn grid<F>(&mut self, cols: usize, f: F)
931 where
932 F: FnOnce(&mut Grid<'_, 'ctx>),
933 {
934 let grid_id = make_label_id(&self.id, "__grid", &mut self.label_counts);
935 let mut child_grid = Grid::new(grid_id.clone(), self.window, cols);
936 f(&mut child_grid);
937 child_grid.finish();
938 self.children.push(grid_id);
939 }
940}