lemur/src/window/game.rs

263 lines
9.2 KiB
Rust
Raw Normal View History

2024-11-28 15:27:18 +00:00
use crate::{
app::UserEvent,
emulator::{EmulatorClient, EmulatorCommand, SimId},
2024-11-26 05:38:03 +00:00
};
use egui::{
2024-12-01 05:45:13 +00:00
menu, Button, CentralPanel, Color32, Context, Frame, Response, Sense, TopBottomPanel, Ui, Vec2,
2024-11-28 17:39:08 +00:00
ViewportBuilder, ViewportCommand, ViewportId, WidgetText,
2024-11-26 05:38:03 +00:00
};
2024-11-28 15:27:18 +00:00
use winit::event_loop::EventLoopProxy;
2024-11-26 05:38:03 +00:00
2024-12-01 04:14:01 +00:00
use super::{
game_screen::{DisplayMode, GameScreen},
AppWindow,
};
2024-11-26 05:38:03 +00:00
2024-12-01 05:45:13 +00:00
const COLOR_PRESETS: [[Color32; 2]; 3] = [
[
Color32::from_rgb(0xff, 0x00, 0x00),
Color32::from_rgb(0x00, 0xc6, 0xf0),
],
[
Color32::from_rgb(0x00, 0xb4, 0x00),
Color32::from_rgb(0xc8, 0x00, 0xff),
],
[
Color32::from_rgb(0xb4, 0x9b, 0x00),
Color32::from_rgb(0x00, 0x00, 0xff),
],
];
2024-11-26 05:38:03 +00:00
pub struct GameWindow {
client: EmulatorClient,
2024-11-28 15:27:18 +00:00
proxy: EventLoopProxy<UserEvent>,
2024-11-26 05:38:03 +00:00
sim_id: SimId,
2024-12-01 04:14:01 +00:00
display_mode: DisplayMode,
2024-12-01 05:45:13 +00:00
colors: [Color32; 2],
2024-11-26 05:38:03 +00:00
screen: Option<GameScreen>,
}
impl GameWindow {
2024-11-28 15:27:18 +00:00
pub fn new(client: EmulatorClient, proxy: EventLoopProxy<UserEvent>, sim_id: SimId) -> Self {
2024-11-26 05:38:03 +00:00
Self {
client,
2024-11-28 15:27:18 +00:00
proxy,
2024-11-26 05:38:03 +00:00
sim_id,
2024-12-01 04:14:01 +00:00
display_mode: DisplayMode::Anaglyph,
2024-12-01 05:45:13 +00:00
colors: COLOR_PRESETS[0],
2024-11-26 05:38:03 +00:00
screen: None,
}
}
fn show_menu(&mut self, ctx: &Context, ui: &mut Ui) {
ui.menu_button("ROM", |ui| {
if ui.button("Open ROM").clicked() {
let rom = rfd::FileDialog::new()
.add_filter("Virtual Boy ROMs", &["vb", "vbrom"])
.pick_file();
if let Some(path) = rom {
self.client
.send_command(EmulatorCommand::LoadGame(SimId::Player1, path));
}
ui.close_menu();
}
if ui.button("Quit").clicked() {
ctx.send_viewport_cmd(ViewportCommand::Close);
}
});
ui.menu_button("Emulation", |ui| {
let has_game = self.client.has_game(self.sim_id);
if self.client.is_running(self.sim_id) {
if ui.add_enabled(has_game, Button::new("Pause")).clicked() {
self.client.send_command(EmulatorCommand::Pause);
ui.close_menu();
}
} else if ui.add_enabled(has_game, Button::new("Resume")).clicked() {
self.client.send_command(EmulatorCommand::Resume);
ui.close_menu();
}
if ui.add_enabled(has_game, Button::new("Reset")).clicked() {
self.client
.send_command(EmulatorCommand::Reset(self.sim_id));
ui.close_menu();
}
});
ui.menu_button("Video", |ui| {
2024-12-01 04:14:01 +00:00
ui.menu_button("Screen Size", |ui| {
let current_dims = ctx.input(|i| i.viewport().inner_rect.unwrap());
let current_dims = current_dims.max - current_dims.min;
2024-11-26 05:38:03 +00:00
2024-12-01 04:14:01 +00:00
for scale in 1..=4 {
let label = format!("x{scale}");
let scale = scale as f32;
let dims = (384.0 * scale, 224.0 * scale + 22.0).into();
if ui
.selectable_button((current_dims - dims).length() < 1.0, label)
.clicked()
{
ctx.send_viewport_cmd(ViewportCommand::InnerSize(dims));
ui.close_menu();
}
}
});
ui.menu_button("Display Mode", |ui| {
2024-11-26 05:38:03 +00:00
if ui
2024-12-01 04:14:01 +00:00
.selectable_option(&mut self.display_mode, DisplayMode::Anaglyph, "Anaglyph")
2024-11-26 05:38:03 +00:00
.clicked()
{
ui.close_menu();
}
2024-12-01 04:14:01 +00:00
if ui
.selectable_option(&mut self.display_mode, DisplayMode::LeftEye, "Left Eye")
.clicked()
{
ui.close_menu();
}
if ui
.selectable_option(&mut self.display_mode, DisplayMode::RightEye, "Right Eye")
.clicked()
{
ui.close_menu();
}
});
2024-12-01 05:45:13 +00:00
ui.menu_button("Colors", |ui| {
for preset in COLOR_PRESETS {
if ui.color_pair_button(preset[0], preset[1]).clicked() {
self.colors = preset;
}
}
});
2024-11-26 05:38:03 +00:00
});
ui.menu_button("Audio", |ui| {
let p1_enabled = self.client.is_audio_enabled(SimId::Player1);
let p2_enabled = self.client.is_audio_enabled(SimId::Player2);
if ui.selectable_button(p1_enabled, "Player 1").clicked() {
self.client
.send_command(EmulatorCommand::SetAudioEnabled(!p1_enabled, p2_enabled));
ui.close_menu();
}
if ui.selectable_button(p2_enabled, "Player 2").clicked() {
self.client
.send_command(EmulatorCommand::SetAudioEnabled(p1_enabled, !p2_enabled));
ui.close_menu();
}
});
ui.menu_button("Input", |ui| {
if ui.button("Bind Inputs").clicked() {
2024-11-28 15:27:18 +00:00
self.proxy.send_event(UserEvent::OpenInput).unwrap();
2024-11-26 05:38:03 +00:00
ui.close_menu();
}
});
ui.menu_button("Multiplayer", |ui| {
if self.sim_id == SimId::Player1
&& !self.client.has_player_2()
&& ui.button("Open Player 2").clicked()
{
self.client
.send_command(EmulatorCommand::StartSecondSim(None));
2024-11-28 15:27:18 +00:00
self.proxy.send_event(UserEvent::OpenPlayer2).unwrap();
2024-11-26 05:38:03 +00:00
ui.close_menu();
}
if self.client.has_player_2() {
let linked = self.client.are_sims_linked();
if linked && ui.button("Unlink").clicked() {
self.client.send_command(EmulatorCommand::Unlink);
ui.close_menu();
}
if !linked && ui.button("Link").clicked() {
self.client.send_command(EmulatorCommand::Link);
ui.close_menu();
}
}
});
}
}
impl AppWindow for GameWindow {
fn viewport_id(&self) -> ViewportId {
match self.sim_id {
SimId::Player1 => ViewportId::ROOT,
SimId::Player2 => ViewportId::from_hash_of("Player2"),
}
}
fn initial_viewport(&self) -> ViewportBuilder {
ViewportBuilder::default()
.with_title("Shrooms VB")
2024-11-28 17:39:08 +00:00
.with_inner_size((384.0, 246.0))
2024-11-26 05:38:03 +00:00
}
fn show(&mut self, ctx: &Context) {
TopBottomPanel::top("menubar")
2024-11-28 17:39:08 +00:00
.exact_height(22.0)
2024-11-26 05:38:03 +00:00
.show(ctx, |ui| {
menu::bar(ui, |ui| {
self.show_menu(ctx, ui);
});
});
2024-11-28 17:39:08 +00:00
let frame = Frame::central_panel(&ctx.style()).fill(Color32::BLACK);
CentralPanel::default().frame(frame).show(ctx, |ui| {
2024-12-01 04:14:01 +00:00
if let Some(screen) = self.screen.as_mut() {
2024-12-01 05:45:13 +00:00
screen.update(self.display_mode, self.colors);
2024-11-26 05:38:03 +00:00
ui.add(screen);
}
});
}
2024-11-28 15:27:18 +00:00
fn on_init(&mut self, render_state: &egui_wgpu::RenderState) {
let (screen, sink) = GameScreen::init(render_state);
self.client
.send_command(EmulatorCommand::SetRenderer(self.sim_id, sink));
self.screen = Some(screen)
}
fn on_destroy(&mut self) {
if self.sim_id == SimId::Player2 {
self.client.send_command(EmulatorCommand::StopSecondSim);
}
}
2024-11-26 05:38:03 +00:00
}
trait UiExt {
fn selectable_button(&mut self, selected: bool, text: impl Into<WidgetText>) -> Response;
2024-12-01 04:14:01 +00:00
fn selectable_option<T: Eq>(
&mut self,
current_value: &mut T,
selected_value: T,
text: impl Into<WidgetText>,
) -> Response {
let response = self.selectable_button(*current_value == selected_value, text);
if response.clicked() {
*current_value = selected_value;
}
response
}
2024-12-01 05:45:13 +00:00
fn color_pair_button(&mut self, left: Color32, right: Color32) -> Response;
2024-11-26 05:38:03 +00:00
}
impl UiExt for Ui {
fn selectable_button(&mut self, selected: bool, text: impl Into<WidgetText>) -> Response {
self.style_mut().visuals.widgets.inactive.bg_fill = Color32::TRANSPARENT;
self.style_mut().visuals.widgets.hovered.bg_fill = Color32::TRANSPARENT;
self.style_mut().visuals.widgets.active.bg_fill = Color32::TRANSPARENT;
let mut selected = selected;
self.checkbox(&mut selected, text)
}
2024-12-01 05:45:13 +00:00
fn color_pair_button(&mut self, left: Color32, right: Color32) -> Response {
let button_size = Vec2::new(60.0, 20.0);
let (rect, response) = self.allocate_at_least(button_size, Sense::click());
let center_x = rect.center().x;
let left_rect = rect.with_max_x(center_x);
self.painter().rect_filled(left_rect, 0.0, left);
let right_rect = rect.with_min_x(center_x);
self.painter().rect_filled(right_rect, 0.0, right);
let style = self.style().interact(&response);
self.painter().rect_stroke(rect, 0.0, style.fg_stroke);
response
}
2024-11-26 05:38:03 +00:00
}