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