A SNES controller that lights the buttons being pressed.
3use std::fmt;
How sure the presser is of a press: a probability, so 0 to 1.
As a whole percentage, which is what fits on a button.
A button being held. A key on the keyboard is pressed without any confidence; a press Jev chose carries the probability it chose it with.
The drawing is laid out on a pad this size and scaled to the room there is.
33const DESIGN: Vec2 = vec2(260.0, 120.0);
Height over width of what [show] draws, for a host that gives it a place.
36pub const ASPECT: f32 = DESIGN.y / DESIGN.x;
38const BODY: Color32 = Color32::from_rgb(0xC8, 0xC6, 0xCF); 39const DARK: Color32 = Color32::from_rgb(0x4B, 0x4B, 0x55); 40const PURPLE: Color32 = Color32::from_rgb(0x5B, 0x3E, 0x9E); 41const LAVENDER: Color32 = Color32::from_rgb(0xA9, 0x98, 0xD8); 42const LIT: Color32 = Color32::from_rgb(0xFF, 0xD2, 0x3C); 43const ON_LIT: Color32 = Color32::from_rgb(0x20, 0x18, 0x00); 44 45enum Shape { 46 Round { centre: Pos2, radius: f32 }, 47 Bar { rect: Rect, rounding: f32 }, 48} 49 50struct Part { 51 button: SnesButton, 52 shape: Shape, 53 colour: Color32,
Printed on the button when it is not lit.
55 legend: &'static str,
Where the legend or the confidence goes: the middle of the button, except on a shoulder, most of which is behind the body.
Shoulders first: the body is drawn over their lower edge.
66fn shoulders() -> [Part; 2] { 67 [ 68 Part { 69 button: SnesButton::L, 70 shape: Shape::Bar { rect: bar(30.0, 2.0, 100.0, 34.0), rounding: 8.0 }, 71 colour: DARK, 72 legend: "L", 73 label: pos2(65.0, 11.0), 74 }, 75 Part { 76 button: SnesButton::R, 77 shape: Shape::Bar { rect: bar(160.0, 2.0, 230.0, 34.0), rounding: 8.0 }, 78 colour: DARK, 79 legend: "R", 80 label: pos2(195.0, 11.0), 81 }, 82 ] 83}
85fn face() -> [Part; 10] { 86 let arm = |button, rect: Rect| Part { 87 button, 88 shape: Shape::Bar { rect, rounding: 2.0 }, 89 colour: DARK, 90 legend: "", 91 label: rect.center(), 92 }; 93 let round = |button, x, y, colour, legend| Part { 94 button, 95 shape: Shape::Round { centre: pos2(x, y), radius: 11.0 }, 96 colour, 97 legend, 98 label: pos2(x, y), 99 }; 100 let pill = |button, x: f32| Part { 101 button, 102 shape: Shape::Bar { rect: bar(x - 12.0, 70.0, x + 12.0, 80.0), rounding: 5.0 }, 103 colour: DARK, 104 legend: "", 105 label: pos2(x, 75.0), 106 }; 107 [ 108 arm(SnesButton::Up, bar(55.0, 38.0, 69.0, 60.0)), 109 arm(SnesButton::Down, bar(55.0, 74.0, 69.0, 96.0)), 110 arm(SnesButton::Left, bar(33.0, 60.0, 55.0, 74.0)), 111 arm(SnesButton::Right, bar(69.0, 60.0, 91.0, 74.0)), 112 round(SnesButton::X, 198.0, 45.0, LAVENDER, "X"), 113 round(SnesButton::A, 222.0, 67.0, PURPLE, "A"), 114 round(SnesButton::B, 198.0, 89.0, PURPLE, "B"), 115 round(SnesButton::Y, 174.0, 67.0, LAVENDER, "Y"), 116 pill(SnesButton::Select, 114.0), 117 pill(SnesButton::Start, 144.0), 118 ] 119}
Draw the controller across the width available. A lit button shows the confidence it was pressed with, as a percentage, if it was pressed with one.
123pub fn show(ui: &mut egui::Ui, pressed: impl Fn(SnesButton) -> Option<Press>) { 124 let scale = ui.available_width() / DESIGN.x; 125 let (response, painter) = ui.allocate_painter(DESIGN * scale, Sense::hover()); 126 let origin = response.rect.min; 127 let place = |at: Pos2| origin + at.to_vec2() * scale; 128 let font = FontId::proportional(9.0 * scale); 129 130 let draw = |part: &Part| { 131 let press = pressed(part.button); 132 let fill = if press.is_some() { LIT } else { part.colour }; 133 match part.shape { 134 Shape::Round { centre, radius } => { 135 painter.circle_filled(place(centre), radius * scale, fill); 136 } 137 Shape::Bar { rect, rounding } => { 138 let placed = Rect::from_min_max(place(rect.min), place(rect.max)); 139 painter.rect_filled(placed, rounding * scale, fill); 140 } 141 } 142 let (text, colour) = match press { 143 Some(Press { confidence: Some(confidence) }) => (confidence.to_string(), ON_LIT), 144 Some(Press { confidence: None }) => (part.legend.to_owned(), ON_LIT), 145 None => (part.legend.to_owned(), BODY), 146 }; 147 painter.text(place(part.label), Align2::CENTER_CENTER, text, font.clone(), colour); 148 }; 149 150 shoulders().iter().for_each(&draw); 151 let body = Rect::from_min_max(place(pos2(8.0, 20.0)), place(pos2(252.0, 114.0))); 152 painter.rect_filled(body, 47.0 * scale, BODY); 153 // The hub of the d-pad, which is not a button. 154 let hub = Rect::from_min_max(place(pos2(55.0, 60.0)), place(pos2(69.0, 74.0))); 155 painter.rect_filled(hub, 0.0, DARK); 156 face().iter().for_each(&draw); 157 for (x, name) in [(114.0, "SELECT"), (144.0, "START")] { 158 painter.text( 159 place(pos2(x, 88.0)), 160 Align2::CENTER_CENTER, 161 name, 162 FontId::proportional(6.0 * scale), 163 DARK, 164 ); 165 } 166}