use egui::{ Button, CentralPanel, Context, Label, Layout, TopBottomPanel, Ui, ViewportBuilder, ViewportId, }; use egui_extras::{Column, TableBuilder}; use crate::{ emulator::{SimId, VBKey}, input::MappingProvider, }; use super::AppWindow; pub struct InputWindow { mappings: MappingProvider, now_binding: Option<(SimId, VBKey)>, active_tab: InputTab, } const KEY_NAMES: [(VBKey, &str); 14] = [ (VBKey::LU, "Up"), (VBKey::LD, "Down"), (VBKey::LL, "Left"), (VBKey::LR, "Right"), (VBKey::SEL, "Select"), (VBKey::STA, "Start"), (VBKey::B, "B"), (VBKey::A, "A"), (VBKey::LT, "L-Trigger"), (VBKey::RT, "R-Trigger"), (VBKey::RU, "R-Up"), (VBKey::RD, "R-Down"), (VBKey::RL, "R-Left"), (VBKey::RR, "R-Right"), ]; impl InputWindow { pub fn new(mappings: MappingProvider) -> Self { Self { mappings, now_binding: None, active_tab: InputTab::Player1, } } fn render_key_bindings(&mut self, ui: &mut Ui, sim_id: SimId) { let mappings = self.mappings.for_sim(sim_id); let binding_names = { let mapping = mappings.read().unwrap(); mapping.keyboard_mapping_names() }; TableBuilder::new(ui) .column(Column::remainder()) .column(Column::remainder()) .cell_layout(Layout::left_to_right(egui::Align::Center)) .body(|mut body| { for keys in KEY_NAMES.chunks_exact(2) { body.row(20.0, |mut row| { for (key, name) in keys { let binding = binding_names.get(key).map(|s| s.as_str()); row.col(|ui| { let size = ui.available_size_before_wrap(); let width = size.x; let height = size.y; ui.add_sized((width * 0.2, height), Label::new(*name)); let label_text = if self.now_binding == Some((sim_id, *key)) { "Press any input" } else { binding.unwrap_or("") }; if ui .add_sized((width * 0.6, height), Button::new(label_text)) .clicked() { self.now_binding = Some((sim_id, *key)) } if ui .add_sized(ui.available_size(), Button::new("Clear")) .clicked() { let mut mapping = mappings.write().unwrap(); mapping.clear_keyboard_mappings(*key); } }); } }); } }); } } impl AppWindow for InputWindow { fn viewport_id(&self) -> ViewportId { ViewportId::from_hash_of("input") } fn initial_viewport(&self) -> ViewportBuilder { ViewportBuilder::default() .with_title("Bind Inputs") .with_inner_size((600.0, 400.0)) } fn show(&mut self, ctx: &Context) { TopBottomPanel::top("options").show(ctx, |ui| { ui.horizontal(|ui| { ui.selectable_value(&mut self.active_tab, InputTab::Player1, "Player 1"); ui.selectable_value(&mut self.active_tab, InputTab::Player2, "Player 2"); }); }); CentralPanel::default().show(ctx, |ui| { match self.active_tab { InputTab::Player1 => self.render_key_bindings(ui, SimId::Player1), InputTab::Player2 => self.render_key_bindings(ui, SimId::Player2), }; }); } fn handle_key_event(&mut self, event: &winit::event::KeyEvent) { if !event.state.is_pressed() { return; } let Some((sim_id, vb)) = self.now_binding.take() else { return; }; let mut mappings = self.mappings.for_sim(sim_id).write().unwrap(); mappings.add_keyboard_mapping(vb, event.physical_key); } } #[derive(Clone, Copy, PartialEq, Eq)] enum InputTab { Player1, Player2, }