Continue work on front panel refactor
This commit is contained in:
parent
8221544efb
commit
1ff2990f2e
984
Cargo.lock
generated
984
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -8,8 +8,8 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "2.3.3"
|
bitflags = "2.3.3"
|
||||||
device_query = "1.1.3"
|
device_query = "1.1.3"
|
||||||
eframe = { version = "0.22.0", features = ["ron", "persistence"] }
|
eframe = { version = "0.25.0", features = ["ron", "persistence"] }
|
||||||
egui-modal = "0.2.4"
|
egui-modal = "0.3.2"
|
||||||
enum_dispatch = "0.3.12"
|
enum_dispatch = "0.3.12"
|
||||||
env_logger = "0.10.0"
|
env_logger = "0.10.0"
|
||||||
ihex = "3.0.0"
|
ihex = "3.0.0"
|
||||||
|
@ -1 +1,481 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use eframe::{
|
||||||
|
egui::{self, TextureOptions, Ui, Widget, Id, Painter},
|
||||||
|
epaint::{vec2, Pos2, Rect, TextureHandle, pos2, Color32},
|
||||||
|
};
|
||||||
|
|
||||||
|
use self::switch::SwitchState;
|
||||||
|
|
||||||
|
pub mod led;
|
||||||
pub mod switch;
|
pub mod switch;
|
||||||
|
|
||||||
|
|
||||||
|
const NULL_UV: Rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
|
||||||
|
const NULL_TINT: Color32 = Color32::WHITE;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default)]
|
||||||
|
struct FrontpanelState {
|
||||||
|
ad_sws: u16,
|
||||||
|
power: bool,
|
||||||
|
runstop: SwitchState,
|
||||||
|
single_step: SwitchState,
|
||||||
|
exam: SwitchState,
|
||||||
|
dep: SwitchState,
|
||||||
|
reset: SwitchState,
|
||||||
|
prot: SwitchState,
|
||||||
|
aux1: SwitchState,
|
||||||
|
aux2: SwitchState,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Frontpanel<'a> {
|
||||||
|
id: Id,
|
||||||
|
textures: &'a Textures,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Frontpanel<'a> {
|
||||||
|
pub fn new(id: Id, textures: &'a Textures) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
textures,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for Frontpanel<'_> {
|
||||||
|
fn ui(self, ui: &mut Ui) -> egui::Response {
|
||||||
|
let mut state: FrontpanelState = ui.data(|data| data.get_temp(self.id).unwrap_or_default());
|
||||||
|
let sw_textures = switch::Textures::new(
|
||||||
|
self.textures.sw_up.clone(),
|
||||||
|
self.textures.sw_neut.clone(),
|
||||||
|
self.textures.sw_down.clone(),
|
||||||
|
);
|
||||||
|
let led_textures =
|
||||||
|
led::Textures::new(self.textures.led_on.clone(), self.textures.led_off.clone());
|
||||||
|
let resp = ui.allocate_response(
|
||||||
|
(800.0, 333.0).into(),
|
||||||
|
egui::Sense {
|
||||||
|
click: false,
|
||||||
|
drag: false,
|
||||||
|
focusable: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let fp_rect = resp.rect;
|
||||||
|
dbg!(fp_rect);
|
||||||
|
let painter = Painter::new(ui.ctx().clone(), ui.layer_id(), ui.clip_rect().intersect(resp.rect));
|
||||||
|
painter.image(self.textures.fp.id(), painter.clip_rect(), NULL_UV, NULL_TINT);
|
||||||
|
let x_scale = painter.clip_rect().width() / 800.0;
|
||||||
|
let y_scale = painter.clip_rect().height() / 333.0;
|
||||||
|
for led in &LEDS {
|
||||||
|
let mut pos = led.pos;
|
||||||
|
pos.x *= x_scale;
|
||||||
|
pos.y *= y_scale;
|
||||||
|
pos += fp_rect.left_top().to_vec2();
|
||||||
|
let led_data = 0xAAAA;
|
||||||
|
let led_on = (led_data & led.mask) > 0;
|
||||||
|
ui.allocate_ui_at_rect(
|
||||||
|
Rect::from_center_size(pos, vec2(16.0, 16.0)),
|
||||||
|
|ui| {
|
||||||
|
ui.add(led::Led::new(led_on, &led_textures));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
// 0: Power
|
||||||
|
// 1..17: A/D left to right
|
||||||
|
// 17..25: Action left to right
|
||||||
|
for (i, switch) in SWITCHES.iter().enumerate() {
|
||||||
|
let mut pos = switch.pos;
|
||||||
|
pos.x *= x_scale;
|
||||||
|
pos.y *= y_scale;
|
||||||
|
pos += fp_rect.left_top().to_vec2();
|
||||||
|
if i == 0 {
|
||||||
|
ui.allocate_ui_at_rect(
|
||||||
|
Rect::from_center_size(pos, vec2(32.0, 32.0)),
|
||||||
|
|ui| {
|
||||||
|
ui.add(switch::ToggleSwitch::new(&mut state.power, &sw_textures));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (1..17).contains(&i) {
|
||||||
|
let bit_mask = 1 << (16 - i);
|
||||||
|
let mut sw_state = state.ad_sws & bit_mask > 0;
|
||||||
|
ui.allocate_ui_at_rect(
|
||||||
|
Rect::from_center_size(pos, vec2(32.0, 32.0)),
|
||||||
|
|ui| {
|
||||||
|
ui.add(switch::ToggleSwitch::new(&mut sw_state, &sw_textures));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
state.ad_sws = (state.ad_sws & !(bit_mask)) | ((sw_state as u16) << (16 - i));
|
||||||
|
}
|
||||||
|
if (17..25).contains(&i) {
|
||||||
|
let state = match i {
|
||||||
|
17 => &mut state.runstop,
|
||||||
|
18 => &mut state.single_step,
|
||||||
|
19 => &mut state.exam,
|
||||||
|
20 => &mut state.dep,
|
||||||
|
21 => &mut state.reset,
|
||||||
|
22 => &mut state.prot,
|
||||||
|
23 => &mut state.aux1,
|
||||||
|
24 => &mut state.aux2,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
ui.allocate_ui_at_rect(
|
||||||
|
Rect::from_center_size(pos, vec2(32.0, 32.0)),
|
||||||
|
|ui| {
|
||||||
|
ui.add(switch::ThreePosSwitch::new(state, &sw_textures));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// todo!();
|
||||||
|
}
|
||||||
|
ui.data_mut(|data| data.insert_temp(self.id, state));
|
||||||
|
resp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Textures {
|
||||||
|
fp: TextureHandle,
|
||||||
|
sw_up: TextureHandle,
|
||||||
|
sw_neut: TextureHandle,
|
||||||
|
sw_down: TextureHandle,
|
||||||
|
led_off: TextureHandle,
|
||||||
|
led_on: TextureHandle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Textures {
|
||||||
|
pub 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
struct SwitchInfo {
|
||||||
|
pos: Pos2,
|
||||||
|
}
|
||||||
|
|
||||||
|
static SWITCHES: [SwitchInfo; 25] = [
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(43.0, 234.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(183.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(228.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(257.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(285.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(330.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(359.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(388.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(433.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(461.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(491.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(536.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(564.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(593.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(638.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(667.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(696.0, 175.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(190.0, 234.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(248.0, 234.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(305.0, 234.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(363.0, 234.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(420.0, 234.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(478.0, 234.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(535.0, 234.0),
|
||||||
|
},
|
||||||
|
SwitchInfo {
|
||||||
|
pos: Pos2::new(593.0, 234.0),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
enum LedSource {
|
||||||
|
Protect,
|
||||||
|
Iff,
|
||||||
|
Run,
|
||||||
|
CpuStatus,
|
||||||
|
Data,
|
||||||
|
Address,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LedInfo {
|
||||||
|
pos: Pos2,
|
||||||
|
source: LedSource,
|
||||||
|
mask: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
static LEDS: [LedInfo; 36] = [
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(118.0, 58.0),
|
||||||
|
source: LedSource::Protect,
|
||||||
|
mask: 0xFF,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(89.0, 58.0),
|
||||||
|
source: LedSource::Iff,
|
||||||
|
mask: 0xFF,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(89.0, 117.0),
|
||||||
|
source: LedSource::Run,
|
||||||
|
mask: 0x0,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(118.0, 117.0),
|
||||||
|
source: LedSource::Iff,
|
||||||
|
mask: 0x0,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(149.0, 58.0),
|
||||||
|
source: LedSource::CpuStatus,
|
||||||
|
mask: 0x80,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(179.0, 58.0),
|
||||||
|
source: LedSource::CpuStatus,
|
||||||
|
mask: 0x40,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(209.0, 58.0),
|
||||||
|
source: LedSource::CpuStatus,
|
||||||
|
mask: 0x20,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(239.0, 58.0),
|
||||||
|
source: LedSource::CpuStatus,
|
||||||
|
mask: 0x10,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(269.0, 58.0),
|
||||||
|
source: LedSource::CpuStatus,
|
||||||
|
mask: 0x8,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(299.0, 58.0),
|
||||||
|
source: LedSource::CpuStatus,
|
||||||
|
mask: 0x4,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(329.0, 58.0),
|
||||||
|
source: LedSource::CpuStatus,
|
||||||
|
mask: 0x2,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(359.0, 58.0),
|
||||||
|
source: LedSource::CpuStatus,
|
||||||
|
mask: 0x1,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(461.0, 58.0),
|
||||||
|
source: LedSource::Data,
|
||||||
|
mask: 0x80,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(491.0, 58.0),
|
||||||
|
source: LedSource::Data,
|
||||||
|
mask: 0x40,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(536.0, 58.0),
|
||||||
|
source: LedSource::Data,
|
||||||
|
mask: 0x20,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(564.0, 58.0),
|
||||||
|
source: LedSource::Data,
|
||||||
|
mask: 0x10,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(593.0, 58.0),
|
||||||
|
source: LedSource::Data,
|
||||||
|
mask: 0x8,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(638.0, 58.0),
|
||||||
|
source: LedSource::Data,
|
||||||
|
mask: 0x4,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(667.0, 58.0),
|
||||||
|
source: LedSource::Data,
|
||||||
|
mask: 0x2,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(696.0, 58.0),
|
||||||
|
source: LedSource::Data,
|
||||||
|
mask: 0x1,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(182.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x8000,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(227.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x4000,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(256.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x2000,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(285.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x1000,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(330.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x800,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(359.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x400,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(388.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x200,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(433.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x100,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(461.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x80,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(490.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x40,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(536.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x20,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(564.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x10,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(593.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x8,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(638.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x4,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(667.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x2,
|
||||||
|
},
|
||||||
|
LedInfo {
|
||||||
|
pos: Pos2::new(696.0, 117.0),
|
||||||
|
source: LedSource::Address,
|
||||||
|
mask: 0x1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
54
src/frontpanel/led.rs
Normal file
54
src/frontpanel/led.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use eframe::{
|
||||||
|
egui::{Sense, Widget},
|
||||||
|
epaint::{pos2, vec2, Color32, Rect, TextureHandle},
|
||||||
|
};
|
||||||
|
|
||||||
|
const NULL_UV: Rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
|
||||||
|
const NULL_TINT: Color32 = Color32::WHITE;
|
||||||
|
|
||||||
|
pub struct Textures {
|
||||||
|
led_on: TextureHandle,
|
||||||
|
led_off: TextureHandle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Textures {
|
||||||
|
pub fn new(led_on: TextureHandle, led_off: TextureHandle) -> Self {
|
||||||
|
Self { led_on, led_off }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Led<'a> {
|
||||||
|
on: bool,
|
||||||
|
textures: &'a Textures,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Led<'a> {
|
||||||
|
pub fn new(on: bool, textures: &'a Textures) -> Self {
|
||||||
|
Self { on, textures }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for Led<'_> {
|
||||||
|
fn ui(self, ui: &mut eframe::egui::Ui) -> eframe::egui::Response {
|
||||||
|
let (resp, painter) = ui.allocate_painter(
|
||||||
|
(16.0, 16.0).into(),
|
||||||
|
Sense {
|
||||||
|
click: false,
|
||||||
|
drag: false,
|
||||||
|
focusable: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let texture = if self.on {
|
||||||
|
self.textures.led_on.id()
|
||||||
|
} else {
|
||||||
|
self.textures.led_off.id()
|
||||||
|
};
|
||||||
|
painter.image(
|
||||||
|
texture,
|
||||||
|
Rect::from_min_size(painter.clip_rect().left_top(), vec2(16.0, 16.0)),
|
||||||
|
NULL_UV,
|
||||||
|
NULL_TINT,
|
||||||
|
);
|
||||||
|
resp
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
use eframe::{
|
use eframe::{
|
||||||
egui::{Id, Sense, Widget},
|
egui::{Sense, Widget},
|
||||||
epaint::{pos2, vec2, Color32, Rect, TextureHandle},
|
epaint::{pos2, vec2, Color32, Rect, TextureHandle, TextureId},
|
||||||
};
|
};
|
||||||
|
|
||||||
const NULL_UV: Rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
|
const NULL_UV: Rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
|
||||||
@ -20,6 +20,22 @@ impl Textures {
|
|||||||
sw_down,
|
sw_down,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_for_state(&self, state: SwitchState) -> TextureId {
|
||||||
|
match state {
|
||||||
|
SwitchState::Up => self.sw_up.id(),
|
||||||
|
SwitchState::Neut => self.sw_neut.id(),
|
||||||
|
SwitchState::Down => self.sw_down.id(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_for_bool(&self, state: bool) -> TextureId {
|
||||||
|
if state {
|
||||||
|
self.sw_up.id()
|
||||||
|
} else {
|
||||||
|
self.sw_down.id()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
@ -29,51 +45,76 @@ pub enum SwitchState {
|
|||||||
Down,
|
Down,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Switch<'a> {
|
impl Default for SwitchState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Neut
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ThreePosSwitch<'a> {
|
||||||
state: &'a mut SwitchState,
|
state: &'a mut SwitchState,
|
||||||
textures: &'a Textures,
|
textures: &'a Textures,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Switch<'a> {
|
impl<'a> ThreePosSwitch<'a> {
|
||||||
pub fn new(state: &'a mut SwitchState, textures: &'a Textures) -> Self {
|
pub fn new(state: &'a mut SwitchState, textures: &'a Textures) -> Self {
|
||||||
Self { state, textures }
|
Self { state, textures }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for Switch<'_> {
|
impl Widget for ThreePosSwitch<'_> {
|
||||||
fn ui(self, ui: &mut eframe::egui::Ui) -> eframe::egui::Response {
|
fn ui(self, ui: &mut eframe::egui::Ui) -> eframe::egui::Response {
|
||||||
let (resp, painter) = ui.allocate_painter((32.0, 32.0).into(), Sense::click());
|
let (resp, painter) = ui.allocate_painter((32.0, 32.0).into(), Sense::drag());
|
||||||
let id = Id::new((
|
|
||||||
painter.clip_rect().left_top().x as u32,
|
|
||||||
painter.clip_rect().left_top().y as u32,
|
|
||||||
));
|
|
||||||
let texture = match self.state {
|
|
||||||
SwitchState::Up => self.textures.sw_up.id(),
|
|
||||||
SwitchState::Neut => self.textures.sw_neut.id(),
|
|
||||||
SwitchState::Down => self.textures.sw_down.id(),
|
|
||||||
};
|
|
||||||
painter.image(
|
painter.image(
|
||||||
texture,
|
self.textures.get_for_state(*self.state),
|
||||||
Rect::from_min_size(painter.clip_rect().left_top(), vec2(32.0, 32.0)),
|
Rect::from_min_size(painter.clip_rect().left_top(), vec2(32.0, 32.0)),
|
||||||
NULL_UV,
|
NULL_UV,
|
||||||
NULL_TINT,
|
NULL_TINT,
|
||||||
);
|
);
|
||||||
let pointer_state = ui.ctx().input(|inp| inp.pointer.clone());
|
if resp.drag_started() {
|
||||||
let interact_pos = pointer_state.interact_pos();
|
let interact_pos = resp.interact_pointer_pos().unwrap();
|
||||||
dbg!(interact_pos);
|
let sw_center = painter.clip_rect().left_top() + vec2(15.0, 16.0);
|
||||||
let interacted = interact_pos.map(|interact_pos| {
|
if Rect::from_center_size(interact_pos, (10.0, 24.0).into()).contains(sw_center) {
|
||||||
Rect::from_center_size(interact_pos, (10.0, 24.0).into()).contains(painter.clip_rect().left_top())
|
if interact_pos.y > sw_center.y {
|
||||||
}).unwrap_or(false);
|
*self.state = SwitchState::Down;
|
||||||
let newstate = if interacted {
|
} else {
|
||||||
if interact_pos.unwrap().y > painter.clip_rect().left_top().y {
|
*self.state = SwitchState::Up;
|
||||||
SwitchState::Down
|
};
|
||||||
} else {
|
|
||||||
SwitchState::Up
|
|
||||||
}
|
}
|
||||||
} else {
|
} else if resp.drag_released() {
|
||||||
SwitchState::Neut
|
*self.state = SwitchState::Neut;
|
||||||
};
|
}
|
||||||
*self.state = newstate;
|
resp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ToggleSwitch<'a> {
|
||||||
|
state: &'a mut bool,
|
||||||
|
textures: &'a Textures,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ToggleSwitch<'a> {
|
||||||
|
pub fn new(state: &'a mut bool, textures: &'a Textures) -> Self {
|
||||||
|
Self { state, textures }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for ToggleSwitch<'_> {
|
||||||
|
fn ui(self, ui: &mut eframe::egui::Ui) -> eframe::egui::Response {
|
||||||
|
let (resp, painter) = ui.allocate_painter((32.0, 32.0).into(), Sense::drag());
|
||||||
|
painter.image(
|
||||||
|
self.textures.get_for_bool(*self.state),
|
||||||
|
Rect::from_min_size(painter.clip_rect().left_top(), vec2(32.0, 32.0)),
|
||||||
|
NULL_UV,
|
||||||
|
NULL_TINT,
|
||||||
|
);
|
||||||
|
if resp.drag_started() {
|
||||||
|
let interact_pos = resp.interact_pointer_pos().unwrap();
|
||||||
|
let sw_center = painter.clip_rect().left_top() + vec2(15.0, 16.0);
|
||||||
|
if Rect::from_center_size(interact_pos, (10.0, 24.0).into()).contains(sw_center) {
|
||||||
|
*self.state = !*self.state;
|
||||||
|
}
|
||||||
|
}
|
||||||
resp
|
resp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
src/main.rs
27
src/main.rs
@ -11,9 +11,9 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use cpu::{MemCycle, Status, I8080};
|
use cpu::{MemCycle, Status, I8080};
|
||||||
use device_query::{DeviceState, Keycode};
|
use device_query::DeviceState;
|
||||||
use eframe::{
|
use eframe::{
|
||||||
egui::{self, menu, Button, Pos2, Rect, TextureHandle, TextureOptions, Ui},
|
egui::{self, menu, Button, Id, Label, Pos2, Rect, TextureHandle, TextureOptions, Ui},
|
||||||
NativeOptions,
|
NativeOptions,
|
||||||
};
|
};
|
||||||
use egui_modal::Modal;
|
use egui_modal::Modal;
|
||||||
@ -24,7 +24,7 @@ use rfd::FileDialog;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use soloud::{audio, AudioExt, LoadExt, Soloud};
|
use soloud::{audio, AudioExt, LoadExt, Soloud};
|
||||||
|
|
||||||
use crate::frontpanel::switch;
|
use crate::frontpanel::{switch, Frontpanel};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum AudioMessage {
|
enum AudioMessage {
|
||||||
@ -182,7 +182,7 @@ struct Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct AltairEmulator {
|
struct AltairEmulator {
|
||||||
textures: Textures,
|
textures: frontpanel::Textures,
|
||||||
ad_sws: u16,
|
ad_sws: u16,
|
||||||
power: bool,
|
power: bool,
|
||||||
runstop: SwitchState,
|
runstop: SwitchState,
|
||||||
@ -253,7 +253,7 @@ impl AltairEmulator {
|
|||||||
// 20: 004
|
// 20: 004
|
||||||
// 21: 005
|
// 21: 005
|
||||||
Self {
|
Self {
|
||||||
textures: Textures::new(&cc.egui_ctx),
|
textures: frontpanel::Textures::new(&cc.egui_ctx),
|
||||||
ad_sws: 0x0,
|
ad_sws: 0x0,
|
||||||
power: false,
|
power: false,
|
||||||
runstop: SwitchState::Neut,
|
runstop: SwitchState::Neut,
|
||||||
@ -315,7 +315,7 @@ impl eframe::App for AltairEmulator {
|
|||||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||||
fn image_topleft(pos: impl Into<Pos2>, texture: &TextureHandle, ui: &mut Ui) {
|
fn image_topleft(pos: impl Into<Pos2>, texture: &TextureHandle, ui: &mut Ui) {
|
||||||
ui.allocate_ui_at_rect(Rect::from_min_size(pos.into(), texture.size_vec2()), |ui| {
|
ui.allocate_ui_at_rect(Rect::from_min_size(pos.into(), texture.size_vec2()), |ui| {
|
||||||
ui.image(texture, texture.size_vec2());
|
ui.image(texture);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,13 +323,13 @@ impl eframe::App for AltairEmulator {
|
|||||||
ui.allocate_ui_at_rect(
|
ui.allocate_ui_at_rect(
|
||||||
Rect::from_center_size(pos.into(), texture.size_vec2()),
|
Rect::from_center_size(pos.into(), texture.size_vec2()),
|
||||||
|ui| {
|
|ui| {
|
||||||
ui.image(texture, texture.size_vec2());
|
ui.image(texture);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.set_pixels_per_point(1.0);
|
ctx.set_pixels_per_point(1.0);
|
||||||
frame.set_window_size((800.0, 333.0).into());
|
// frame.set_window_size((800.0, 333.0).into());
|
||||||
let mut disable_fp_sws = self.option_window.is_some();
|
let mut disable_fp_sws = self.option_window.is_some();
|
||||||
egui::TopBottomPanel::top("menu").show(ctx, |ui| {
|
egui::TopBottomPanel::top("menu").show(ctx, |ui| {
|
||||||
menu::bar(ui, |ui| {
|
menu::bar(ui, |ui| {
|
||||||
@ -378,6 +378,11 @@ impl eframe::App for AltairEmulator {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ui.add(Label::new("Hello"));
|
||||||
|
ui.add(Frontpanel::new(Id::new("frontpanel"), &self.textures));
|
||||||
|
ui.add(Frontpanel::new(Id::new("frontpanel2"), &self.textures));
|
||||||
|
// 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());
|
// let (_, fp_rect) = ui.allocate_space((800.0, 333.0).into());
|
||||||
// image_topleft(fp_rect.left_top(), &self.textures.fp, ui);
|
// image_topleft(fp_rect.left_top(), &self.textures.fp, ui);
|
||||||
// for led in &LEDS {
|
// for led in &LEDS {
|
||||||
@ -728,12 +733,6 @@ impl eframe::App for AltairEmulator {
|
|||||||
// self.cpu.finish_m_cycle(data);
|
// self.cpu.finish_m_cycle(data);
|
||||||
// self.update_fp();
|
// self.update_fp();
|
||||||
// }
|
// }
|
||||||
let sw_texts = switch::Textures::new(
|
|
||||||
self.textures.sw_up.clone(),
|
|
||||||
self.textures.sw_neut.clone(),
|
|
||||||
self.textures.sw_down.clone(),
|
|
||||||
);
|
|
||||||
ui.add(switch::Switch::new(&mut self.dep, &sw_texts));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let old_fan_enabled = self.options.fan_enabled;
|
let old_fan_enabled = self.options.fan_enabled;
|
||||||
|
Loading…
Reference in New Issue
Block a user