egui/examples/custom_keypad/src/keypad.rs

256 lines
7.9 KiB
Rust

use eframe::egui::{self, pos2, vec2, Button, Ui, Vec2};
#[derive(Clone, Copy, Debug, Default, PartialEq)]
enum Transition {
#[default]
None,
CloseOnNextFrame,
CloseImmediately,
}
#[derive(Clone, Debug)]
struct State {
open: bool,
closable: bool,
close_on_next_frame: bool,
start_pos: egui::Pos2,
focus: Option<egui::Id>,
events: Option<Vec<egui::Event>>,
}
impl State {
fn new() -> Self {
Self {
open: false,
closable: false,
close_on_next_frame: false,
start_pos: pos2(100.0, 100.0),
focus: None,
events: None,
}
}
fn queue_char(&mut self, c: char) {
let events = self.events.get_or_insert(vec![]);
if let Some(key) = egui::Key::from_name(&c.to_string()) {
events.push(egui::Event::Key {
key,
physical_key: Some(key),
pressed: true,
repeat: false,
modifiers: Default::default(),
});
}
events.push(egui::Event::Text(c.to_string()));
}
fn queue_key(&mut self, key: egui::Key) {
let events = self.events.get_or_insert(vec![]);
events.push(egui::Event::Key {
key,
physical_key: Some(key),
pressed: true,
repeat: false,
modifiers: Default::default(),
});
}
}
impl Default for State {
fn default() -> Self {
Self::new()
}
}
/// A simple keypad widget.
pub struct Keypad {
id: egui::Id,
}
impl Keypad {
pub fn new() -> Self {
Self {
id: egui::Id::new("keypad"),
}
}
pub fn bump_events(&self, ctx: &egui::Context, raw_input: &mut egui::RawInput) {
let events = ctx.memory_mut(|m| {
m.data
.get_temp_mut_or_default::<State>(self.id)
.events
.take()
});
if let Some(mut events) = events {
events.append(&mut raw_input.events);
raw_input.events = events;
}
}
fn buttons(ui: &mut Ui, state: &mut State) -> Transition {
let mut trans = Transition::None;
ui.vertical(|ui| {
let window_margin = ui.spacing().window_margin;
let size_1x1 = vec2(32.0, 26.0);
let _size_1x2 = vec2(32.0, 52.0 + window_margin.top);
let _size_2x1 = vec2(64.0 + window_margin.left, 26.0);
ui.spacing_mut().item_spacing = Vec2::splat(window_margin.left);
ui.horizontal(|ui| {
if ui.add_sized(size_1x1, Button::new("1")).clicked() {
state.queue_char('1');
}
if ui.add_sized(size_1x1, Button::new("2")).clicked() {
state.queue_char('2');
}
if ui.add_sized(size_1x1, Button::new("3")).clicked() {
state.queue_char('3');
}
if ui.add_sized(size_1x1, Button::new("")).clicked() {
state.queue_key(egui::Key::Home);
}
if ui.add_sized(size_1x1, Button::new("🔙")).clicked() {
state.queue_key(egui::Key::Backspace);
}
});
ui.horizontal(|ui| {
if ui.add_sized(size_1x1, Button::new("4")).clicked() {
state.queue_char('4');
}
if ui.add_sized(size_1x1, Button::new("5")).clicked() {
state.queue_char('5');
}
if ui.add_sized(size_1x1, Button::new("6")).clicked() {
state.queue_char('6');
}
if ui.add_sized(size_1x1, Button::new("")).clicked() {
state.queue_key(egui::Key::End);
}
if ui.add_sized(size_1x1, Button::new("")).clicked() {
state.queue_key(egui::Key::Enter);
trans = Transition::CloseOnNextFrame;
}
});
ui.horizontal(|ui| {
if ui.add_sized(size_1x1, Button::new("7")).clicked() {
state.queue_char('7');
}
if ui.add_sized(size_1x1, Button::new("8")).clicked() {
state.queue_char('8');
}
if ui.add_sized(size_1x1, Button::new("9")).clicked() {
state.queue_char('9');
}
if ui.add_sized(size_1x1, Button::new("")).clicked() {
state.queue_key(egui::Key::ArrowUp);
}
if ui.add_sized(size_1x1, Button::new("")).clicked() {
trans = Transition::CloseImmediately;
}
});
ui.horizontal(|ui| {
if ui.add_sized(size_1x1, Button::new("0")).clicked() {
state.queue_char('0');
}
if ui.add_sized(size_1x1, Button::new(".")).clicked() {
state.queue_char('.');
}
if ui.add_sized(size_1x1, Button::new("")).clicked() {
state.queue_key(egui::Key::ArrowLeft);
}
if ui.add_sized(size_1x1, Button::new("")).clicked() {
state.queue_key(egui::Key::ArrowDown);
}
if ui.add_sized(size_1x1, Button::new("")).clicked() {
state.queue_key(egui::Key::ArrowRight);
}
});
});
trans
}
pub fn show(&self, ctx: &egui::Context) {
let (focus, mut state) = ctx.memory(|m| {
(
m.focused(),
m.data.get_temp::<State>(self.id).unwrap_or_default(),
)
});
let mut is_first_show = false;
if ctx.wants_keyboard_input() && state.focus != focus {
let y = ctx.style().spacing.interact_size.y * 1.25;
state.open = true;
state.start_pos = ctx.input(|i| {
i.pointer
.hover_pos()
.map_or(pos2(100.0, 100.0), |p| p + vec2(0.0, y))
});
state.focus = focus;
is_first_show = true;
}
if state.close_on_next_frame {
state.open = false;
state.close_on_next_frame = false;
state.focus = None;
}
let mut open = state.open;
let win = egui::Window::new("⌨ Keypad");
let win = if is_first_show {
win.current_pos(state.start_pos)
} else {
win.default_pos(state.start_pos)
};
let resp = win
.movable(true)
.resizable(false)
.open(&mut open)
.show(ctx, |ui| Self::buttons(ui, &mut state));
state.open = open;
if let Some(resp) = resp {
match resp.inner {
Some(Transition::CloseOnNextFrame) => {
state.close_on_next_frame = true;
}
Some(Transition::CloseImmediately) => {
state.open = false;
state.focus = None;
}
_ => {}
}
if !state.closable && resp.response.hovered() {
state.closable = true;
}
if state.closable && resp.response.clicked_elsewhere() {
state.open = false;
state.closable = false;
state.focus = None;
}
if is_first_show {
ctx.move_to_top(resp.response.layer_id);
}
}
if let (true, Some(focus)) = (state.open, state.focus) {
ctx.memory_mut(|m| {
m.request_focus(focus);
});
}
ctx.memory_mut(|m| m.data.insert_temp(self.id, state));
}
}
impl Default for Keypad {
fn default() -> Self {
Self::new()
}
}