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