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_image(sink: &mut impl WidgetSink, label: &str, data_uri: &str, width: Option<u32>, height: Option<u32>) {
400 let id = sink.make_id(label);
401 sink.record_child(id.clone());
402 sink.declare(ElementDecl {
403 id,
404 kind: ElementKind::Image,
405 label: label.to_string(),
406 value: Value::ImageValue {
407 data: data_uri.to_string(),
408 width,
409 height,
410 },
411 meta: ElementMeta::default(),
412 window: sink.window_name(),
413 });
414}
415
416fn widget_image_button(sink: &mut impl WidgetSink, label: &str, data_uri: &str, selected: bool) -> Response {
417 let id = sink.make_id(label);
418 let clicked = matches!(sink.consume_edit(&id), Some(Value::Button(true)));
419 sink.record_child(id.clone());
420 sink.declare(ElementDecl {
421 id,
422 kind: ElementKind::ImageButton,
423 label: label.to_string(),
424 value: Value::ImageValue {
425 data: data_uri.to_string(),
426 width: None,
427 height: None,
428 },
429 meta: ElementMeta {
430 accent: if selected { Some(AccentColor::Teal) } else { None },
431 ..Default::default()
432 },
433 window: sink.window_name(),
434 });
435 Response { clicked, changed: clicked }
436}
437
438fn widget_plot(
439 sink: &mut impl WidgetSink,
440 label: &str,
441 series: &[(&str, &[f32], AccentColor, bool)],
442 x_label: Option<&str>,
443 y_label: Option<&str>,
444) {
445 let id = sink.make_id(label);
446 let plot_series: Vec<PlotSeries> = series
447 .iter()
448 .map(|(name, values, color, autoscale)| PlotSeries {
449 name: name.to_string(),
450 values: values.to_vec(),
451 color: color.as_str().to_string(),
452 autoscale: *autoscale,
453 })
454 .collect();
455 sink.record_child(id.clone());
456 sink.declare(ElementDecl {
457 id,
458 kind: ElementKind::Plot,
459 label: label.to_string(),
460 value: Value::PlotValue {
461 series: plot_series,
462 x_label: x_label.map(|s| s.to_string()),
463 y_label: y_label.map(|s| s.to_string()),
464 },
465 meta: ElementMeta::default(),
466 window: sink.window_name(),
467 });
468}
469
470fn make_label_id(prefix: &str, label: &str, label_counts: &mut HashMap<String, usize>) -> String {
473 let count = label_counts.entry(label.to_string()).or_insert(0);
474 let id = if *count == 0 {
475 format!("{prefix}::{label}")
476 } else {
477 format!("{prefix}::{label}#{count}")
478 };
479 *count += 1;
480 id
481}
482
483pub struct Window<'a> {
487 name: Arc<str>,
488 ctx: &'a mut Context,
489 label_counts: HashMap<String, usize>,
490}
491
492impl<'a> WidgetSink for Window<'a> {
493 fn make_id(&mut self, label: &str) -> String {
494 make_label_id(&self.name, label, &mut self.label_counts)
495 }
496
497 fn declare(&mut self, decl: ElementDecl) {
498 self.ctx.declare(decl);
499 }
500
501 fn consume_edit(&mut self, id: &str) -> Option<Value> {
502 self.ctx.consume_edit(id)
503 }
504
505 fn window_name(&self) -> Arc<str> {
506 self.name.clone()
507 }
508
509 fn record_child(&mut self, _id: String) {
510 }
512}
513
514impl<'a> Window<'a> {
515 pub(crate) fn new(name: String, ctx: &'a mut Context) -> Self {
516 Self {
517 name: Arc::from(name.as_str()),
518 ctx,
519 label_counts: HashMap::new(),
520 }
521 }
522
523 pub fn slider(&mut self, label: &str, value: &mut f32, range: RangeInclusive<f32>) -> Response {
524 widget_slider(self, label, value, &range)
525 }
526
527 pub fn slider_f64(&mut self, label: &str, value: &mut f64, range: RangeInclusive<f64>) -> Response {
528 widget_slider_f64(self, label, value, &range)
529 }
530
531 pub fn slider_int(&mut self, label: &str, value: &mut i32, range: RangeInclusive<i32>) -> Response {
532 widget_slider_int(self, label, value, &range)
533 }
534
535 pub fn slider_uint(&mut self, label: &str, value: &mut u32, range: RangeInclusive<u32>) -> Response {
536 widget_slider_uint(self, label, value, &range)
537 }
538
539 pub fn checkbox(&mut self, label: &str, value: &mut bool) -> Response {
540 widget_checkbox(self, label, value)
541 }
542
543 pub fn color_picker(&mut self, label: &str, value: &mut [f32; 3]) -> Response {
544 widget_color3(self, label, value)
545 }
546
547 pub fn color_picker4(&mut self, label: &str, value: &mut [f32; 4]) -> Response {
548 widget_color4(self, label, value)
549 }
550
551 pub fn text_input(&mut self, label: &str, value: &mut String) -> Response {
552 widget_text_input(self, label, value)
553 }
554
555 pub fn dropdown(&mut self, label: &str, selected: &mut usize, options: &[&str]) -> Response {
556 widget_dropdown(self, label, selected, options)
557 }
558
559 pub fn button(&mut self, label: &str) -> Response {
560 widget_button(self, label)
561 }
562
563 pub fn label(&mut self, text: &str) {
564 let id = self.make_id("__label");
565 self.record_child(id.clone());
566 self.declare(ElementDecl {
567 id,
568 kind: ElementKind::LabelInline,
569 label: String::new(),
570 value: Value::String(text.to_string()),
571 meta: ElementMeta::default(),
572 window: self.window_name(),
573 });
574 }
575
576 pub fn kv(&mut self, label: &str, value: &str) {
577 widget_kv(self, label, value);
578 }
579
580 pub fn kv_value(&mut self, label: &str, value: &mut String) -> Response {
581 let id = self.make_id(label);
582 let (clicked, changed) = if let Some(Value::String(v)) = self.ctx.consume_edit(&id) {
583 let changed = *value != v;
584 *value = v;
585 (true, changed)
586 } else {
587 (false, false)
588 };
589 self.ctx.declare(ElementDecl {
590 id,
591 kind: ElementKind::KeyValue,
592 label: label.to_string(),
593 value: Value::String(value.clone()),
594 meta: ElementMeta::default(),
595 window: self.name.clone(),
596 });
597 Response { clicked, changed }
598 }
599
600 pub fn button_compact(&mut self, label: &str) -> Response {
601 widget_button_compact(self, label, None)
602 }
603
604 pub fn button_compact_accent(&mut self, label: &str, accent: AccentColor) -> Response {
605 widget_button_compact(self, label, Some(accent))
606 }
607
608 pub fn horizontal<F>(&mut self, f: F)
609 where
610 F: FnOnce(&mut Horizontal<'_, 'a>),
611 {
612 let h_id = self.make_id("__horiz");
613 let mut horiz = Horizontal::new(h_id, self);
614 f(&mut horiz);
615 horiz.finish();
616 }
617
618 pub fn separator(&mut self) {
619 let id = self.make_id("__sep");
620 self.ctx.declare(ElementDecl {
621 id,
622 kind: ElementKind::Separator,
623 label: String::new(),
624 value: Value::Bool(false),
625 meta: ElementMeta::default(),
626 window: self.name.clone(),
627 });
628 }
629
630 pub fn section(&mut self, title: &str) {
631 let id = self.make_id(title);
632 self.ctx.declare(ElementDecl {
633 id,
634 kind: ElementKind::Section,
635 label: title.to_string(),
636 value: Value::String(title.to_string()),
637 meta: ElementMeta::default(),
638 window: self.name.clone(),
639 });
640 }
641
642 pub fn progress_bar(&mut self, label: &str, value: f64, accent: AccentColor) {
643 widget_progress_bar(self, label, value, accent, None);
644 }
645
646 pub fn progress_bar_with_subtitle(&mut self, label: &str, value: f64, accent: AccentColor, subtitle: &str) {
647 widget_progress_bar(self, label, value, accent, Some(subtitle));
648 }
649
650 pub fn stat(&mut self, label: &str, value: &str, subvalue: Option<&str>, accent: AccentColor) {
651 widget_stat(self, label, value, subvalue, accent);
652 }
653
654 pub fn status(
655 &mut self,
656 label: &str,
657 active: bool,
658 active_text: Option<&str>,
659 inactive_text: Option<&str>,
660 active_color: AccentColor,
661 inactive_color: AccentColor,
662 ) {
663 widget_status(self, label, active, active_text, inactive_text, active_color, inactive_color);
664 }
665
666 pub fn mini_chart(&mut self, label: &str, values: &[f32], unit: Option<&str>, accent: AccentColor) {
667 widget_mini_chart(self, label, values, unit, accent);
668 }
669
670 pub fn set_accent(&mut self, accent: AccentColor) {
671 let id = self.make_id(&format!("__accent_{}", accent.as_str()));
672 self.ctx.declare(ElementDecl {
673 id,
674 kind: ElementKind::Label,
675 label: String::new(),
676 value: Value::String(String::new()),
677 meta: ElementMeta {
678 accent: Some(accent),
679 ..Default::default()
680 },
681 window: self.name.clone(),
682 });
683 }
684
685 pub fn grid<F>(&mut self, cols: usize, f: F)
686 where
687 F: FnOnce(&mut Grid<'_, 'a>),
688 {
689 let grid_id = self.make_id("__grid");
690 let mut grid = Grid::new(grid_id, self, cols);
691 f(&mut grid);
692 grid.finish();
693 }
694
695 pub fn plot(
696 &mut self,
697 label: &str,
698 series: &[(&str, &[f32], AccentColor)],
699 x_label: Option<&str>,
700 y_label: Option<&str>,
701 ) {
702 let series_with_autoscale: Vec<(&str, &[f32], AccentColor, bool)> = series
704 .iter()
705 .map(|(name, values, color)| (*name, *values, *color, true))
706 .collect();
707 widget_plot(self, label, &series_with_autoscale, x_label, y_label);
708 }
709
710 pub fn plot_with_autoscale(
713 &mut self,
714 label: &str,
715 series: &[(&str, &[f32], AccentColor, bool)],
716 x_label: Option<&str>,
717 y_label: Option<&str>,
718 ) {
719 widget_plot(self, label, series, x_label, y_label);
720 }
721
722 pub fn image(&mut self, label: &str, data_uri: &str) {
724 widget_image(self, label, data_uri, None, None);
725 }
726
727 pub fn image_with_size(&mut self, label: &str, data_uri: &str, width: u32, height: u32) {
729 widget_image(self, label, data_uri, Some(width), Some(height));
730 }
731
732 pub fn image_button(&mut self, label: &str, data_uri: &str, selected: bool) -> Response {
735 widget_image_button(self, label, data_uri, selected)
736 }
737}
738
739pub struct Horizontal<'a, 'ctx> {
743 id: String,
744 window: &'a mut Window<'ctx>,
745 children: Vec<String>,
746 label_counts: HashMap<String, usize>,
747}
748
749impl<'a, 'ctx> WidgetSink for Horizontal<'a, 'ctx> {
750 fn make_id(&mut self, label: &str) -> String {
751 make_label_id(&self.id, label, &mut self.label_counts)
752 }
753
754 fn declare(&mut self, decl: ElementDecl) {
755 self.window.ctx.declare(decl);
756 }
757
758 fn consume_edit(&mut self, id: &str) -> Option<Value> {
759 self.window.ctx.consume_edit(id)
760 }
761
762 fn window_name(&self) -> Arc<str> {
763 self.window.name.clone()
764 }
765
766 fn record_child(&mut self, id: String) {
767 self.children.push(id);
768 }
769}
770
771impl<'a, 'ctx> Horizontal<'a, 'ctx> {
772 fn new(id: String, window: &'a mut Window<'ctx>) -> Self {
773 Self {
774 id,
775 window,
776 children: Vec::new(),
777 label_counts: HashMap::new(),
778 }
779 }
780
781 fn finish(self) {
782 self.window.ctx.declare(ElementDecl {
783 id: self.id,
784 kind: ElementKind::Horizontal,
785 label: String::new(),
786 value: Value::GridValue {
787 cols: self.children.len(),
788 children: self.children,
789 },
790 meta: ElementMeta::default(),
791 window: self.window.name.clone(),
792 });
793 }
794
795 pub fn button(&mut self, label: &str) -> Response {
796 self.button_accent_inner(label, None)
797 }
798
799 pub fn button_accent(&mut self, label: &str, accent: AccentColor) -> Response {
800 self.button_accent_inner(label, Some(accent))
801 }
802
803 fn button_accent_inner(&mut self, label: &str, accent: Option<AccentColor>) -> Response {
804 let id = self.make_id(label);
805 let clicked = matches!(self.window.ctx.consume_edit(&id), Some(Value::Button(true)));
806 self.children.push(id.clone());
807 self.window.ctx.declare(ElementDecl {
808 id,
809 kind: ElementKind::ButtonInline,
810 label: label.to_string(),
811 value: Value::Button(false),
812 meta: ElementMeta {
813 accent,
814 ..Default::default()
815 },
816 window: self.window.name.clone(),
817 });
818 Response { clicked, changed: clicked }
819 }
820
821 pub fn label(&mut self, text: &str) {
822 let id = self.make_id("__label");
823 self.children.push(id.clone());
824 self.window.ctx.declare(ElementDecl {
825 id,
826 kind: ElementKind::LabelInline,
827 label: String::new(),
828 value: Value::String(text.to_string()),
829 meta: ElementMeta::default(),
830 window: self.window.name.clone(),
831 });
832 }
833
834 pub fn kv(&mut self, label: &str, value: &str) {
835 widget_kv(self, label, value);
836 }
837
838 pub fn text_input(&mut self, label: &str, value: &mut String) -> Response {
839 widget_text_input(self, label, value)
840 }
841
842 pub fn text_input_inline(&mut self, placeholder: &str, value: &mut String) -> Response {
843 let id = self.make_id(placeholder);
844 let (clicked, changed) = if let Some(Value::String(s)) = self.window.ctx.consume_edit(&id) {
845 let changed = *value != s;
846 *value = s;
847 (true, changed)
848 } else {
849 (false, false)
850 };
851 self.children.push(id.clone());
852 self.window.ctx.declare(ElementDecl {
853 id,
854 kind: ElementKind::TextInputInline,
855 label: placeholder.to_string(),
856 value: Value::String(value.clone()),
857 meta: ElementMeta::default(),
858 window: self.window.name.clone(),
859 });
860 Response { clicked, changed }
861 }
862
863 pub fn slider(&mut self, label: &str, value: &mut f32, range: RangeInclusive<f32>) -> Response {
864 widget_slider(self, label, value, &range)
865 }
866
867 pub fn slider_f64(&mut self, label: &str, value: &mut f64, range: RangeInclusive<f64>) -> Response {
868 widget_slider_f64(self, label, value, &range)
869 }
870
871 pub fn slider_int(&mut self, label: &str, value: &mut i32, range: RangeInclusive<i32>) -> Response {
872 widget_slider_int(self, label, value, &range)
873 }
874
875 pub fn slider_uint(&mut self, label: &str, value: &mut u32, range: RangeInclusive<u32>) -> Response {
876 widget_slider_uint(self, label, value, &range)
877 }
878
879 pub fn checkbox(&mut self, label: &str, value: &mut bool) -> Response {
880 widget_checkbox(self, label, value)
881 }
882
883 pub fn color_picker(&mut self, label: &str, value: &mut [f32; 3]) -> Response {
884 widget_color3(self, label, value)
885 }
886
887 pub fn color_picker4(&mut self, label: &str, value: &mut [f32; 4]) -> Response {
888 widget_color4(self, label, value)
889 }
890
891 pub fn dropdown(&mut self, label: &str, selected: &mut usize, options: &[&str]) -> Response {
892 widget_dropdown(self, label, selected, options)
893 }
894
895 pub fn image(&mut self, label: &str, data_uri: &str) {
896 widget_image(self, label, data_uri, None, None);
897 }
898}
899
900pub struct Grid<'a, 'ctx> {
904 id: String,
905 window: &'a mut Window<'ctx>,
906 cols: usize,
907 children: Vec<String>,
908 label_counts: HashMap<String, usize>,
909}
910
911impl<'a, 'ctx> WidgetSink for Grid<'a, 'ctx> {
912 fn make_id(&mut self, label: &str) -> String {
913 make_label_id(&self.id, label, &mut self.label_counts)
914 }
915
916 fn declare(&mut self, decl: ElementDecl) {
917 self.window.ctx.declare(decl);
918 }
919
920 fn consume_edit(&mut self, id: &str) -> Option<Value> {
921 self.window.ctx.consume_edit(id)
922 }
923
924 fn window_name(&self) -> Arc<str> {
925 self.window.name.clone()
926 }
927
928 fn record_child(&mut self, id: String) {
929 self.children.push(id);
930 }
931}
932
933impl<'a, 'ctx> Grid<'a, 'ctx> {
934 fn new(id: String, window: &'a mut Window<'ctx>, cols: usize) -> Self {
935 Self {
936 id,
937 window,
938 cols,
939 children: Vec::new(),
940 label_counts: HashMap::new(),
941 }
942 }
943
944 fn finish(self) {
945 self.window.ctx.declare(ElementDecl {
946 id: self.id,
947 kind: ElementKind::Grid,
948 label: String::new(),
949 value: Value::GridValue {
950 cols: self.cols,
951 children: self.children,
952 },
953 meta: ElementMeta::default(),
954 window: self.window.name.clone(),
955 });
956 }
957
958 pub fn slider(&mut self, label: &str, value: &mut f32, range: RangeInclusive<f32>) -> Response {
959 widget_slider(self, label, value, &range)
960 }
961
962 pub fn slider_f64(&mut self, label: &str, value: &mut f64, range: RangeInclusive<f64>) -> Response {
963 widget_slider_f64(self, label, value, &range)
964 }
965
966 pub fn slider_int(&mut self, label: &str, value: &mut i32, range: RangeInclusive<i32>) -> Response {
967 widget_slider_int(self, label, value, &range)
968 }
969
970 pub fn slider_uint(&mut self, label: &str, value: &mut u32, range: RangeInclusive<u32>) -> Response {
971 widget_slider_uint(self, label, value, &range)
972 }
973
974 pub fn checkbox(&mut self, label: &str, value: &mut bool) -> Response {
975 widget_checkbox(self, label, value)
976 }
977
978 pub fn color_picker(&mut self, label: &str, value: &mut [f32; 3]) -> Response {
979 widget_color3(self, label, value)
980 }
981
982 pub fn color_picker4(&mut self, label: &str, value: &mut [f32; 4]) -> Response {
983 widget_color4(self, label, value)
984 }
985
986 pub fn text_input(&mut self, label: &str, value: &mut String) -> Response {
987 widget_text_input(self, label, value)
988 }
989
990 pub fn dropdown(&mut self, label: &str, selected: &mut usize, options: &[&str]) -> Response {
991 widget_dropdown(self, label, selected, options)
992 }
993
994 pub fn button(&mut self, label: &str) -> Response {
995 widget_button(self, label)
996 }
997
998 pub fn label(&mut self, text: &str) {
999 widget_label(self, text);
1000 }
1001
1002 pub fn progress_bar(&mut self, label: &str, value: f64, accent: AccentColor) {
1003 widget_progress_bar(self, label, value, accent, None);
1004 }
1005
1006 pub fn progress_bar_with_subtitle(&mut self, label: &str, value: f64, accent: AccentColor, subtitle: &str) {
1007 widget_progress_bar(self, label, value, accent, Some(subtitle));
1008 }
1009
1010 pub fn stat(&mut self, label: &str, value: &str, subvalue: Option<&str>, accent: AccentColor) {
1011 widget_stat(self, label, value, subvalue, accent);
1012 }
1013
1014 pub fn status(
1015 &mut self,
1016 label: &str,
1017 active: bool,
1018 active_text: Option<&str>,
1019 inactive_text: Option<&str>,
1020 active_color: AccentColor,
1021 inactive_color: AccentColor,
1022 ) {
1023 widget_status(self, label, active, active_text, inactive_text, active_color, inactive_color);
1024 }
1025
1026 pub fn mini_chart(&mut self, label: &str, values: &[f32], unit: Option<&str>, accent: AccentColor) {
1027 widget_mini_chart(self, label, values, unit, accent);
1028 }
1029
1030 pub fn plot(
1031 &mut self,
1032 label: &str,
1033 series: &[(&str, &[f32], AccentColor)],
1034 x_label: Option<&str>,
1035 y_label: Option<&str>,
1036 ) {
1037 let series_with_autoscale: Vec<(&str, &[f32], AccentColor, bool)> = series
1039 .iter()
1040 .map(|(name, values, color)| (*name, *values, *color, true))
1041 .collect();
1042 widget_plot(self, label, &series_with_autoscale, x_label, y_label);
1043 }
1044
1045 pub fn plot_with_autoscale(
1048 &mut self,
1049 label: &str,
1050 series: &[(&str, &[f32], AccentColor, bool)],
1051 x_label: Option<&str>,
1052 y_label: Option<&str>,
1053 ) {
1054 widget_plot(self, label, series, x_label, y_label);
1055 }
1056
1057 pub fn image(&mut self, label: &str, data_uri: &str) {
1058 widget_image(self, label, data_uri, None, None);
1059 }
1060
1061 pub fn image_button(&mut self, label: &str, data_uri: &str, selected: bool) -> Response {
1063 widget_image_button(self, label, data_uri, selected)
1064 }
1065
1066 pub fn button_compact(&mut self, label: &str) -> Response {
1067 widget_button_compact(self, label, None)
1068 }
1069
1070 pub fn button_compact_accent(&mut self, label: &str, accent: AccentColor) -> Response {
1071 widget_button_compact(self, label, Some(accent))
1072 }
1073
1074 pub fn kv(&mut self, label: &str, value: &str) {
1075 widget_kv(self, label, value);
1076 }
1077
1078 pub fn separator(&mut self) {
1079 let id = self.make_id("__sep");
1080 self.children.push(id.clone());
1081 self.window.ctx.declare(ElementDecl {
1082 id,
1083 kind: ElementKind::Separator,
1084 label: String::new(),
1085 value: Value::Bool(false),
1086 meta: ElementMeta::default(),
1087 window: self.window.name.clone(),
1088 });
1089 }
1090
1091 pub fn grid<F>(&mut self, cols: usize, f: F)
1092 where
1093 F: FnOnce(&mut Grid<'_, 'ctx>),
1094 {
1095 let grid_id = make_label_id(&self.id, "__grid", &mut self.label_counts);
1096 let mut child_grid = Grid::new(grid_id.clone(), self.window, cols);
1097 f(&mut child_grid);
1098 child_grid.finish();
1099 self.children.push(grid_id);
1100 }
1101}