Support choosing colors

This commit is contained in:
Simon Gellis 2024-12-01 00:45:13 -05:00
parent 4e42179ef3
commit 2e5d5e140f
2 changed files with 85 additions and 13 deletions

View File

@ -3,7 +3,7 @@ use crate::{
emulator::{EmulatorClient, EmulatorCommand, SimId}, emulator::{EmulatorClient, EmulatorCommand, SimId},
}; };
use egui::{ use egui::{
menu, Button, CentralPanel, Color32, Context, Frame, Response, TopBottomPanel, Ui, menu, Button, CentralPanel, Color32, Context, Frame, Response, Sense, TopBottomPanel, Ui, Vec2,
ViewportBuilder, ViewportCommand, ViewportId, WidgetText, ViewportBuilder, ViewportCommand, ViewportId, WidgetText,
}; };
use winit::event_loop::EventLoopProxy; use winit::event_loop::EventLoopProxy;
@ -13,11 +13,27 @@ use super::{
AppWindow, AppWindow,
}; };
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),
],
];
pub struct GameWindow { pub struct GameWindow {
client: EmulatorClient, client: EmulatorClient,
proxy: EventLoopProxy<UserEvent>, proxy: EventLoopProxy<UserEvent>,
sim_id: SimId, sim_id: SimId,
display_mode: DisplayMode, display_mode: DisplayMode,
colors: [Color32; 2],
screen: Option<GameScreen>, screen: Option<GameScreen>,
} }
@ -28,6 +44,7 @@ impl GameWindow {
proxy, proxy,
sim_id, sim_id,
display_mode: DisplayMode::Anaglyph, display_mode: DisplayMode::Anaglyph,
colors: COLOR_PRESETS[0],
screen: None, screen: None,
} }
} }
@ -104,6 +121,13 @@ impl GameWindow {
ui.close_menu(); ui.close_menu();
} }
}); });
ui.menu_button("Colors", |ui| {
for preset in COLOR_PRESETS {
if ui.color_pair_button(preset[0], preset[1]).clicked() {
self.colors = preset;
}
}
});
}); });
ui.menu_button("Audio", |ui| { ui.menu_button("Audio", |ui| {
let p1_enabled = self.client.is_audio_enabled(SimId::Player1); let p1_enabled = self.client.is_audio_enabled(SimId::Player1);
@ -175,7 +199,7 @@ impl AppWindow for GameWindow {
let frame = Frame::central_panel(&ctx.style()).fill(Color32::BLACK); let frame = Frame::central_panel(&ctx.style()).fill(Color32::BLACK);
CentralPanel::default().frame(frame).show(ctx, |ui| { CentralPanel::default().frame(frame).show(ctx, |ui| {
if let Some(screen) = self.screen.as_mut() { if let Some(screen) = self.screen.as_mut() {
screen.display_mode = self.display_mode; screen.update(self.display_mode, self.colors);
ui.add(screen); ui.add(screen);
} }
}); });
@ -209,6 +233,8 @@ trait UiExt {
} }
response response
} }
fn color_pair_button(&mut self, left: Color32, right: Color32) -> Response;
} }
impl UiExt for Ui { impl UiExt for Ui {
@ -219,4 +245,18 @@ impl UiExt for Ui {
let mut selected = selected; let mut selected = selected;
self.checkbox(&mut selected, text) self.checkbox(&mut selected, text)
} }
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
}
} }

View File

