Add keyboard shortcuts

This commit is contained in:
pjht 2024-01-29 14:19:56 -06:00
parent dd93e414c5
commit 3137312831
Signed by: pjht
GPG Key ID: CA239FC6934E6F3A
4 changed files with 91 additions and 470 deletions

View File

@ -49,14 +49,6 @@ enum FanState {
}
impl FanState {
/// Returns `true` if the fan state is [`Off`].
///
/// [`Off`]: FanState::Off
#[must_use]
fn is_off(&self) -> bool {
matches!(self, Self::Off)
}
/// Returns `true` if the fan state is [`Starting`].
///
/// [`Starting`]: FanState::Starting
@ -72,14 +64,6 @@ impl FanState {
fn is_on(&self) -> bool {
matches!(self, Self::On)
}
/// Returns `true` if the fan state is [`Stopping`].
///
/// [`Stopping`]: FanState::Stopping
#[must_use]
fn is_stopping(&self) -> bool {
matches!(self, Self::Stopping { .. })
}
}
impl AudioThread {

View File

@ -1,7 +1,7 @@
use std::path::Path;
use eframe::{
egui::{self, Sense, TextureOptions, Ui, Widget},
egui::{self, KeyboardShortcut, Modifiers, Sense, TextureOptions, Ui, Widget, Key},
epaint::{vec2, Pos2, Rect, TextureHandle},
};
@ -26,7 +26,7 @@ pub enum ActionSwitch {
#[derive(Clone, Copy, Debug)]
pub enum FrontpanelInteraction {
ActionSwClicked(ActionSwitch, SwitchState),
ActionSwChanged(ActionSwitch, SwitchState),
AdChanged(u16),
PowerChanged(bool),
}
@ -119,12 +119,9 @@ impl Widget for &mut Frontpanel<'_> {
led::Textures::new(self.textures.led_on.clone(), self.textures.led_off.clone());
let resp = ui.allocate_response(
vec2(800.0, 333.0),
Sense {
click: false,
drag: false,
focusable: false,
},
Sense::click(),
);
resp.request_focus();
let fp_rect = resp.rect;
ui.allocate_ui_at_rect(fp_rect, |ui| {
ui.image(&self.textures.fp);
@ -152,8 +149,12 @@ impl Widget for &mut Frontpanel<'_> {
ui.allocate_ui_at_rect(Rect::from_center_size(pos, vec2(11.0, 25.0)), |ui| {
let mut power_inv = !self.state.power;
if ui
.add(switch::ToggleSwitch::new(&mut power_inv, &sw_textures))
.drag_started()
.add(switch::ToggleSwitch::new(
&mut power_inv,
&sw_textures,
Some(KeyboardShortcut::new(Modifiers::NONE, Key::Q)),
))
.changed()
{
self.state.power = !power_inv;
self.interaction =
@ -165,9 +166,18 @@ impl Widget for &mut Frontpanel<'_> {
let bit_mask = 1 << (16 - i);
let mut sw_state = self.state.ad_sws & bit_mask > 0;
ui.allocate_ui_at_rect(Rect::from_center_size(pos, vec2(11.0, 25.0)), |ui| {
let key_offset = (i - 1) % 8;
let key = (b'1' + key_offset as u8) as char;
let key = Key::from_name(&key.to_string()).unwrap();
let mods = if (i - 1) >= 8 {
Modifiers::SHIFT
} else {
Modifiers::NONE
};
let shortcut = KeyboardShortcut::new(mods, key);
if ui
.add(switch::ToggleSwitch::new(&mut sw_state, &sw_textures))
.drag_started()
.add(switch::ToggleSwitch::new(&mut sw_state, &sw_textures, Some(shortcut)))
.changed()
{
self.state.ad_sws =
(self.state.ad_sws & !(bit_mask)) | ((sw_state as u16) << (16 - i));
@ -189,9 +199,16 @@ impl Widget for &mut Frontpanel<'_> {
_ => unreachable!(),
};
ui.allocate_ui_at_rect(Rect::from_center_size(pos, vec2(11.0, 25.0)), |ui| {
let sw_offset = i - 17;
let up_key = ["W", "E", "R", "T", "Y", "U", "I", "O"][sw_offset];
let down_key = ["S", "D", "F", "G", "H", "J", "K", "L"][sw_offset];
let up_key = Key::from_name(up_key).unwrap();
let down_key = Key::from_name(down_key).unwrap();
let up_shortcut = KeyboardShortcut::new(Modifiers::NONE, up_key);
let down_shortcut = KeyboardShortcut::new(Modifiers::NONE, down_key);
if ui
.add(switch::ThreePosSwitch::new(state, &sw_textures))
.drag_started()
.add(switch::ThreePosSwitch::new(state, &sw_textures, Some(up_shortcut), Some(down_shortcut)))
.changed()
{
let sw = match i {
17 => ActionSwitch::RunStop,
@ -205,12 +222,15 @@ impl Widget for &mut Frontpanel<'_> {
_ => unreachable!(),
};
self.interaction =
Some(FrontpanelInteraction::ActionSwClicked(sw, *state));
Some(FrontpanelInteraction::ActionSwChanged(sw, *state));
};
});
}
}
});
// if ui.input_mut(|input| input.consume_shortcut(&KeyboardShortcut::new(Modifiers::NONE, Key::Q))) {
// dbg!();
// }
resp
}
}

View File

@ -1,5 +1,5 @@
use eframe::{
egui::{Sense, Widget},
egui::{KeyboardShortcut, Sense, Widget},
epaint::{pos2, vec2, Color32, Rect, TextureHandle, TextureId},
};
@ -54,11 +54,13 @@ impl Default for SwitchState {
pub struct ThreePosSwitch<'a> {
state: &'a mut SwitchState,
textures: &'a Textures,
up_shortcut: Option<KeyboardShortcut>,
down_shortcut: Option<KeyboardShortcut>,
}
impl<'a> ThreePosSwitch<'a> {
pub fn new(state: &'a mut SwitchState, textures: &'a Textures) -> Self {
Self { state, textures }
pub fn new(state: &'a mut SwitchState, textures: &'a Textures, up_shortcut: Option<KeyboardShortcut>, down_shortcut: Option<KeyboardShortcut>) -> Self {
Self { state, textures, up_shortcut, down_shortcut }
}
}
@ -71,6 +73,18 @@ impl Widget for ThreePosSwitch<'_> {
NULL_UV,
NULL_TINT,
);
let up_shortcut_pressed = self.up_shortcut.map_or(false, |shortcut| {
ui.input_mut(|input| input.key_pressed(shortcut.logical_key) && shortcut.modifiers == input.modifiers)
});
let down_shortcut_pressed = self.down_shortcut.map_or(false, |shortcut| {
ui.input_mut(|input| input.key_pressed(shortcut.logical_key) && shortcut.modifiers == input.modifiers)
});
let up_shortcut_released = self.up_shortcut.map_or(false, |shortcut| {
ui.input_mut(|input| input.key_released(shortcut.logical_key) && shortcut.modifiers == input.modifiers)
});
let down_shortcut_released = self.down_shortcut.map_or(false, |shortcut| {
ui.input_mut(|input| input.key_released(shortcut.logical_key) && shortcut.modifiers == input.modifiers)
});
if resp.drag_started() {
let interact_pos = resp.interact_pointer_pos().unwrap();
let sw_center = painter.clip_rect().left_top() + vec2(5.0, 12.0);
@ -80,7 +94,17 @@ impl Widget for ThreePosSwitch<'_> {
*self.state = SwitchState::Up;
};
resp.mark_changed();
} else if resp.drag_released() {
} else if up_shortcut_pressed {
*self.state = SwitchState::Up;
resp.mark_changed();
} else if down_shortcut_pressed {
*self.state = SwitchState::Down;
resp.mark_changed();
} else if resp.drag_released() || up_shortcut_released || down_shortcut_released {
*self.state = SwitchState::Neut;
resp.mark_changed();
}
if *self.state != SwitchState::Neut && !resp.dragged() && ui.input(|input| input.keys_down.is_empty()) {
*self.state = SwitchState::Neut;
resp.mark_changed();
}
@ -91,11 +115,20 @@ impl Widget for ThreePosSwitch<'_> {
pub struct ToggleSwitch<'a> {
state: &'a mut bool,
textures: &'a Textures,
press_shortcut: Option<KeyboardShortcut>,
}
impl<'a> ToggleSwitch<'a> {
pub fn new(state: &'a mut bool, textures: &'a Textures) -> Self {
Self { state, textures }
pub fn new(
state: &'a mut bool,
textures: &'a Textures,
press_shortcut: Option<KeyboardShortcut>,
) -> Self {
Self {
state,
textures,
press_shortcut,
}
}
}
@ -108,7 +141,10 @@ impl Widget for ToggleSwitch<'_> {
NULL_UV,
NULL_TINT,
);
if resp.drag_started() {
let shortcut_pressed = self.press_shortcut.map_or(false, |press_shortcut| {
ui.input_mut(|input| input.consume_shortcut(&press_shortcut))
});
if resp.drag_started() || shortcut_pressed {
*self.state = !*self.state;
resp.mark_changed();
}

View File

@ -4,16 +4,16 @@ mod cpu;
mod frontpanel;
mod ram;
use std::{path::Path, sync::mpsc::Sender};
use std::sync::mpsc::Sender;
use audio::{AudioMessage, AudioThread};
use cpu::{MemCycle, Status, I8080};
use device_query::DeviceState;
use eframe::{
egui::{self, menu, Button, Label, TextureHandle, TextureOptions},
egui::{self, menu, Button},
NativeOptions,
};
use egui_modal::Modal;
use frontpanel::Textures;
use rand::RngCore;
use rfd::FileDialog;
use serde::{Deserialize, Serialize};
@ -36,16 +36,13 @@ struct Options {
}
struct AltairEmulator {
textures: frontpanel::Textures,
textures: Textures,
mem: [u8; 65536],
cpu: I8080,
running: bool,
audio_tx: Sender<AudioMessage>,
options: Options,
option_window: Option<OptionWindow>,
device_state: DeviceState,
kbd_newdown: bool,
kbd_olddown: bool,
fp_state: FrontpanelState,
}
@ -88,16 +85,13 @@ impl AltairEmulator {
// 20: 004
// 21: 005
Self {
textures: frontpanel::Textures::new(&cc.egui_ctx),
textures: Textures::new(&cc.egui_ctx),
mem,
cpu,
running: false,
audio_tx,
options,
option_window: None,
device_state: DeviceState::new(),
kbd_newdown: false,
kbd_olddown: false,
fp_state: FrontpanelState::new(),
}
}
@ -210,18 +204,22 @@ impl eframe::App for AltairEmulator {
if let Some(interaction) = interaction {
self.audio_tx.send(AudioMessage::PlaySwitchClick).unwrap();
match interaction {
FrontpanelInteraction::ActionSwClicked(sw, state) => {
FrontpanelInteraction::ActionSwChanged(sw, state) => {
if self.fp_state.power() {
match sw {
frontpanel::ActionSwitch::RunStop => {
if state == SwitchState::Up {
println!("STOP RUN");
self.running = false;
} else {
} else if state == SwitchState::Down {
println!("START RUN");
self.running = true;
}
}
frontpanel::ActionSwitch::SingleStep => {
self.run_cpu_cycle();
if state == SwitchState::Up {
self.run_cpu_cycle();
}
}
frontpanel::ActionSwitch::Examine => {
if state == SwitchState::Up {
@ -231,7 +229,7 @@ impl eframe::App for AltairEmulator {
self.cpu
.finish_m_cycle((self.fp_state.ad_sws() >> 8) as u8);
self.update_fp();
} else {
} else if state == SwitchState::Down {
// Assume M1
self.cpu.finish_m_cycle(0x0); // NOP
self.update_fp();
@ -243,7 +241,7 @@ impl eframe::App for AltairEmulator {
self.mem[self.cpu.get_mem_cycle().address() as usize] =
self.fp_state.ad_sws() as u8;
self.update_fp();
} else {
} else if state == SwitchState::Down {
// Assume M1
self.cpu.finish_m_cycle(0x0); // NOP
self.mem[self.cpu.get_mem_cycle().address() as usize] =
@ -282,358 +280,6 @@ impl eframe::App for AltairEmulator {
if self.running {
self.run_cpu_cycle();
}
// dbg!(ui.input(|input| input.pointer.latest_pos()));
// dbg!(ui.input(|input| input.pointer.latest_pos()));
// let (_, fp_rect) = ui.allocate_space((800.0, 333.0).into());
// image_topleft(fp_rect.left_top(), &self.textures.fp, ui);
// for led in &LEDS {
// let led_data = match led.source {
// LedSource::Protect => 0x0,
// LedSource::Iff => 0x0,
// LedSource::Run => 0x0,
// LedSource::CpuStatus => u16::from(self.fp_status.bits()),
// LedSource::Data => u16::from(self.fp_data),
// LedSource::Address => self.fp_address,
// };
// let led_on = (led_data & led.mask) > 0;
// let texture = if led_on {
// &self.textures.led_on
// } else {
// &self.textures.led_off
// };
// image_center(led.pos + fp_rect.left_top().to_vec2(), texture, ui);
// }
// let pointer_state = ctx.input(|inp| inp.pointer.clone());
// let interact_pos = pointer_state.interact_pos();
// let mut switch_clicked = false;
// fn bool_to_texture(state: bool, textures: &Textures) -> &TextureHandle {
// if state {
// &textures.sw_up
// } else {
// &textures.sw_down
// }
// }
// fn state_to_texture(state: SwitchState, textures: &Textures) -> &TextureHandle {
// match state {
// SwitchState::Up => &textures.sw_up,
// SwitchState::Neut => &textures.sw_neut,
// SwitchState::Down => &textures.sw_down,
// }
// }
// for (i, switch) in SWITCHES.iter().enumerate() {
// let pos = switch.pos + fp_rect.left_top().to_vec2();
// let texture = match i {
// 0 => bool_to_texture(!self.power, &self.textures),
// (1..=16) => {
// bool_to_texture((self.ad_sws & (1 << (16 - i))) > 0, &self.textures)
// }
// 17 => state_to_texture(self.runstop, &self.textures),
// 18 => state_to_texture(self.single_step, &self.textures),
// 19 => state_to_texture(self.exam, &self.textures),
// 20 => state_to_texture(self.dep, &self.textures),
// 21 => state_to_texture(self.reset, &self.textures),
// 22 => state_to_texture(self.prot, &self.textures),
// 23 => state_to_texture(self.aux1, &self.textures),
// 24 => state_to_texture(self.aux2, &self.textures),
// _ => unreachable!(),
// };
// image_center(pos, texture, ui);
// let interacted = interact_pos.map(|interact_pos| {
// Rect::from_center_size(interact_pos, (10.0, 24.0).into()).contains(pos)
// && !disable_fp_sws
// });
// if pointer_state.primary_clicked() && interacted.unwrap() {
// match i {
// 0 => {
// switch_clicked = true;
// self.power = !self.power;
// if self.options.fan_enabled {
// self.fan_audio_tx.send(self.power).unwrap();
// }
// if self.power {
// self.cpu = I8080::new();
// self.update_fp();
// } else {
// self.running = false;
// self.fp_status = Status::empty();
// self.fp_data = 0;
// self.fp_address = 0;
// }
// }
// (1..=16) => {
// switch_clicked = true;
// if (self.ad_sws & (1 << (16 - i))) > 0 {
// self.ad_sws &= !(1 << (16 - i));
// } else {
// self.ad_sws |= 1 << (16 - i);
// }
// }
// _ => (),
// }
// }
// if pointer_state.primary_down() && interacted.unwrap() {
// if (17..=24).contains(&i) && self.mouse_newdown {
// switch_clicked = true;
// }
// let newstate = if interact_pos.unwrap().y > pos.y {
// SwitchState::Down
// } else {
// SwitchState::Up
// };
// match i {
// 17 => self.runstop = newstate,
// 18 => self.single_step = newstate,
// 19 => self.exam = newstate,
// 20 => self.dep = newstate,
// 21 => self.reset = newstate,
// 22 => self.prot = newstate,
// 23 => self.aux1 = newstate,
// 24 => self.aux2 = newstate,
// _ => (),
// }
// } else {
// match i {
// 17 => self.runstop = SwitchState::Neut,
// 18 => self.single_step = SwitchState::Neut,
// 19 => self.exam = SwitchState::Neut,
// 20 => self.dep = SwitchState::Neut,
// 21 => self.reset = SwitchState::Neut,
// 22 => self.prot = SwitchState::Neut,
// 23 => self.aux1 = SwitchState::Neut,
// 24 => self.aux2 = SwitchState::Neut,
// _ => (),
// }
// }
// }
// if !disable_fp_sws {
// let mut kbd_ad = None;
// let pressed_keys = self.device_state.query_keymap();
// if pressed_keys
// .iter()
// .filter(|&&k| k != Keycode::LShift && k != Keycode::RShift)
// .count()
// != 0
// {
// if self.kbd_newdown {
// self.kbd_olddown = true;
// self.kbd_newdown = false;
// } else if !self.kbd_newdown && !self.kbd_olddown {
// self.kbd_newdown = true;
// }
// } else {
// self.kbd_newdown = false;
// self.kbd_olddown = false;
// }
// if self.kbd_newdown {
// if pressed_keys.contains(&Keycode::W)
// || pressed_keys.contains(&Keycode::E)
// || pressed_keys.contains(&Keycode::R)
// || pressed_keys.contains(&Keycode::T)
// || pressed_keys.contains(&Keycode::Y)
// || pressed_keys.contains(&Keycode::U)
// || pressed_keys.contains(&Keycode::I)
// || pressed_keys.contains(&Keycode::O)
// || pressed_keys.contains(&Keycode::S)
// || pressed_keys.contains(&Keycode::D)
// || pressed_keys.contains(&Keycode::F)
// || pressed_keys.contains(&Keycode::G)
// || pressed_keys.contains(&Keycode::H)
// || pressed_keys.contains(&Keycode::J)
// || pressed_keys.contains(&Keycode::K)
// || pressed_keys.contains(&Keycode::L)
// {
// switch_clicked = true;
// }
// if pressed_keys.contains(&Keycode::LShift)
// || pressed_keys.contains(&Keycode::RShift)
// {
// if pressed_keys.contains(&Keycode::Key1) {
// kbd_ad = Some(15);
// } else if pressed_keys.contains(&Keycode::Key2) {
// kbd_ad = Some(14);
// } else if pressed_keys.contains(&Keycode::Key3) {
// kbd_ad = Some(13);
// } else if pressed_keys.contains(&Keycode::Key4) {
// kbd_ad = Some(12);
// } else if pressed_keys.contains(&Keycode::Key5) {
// kbd_ad = Some(11);
// } else if pressed_keys.contains(&Keycode::Key6) {
// kbd_ad = Some(10);
// } else if pressed_keys.contains(&Keycode::Key7) {
// kbd_ad = Some(9);
// } else if pressed_keys.contains(&Keycode::Key8) {
// kbd_ad = Some(8);
// }
// } else {
// if pressed_keys.contains(&Keycode::Key1) {
// kbd_ad = Some(7);
// } else if pressed_keys.contains(&Keycode::Key2) {
// kbd_ad = Some(6);
// } else if pressed_keys.contains(&Keycode::Key3) {
// kbd_ad = Some(5);
// } else if pressed_keys.contains(&Keycode::Key4) {
// kbd_ad = Some(4);
// } else if pressed_keys.contains(&Keycode::Key5) {
// kbd_ad = Some(3);
// } else if pressed_keys.contains(&Keycode::Key6) {
// kbd_ad = Some(2);
// } else if pressed_keys.contains(&Keycode::Key7) {
// kbd_ad = Some(1);
// } else if pressed_keys.contains(&Keycode::Key8) {
// kbd_ad = Some(0);
// }
// if pressed_keys.contains(&Keycode::Q) {
// switch_clicked = true;
// self.power = !self.power;
// if self.options.fan_enabled {
// self.fan_audio_tx.send(self.power).unwrap();
// }
// if self.power {
// self.cpu = I8080::new();
// self.update_fp();
// } else {
// self.running = false;
// self.fp_status = Status::empty();
// self.fp_data = 0;
// self.fp_address = 0;
// }
// }
// }
// }
// if pressed_keys.contains(&Keycode::W) {
// self.runstop = SwitchState::Up;
// }
// if pressed_keys.contains(&Keycode::E) {
// self.single_step = SwitchState::Up;
// }
// if pressed_keys.contains(&Keycode::R) {
// self.exam = SwitchState::Up;
// }
// if pressed_keys.contains(&Keycode::T) {
// self.dep = SwitchState::Up;
// }
// if pressed_keys.contains(&Keycode::Y) {
// self.reset = SwitchState::Up;
// }
// if pressed_keys.contains(&Keycode::U) {
// self.prot = SwitchState::Up;
// }
// if pressed_keys.contains(&Keycode::I) {
// self.aux1 = SwitchState::Up;
// }
// if pressed_keys.contains(&Keycode::O) {
// self.aux2 = SwitchState::Up;
// }
// if pressed_keys.contains(&Keycode::S) {
// self.runstop = SwitchState::Down;
// }
// if pressed_keys.contains(&Keycode::D) {
// self.single_step = SwitchState::Down;
// }
// if pressed_keys.contains(&Keycode::F) {
// self.exam = SwitchState::Down;
// }
// if pressed_keys.contains(&Keycode::G) {
// self.dep = SwitchState::Down;
// }
// if pressed_keys.contains(&Keycode::H) {
// self.reset = SwitchState::Down;
// }
// if pressed_keys.contains(&Keycode::J) {
// self.prot = SwitchState::Down;
// }
// if pressed_keys.contains(&Keycode::K) {
// self.aux1 = SwitchState::Down;
// }
// if pressed_keys.contains(&Keycode::L) {
// self.aux2 = SwitchState::Down;
// }
// if let Some(kbd_ad) = kbd_ad {
// switch_clicked = true;
// if (self.ad_sws & (1 << kbd_ad)) > 0 {
// self.ad_sws &= !(1 << kbd_ad);
// } else {
// self.ad_sws |= 1 << kbd_ad;
// }
// }
// }
// if switch_clicked {
// self.main_audio_tx
// .send(AudioMessage::PlaySwitchClick)
// .unwrap();
// }
// if pointer_state.primary_down() {
// if self.mouse_newdown {
// self.mouse_olddown = true;
// self.mouse_newdown = false;
// } else if !self.mouse_newdown && !self.mouse_olddown {
// self.mouse_newdown = true;
// }
// } else {
// self.mouse_newdown = false;
// self.mouse_olddown = false;
// }
// if switch_clicked && self.power {
// if !self.running {
// if self.exam == SwitchState::Up {
// // Assume M1
// self.cpu.finish_m_cycle(0xC3); // JMP
// self.cpu.finish_m_cycle(self.ad_sws as u8);
// self.cpu.finish_m_cycle((self.ad_sws >> 8) as u8);
// self.update_fp();
// }
// if self.exam == SwitchState::Down {
// // Assume M1
// self.cpu.finish_m_cycle(0x0); // NOP
// self.update_fp();
// }
// if self.dep == SwitchState::Up {
// // Assume M1
// self.mem[self.cpu.get_mem_cycle().address() as usize] = self.ad_sws as u8;
// self.update_fp();
// }
// if self.dep == SwitchState::Down {
// // Assume M1
// self.cpu.finish_m_cycle(0x0); // NOP
// self.mem[self.cpu.get_mem_cycle().address() as usize] = self.ad_sws as u8;
// self.update_fp();
// }
// }
// if self.runstop == SwitchState::Up {
// self.running = false;
// }
// if self.runstop == SwitchState::Down {
// self.running = true;
// }
// if self.reset == SwitchState::Up {
// self.cpu.reset();
// self.update_fp();
// }
// }
// if ((switch_clicked && self.single_step != SwitchState::Neut) || self.running)
// && self.power
// {
// let cycle = self.cpu.get_mem_cycle();
// let data = match cycle {
// MemCycle::Fetch(a) | MemCycle::Read(a) | MemCycle::StackRead(a) => {
// self.mem[a as usize]
// }
// MemCycle::Write(a, d) | MemCycle::StackWrite(a, d) => {
// self.mem[a as usize] = d;
// 0
// }
// MemCycle::In(_) => 0,
// MemCycle::Out(_, _) => 0,
// MemCycle::Inta(_) => todo!(),
// MemCycle::Hlta(_) => {
// self.running = false;
// 0
// }
// MemCycle::IntaHlt(_) => todo!(),
// };
// self.cpu.finish_m_cycle(data);
// self.update_fp();
// }
});
let old_fan_enabled = self.options.fan_enabled;
@ -703,68 +349,3 @@ impl OptionWindow {
!modal.is_open()
}
}
struct Textures {
fp: TextureHandle,
sw_up: TextureHandle,
sw_neut: TextureHandle,
sw_down: TextureHandle,
led_off: TextureHandle,
led_on: TextureHandle,
}
impl Textures {
fn new(ctx: &egui::Context) -> Self {
Self {
fp: Self::load_texture(
ctx,
"fp",
"/home/pjht/projects/altair_emu/resources/altair800.png",
)
.unwrap(),
sw_up: Self::load_texture(
ctx,
"sw_up",
"/home/pjht/projects/altair_emu/resources/Togup.png",
)
.unwrap(),
sw_neut: Self::load_texture(
ctx,
"sw_neut",
"/home/pjht/projects/altair_emu/resources/Togneut.png",
)
.unwrap(),
sw_down: Self::load_texture(
ctx,
"sw_down",
"/home/pjht/projects/altair_emu/resources/Togdown.png",
)
.unwrap(),
led_off: Self::load_texture(
ctx,
"led_off",
"/home/pjht/projects/altair_emu/resources/Led_off.png",
)
.unwrap(),
led_on: Self::load_texture(
ctx,
"led_on",
"/home/pjht/projects/altair_emu/resources/Led_on.png",
)
.unwrap(),
}
}
fn load_texture(
ctx: &egui::Context,
name: impl Into<String>,
path: impl AsRef<Path>,
) -> Result<TextureHandle, image::ImageError> {
let image = image::io::Reader::open(path.as_ref())?.decode()?;
let image = egui::ColorImage::from_rgba_unmultiplied(
[image.width() as _, image.height() as _],
image.to_rgba8().as_flat_samples().as_slice(),
);
Ok(ctx.load_texture(name, image, TextureOptions::LINEAR))
}
}