armas_basic/components/
kbd.rs1use crate::ext::ArmasContextExt;
6use crate::theme::Theme;
7use egui::{Response, Ui, Vec2};
8
9pub struct Kbd {
26 text: String,
27}
28
29impl Kbd {
30 pub fn new(text: impl Into<String>) -> Self {
32 Self { text: text.into() }
33 }
34
35 pub fn show(self, ui: &mut Ui) -> Response {
37 let theme = ui.ctx().armas_theme();
38 let parts: Vec<&str> = self.text.split('+').map(str::trim).collect();
40
41 if parts.len() > 1 {
42 let response = ui.horizontal(|ui| {
44 ui.spacing_mut().item_spacing.x = 2.0;
45 for part in &parts {
46 render_key(ui, part, &theme);
47 }
48 });
49 response.response
50 } else {
51 render_key(ui, &self.text, &theme)
53 }
54 }
55}
56
57fn key_glyph(text: &str) -> &str {
60 match text {
61 "Cmd" | "Command" | "Meta" | "Super" | "Win" => "⌘",
62 "Shift" => "⇧",
63 "Alt" | "Option" | "Opt" => "⌥",
64 "Ctrl" | "Control" => "⌃",
65 "Enter" | "Return" => "⏎",
66 "Backspace" => "⌫",
67 "Delete" | "Del" => "⌦",
68 "Escape" | "Esc" => "⎋",
69 "Tab" => "⇥",
70 "CapsLock" => "⇪",
71 "Up" | "ArrowUp" => "↑",
72 "Down" | "ArrowDown" => "↓",
73 "Left" | "ArrowLeft" => "←",
74 "Right" | "ArrowRight" => "→",
75 "PageUp" => "⇞",
76 "PageDown" => "⇟",
77 "Home" => "↖",
78 "End" => "↘",
79 "Space" => "␣",
80 other => other,
81 }
82}
83
84fn render_key(ui: &mut Ui, text: &str, theme: &Theme) -> Response {
85 let display = key_glyph(text);
86 let font_size = theme.typography.sm;
87 let font_id = egui::FontId::proportional(font_size);
88 let text_color = theme.muted_foreground();
89 let bg_color = theme.muted();
90
91 let galley = ui
93 .painter()
94 .layout_no_wrap(display.to_string(), font_id, text_color);
95
96 let text_size = galley.size();
97 let padding_x = 6.0;
98 let padding_y = 3.0;
99 let min_width = 20.0;
100 let height = font_size + padding_y * 2.0;
101
102 let size = Vec2::new((text_size.x + padding_x * 2.0).max(min_width), height);
103
104 let (rect, response) = ui.allocate_exact_size(size, egui::Sense::hover());
105
106 if ui.is_rect_visible(rect) {
107 let rounding = 4.0;
108
109 ui.painter().rect_filled(rect, rounding, bg_color);
111
112 ui.painter().rect_stroke(
114 rect,
115 rounding,
116 egui::Stroke::new(1.0, theme.border()),
117 egui::StrokeKind::Inside,
118 );
119
120 ui.painter()
122 .galley(rect.center() - text_size / 2.0, galley, text_color);
123 }
124
125 response
126}