@ -1,13 +1,15 @@
use std::{collections::HashMap, sync::Arc}; use std::{collections::HashMap, sync::Arc};
use egui::Widget; use egui::{Color32, Rgba, Widget};
use wgpu::{util::DeviceExt as _, BindGroup, BindGroupLayout, RenderPipeline}; use wgpu::{util::DeviceExt as _, BindGroup, BindGroupLayout, Buffer, RenderPipeline};
use crate::graphics::TextureSink; use crate::graphics::TextureSink;
pub struct GameScreen { pub struct GameScreen {
bind_group: Arc<BindGroup>, bind_group: Arc<BindGroup>,
pub display_mode: DisplayMode, color_buffer: Arc<Buffer>,
display_mode: DisplayMode,
colors: Colors,
} }
impl GameScreen { impl GameScreen {
@ -117,12 +119,12 @@ impl GameScreen {
let (sink, texture_view) = TextureSink::new(device, queue.clone()); let (sink, texture_view) = TextureSink::new(device, queue.clone());
let sampler = device.create_sampler(&wgpu::SamplerDescriptor::default()); let sampler = device.create_sampler(&wgpu::SamplerDescriptor::default());
let colors = Colors { let colors = Colors::new(
left: [1.0, 0.0, 0.0, 1.0], Color32::from_rgb(0xff, 0x00, 0x00),
right: [0.0, 0.7734375, 0.9375, 1.0], Color32::from_rgb(0x00, 0xc6, 0xf0),
}; );
let color_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let color_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("colors"), label: Some("colors"),
contents: bytemuck::bytes_of(&colors), contents: bytemuck::bytes_of(&colors),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
@ -145,7 +147,7 @@ impl GameScreen {
}, },
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 2, binding: 2,
resource: color_buf.as_entire_binding(), resource: color_buffer.as_entire_binding(),
}, },
], ],
}); });
@ -153,11 +155,18 @@ impl GameScreen {
( (
Self { Self {
bind_group: Arc::new(bind_group), bind_group: Arc::new(bind_group),
color_buffer: Arc::new(color_buffer),
display_mode: DisplayMode::Anaglyph, display_mode: DisplayMode::Anaglyph,
colors,
}, },
sink, sink,
) )
} }
pub fn update(&mut self, display_mode: DisplayMode, colors: [Color32; 2]) {
self.display_mode = display_mode;
self.colors = Colors::new(colors[0], colors[1]);
}
} }
impl Widget for &mut GameScreen { impl Widget for &mut GameScreen {
@ -167,7 +176,9 @@ impl Widget for &mut GameScreen {
response.rect, response.rect,
GameScreenCallback { GameScreenCallback {
bind_group: self.bind_group.clone(), bind_group: self.bind_group.clone(),
color_buffer: self.color_buffer.clone(),
display_mode: self.display_mode, display_mode: self.display_mode,
colors: self.colors,
}, },
); );
ui.painter().add(callback); ui.painter().add(callback);
@ -177,10 +188,23 @@ impl Widget for &mut GameScreen {
struct GameScreenCallback { struct GameScreenCallback {
bind_group: Arc<BindGroup>, bind_group: Arc<BindGroup>,
color_buffer: Arc<Buffer>,
display_mode: DisplayMode, display_mode: DisplayMode,
colors: Colors,
} }
impl egui_wgpu::CallbackTrait for GameScreenCallback { impl egui_wgpu::CallbackTrait for GameScreenCallback {
fn prepare(
&self,
_device: &wgpu::Device,
queue: &wgpu::Queue,
_screen_descriptor: &egui_wgpu::ScreenDescriptor,
_egui_encoder: &mut wgpu::CommandEncoder,
_callback_resources: &mut egui_wgpu::CallbackResources,
) -> Vec<wgpu::CommandBuffer> {
queue.write_buffer(&self.color_buffer, 0, bytemuck::bytes_of(&self.colors));
vec![]
}
fn paint( fn paint(
&self, &self,
info: egui::PaintCallbackInfo, info: egui::PaintCallbackInfo,
@ -217,8 +241,16 @@ struct SharedGameScreenResources {
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
#[repr(C)] #[repr(C)]
struct Colors { struct Colors {
left: [f32; 4], left: Rgba,
right: [f32; 4], right: Rgba,
}
impl Colors {
fn new(left: Color32, right: Color32) -> Self {
Self {
left: left.into(),
right: right.into(),
}
}
} }
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]