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_number(sink: &mut impl WidgetSink, label: &str, value: &mut f32) -> Response {
234 let id = sink.make_id(label);
235 let (clicked, changed) = if let Some(Value::Float(v)) = sink.consume_edit(&id) {
236 let new = v as f32;
237 let changed = *value != new;
238 *value = new;
239 (true, changed)
240 } else {
241 (false, false)
242 };
243 sink.record_child(id.clone());
244 sink.declare(ElementDecl {
245 id,
246 kind: ElementKind::NumberInput,
247 label: label.to_string(),
248 value: Value::Float(*value as f64),
249 meta: ElementMeta::default(),
250 window: sink.window_name(),
251 });
252 Response { clicked, changed }
253}
254
255fn widget_number_f64(sink: &mut impl WidgetSink, label: &str, value: &mut f64) -> Response {
256 let id = sink.make_id(label);
257 let (clicked, changed) = if let Some(Value::Float(v)) = sink.consume_edit(&id) {
258 let changed = *value != v;
259 *value = v;
260 (true, changed)
261 } else {
262 (false, false)
263 };
264 sink.record_child(id.clone());
265 sink.declare(ElementDecl {
266 id,
267 kind: ElementKind::NumberInput,
268 label: label.to_string(),
269 value: Value::Float(*value),
270 meta: ElementMeta::default(),
271 window: sink.window_name(),
272 });
273 Response { clicked, changed }
274}
275
276fn widget_number_int(sink: &mut impl WidgetSink, label: &str, value: &mut i32) -> Response {
277 let id = sink.make_id(label);
278 let (clicked, changed) = if let Some(Value::Int(v)) = sink.consume_edit(&id) {
279 let new = v as i32;
280 let changed = *value != new;
281 *value = new;
282 (true, changed)
283 } else {
284 (false, false)
285 };
286 sink.record_child(id.clone());
287 sink.declare(ElementDecl {
288 id,
289 kind: ElementKind::NumberInput,
290 label: label.to_string(),
291 value: Value::Int(*value as i64),
292 meta: ElementMeta::default(),
293 window: sink.window_name(),
294 });
295 Response { clicked, changed }
296}
297
298fn widget_number_uint(sink: &mut impl WidgetSink, label: &str, value: &mut u32) -> Response {
299 let id = sink.make_id(label);
300 let (clicked, changed) = if let Some(Value::Int(v)) = sink.consume_edit(&id) {
301 let new = v.max(0) as u32;
302 let changed = *value != new;
303 *value = new;
304 (true, changed)
305 } else {
306 (false, false)
307 };
308 sink.record_child(id.clone());
309 sink.declare(ElementDecl {
310 id,
311 kind: ElementKind::NumberInput,
312 label: label.to_string(),
313 value: Value::Int(*value as i64),
314 meta: ElementMeta::default(),
315 window: sink.window_name(),
316 });
317 Response { clicked, changed }
318}
319
320fn widget_dropdown(sink: &mut impl WidgetSink, label: &str, selected: &mut usize, options: &[&str]) -> Response {
321 let id = sink.make_id(label);
322 let (clicked, changed) = if let Some(Value::Enum { selected: s, .. }) = sink.consume_edit(&id) {
323 let changed = *selected != s;
324 *selected = s;
325 (true, changed)
326 } else {
327 (false, false)
328 };
329 sink.record_child(id.clone());
330 sink.declare(ElementDecl {
331 id,
332 kind: ElementKind::Dropdown,
333 label: label.to_string(),
334 value: Value::Enum {
335 selected: *selected,
336 options: options.iter().map(|s| s.to_string()).collect(),
337 },
338 meta: ElementMeta::default(),
339 window: sink.window_name(),
340 });
341 Response { clicked, changed }
342}
343
344fn widget_button(sink: &mut impl WidgetSink, label: &str) -> Response {
345 let id = sink.make_id(label);
346 let clicked = matches!(sink.consume_edit(&id), Some(Value::Button(true)));
347 sink.record_child(id.clone());
348 sink.declare(ElementDecl {
349 id,
350 kind: ElementKind::Button,
351 label: label.to_string(),
352 value: Value::Button(false),
353 meta: ElementMeta::default(),
354 window: sink.window_name(),
355 });
356 Response { clicked, changed: clicked }
357}
358
359fn widget_button_compact(sink: &mut impl WidgetSink, label: &str, accent: Option<AccentColor>) -> Response {
360 let id = sink.make_id(label);
361 let clicked = matches!(sink.consume_edit(&id), Some(Value::Button(true)));
362 sink.record_child(id.clone());
363 sink.declare(ElementDecl {
364 id,
365 kind: ElementKind::ButtonCompact,
366 label: label.to_string(),
367 value: Value::Button(false),
368 meta: ElementMeta {
369 accent,
370 ..Default::default()
371 },
372 window: sink.window_name(),
373 });
374 Response { clicked, changed: clicked }
375}
376
377fn widget_label(sink: &mut impl WidgetSink, text: &str) {
378 let id = sink.make_id("__label");
379 sink.record_child(id.clone());
380 sink.declare(ElementDecl {
381 id,
382 kind: ElementKind::Label,
383 label: String::new(),
384 value: Value::String(text.to_string()),
385 meta: ElementMeta::default(),
386 window: sink.window_name(),
387 });
388}
389
390fn widget_kv(sink: &mut impl WidgetSink, label: &str, value: &str) {
391 let id = sink.make_id(label);
392 sink.record_child(id.clone());
393 sink.declare(ElementDecl {
394 id,
395 kind: ElementKind::KeyValue,
396 label: label.to_string(),
397 value: Value::String(value.to_string()),
398 meta: ElementMeta::default(),
399 window: sink.window_name(),
400 });
401}
402
403fn widget_progress_bar(sink: &mut impl WidgetSink, label: &str, value: f64, accent: AccentColor, subtitle: Option<&str>) {
404 let id = sink.make_id(label);
405 sink.record_child(id.clone());
406 sink.declare(ElementDecl {
407 id,
408 kind: ElementKind::ProgressBar,
409 label: label.to_string(),
410 value: Value::Progress(value.clamp(0.0, 1.0)),
411 meta: ElementMeta {
412 accent: Some(accent),
413 subtitle: subtitle.map(|s| s.to_string()),
414 ..Default::default()
415 },
416 window: sink.window_name(),
417 });
418}
419
420fn widget_stat(sink: &mut impl WidgetSink, label: &str, value: &str, subvalue: Option<&str>, accent: AccentColor) {
421 let id = sink.make_id(label);
422 sink.record_child(id.clone());
423 sink.declare(ElementDecl {
424 id,
425 kind: ElementKind::Stat,
426 label: label.to_string(),
427 value: Value::StatValue {
428 value: value.to_string(),
429 subvalue: subvalue.map(|s| s.to_string()),
430 },
431 meta: ElementMeta {
432 accent: Some(accent),
433 ..Default::default()
434 },
435 window: sink.window_name(),
436 });
437}
438
439fn widget_status(
440 sink: &mut impl WidgetSink,
441 label: &str,
442 active: bool,
443 active_text: Option<&str>,
444 inactive_text: Option<&str>,
445 active_color: AccentColor,
446 inactive_color: AccentColor,
447) {
448 let id = sink.make_id(label);
449 sink.record_child(id.clone());
450 sink.declare(ElementDecl {
451 id,
452 kind: ElementKind::Status,
453 label: label.to_string(),
454 value: Value::StatusValue {
455 active,
456 active_text: active_text.map(|s| s.to_string()),
457 inactive_text: inactive_text.map(|s| s.to_string()),
458 active_color: Some(active_color.as_str().to_string()),
459 inactive_color: Some(inactive_color.as_str().to_string()),
460 },
461 meta: ElementMeta::default(),
462 window: sink.window_name(),
463 });
464}
465
466fn widget_mini_chart(sink: &mut impl WidgetSink, label: &str, values: &[f32], unit: Option<&str>, accent: AccentColor) {
467 let id = sink.make_id(label);
468 sink.record_child(id.clone());
469 sink.declare(ElementDecl {
470 id,
471 kind: ElementKind::MiniChart,
472 label: label.to_string(),
473 value: Value::ChartValue {
474 values: values.to_vec(),
475 current: values.last().copied(),
476 unit: unit.map(|s| s.to_string()),
477 },
478 meta: ElementMeta {
479 accent: Some(accent),
480 ..Default::default()
481 },
482 window: sink.window_name(),
483 });
484}
485
486fn widget_image(sink: &mut impl WidgetSink, label: &str, data_uri: &str, width: Option<u32>, height: Option<u32>) {
487 let id = sink.make_id(label);
488 sink.record_child(id.clone());
489 sink.declare(ElementDecl {
490 id,
491 kind: ElementKind::Image,
492 label: label.to_string(),
493 value: Value::ImageValue {
494 data: data_uri.to_string(),
495 width,
496 height,
497 },
498 meta: ElementMeta::default(),
499 window: sink.window_name(),
500 });
501}
502
503fn widget_image_button(sink: &mut impl WidgetSink, label: &str, data_uri: &str, selected: bool) -> Response {
504 let id = sink.make_id(label);
505 let clicked = matches!(sink.consume_edit(&id), Some(Value::Button(true)));
506 sink.record_child(id.clone());
507 sink.declare(ElementDecl {
508 id,
509 kind: ElementKind::ImageButton,
510 label: label.to_string(),
511 value: Value::ImageValue {
512 data: data_uri.to_string(),
513 width: None,
514 height: None,
515 },
516 meta: ElementMeta {
517 accent: if selected { Some(AccentColor::Teal) } else { None },
518 ..Default::default()
519 },
520 window: sink.window_name(),
521 });
522 Response { clicked, changed: clicked }
523}
524
525fn widget_plot(
526 sink: &mut impl WidgetSink,
527 label: &str,
528 series: &[(&str, &[f32], AccentColor, bool)],
529 x_label: Option<&str>,
530 y_label: Option<&str>,
531) {
532 let id = sink.make_id(label);
533 let plot_series: Vec<PlotSeries> = series
534 .iter()
535 .map(|(name, values, color, autoscale)| PlotSeries {
536 name: name.to_string(),
537 values: values.to_vec(),
538 color: color.as_str().to_string(),
539 autoscale: *autoscale,
540 })
541 .collect();
542 sink.record_child(id.clone());
543 sink.declare(ElementDecl {
544 id,
545 kind: ElementKind::Plot,
546 label: label.to_string(),
547 value: Value::PlotValue {
548 series: plot_series,
549 x_label: x_label.map(|s| s.to_string()),
550 y_label: y_label.map(|s| s.to_string()),
551 },
552 meta: ElementMeta::default(),
553 window: sink.window_name(),
554 });
555}
556
557fn make_label_id(prefix: &str, label: &str, label_counts: &mut HashMap<String, usize>) -> String {
560 let count = label_counts.entry(label.to_string()).or_insert(0);
561 let id = if *count == 0 {
562 format!("{prefix}::{label}")
563 } else {
564 format!("{prefix}::{label}#{count}")
565 };
566 *count += 1;
567 id
568}
569
570pub struct Window<'a> {
574 name: Arc<str>,
575 ctx: &'a mut Context,
576 label_counts: HashMap<String, usize>,
577}
578
579impl<'a> WidgetSink for Window<'a> {
580 fn make_id(&mut self, label: &str) -> String {
581 make_label_id(&self.name, label, &mut self.label_counts)
582 }
583
584 fn declare(&mut self, decl: ElementDecl) {
585 self.ctx.declare(decl);
586 }
587
588 fn consume_edit(&mut self, id: &str) -> Option<Value> {
589 self.ctx.consume_edit(id)
590 }
591
592 fn window_name(&self) -> Arc<str> {
593 self.name.clone()
594 }
595
596 fn record_child(&mut self, _id: String) {
597 }
599}
600
601impl<'a> Window<'a> {
602 pub(crate) fn new(name: String, ctx: &'a mut Context) -> Self {
603 Self {
604 name: Arc::from(name.as_str()),
605 ctx,
606 label_counts: HashMap::new(),
607 }
608 }
609
610 pub fn slider(&mut self, label: &str, value: &mut f32, range: RangeInclusive<f32>) -> Response {
611 widget_slider(self, label, value, &range)
612 }
613
614 pub fn slider_f64(&mut self, label: &str, value: &mut f64, range: RangeInclusive<f64>) -> Response {
615 widget_slider_f64(self, label, value, &range)
616 }
617
618 pub fn slider_int(&mut self, label: &str, value: &mut i32, range: RangeInclusive<i32>) -> Response {
619 widget_slider_int(self, label, value, &range)
620 }
621
622 pub fn slider_uint(&mut self, label: &str, value: &mut u32, range: RangeInclusive<u32>) -> Response {
623 widget_slider_uint(self, label, value, &range)
624 }
625
626 pub fn number_input(&mut self, label: &str, value: &mut f32) -> Response {
628 widget_number(self, label, value)
629 }
630
631 pub fn number_input_f64(&mut self, label: &str, value: &mut f64) -> Response {
632 widget_number_f64(self, label, value)
633 }
634
635 pub fn number_input_int(&mut self, label: &str, value: &mut i32) -> Response {
636 widget_number_int(self, label, value)
637 }
638
639 pub fn number_input_uint(&mut self, label: &str, value: &mut u32) -> Response {
640 widget_number_uint(self, label, value)
641 }
642
643 pub fn checkbox(&mut self, label: &str, value: &mut bool) -> Response {
644 widget_checkbox(self, label, value)
645 }
646
647 pub fn color_picker(&mut self, label: &str, value: &mut [f32; 3]) -> Response {
648 widget_color3(self, label, value)
649 }
650
651 pub fn color_picker4(&mut self, label: &str, value: &mut [f32; 4]) -> Response {
652 widget_color4(self, label, value)
653 }
654
655 pub fn text_input(&mut self, label: &str, value: &mut String) -> Response {
656 widget_text_input(self, label, value)
657 }
658
659 pub fn dropdown(&mut self, label: &str, selected: &mut usize, options: &[&str]) -> Response {
660 widget_dropdown(self, label, selected, options)
661 }
662
663 pub fn button(&mut self, label: &str) -> Response {
664 widget_button(self, label)
665 }
666
667 pub fn label(&mut self, text: &str) {
668 let id = self.make_id("__label");
669 self.record_child(id.clone());
670 self.declare(ElementDecl {
671 id,
672 kind: ElementKind::LabelInline,
673 label: String::new(),
674 value: Value::String(text.to_string()),
675 meta: ElementMeta::default(),
676 window: self.window_name(),
677 });
678 }
679
680 pub fn kv(&mut self, label: &str, value: &str) {
681 widget_kv(self, label, value);
682 }
683
684 pub fn kv_value(&mut self, label: &str, value: &mut String) -> Response {
685 let id = self.make_id(label);
686 let (clicked, changed) = if let Some(Value::String(v)) = self.ctx.consume_edit(&id) {
687 let changed = *value != v;
688 *value = v;
689 (true, changed)
690 } else {
691 (false, false)
692 };
693 self.ctx.declare(ElementDecl {
694 id,
695 kind: ElementKind::KeyValue,
696 label: label.to_string(),
697 value: Value::String(value.clone()),
698 meta: ElementMeta::default(),
699 window: self.name.clone(),
700 });
701 Response { clicked, changed }
702 }
703
704 pub fn button_compact(&mut self, label: &str) -> Response {
705 widget_button_compact(self, label, None)
706 }
707
708 pub fn button_compact_accent(&mut self, label: &str, accent: AccentColor) -> Response {
709 widget_button_compact(self, label, Some(accent))
710 }
711
712 pub fn horizontal<F>(&mut self, f: F)
713 where
714 F: FnOnce(&mut Horizontal<'_, 'a>),
715 {
716 let h_id = self.make_id("__horiz");
717 let mut horiz = Horizontal::new(h_id, self);
718 f(&mut horiz);
719 horiz.finish();
720 }
721
722 pub fn separator(&mut self) {
723 let id = self.make_id("__sep");
724 self.ctx.declare(ElementDecl {
725 id,
726 kind: ElementKind::Separator,
727 label: String::new(),
728 value: Value::Bool(false),
729 meta: ElementMeta::default(),
730 window: self.name.clone(),
731 });
732 }
733
734 pub fn section(&mut self, title: &str) {
735 let id = self.make_id(title);
736 self.ctx.declare(ElementDecl {
737 id,
738 kind: ElementKind::Section,
739 label: title.to_string(),
740 value: Value::String(title.to_string()),
741 meta: ElementMeta::default(),
742 window: self.name.clone(),
743 });
744 }
745
746 pub fn progress_bar(&mut self, label: &str, value: f64, accent: AccentColor) {
747 widget_progress_bar(self, label, value, accent, None);
748 }
749
750 pub fn progress_bar_with_subtitle(&mut self, label: &str, value: f64, accent: AccentColor, subtitle: &str) {
751 widget_progress_bar(self, label, value, accent, Some(subtitle));
752 }
753
754 pub fn stat(&mut self, label: &str, value: &str, subvalue: Option<&str>, accent: AccentColor) {
755 widget_stat(self, label, value, subvalue, accent);
756 }
757
758 pub fn status(
759 &mut self,
760 label: &str,
761 active: bool,
762 active_text: Option<&str>,
763 inactive_text: Option<&str>,
764 active_color: AccentColor,
765 inactive_color: AccentColor,
766 ) {
767 widget_status(self, label, active, active_text, inactive_text, active_color, inactive_color);
768 }
769
770 pub fn mini_chart(&mut self, label: &str, values: &[f32], unit: Option<&str>, accent: AccentColor) {
771 widget_mini_chart(self, label, values, unit, accent);
772 }
773
774 pub fn set_accent(&mut self, accent: AccentColor) {
775 let id = self.make_id(&format!("__accent_{}", accent.as_str()));
776 self.ctx.declare(ElementDecl {
777 id,
778 kind: ElementKind::Label,
779 label: String::new(),
780 value: Value::String(String::new()),
781 meta: ElementMeta {
782 accent: Some(accent),
783 ..Default::default()
784 },
785 window: self.name.clone(),
786 });
787 }
788
789 pub fn grid<F>(&mut self, cols: usize, f: F)
790 where
791 F: FnOnce(&mut Grid<'_, 'a>),
792 {
793 let grid_id = self.make_id("__grid");
794 let mut grid = Grid::new(grid_id, self, cols);
795 f(&mut grid);
796 grid.finish();
797 }
798
799 pub fn plot(
800 &mut self,
801 label: &str,
802 series: &[(&str, &[f32], AccentColor)],
803 x_label: Option<&str>,
804 y_label: Option<&str>,
805 ) {
806 let series_with_autoscale: Vec<(&str, &[f32], AccentColor, bool)> = series
808 .iter()
809 .map(|(name, values, color)| (*name, *values, *color, true))
810 .collect();
811 widget_plot(self, label, &series_with_autoscale, x_label, y_label);
812 }
813
814 pub fn plot_with_autoscale(
817 &mut self,
818 label: &str,
819 series: &[(&str, &[f32], AccentColor, bool)],
820 x_label: Option<&str>,
821 y_label: Option<&str>,
822 ) {
823 widget_plot(self, label, series, x_label, y_label);
824 }
825
826 pub fn image(&mut self, label: &str, data_uri: &str) {
828 widget_image(self, label, data_uri, None, None);
829 }
830
831 pub fn image_with_size(&mut self, label: &str, data_uri: &str, width: u32, height: u32) {
833 widget_image(self, label, data_uri, Some(width), Some(height));
834 }
835
836 pub fn image_button(&mut self, label: &str, data_uri: &str, selected: bool) -> Response {
839 widget_image_button(self, label, data_uri, selected)
840 }
841}
842
843pub struct Horizontal<'a, 'ctx> {
847 id: String,
848 window: &'a mut Window<'ctx>,
849 children: Vec<String>,
850 label_counts: HashMap<String, usize>,
851}
852
853impl<'a, 'ctx> WidgetSink for Horizontal<'a, 'ctx> {
854 fn make_id(&mut self, label: &str) -> String {
855 make_label_id(&self.id, label, &mut self.label_counts)
856 }
857
858 fn declare(&mut self, decl: ElementDecl) {
859 self.window.ctx.declare(decl);
860 }
861
862 fn consume_edit(&mut self, id: &str) -> Option<Value> {
863 self.window.ctx.consume_edit(id)
864 }
865
866 fn window_name(&self) -> Arc<str> {
867 self.window.name.clone()
868 }
869
870 fn record_child(&mut self, id: String) {
871 self.children.push(id);
872 }
873}
874
875impl<'a, 'ctx> Horizontal<'a, 'ctx> {
876 fn new(id: String, window: &'a mut Window<'ctx>) -> Self {
877 Self {
878 id,
879 window,
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::Horizontal,
889 label: String::new(),
890 value: Value::GridValue {
891 cols: self.children.len(),
892 children: self.children,
893 },
894 meta: ElementMeta::default(),
895 window: self.window.name.clone(),
896 });
897 }
898
899 pub fn button(&mut self, label: &str) -> Response {
900 self.button_accent_inner(label, None)
901 }
902
903 pub fn button_accent(&mut self, label: &str, accent: AccentColor) -> Response {
904 self.button_accent_inner(label, Some(accent))
905 }
906
907 fn button_accent_inner(&mut self, label: &str, accent: Option<AccentColor>) -> Response {
908 let id = self.make_id(label);
909 let clicked = matches!(self.window.ctx.consume_edit(&id), Some(Value::Button(true)));
910 self.children.push(id.clone());
911 self.window.ctx.declare(ElementDecl {
912 id,
913 kind: ElementKind::ButtonInline,
914 label: label.to_string(),
915 value: Value::Button(false),
916 meta: ElementMeta {
917 accent,
918 ..Default::default()
919 },
920 window: self.window.name.clone(),
921 });
922 Response { clicked, changed: clicked }
923 }
924
925 pub fn label(&mut self, text: &str) {
926 let id = self.make_id("__label");
927 self.children.push(id.clone());
928 self.window.ctx.declare(ElementDecl {
929 id,
930 kind: ElementKind::LabelInline,
931 label: String::new(),
932 value: Value::String(text.to_string()),
933 meta: ElementMeta::default(),
934 window: self.window.name.clone(),
935 });
936 }
937
938 pub fn kv(&mut self, label: &str, value: &str) {
939 widget_kv(self, label, value);
940 }
941
942 pub fn text_input(&mut self, label: &str, value: &mut String) -> Response {
943 widget_text_input(self, label, value)
944 }
945
946 pub fn text_input_inline(&mut self, placeholder: &str, value: &mut String) -> Response {
947 let id = self.make_id(placeholder);
948 let (clicked, changed) = if let Some(Value::String(s)) = self.window.ctx.consume_edit(&id) {
949 let changed = *value != s;
950 *value = s;
951 (true, changed)
952 } else {
953 (false, false)
954 };
955 self.children.push(id.clone());
956 self.window.ctx.declare(ElementDecl {
957 id,
958 kind: ElementKind::TextInputInline,
959 label: placeholder.to_string(),
960 value: Value::String(value.clone()),
961 meta: ElementMeta::default(),
962 window: self.window.name.clone(),
963 });
964 Response { clicked, changed }
965 }
966
967 pub fn slider(&mut self, label: &str, value: &mut f32, range: RangeInclusive<f32>) -> Response {
968 widget_slider(self, label, value, &range)
969 }
970
971 pub fn slider_f64(&mut self, label: &str, value: &mut f64, range: RangeInclusive<f64>) -> Response {
972 widget_slider_f64(self, label, value, &range)
973 }
974
975 pub fn slider_int(&mut self, label: &str, value: &mut i32, range: RangeInclusive<i32>) -> Response {
976 widget_slider_int(self, label, value, &range)
977 }
978
979 pub fn slider_uint(&mut self, label: &str, value: &mut u32, range: RangeInclusive<u32>) -> Response {
980 widget_slider_uint(self, label, value, &range)
981 }
982
983 pub fn number_input(&mut self, label: &str, value: &mut f32) -> Response {
985 widget_number(self, label, value)
986 }
987
988 pub fn number_input_f64(&mut self, label: &str, value: &mut f64) -> Response {
989 widget_number_f64(self, label, value)
990 }
991
992 pub fn number_input_int(&mut self, label: &str, value: &mut i32) -> Response {
993 widget_number_int(self, label, value)
994 }
995
996 pub fn number_input_uint(&mut self, label: &str, value: &mut u32) -> Response {
997 widget_number_uint(self, label, value)
998 }
999
1000 pub fn checkbox(&mut self, label: &str, value: &mut bool) -> Response {
1001 widget_checkbox(self, label, value)
1002 }
1003
1004 pub fn color_picker(&mut self, label: &str, value: &mut [f32; 3]) -> Response {
1005 widget_color3(self, label, value)
1006 }
1007
1008 pub fn color_picker4(&mut self, label: &str, value: &mut [f32; 4]) -> Response {
1009 widget_color4(self, label, value)
1010 }
1011
1012 pub fn dropdown(&mut self, label: &str, selected: &mut usize, options: &[&str]) -> Response {
1013 widget_dropdown(self, label, selected, options)
1014 }
1015
1016 pub fn image(&mut self, label: &str, data_uri: &str) {
1017 widget_image(self, label, data_uri, None, None);
1018 }
1019}
1020
1021pub struct Grid<'a, 'ctx> {
1025 id: String,
1026 window: &'a mut Window<'ctx>,
1027 cols: usize,
1028 children: Vec<String>,
1029 label_counts: HashMap<String, usize>,
1030}
1031
1032impl<'a, 'ctx> WidgetSink for Grid<'a, 'ctx> {
1033 fn make_id(&mut self, label: &str) -> String {
1034 make_label_id(&self.id, label, &mut self.label_counts)
1035 }
1036
1037 fn declare(&mut self, decl: ElementDecl) {
1038 self.window.ctx.declare(decl);
1039 }
1040
1041 fn consume_edit(&mut self, id: &str) -> Option<Value> {
1042 self.window.ctx.consume_edit(id)
1043 }
1044
1045 fn window_name(&self) -> Arc<str> {
1046 self.window.name.clone()
1047 }
1048
1049 fn record_child(&mut self, id: String) {
1050 self.children.push(id);
1051 }
1052}
1053
1054impl<'a, 'ctx> Grid<'a, 'ctx> {
1055 fn new(id: String, window: &'a mut Window<'ctx>, cols: usize) -> Self {
1056 Self {
1057 id,
1058 window,
1059 cols,
1060 children: Vec::new(),
1061 label_counts: HashMap::new(),
1062 }
1063 }
1064
1065 fn finish(self) {
1066 self.window.ctx.declare(ElementDecl {
1067 id: self.id,
1068 kind: ElementKind::Grid,
1069 label: String::new(),
1070 value: Value::GridValue {
1071 cols: self.cols,
1072 children: self.children,
1073 },
1074 meta: ElementMeta::default(),
1075 window: self.window.name.clone(),
1076 });
1077 }
1078
1079 pub fn slider(&mut self, label: &str, value: &mut f32, range: RangeInclusive<f32>) -> Response {
1080 widget_slider(self, label, value, &range)
1081 }
1082
1083 pub fn slider_f64(&mut self, label: &str, value: &mut f64, range: RangeInclusive<f64>) -> Response {
1084 widget_slider_f64(self, label, value, &range)
1085 }
1086
1087 pub fn slider_int(&mut self, label: &str, value: &mut i32, range: RangeInclusive<i32>) -> Response {
1088 widget_slider_int(self, label, value, &range)
1089 }
1090
1091 pub fn slider_uint(&mut self, label: &str, value: &mut u32, range: RangeInclusive<u32>) -> Response {
1092 widget_slider_uint(self, label, value, &range)
1093 }
1094
1095 pub fn number_input(&mut self, label: &str, value: &mut f32) -> Response {
1097 widget_number(self, label, value)
1098 }
1099
1100 pub fn number_input_f64(&mut self, label: &str, value: &mut f64) -> Response {
1101 widget_number_f64(self, label, value)
1102 }
1103
1104 pub fn number_input_int(&mut self, label: &str, value: &mut i32) -> Response {
1105 widget_number_int(self, label, value)
1106 }
1107
1108 pub fn number_input_uint(&mut self, label: &str, value: &mut u32) -> Response {
1109 widget_number_uint(self, label, value)
1110 }
1111
1112 pub fn checkbox(&mut self, label: &str, value: &mut bool) -> Response {
1113 widget_checkbox(self, label, value)
1114 }
1115
1116 pub fn color_picker(&mut self, label: &str, value: &mut [f32; 3]) -> Response {
1117 widget_color3(self, label, value)
1118 }
1119
1120 pub fn color_picker4(&mut self, label: &str, value: &mut [f32; 4]) -> Response {
1121 widget_color4(self, label, value)
1122 }
1123
1124 pub fn text_input(&mut self, label: &str, value: &mut String) -> Response {
1125 widget_text_input(self, label, value)
1126 }
1127
1128 pub fn dropdown(&mut self, label: &str, selected: &mut usize, options: &[&str]) -> Response {
1129 widget_dropdown(self, label, selected, options)
1130 }
1131
1132 pub fn button(&mut self, label: &str) -> Response {
1133 widget_button(self, label)
1134 }
1135
1136 pub fn label(&mut self, text: &str) {
1137 widget_label(self, text);
1138 }
1139
1140 pub fn progress_bar(&mut self, label: &str, value: f64, accent: AccentColor) {
1141 widget_progress_bar(self, label, value, accent, None);
1142 }
1143
1144 pub fn progress_bar_with_subtitle(&mut self, label: &str, value: f64, accent: AccentColor, subtitle: &str) {
1145 widget_progress_bar(self, label, value, accent, Some(subtitle));
1146 }
1147
1148 pub fn stat(&mut self, label: &str, value: &str, subvalue: Option<&str>, accent: AccentColor) {
1149 widget_stat(self, label, value, subvalue, accent);
1150 }
1151
1152 pub fn status(
1153 &mut self,
1154 label: &str,
1155 active: bool,
1156 active_text: Option<&str>,
1157 inactive_text: Option<&str>,
1158 active_color: AccentColor,
1159 inactive_color: AccentColor,
1160 ) {
1161 widget_status(self, label, active, active_text, inactive_text, active_color, inactive_color);
1162 }
1163
1164 pub fn mini_chart(&mut self, label: &str, values: &[f32], unit: Option<&str>, accent: AccentColor) {
1165 widget_mini_chart(self, label, values, unit, accent);
1166 }
1167
1168 pub fn plot(
1169 &mut self,
1170 label: &str,
1171 series: &[(&str, &[f32], AccentColor)],
1172 x_label: Option<&str>,
1173 y_label: Option<&str>,
1174 ) {
1175 let series_with_autoscale: Vec<(&str, &[f32], AccentColor, bool)> = series
1177 .iter()
1178 .map(|(name, values, color)| (*name, *values, *color, true))
1179 .collect();
1180 widget_plot(self, label, &series_with_autoscale, x_label, y_label);
1181 }
1182
1183 pub fn plot_with_autoscale(
1186 &mut self,
1187 label: &str,
1188 series: &[(&str, &[f32], AccentColor, bool)],
1189 x_label: Option<&str>,
1190 y_label: Option<&str>,
1191 ) {
1192 widget_plot(self, label, series, x_label, y_label);
1193 }
1194
1195 pub fn image(&mut self, label: &str, data_uri: &str) {
1196 widget_image(self, label, data_uri, None, None);
1197 }
1198
1199 pub fn image_button(&mut self, label: &str, data_uri: &str, selected: bool) -> Response {
1201 widget_image_button(self, label, data_uri, selected)
1202 }
1203
1204 pub fn button_compact(&mut self, label: &str) -> Response {
1205 widget_button_compact(self, label, None)
1206 }
1207
1208 pub fn button_compact_accent(&mut self, label: &str, accent: AccentColor) -> Response {
1209 widget_button_compact(self, label, Some(accent))
1210 }
1211
1212 pub fn kv(&mut self, label: &str, value: &str) {
1213 widget_kv(self, label, value);
1214 }
1215
1216 pub fn separator(&mut self) {
1217 let id = self.make_id("__sep");
1218 self.children.push(id.clone());
1219 self.window.ctx.declare(ElementDecl {
1220 id,
1221 kind: ElementKind::Separator,
1222 label: String::new(),
1223 value: Value::Bool(false),
1224 meta: ElementMeta::default(),
1225 window: self.window.name.clone(),
1226 });
1227 }
1228
1229 pub fn grid<F>(&mut self, cols: usize, f: F)
1230 where
1231 F: FnOnce(&mut Grid<'_, 'ctx>),
1232 {
1233 let grid_id = make_label_id(&self.id, "__grid", &mut self.label_counts);
1234 let mut child_grid = Grid::new(grid_id.clone(), self.window, cols);
1235 f(&mut child_grid);
1236 child_grid.finish();
1237 self.children.push(grid_id);
1238 }
1239}