blockworx/
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> = egui::include_image!("../icons/icon-pin-inout.svg");
24
25fn icon(dir: PinDir) -> egui::ImageSource<'static> {
28 match dir {
29 PinDir::Input => INPUT_ICON,
30 PinDir::Output => OUTPUT_ICON,
31 PinDir::InOut => INOUT_ICON,
32 }
33}
34
35pub enum PinTypePick {
37 Set(PinDir),
39 Dismiss,
41 None,
43}
44
45pub fn show(ctx: &egui::Context, at: Pos2, current: Option<PinDir>) -> PinTypePick {
51 let mut pick = PinTypePick::None;
52 let area = egui::Area::new(egui::Id::new("pin_type_picker"))
53 .order(egui::Order::Foreground)
54 .fixed_pos(at)
55 .pivot(egui::Align2::RIGHT_BOTTOM)
56 .show(ctx, |ui| {
57 egui::Frame::popup(ui.style()).show(ui, |ui| {
58 ui.horizontal(|ui| {
59 for (_, label, kind) in blockworx_tools::commands::PIN_DIRS {
60 let active = current == Some(kind);
61 let cell = egui::Button::image(glass::image(ui, icon(kind), ICON))
62 .min_size(egui::Vec2::splat(glass::TAP))
63 .selected(active);
64 if ui.add(cell).on_hover_text(label).clicked() {
65 pick = PinTypePick::Set(kind);
66 }
67 }
68 });
69 });
70 });
71 if matches!(pick, PinTypePick::None) && area.response.clicked_elsewhere() {
73 pick = PinTypePick::Dismiss;
74 }
75 pick
76}
77
78const ICON: f32 = 21.0;
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
91 fn every_direction_has_a_glyph_of_its_own_and_a_word_to_go_with_it() {
92 let uri = |dir: PinDir| match icon(dir) {
93 egui::ImageSource::Bytes { uri, .. } => uri.to_string(),
94 _ => panic!("the picker's icons are embedded bytes"),
95 };
96 let cells = blockworx_tools::commands::PIN_DIRS;
97 let glyphs: std::collections::HashSet<String> =
98 cells.iter().map(|(_, _, dir)| uri(*dir)).collect();
99 assert_eq!(glyphs.len(), cells.len(), "two directions share one glyph");
100 let words: std::collections::HashSet<&str> =
101 cells.iter().map(|(_, word, _)| *word).collect();
102 assert_eq!(words, ["Input", "Output", "Input Output"].into());
103 assert_eq!(
104 uri(PinDir::Input),
105 match INPUT_ICON {
106 egui::ImageSource::Bytes { uri, .. } => uri.to_string(),
107 _ => unreachable!(),
108 },
109 "the overlay's trigger and the picker's Input cell have parted",
110 );
111 }
112}