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 let id = self.make_id("__label");
526 self.record_child(id.clone());
527 self.declare(ElementDecl {
528 id,
529 kind: ElementKind::LabelInline,
530 label: String::new(),
531 value: Value::String(text.to_string()),
532 meta: ElementMeta::default(),
533 window: self.window_name(),
534 });
535 }
536
537 pub fn kv(&mut self, label: &str, value: &str) {
538 widget_kv(self, label, value);
539 }
540
541 pub fn kv_value(&mut self, label: &str, value: &mut String) -> Response {
542 let id = self.make_id(label);
543 let (clicked, changed) = if let Some(Value::String(v)) = self.ctx.consume_edit(&id) {
544 let changed = *value != v;
545 *value = v;
546 (true, changed)
547 } else {
548 (false, false)
549 };
550 self.ctx.declare(ElementDecl {
551 id,
552 kind: ElementKind::KeyValue,
553 label: label.to_string(),
554 value: Value::String(value.clone()),
555 meta: ElementMeta::default(),
556 window: self.name.clone(),
557 });
558 Response { clicked, changed }
559 }
560
561 pub fn button_compact(&mut self, label: &str) -> Response {
562 widget_button_compact(self, label, None)
563 }
564
565 pub fn button_compact_accent(&mut self, label: &str, accent: AccentColor) -> Response {
566 widget_button_compact(self, label, Some(accent))
567 }
568
569 pub fn horizontal<F>(&mut self, f: F)
570 where
571 F: FnOnce(&mut Horizontal<'_, 'a>),
572 {
573 let h_id = self.make_id("__horiz");
574 let mut horiz = Horizontal::new(h_id, self);
575 f(&mut horiz);
576 horiz.finish();
577 }
578
579 pub fn separator(&mut self) {
580 let id = self.make_id("__sep");
581 self.ctx.declare(ElementDecl {
582 id,
583 kind: ElementKind::Separator,
584 label: String::new(),
585 value: Value::Bool(false),
586 meta: ElementMeta::default(),
587 window: self.name.clone(),
588 });
589 }
590
591 pub fn section(&mut self, title: &str) {
592 let id = self.make_id(title);
593 self.ctx.declare(ElementDecl {
594 id,
595 kind: ElementKind::Section,
596 label: title.to_string(),
597 value: Value::String(title.to_string()),
598 meta: ElementMeta::default(),
599 window: self.name.clone(),
600 });
601 }
602
603 pub fn progress_bar(&mut self, label: &str, value: f64, accent: AccentColor) {
604 widget_progress_bar(self, label, value, accent, None);
605 }
606
607 pub fn progress_bar_with_subtitle(&mut self, label: &str, value: f64, accent: AccentColor, subtitle: &str) {
608 widget_progress_bar(self, label, value, accent, Some(subtitle));
609 }
610
611 pub fn stat(&mut self, label: &str, value: &str, subvalue: Option<&str>, accent: AccentColor) {
612 widget_stat(self, label, value, subvalue, accent);
613 }
614
615 pub fn status(
616 &mut self,
617 label: &str,
618 active: bool,
619 active_text: Option<&str>,
620 inactive_text: Option<&str>,
621 active_color: AccentColor,
622 inactive_color: AccentColor,
623 ) {
624 widget_status(self, label, active, active_text, inactive_text, active_color, inactive_color);
625 }
626
627 pub fn mini_chart(&mut self, label: &str, values: &[f32], unit: Option<&str>, accent: AccentColor) {
628 widget_mini_chart(self, label, values, unit, accent);
629 }
630
631 pub fn set_accent(&mut self, accent: AccentColor) {
632 let id = self.make_id(&format!("__accent_{}", accent.as_str()));
633 self.ctx.declare(ElementDecl {
634 id,
635 kind: ElementKind::Label,
636 label: String::new(),
637 value: Value::String(String::new()),
638 meta: ElementMeta {
639 accent: Some(accent),
640 ..Default::default()
641 },
642 window: self.name.clone(),
643 });
644 }
645
646 pub fn grid<F>(&mut self, cols: usize, f: F)
647 where
648 F: FnOnce(&mut Grid<'_, 'a>),
649 {
650 let grid_id = self.make_id("__grid");
651 let mut grid = Grid::new(grid_id, self, cols);
652 f(&mut grid);
653 grid.finish();
654 }
655
656 pub fn plot(
657 &mut self,
658 label: &str,
659 series: &[(&str, &[f32], AccentColor)],
660 x_label: Option<&str>,
661 y_label: Option<&str>,
662 ) {
663 let series_with_autoscale: Vec<(&str, &[f32], AccentColor, bool)> = series
665 .iter()
666 .map(|(name, values, color)| (*name, *values, *color, true))
667 .collect();
668 widget_plot(self, label, &series_with_autoscale, x_label, y_label);
669 }
670
671 pub fn plot_with_autoscale(
674 &mut self,
675 label: &str,
676 series: &[(&str, &[f32], AccentColor, bool)],
677 x_label: Option<&str>,
678 y_label: Option<&str>,
679 ) {
680 widget_plot(self, label, series, x_label, y_label);
681 }
682}
683
684pub struct Horizontal<'a, 'ctx> {
688 id: String,
689 window: &'a mut Window<'ctx>,
690 children: Vec<String>,
691 label_counts: HashMap<String, usize>,
692}
693
694impl<'a, 'ctx> WidgetSink for Horizontal<'a, 'ctx> {
695 fn make_id(&mut self, label: &str) -> String {
696 make_label_id(&self.id, label, &mut self.label_counts)
697 }
698
699 fn declare(&mut self, decl: ElementDecl) {
700 self.window.ctx.declare(decl);
701 }
702
703 fn consume_edit(&mut self, id: &str) -> Option<Value> {
704 self.window.ctx.consume_edit(id)
705 }
706
707 fn window_name(&self) -> Arc<str> {
708 self.window.name.clone()
709 }
710
711 fn record_child(&mut self, id: String) {
712 self.children.push(id);
713 }
714}
715
716impl<'a, 'ctx> Horizontal<'a, 'ctx> {
717 fn new(id: String, window: &'a mut Window<'ctx>) -> Self {
718 Self {
719 id,
720 window,
721 children: Vec::new(),
722 label_counts: HashMap::new(),
723 }
724 }
725
726 fn finish(self) {
727 self.window.ctx.declare(ElementDecl {
728 id: self.id,
729 kind: ElementKind::Horizontal,
730 label: String::new(),
731 value: Value::GridValue {
732 cols: self.children.len(),
733 children: self.children,
734 },
735 meta: ElementMeta::default(),
736 window: self.window.name.clone(),
737 });
738 }
739
740 pub fn button(&mut self, label: &str) -> Response {
741 self.button_accent_inner(label, None)
742 }
743
744 pub fn button_accent(&mut self, label: &str, accent: AccentColor) -> Response {
745 self.button_accent_inner(label, Some(accent))
746 }
747
748 fn button_accent_inner(&mut self, label: &str, accent: Option<AccentColor>) -> Response {
749 let id = self.make_id(label);
750 let clicked = matches!(self.window.ctx.consume_edit(&id), Some(Value::Button(true)));
751 self.children.push(id.clone());
752 self.window.ctx.declare(ElementDecl {
753 id,
754 kind: ElementKind::ButtonInline,
755 label: label.to_string(),
756 value: Value::Button(false),
757 meta: ElementMeta {
758 accent,
759 ..Default::default()
760 },
761 window: self.window.name.clone(),
762 });
763 Response { clicked, changed: clicked }
764 }
765
766 pub fn label(&mut self, text: &str) {
767 let id = self.make_id("__label");
768 self.children.push(id.clone());
769 self.window.ctx.declare(ElementDecl {
770 id,
771 kind: ElementKind::LabelInline,
772 label: String::new(),
773 value: Value::String(text.to_string()),
774 meta: ElementMeta::default(),
775 window: self.window.name.clone(),
776 });
777 }
778
779 pub fn kv(&mut self, label: &str, value: &str) {
780 widget_kv(self, label, value);
781 }
782
783 pub fn text_input(&mut self, label: &str, value: &mut String) -> Response {
784 widget_text_input(self, label, value)
785 }
786
787 pub fn text_input_inline(&mut self, placeholder: &str, value: &mut String) -> Response {
788 let id = self.make_id(placeholder);
789 let (clicked, changed) = if let Some(Value::String(s)) = self.window.ctx.consume_edit(&id) {
790 let changed = *value != s;
791 *value = s;
792 (true, changed)
793 } else {
794 (false, false)
795 };
796 self.children.push(id.clone());
797 self.window.ctx.declare(ElementDecl {
798 id,
799 kind: ElementKind::TextInputInline,
800 label: placeholder.to_string(),
801 value: Value::String(value.clone()),
802 meta: ElementMeta::default(),
803 window: self.window.name.clone(),
804 });
805 Response { clicked, changed }
806 }
807
808 pub fn slider(&mut self, label: &str, value: &mut f32, range: RangeInclusive<f32>) -> Response {
809 widget_slider(self, label, value, &range)
810 }
811
812 pub fn slider_f64(&mut self, label: &str, value: &mut f64, range: RangeInclusive<f64>) -> Response {
813 widget_slider_f64(self, label, value, &range)
814 }
815
816 pub fn slider_int(&mut self, label: &str, value: &mut i32, range: RangeInclusive<i32>) -> Response {
817 widget_slider_int(self, label, value, &range)
818 }
819
820 pub fn slider_uint(&mut self, label: &str, value: &mut u32, range: RangeInclusive<u32>) -> Response {
821 widget_slider_uint(self, label, value, &range)
822 }
823
824 pub fn checkbox(&mut self, label: &str, value: &mut bool) -> Response {
825 widget_checkbox(self, label, value)
826 }
827
828 pub fn color_picker(&mut self, label: &str, value: &mut [f32; 3]) -> Response {
829 widget_color3(self, label, value)
830 }
831
832 pub fn color_picker4(&mut self, label: &str, value: &mut [f32; 4]) -> Response {
833 widget_color4(self, label, value)
834 }
835
836 pub fn dropdown(&mut self, label: &str, selected: &mut usize, options: &[&str]) -> Response {
837 widget_dropdown(self, label, selected, options)
838 }
839}
840
841pub struct Grid<'a, 'ctx> {
845 id: String,
846 window: &'a mut Window<'ctx>,
847 cols: usize,
848 children: Vec<String>,
849 label_counts: HashMap<String, usize>,
850}
851
852impl<'a, 'ctx> WidgetSink for Grid<'a, 'ctx> {
853 fn make_id(&mut self, label: &str) -> String {
854 make_label_id(&self.id, label, &mut self.label_counts)
855 }
856
857 fn declare(&mut self, decl: ElementDecl) {
858 self.window.ctx.declare(decl);
859 }
860
861 fn consume_edit(&mut self, id: &str) -> Option<Value> {
862 self.window.ctx.consume_edit(id)
863 }
864
865 fn window_name(&self) -> Arc<str> {
866 self.window.name.clone()
867 }
868
869 fn record_child(&mut self, id: String) {
870 self.children.push(id);
871 }
872}
873
874impl<'a, 'ctx> Grid<'a, 'ctx> {
875 fn new(id: String, window: &'a mut Window<'ctx>, cols: usize) -> Self {
876 Self {
877 id,
878 window,
879 cols,
880 children: Vec::new(),
881 label_counts: HashMap::new(),
882 }
883 }
884
885 fn finish(self) {
886 self.window.ctx.declare(ElementDecl {
887 id: self.id,
888 kind: ElementKind::Grid,
889 label: String::new(),
890 value: Value::GridValue {
891 cols: self.cols,
892 children: self.children,
893 },
894 meta: ElementMeta::default(),
895 window: self.window.name.clone(),
896 });
897 }
898
899 pub fn slider(&mut self, label: &str, value: &mut f32, range: RangeInclusive<f32>) -> Response {
900 widget_slider(self, label, value, &range)
901 }
902
903 pub fn slider_f64(&mut self, label: &str, value: &mut f64, range: RangeInclusive<f64>) -> Response {
904 widget_slider_f64(self, label, value, &range)
905 }
906
907 pub fn slider_int(&mut self, label: &str, value: &mut i32, range: RangeInclusive<i32>) -> Response {
908 widget_slider_int(self, label, value, &range)
909 }
910
911 pub fn slider_uint(&mut self, label: &str, value: &mut u32, range: RangeInclusive<u32>) -> Response {
912 widget_slider_uint(self, label, value, &range)
913 }
914
915 pub fn checkbox(&mut self, label: &str, value: &mut bool) -> Response {
916 widget_checkbox(self, label, value)
917 }
918
919 pub fn color_picker(&mut self, label: &str, value: &mut [f32; 3]) -> Response {
920 widget_color3(self, label, value)
921 }
922
923 pub fn color_picker4(&mut self, label: &str, value: &mut [f32; 4]) -> Response {
924 widget_color4(self, label, value)
925 }
926
927 pub fn text_input(&mut self, label: &str, value: &mut String) -> Response {
928 widget_text_input(self, label, value)
929 }
930
931 pub fn dropdown(&mut self, label: &str, selected: &mut usize, options: &[&str]) -> Response {
932 widget_dropdown(self, label, selected, options)
933 }
934
935 pub fn button(&mut self, label: &str) -> Response {
936 widget_button(self, label)
937 }
938
939 pub fn label(&mut self, text: &str) {
940 widget_label(self, text);
941 }
942
943 pub fn progress_bar(&mut self, label: &str, value: f64, accent: AccentColor) {
944 widget_progress_bar(self, label, value, accent, None);
945 }
946
947 pub fn progress_bar_with_subtitle(&mut self, label: &str, value: f64, accent: AccentColor, subtitle: &str) {
948 widget_progress_bar(self, label, value, accent, Some(subtitle));
949 }
950
951 pub fn stat(&mut self, label: &str, value: &str, subvalue: Option<&str>, accent: AccentColor) {
952 widget_stat(self, label, value, subvalue, accent);
953 }
954
955 pub fn status(
956 &mut self,
957 label: &str,
958 active: bool,
959 active_text: Option<&str>,
960 inactive_text: Option<&str>,
961 active_color: AccentColor,
962 inactive_color: AccentColor,
963 ) {
964 widget_status(self, label, active, active_text, inactive_text, active_color, inactive_color);
965 }
966
967 pub fn mini_chart(&mut self, label: &str, values: &[f32], unit: Option<&str>, accent: AccentColor) {
968 widget_mini_chart(self, label, values, unit, accent);
969 }
970
971 pub fn plot(
972 &mut self,
973 label: &str,
974 series: &[(&str, &[f32], AccentColor)],
975 x_label: Option<&str>,
976 y_label: Option<&str>,
977 ) {
978 let series_with_autoscale: Vec<(&str, &[f32], AccentColor, bool)> = series
980 .iter()
981 .map(|(name, values, color)| (*name, *values, *color, true))
982 .collect();
983 widget_plot(self, label, &series_with_autoscale, x_label, y_label);
984 }
985
986 pub fn plot_with_autoscale(
989 &mut self,
990 label: &str,
991 series: &[(&str, &[f32], AccentColor, bool)],
992 x_label: Option<&str>,
993 y_label: Option<&str>,
994 ) {
995 widget_plot(self, label, series, x_label, y_label);
996 }
997
998 pub fn button_compact(&mut self, label: &str) -> Response {
999 widget_button_compact(self, label, None)
1000 }
1001
1002 pub fn button_compact_accent(&mut self, label: &str, accent: AccentColor) -> Response {
1003 widget_button_compact(self, label, Some(accent))
1004 }
1005
1006 pub fn kv(&mut self, label: &str, value: &str) {
1007 widget_kv(self, label, value);
1008 }
1009
1010 pub fn separator(&mut self) {
1011 let id = self.make_id("__sep");
1012 self.children.push(id.clone());
1013 self.window.ctx.declare(ElementDecl {
1014 id,
1015 kind: ElementKind::Separator,
1016 label: String::new(),
1017 value: Value::Bool(false),
1018 meta: ElementMeta::default(),
1019 window: self.window.name.clone(),
1020 });
1021 }
1022
1023 pub fn grid<F>(&mut self, cols: usize, f: F)
1024 where
1025 F: FnOnce(&mut Grid<'_, 'ctx>),
1026 {
1027 let grid_id = make_label_id(&self.id, "__grid", &mut self.label_counts);
1028 let mut child_grid = Grid::new(grid_id.clone(), self.window, cols);
1029 f(&mut child_grid);
1030 child_grid.finish();
1031 self.children.push(grid_id);
1032 }
1033}