blockworx/widget/
io_pin_picker.rs1use egui::Pos2;
15
16use crate::shape::pin::PinDir;
17use crate::shell::glass;
18
19pub const INPUT_ICON: egui::ImageSource<'static> =
20 egui::include_image!("../../icons/icon-pin-input.svg");
21const OUTPUT_ICON: egui::ImageSource<'static> =
22 egui::include_image!("../../icons/icon-pin-output.svg");
23const INOUT_ICON: egui::ImageSource<'static> =
24 egui::include_image!("../../icons/icon-pin-inout.svg");
25
26fn icon(dir: PinDir) -> egui::ImageSource<'static> {
29 match dir {
30 PinDir::Input => INPUT_ICON,
31 PinDir::Output => OUTPUT_ICON,
32 PinDir::InOut => INOUT_ICON,
33 }
34}
35
36pub enum PinTypePick {
38 Set(PinDir),
40 Dismiss,
42 None,
44}
45
46const CELLS: [(PinDir, &str); 3] = [
49 (PinDir::Input, "Input"),
50 (PinDir::InOut, "Input Output"),
51 (PinDir::Output, "Output"),
52];
53
54pub fn show(ctx: &egui::Context, at: Pos2, current: Option<PinDir>) -> PinTypePick {
60 let mut pick = PinTypePick::None;
61 let area = egui::Area::new(egui::Id::new("pin_type_picker"))
62 .order(egui::Order::Foreground)
63 .fixed_pos(at)
64 .pivot(egui::Align2::RIGHT_BOTTOM)
65 .show(ctx, |ui| {
66 egui::Frame::popup(ui.style()).show(ui, |ui| {
67 ui.horizontal(|ui| {
68 for (kind, label) in CELLS {
69 let active = current == Some(kind);
70 let cell = egui::Button::image(glass::image(ui, icon(kind), ICON))
71 .min_size(egui::Vec2::splat(glass::TAP))
72 .selected(active);
73 if ui.add(cell).on_hover_text(label).clicked() {
74 pick = PinTypePick::Set(kind);
75 }
76 }
77 });
78 });
79 });
80 if matches!(pick, PinTypePick::None) && area.response.clicked_elsewhere() {
82 pick = PinTypePick::Dismiss;
83 }
84 pick
85}
86
87const ICON: f32 = 21.0;
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
100 fn every_direction_has_a_glyph_of_its_own_and_a_word_to_go_with_it() {
101 let uri = |dir: PinDir| match icon(dir) {
102 egui::ImageSource::Bytes { uri, .. } => uri.to_string(),
103 _ => panic!("the picker's icons are embedded bytes"),
104 };
105 let glyphs: std::collections::HashSet<String> =
106 CELLS.iter().map(|(dir, _)| uri(*dir)).collect();
107 assert_eq!(glyphs.len(), CELLS.len(), "two directions share one glyph");
108 let words: std::collections::HashSet<&str> = CELLS.iter().map(|(_, word)| *word).collect();
109 assert_eq!(words, ["Input", "Output", "Input Output"].into());
110 assert_eq!(
111 uri(PinDir::Input),
112 match INPUT_ICON {
113 egui::ImageSource::Bytes { uri, .. } => uri.to_string(),
114 _ => unreachable!(),
115 },
116 "the overlay's trigger and the picker's Input cell have parted",
117 );
118 }
119}