Update before heading home
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 26s

This commit is contained in:
2025-12-19 20:38:47 -06:00
parent ce4532bcdf
commit 5c3d537cfd
8 changed files with 555 additions and 109 deletions

47
Cargo.lock generated
View File

@@ -774,8 +774,7 @@ dependencies = [
[[package]]
name = "cosmic-text"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "173852283a9a57a3cbe365d86e74dc428a09c50421477d5ad6fe9d9509e37737"
source = "git+https://github.com/pop-os/cosmic-text.git?rev=a07a6190548c8e40a55f6b7761387047ff1bf6ff#a07a6190548c8e40a55f6b7761387047ff1bf6ff"
dependencies = [
"bitflags 2.10.0",
"fontdb",
@@ -838,8 +837,7 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "cryoglyph"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08bc795bdbccdbd461736fb163930a009da6597b226d6f6fce33e7a8eb6ec519"
source = "git+https://github.com/iced-rs/cryoglyph.git?rev=89883bcf38b5bed0d7bade788ef738d9facc857c#89883bcf38b5bed0d7bade788ef738d9facc857c"
dependencies = [
"cosmic-text",
"etagere",
@@ -902,9 +900,8 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "dpi"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
version = "0.1.1"
source = "git+https://github.com/iced-rs/winit.git?rev=05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed#05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed"
[[package]]
name = "either"
@@ -1491,7 +1488,7 @@ dependencies = [
[[package]]
name = "iced"
version = "0.14.0"
version = "0.15.0-dev"
dependencies = [
"iced_core",
"iced_debug",
@@ -1507,7 +1504,7 @@ dependencies = [
[[package]]
name = "iced_beacon"
version = "0.14.0"
version = "0.15.0-dev"
dependencies = [
"bincode",
"futures",
@@ -1521,7 +1518,7 @@ dependencies = [
[[package]]
name = "iced_core"
version = "0.14.0"
version = "0.15.0-dev"
dependencies = [
"bitflags 2.10.0",
"bytes",
@@ -1538,7 +1535,7 @@ dependencies = [
[[package]]
name = "iced_debug"
version = "0.14.0"
version = "0.15.0-dev"
dependencies = [
"iced_beacon",
"iced_core",
@@ -1548,7 +1545,7 @@ dependencies = [
[[package]]
name = "iced_devtools"
version = "0.14.0"
version = "0.15.0-dev"
dependencies = [
"iced_debug",
"iced_program",
@@ -1558,7 +1555,7 @@ dependencies = [
[[package]]
name = "iced_futures"
version = "0.14.0"
version = "0.15.0-dev"
dependencies = [
"futures",
"iced_core",
@@ -1571,7 +1568,7 @@ dependencies = [
[[package]]
name = "iced_graphics"
version = "0.14.0"
version = "0.15.0-dev"
dependencies = [
"bitflags 2.10.0",
"bytemuck",
@@ -1591,7 +1588,7 @@ dependencies = [
[[package]]
name = "iced_program"
version = "0.14.0"
version = "0.15.0-dev"
dependencies = [
"iced_graphics",
"iced_runtime",
@@ -1599,7 +1596,7 @@ dependencies = [
[[package]]
name = "iced_renderer"
version = "0.14.0"
version = "0.15.0-dev"
dependencies = [
"iced_graphics",
"iced_tiny_skia",
@@ -1610,7 +1607,7 @@ dependencies = [
[[package]]
name = "iced_runtime"
version = "0.14.0"
version = "0.15.0-dev"
dependencies = [
"bytes",
"iced_core",
@@ -1621,7 +1618,7 @@ dependencies = [
[[package]]
name = "iced_tiny_skia"
version = "0.14.0"
version = "0.15.0-dev"
dependencies = [
"bytemuck",
"cosmic-text",
@@ -1636,7 +1633,7 @@ dependencies = [
[[package]]
name = "iced_wgpu"
version = "0.14.0"
version = "0.15.0-dev"
dependencies = [
"bitflags 2.10.0",
"bytemuck",
@@ -1655,7 +1652,7 @@ dependencies = [
[[package]]
name = "iced_widget"
version = "0.14.2"
version = "0.15.0-dev"
dependencies = [
"iced_renderer",
"log",
@@ -1668,7 +1665,7 @@ dependencies = [
[[package]]
name = "iced_winit"
version = "0.14.0"
version = "0.15.0-dev"
dependencies = [
"iced_debug",
"iced_program",
@@ -2199,6 +2196,7 @@ dependencies = [
"bitfield",
"iced",
"thiserror 2.0.17",
"tokio",
"tracing",
"tracing-subscriber",
]
@@ -3734,7 +3732,9 @@ dependencies = [
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.61.2",
@@ -4822,9 +4822,8 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "winit"
version = "0.30.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732"
version = "0.30.8"
source = "git+https://github.com/iced-rs/winit.git?rev=05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed#05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed"
dependencies = [
"ahash",
"android-activity",

View File

@@ -10,5 +10,6 @@ iced = { path = "../iced", features = ["debug", "canvas", "tokio", "lazy", "imag
# iced_graphics = { version = "0.14.0", features = ["geometry", "image"] }
# iced_widget = { version = "0.13.4", features = ["canvas", "image"] }
thiserror = "2.0.17"
tokio = { version = "1.48.0", features = ["full"] }
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.20", features = ["ansi", "chrono", "env-filter", "json", "serde"] }

View File

@@ -237,6 +237,7 @@ where
align_y: alignment::Vertical::Center,
shaping: self.text_shaping,
wrapping: text::Wrapping::default(),
hint_factor: renderer.scale_factor(),
};
for (option, paragraph) in options.iter().zip(state.options.iter_mut()) {
@@ -474,6 +475,7 @@ where
align_y: alignment::Vertical::Center,
shaping,
wrapping: text::Wrapping::default(),
hint_factor: renderer.scale_factor(),
},
Point::new(
bounds.x + bounds.width - self.padding.right,
@@ -500,6 +502,7 @@ where
align_y: alignment::Vertical::Center,
shaping: self.text_shaping,
wrapping: text::Wrapping::default(),
hint_factor: renderer.scale_factor(),
},
Point::new(bounds.x + self.padding.left, bounds.center_y()),
style.text_color,

View File

@@ -10,6 +10,7 @@ pub trait Memory {
#[derive(Debug, Clone)]
pub enum HexEvent {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HexView {
}

View File

@@ -8,6 +8,7 @@ mod mem;
mod ppu;
#[cfg(test)]
mod test_roms;
pub mod resize_watcher;
pub use ppu::{Color, PPU, RenderBuffer};

View File

@@ -1,8 +1,8 @@
use std::{collections::HashMap, fmt};
use std::{collections::HashMap, fmt, time::Duration};
use iced::{
Color, Element, Font,
Length::Fill,
Length::{Fill, Shrink},
Point, Renderer, Size, Subscription, Task, Theme, mouse,
widget::{
Canvas, button,
@@ -16,7 +16,9 @@ use nes_emu::{
debugger::{DebuggerMessage, DebuggerState},
header_menu::header_menu,
hex_view::{HexEvent, HexView},
resize_watcher::resize_watcher,
};
use tokio::runtime::Runtime;
use tracing_subscriber::EnvFilter;
const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "even_odd.nes");
@@ -33,13 +35,16 @@ fn main() -> Result<(), iced::Error> {
.subscription(Emulator::subscriptions)
.theme(Theme::Dark)
.title(Emulator::title)
.executor::<Runtime>()
.run()
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum MemoryTy {
Cpu,
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum WindowType {
Main,
Memory(MemoryTy, HexView),
@@ -51,10 +56,11 @@ enum WindowType {
#[derive(Debug, Clone, PartialEq, Eq)]
enum HeaderButton {
OpenMemory,
OpenTileMap,
OpenTileViewer,
OpenDebugger,
// OpenMemory,
// OpenTileMap,
// OpenTileViewer,
// OpenDebugger,
Open(WindowType),
Reset,
PowerCycle,
}
@@ -62,10 +68,12 @@ enum HeaderButton {
impl fmt::Display for HeaderButton {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::OpenMemory => write!(f, "Open Memory Viewer"),
Self::OpenTileMap => write!(f, "Open TileMap Viewer"),
Self::OpenTileViewer => write!(f, "Open Tile Viewer"),
Self::OpenDebugger => write!(f, "Open Debugger"),
Self::Open(WindowType::Memory(MemoryTy::Cpu, _)) => write!(f, "Open Memory Viewer"),
Self::Open(WindowType::TileMap) => write!(f, "Open TileMap Viewer"),
Self::Open(WindowType::TileViewer) => write!(f, "Open Tile Viewer"),
Self::Open(WindowType::Debugger) => write!(f, "Open Debugger"),
Self::Open(WindowType::Palette) => write!(f, "Open Palette"),
Self::Open(WindowType::Main) => write!(f, "Create new Main window"),
Self::Reset => write!(f, "Reset"),
Self::PowerCycle => write!(f, "Power Cycle"),
}
@@ -76,6 +84,7 @@ struct Emulator {
nes: NES,
windows: HashMap<Id, WindowType>,
debugger: DebuggerState,
main_win_size: Size,
}
#[derive(Debug, Clone)]
@@ -85,25 +94,29 @@ enum Message {
DMA,
CPU,
DebugInt,
WindowOpened(Id),
WindowClosed(Id),
Header(HeaderButton),
Hex(Id, HexEvent),
Debugger(DebuggerMessage),
SetSize(window::Id, Size),
}
impl Emulator {
fn new() -> (Self, Task<Message>) {
let mut nes = nes_emu::NES::load_nes_file(ROM_FILE).expect("Failed to load nes file");
nes.reset();
let (win, task) = iced::window::open(Settings::default());
let (win, task) = iced::window::open(Settings {
min_size: None,
..Settings::default()
});
(
Self {
nes,
windows: HashMap::from_iter([(win, WindowType::Main)]),
debugger: DebuggerState::new(),
main_win_size: Size::new(0., 0.),
},
task.map(Message::WindowOpened),
task.discard(),
)
}
fn title(&self, win: Id) -> String {
@@ -121,7 +134,7 @@ impl Emulator {
fn open(&mut self, ty: WindowType) -> Task<Message> {
let (win, task) = iced::window::open(Settings::default());
self.windows.insert(win, ty);
return task.map(Message::WindowOpened);
return task.discard();
}
fn update(&mut self, message: Message) -> Task<Message> {
@@ -135,25 +148,13 @@ impl Emulator {
Message::DMA => while !self.nes.run_one_clock_cycle().dma {},
Message::CPU => while !self.nes.run_one_clock_cycle().cpu_exec {},
Message::DebugInt => while !self.nes.run_one_clock_cycle().dbg_int {},
Message::WindowOpened(_id) => {
// Window
}
Message::WindowClosed(id) => {
if let Some(WindowType::Main) = self.windows.remove(&id) {
return iced::exit();
}
}
Message::Header(HeaderButton::OpenMemory) => {
return self.open(WindowType::Memory(MemoryTy::Cpu, HexView::new()));
}
Message::Header(HeaderButton::OpenTileMap) => {
return self.open(WindowType::TileMap);
}
Message::Header(HeaderButton::OpenTileViewer) => {
return self.open(WindowType::TileViewer);
}
Message::Header(HeaderButton::OpenDebugger) => {
return self.open(WindowType::Debugger);
Message::Header(HeaderButton::Open(w)) => {
return self.open(w);
}
Message::Hex(id, val) => {
if let Some(WindowType::Memory(_, view)) = self.windows.get_mut(&id) {
@@ -169,13 +170,31 @@ impl Emulator {
Message::Debugger(debugger_message) => {
self.debugger.update(debugger_message, &mut self.nes)
}
Message::SetSize(id, size) => {
if let Some(WindowType::Main) = self.windows.get(&id) {
self.main_win_size = size;
}
println!("New size for {:?}, {:?}", id, size);
// return iced::window::set_min_size(id, size.into())
// .then(move |_: ()| iced::window::resize(id, size));
return Task::future(async {
tokio::time::sleep(Duration::from_millis(50)).await;
})
.then(move |_| {
iced::window::resize(id, size)
});
}
}
// self.image.0.clone_from(self.nes.image());
Task::none()
}
fn subscriptions(&self) -> Subscription<Message> {
window::close_events().map(Message::WindowClosed)
Subscription::batch([
window::close_events().map(Message::WindowClosed),
// window::events().map(Message::Window),
// window::resize_events().map(Message::WindowResized),
])
}
fn view(&self, win: Id) -> Element<'_, Message> {
@@ -183,12 +202,16 @@ impl Emulator {
Some(WindowType::Main) => {
let content = column![
self.dropdowns(),
Element::from(Canvas::new(self).width(Fill).height(255. * 2.)),
self.cpu_state(),
self.controls(),
Element::from(Canvas::new(self).width(256. * 2.).height(240. * 2.)),
// self.cpu_state(),
// self.controls(),
]
.height(Fill);
container(content).width(Fill).height(Fill).into()
.height(Shrink);
resize_watcher(content)
.on_resize(move |s| Message::SetSize(win, s))
.width(Shrink)
.height(Shrink)
.into()
}
Some(WindowType::Memory(ty, view)) => {
let hex = match ty {
@@ -211,12 +234,10 @@ impl Emulator {
.height(Fill)
.into()
}
Some(WindowType::Palette) => {
container(Canvas::new(DbgImage::Palette(self.nes.ppu())))
.width(Fill)
.height(Fill)
.into()
}
Some(WindowType::Palette) => container(Canvas::new(DbgImage::Palette(self.nes.ppu())))
.width(Fill)
.height(Fill)
.into(),
Some(WindowType::Debugger) => {
container(self.debugger.view(&self.nes).map(Message::Debugger))
.width(Fill)
@@ -228,26 +249,26 @@ impl Emulator {
}
}
fn cpu_state(&self) -> Element<'_, Message> {
row![column![
// text!("Registers").font(Font::MONOSPACE),
text!("{:?}", self.nes).font(Font::MONOSPACE),
],]
.width(Fill)
.into()
}
// fn cpu_state(&self) -> Element<'_, Message> {
// row![column![
// // text!("Registers").font(Font::MONOSPACE),
// text!("{:?}", self.nes).font(Font::MONOSPACE),
// ],]
// .width(Fill)
// .into()
// }
fn controls(&self) -> Element<'_, Message> {
row![
button("Clock tick").on_press(Message::Tick(1)),
button("CPU tick").on_press(Message::CPU),
button("Next Frame").on_press(Message::Frame),
button("Next DMA").on_press(Message::DMA),
button("Next DBG").on_press(Message::DebugInt),
]
.width(Fill)
.into()
}
// fn controls(&self) -> Element<'_, Message> {
// row![
// button("Clock tick").on_press(Message::Tick(1)),
// button("CPU tick").on_press(Message::CPU),
// button("Next Frame").on_press(Message::Frame),
// button("Next DMA").on_press(Message::DMA),
// button("Next DBG").on_press(Message::DebugInt),
// ]
// .width(Fill)
// .into()
// }
fn dropdowns(&self) -> Element<'_, Message> {
row![
@@ -259,10 +280,11 @@ impl Emulator {
header_menu(
"Debugging",
[
HeaderButton::OpenDebugger,
HeaderButton::OpenMemory,
HeaderButton::OpenTileMap,
HeaderButton::OpenTileViewer,
HeaderButton::Open(WindowType::Debugger),
HeaderButton::Open(WindowType::Memory(MemoryTy::Cpu, HexView {})),
HeaderButton::Open(WindowType::TileMap),
HeaderButton::Open(WindowType::TileViewer),
HeaderButton::Open(WindowType::Palette),
],
Message::Header
)
@@ -280,16 +302,16 @@ impl Program<Message> for Emulator {
_state: &Self::State,
renderer: &Renderer,
_theme: &Theme,
_bounds: iced::Rectangle,
bounds: iced::Rectangle,
_cursor: mouse::Cursor,
) -> Vec<iced::widget::canvas::Geometry<Renderer>> {
// const SIZE: f32 = 2.;
let mut frame = Frame::new(
renderer,
iced::Size {
width: 256. * 2.,
height: 240. * 2.,
},
bounds.size(), // iced::Size {
// width: 256. * 2.,
// height: 240. * 2.,
// },
);
frame.scale(2.);
for y in 0..240 {

View File

@@ -98,13 +98,76 @@ pub struct Mask {
em_blue: bool,
}
const COLORS: &'static [Color; 0b11_1111] = &[
Color { r: 0, g: 0, b: 0}; 0b11_1111
const COLORS: &'static [Color; 0b100_0000] = &[
Color { r: 0x66, g: 0x66, b: 0x66 }, // 00
Color { r: 0x00, g: 0x2A, b: 0x88 }, // 01
Color { r: 0x14, g: 0x12, b: 0xA7 }, // 02
Color { r: 0x3B, g: 0x00, b: 0xA4 }, // 03
Color { r: 0x5C, g: 0x00, b: 0x7E }, // 04
Color { r: 0x6E, g: 0x00, b: 0x40 }, // 05
Color { r: 0x6C, g: 0x06, b: 0x00 }, // 06
Color { r: 0x56, g: 0x1D, b: 0x00 }, // 07
Color { r: 0x33, g: 0x35, b: 0x00 }, // 08
Color { r: 0x0B, g: 0x48, b: 0x00 }, // 09
Color { r: 0x00, g: 0x52, b: 0x00 }, // 0A
Color { r: 0x00, g: 0x4F, b: 0x08 }, // 0B
Color { r: 0x00, g: 0x40, b: 0x4D }, // 0C
Color { r: 0x00, g: 0x00, b: 0x00 }, // 0D
Color { r: 0x00, g: 0x00, b: 0x00 }, // 0E
Color { r: 0x00, g: 0x00, b: 0x00 }, // 0F
Color { r: 0xAD, g: 0xAD, b: 0xAD }, // 10
Color { r: 0x15, g: 0x5F, b: 0xD9 }, // 11
Color { r: 0x42, g: 0x40, b: 0xFF }, // 12
Color { r: 0x75, g: 0x27, b: 0xFE }, // 13
Color { r: 0xA0, g: 0x1A, b: 0xCC }, // 14
Color { r: 0xB7, g: 0x1E, b: 0x7B }, // 15
Color { r: 0xB5, g: 0x31, b: 0x20 }, // 16
Color { r: 0x99, g: 0x4E, b: 0x00 }, // 17
Color { r: 0x6B, g: 0x6D, b: 0x00 }, // 18
Color { r: 0x38, g: 0x87, b: 0x00 }, // 19
Color { r: 0x0C, g: 0x93, b: 0x00 }, // 1A
Color { r: 0x00, g: 0x8F, b: 0x32 }, // 1B
Color { r: 0x00, g: 0x7C, b: 0x8D }, // 1C
Color { r: 0x00, g: 0x00, b: 0x00 }, // 1D
Color { r: 0x00, g: 0x00, b: 0x00 }, // 1E
Color { r: 0x00, g: 0x00, b: 0x00 }, // 1F
Color { r: 0xFF, g: 0xFE, b: 0xFF }, // 20
Color { r: 0x64, g: 0xB0, b: 0xFF }, // 21
Color { r: 0x92, g: 0x90, b: 0xFF }, // 22
Color { r: 0xC6, g: 0x76, b: 0xFF }, // 23
Color { r: 0xF3, g: 0x6A, b: 0xFF }, // 24
Color { r: 0xFE, g: 0x6E, b: 0xCC }, // 25
Color { r: 0xFE, g: 0x81, b: 0x70 }, // 26
Color { r: 0xEA, g: 0x9E, b: 0x22 }, // 27
Color { r: 0xBC, g: 0xBE, b: 0x00 }, // 28
Color { r: 0x88, g: 0xD8, b: 0x00 }, // 29
Color { r: 0x5C, g: 0xE4, b: 0x30 }, // 2A
Color { r: 0x45, g: 0xE0, b: 0x82 }, // 2B
Color { r: 0x48, g: 0xCD, b: 0xDE }, // 2C
Color { r: 0x4F, g: 0x4F, b: 0x4F }, // 2D
Color { r: 0x00, g: 0x00, b: 0x00 }, // 2E
Color { r: 0x00, g: 0x00, b: 0x00 }, // 2F
Color { r: 0xFF, g: 0xFE, b: 0xFF }, // 30
Color { r: 0xC0, g: 0xDF, b: 0xFF }, // 31
Color { r: 0xD3, g: 0xD2, b: 0xFF }, // 32
Color { r: 0xE8, g: 0xC8, b: 0xFF }, // 33
Color { r: 0xFB, g: 0xC2, b: 0xFF }, // 34
Color { r: 0xFE, g: 0xC4, b: 0xEA }, // 35
Color { r: 0xFE, g: 0xCC, b: 0xC5 }, // 36
Color { r: 0xF7, g: 0xD8, b: 0xA5 }, // 37
Color { r: 0xE4, g: 0xE5, b: 0x94 }, // 38
Color { r: 0xCF, g: 0xEF, b: 0x96 }, // 39
Color { r: 0xBD, g: 0xF4, b: 0xAB }, // 3A
Color { r: 0xB3, g: 0xF3, b: 0xCC }, // 3B
Color { r: 0xB5, g: 0xEB, b: 0xF2 }, // 3C
Color { r: 0xB8, g: 0xB8, b: 0xB8 }, // 3D
Color { r: 0x00, g: 0x00, b: 0x00 }, // 3E
Color { r: 0x00, g: 0x00, b: 0x00 }, // 3F
];
pub struct Palette {
colors: &'static [Color; 0b11_1111],
ram: [u8; 0x20]
colors: &'static [Color; 0x40],
ram: [u8; 0x20],
}
pub struct PPU {
@@ -162,7 +225,19 @@ impl PPU {
em_green: false,
em_blue: false,
},
palette: Palette { colors: COLORS, ram: [0; _] },
palette: Palette {
colors: COLORS,
ram: [
0x20, 0x21, 0x22, 0x23,
0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2A, 0x2B,
0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33,
0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3A, 0x3B,
0x3C, 0x3D, 0x3E, 0x3F,
],
},
vblank: false,
frame_count: 0,
nmi_enabled: false,
@@ -220,9 +295,12 @@ impl PPU {
5 => panic!("ppuscroll is write-only"),
6 => panic!("ppuaddr is write-only"),
7 => {
let val = self.memory.read(self.background.v).reg_map(|a, _| match a {
PPUMMRegisters::Palette => todo!(),
});
let val = self
.memory
.read(self.background.v)
.reg_map(|a, off| match a {
PPUMMRegisters::Palette => self.palette.ram[off as usize],
});
// if self.background
self.background.v = self
.background
@@ -513,16 +591,17 @@ impl PPU {
}
}
}
pub fn render_palette<R: Renderer>(&self, frame: &mut Frame<R>) {
frame.fill_rectangle(
Point::new(0., 0.),
Size::new(10., 10.),
Color {
r: todo!(),
g: todo!(),
b: todo!(),
},
);
for y in 0..8 {
for x in 0..4 {
frame.fill_rectangle(
Point::new(x as f32 * 10., y as f32 * 10.),
Size::new(10., 10.),
self.palette.colors[(self.palette.ram[x + y * 4] & 0x3F) as usize]
);
}
}
}
}

340
src/resize_watcher.rs Normal file
View File

@@ -0,0 +1,340 @@
use iced::{
Element, Event, Length, Padding, Pixels, Rectangle, Size, Vector,
advanced::{
Clipboard, Layout, Shell, Widget, layout, mouse, overlay, renderer,
widget::{Operation, Tree, tree},
},
alignment,
widget::{
self,
container::{self, Catalog, Style, StyleFn},
},
};
pub fn resize_watcher<'a, Message, Theme, Renderer>(
content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> ResizeWatcher<'a, Message, Theme, Renderer>
where
Theme: container::Catalog + 'a,
Renderer: iced::advanced::Renderer,
{
ResizeWatcher::new(content)
}
pub struct ResizeWatcher<'a, Message, Theme = iced::Theme, Renderer = iced::Renderer>
where
Theme: container::Catalog + 'a,
Renderer: iced::advanced::Renderer,
{
c: container::Container<'a, Message, Theme, Renderer>,
on_resize: Option<Box<dyn ResizeFn<Message> + 'a>>,
}
pub trait ResizeFn<Message> {
fn resized(&self, size: Size) -> Message;
}
impl<F: Fn(Size) -> M, M> ResizeFn<M> for F {
fn resized(&self, size: Size) -> M {
self(size)
}
}
impl<'a, Message, Theme, Renderer> ResizeWatcher<'a, Message, Theme, Renderer>
where
Theme: container::Catalog + 'a,
Renderer: iced::advanced::Renderer,
{
pub fn new(content: impl Into<Element<'a, Message, Theme, Renderer>>) -> Self {
Self {
c: widget::container(content),
on_resize: None,
}
}
pub fn on_resize(mut self, f: impl ResizeFn<Message> + 'a) -> Self {
self.on_resize = Some(Box::new(f));
self
}
/// Sets the [`widget::Id`] of the [`Container`].
pub fn id(mut self, id: impl Into<widget::Id>) -> Self {
self.c = self.c.id(id);
self
}
/// Sets the [`Padding`] of the [`Container`].
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
self.c = self.c.padding(padding);
self
}
/// Sets the width of the [`Container`].
pub fn width(mut self, width: impl Into<Length>) -> Self {
self.c = self.c.width(width);
// self.width = width.into();
self
}
/// Sets the height of the [`Container`].
pub fn height(mut self, height: impl Into<Length>) -> Self {
self.c = self.c.height(height);
self
}
/// Sets the maximum width of the [`Container`].
pub fn max_width(mut self, max_width: impl Into<Pixels>) -> Self {
self.c = self.c.max_width(max_width);
self
}
/// Sets the maximum height of the [`Container`].
pub fn max_height(mut self, max_height: impl Into<Pixels>) -> Self {
self.c = self.c.max_height(max_height);
self
}
/// Sets the width of the [`Container`] and centers its contents horizontally.
pub fn center_x(mut self, width: impl Into<Length>) -> Self {
self.c = self.c.center_x(width);
self
}
/// Sets the height of the [`Container`] and centers its contents vertically.
pub fn center_y(mut self, height: impl Into<Length>) -> Self {
self.c = self.c.center_y(height);
self
}
/// Sets the width and height of the [`Container`] and centers its contents in
/// both the horizontal and vertical axes.
///
/// This is equivalent to chaining [`center_x`] and [`center_y`].
///
/// [`center_x`]: Self::center_x
/// [`center_y`]: Self::center_y
pub fn center(mut self, length: impl Into<Length>) -> Self {
self.c = self.c.center(length);
self
}
/// Sets the width of the [`Container`] and aligns its contents to the left.
pub fn align_left(mut self, width: impl Into<Length>) -> Self {
self.c = self.c.align_left(width);
self
}
/// Sets the width of the [`Container`] and aligns its contents to the right.
pub fn align_right(mut self, width: impl Into<Length>) -> Self {
self.c = self.c.align_right(width);
self
}
/// Sets the height of the [`Container`] and aligns its contents to the top.
pub fn align_top(mut self, height: impl Into<Length>) -> Self {
self.c = self.c.align_top(height);
self
}
/// Sets the height of the [`Container`] and aligns its contents to the bottom.
pub fn align_bottom(mut self, height: impl Into<Length>) -> Self {
self.c = self.c.align_bottom(height);
self
}
/// Sets the content alignment for the horizontal axis of the [`Container`].
pub fn align_x(mut self, alignment: impl Into<alignment::Horizontal>) -> Self {
self.c = self.c.align_x(alignment);
self
}
/// Sets the content alignment for the vertical axis of the [`Container`].
pub fn align_y(mut self, alignment: impl Into<alignment::Vertical>) -> Self {
self.c = self.c.align_y(alignment);
self
}
/// Sets whether the contents of the [`Container`] should be clipped on
/// overflow.
pub fn clip(mut self, clip: bool) -> Self {
self.c = self.c.clip(clip);
self
}
/// Sets the style of the [`Container`].
#[must_use]
pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self
where
Theme::Class<'a>: From<StyleFn<'a, Theme>>,
{
self.c = self.c.style(style);
self
}
/// Sets the style class of the [`Container`].
#[must_use]
pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self {
self.c = self.c.class(class);
self
}
}
struct State {
last_size: Size,
cur_size: Size,
}
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for ResizeWatcher<'_, Message, Theme, Renderer>
where
Theme: Catalog,
Renderer: iced::advanced::Renderer,
{
fn tag(&self) -> tree::Tag {
tree::Tag::of::<State>()
}
fn state(&self) -> tree::State {
tree::State::new(State {
last_size: Size::new(0., 0.),
cur_size: Size::new(0., 0.),
})
}
fn children(&self) -> Vec<Tree> {
vec![Tree::new(&self.c as &dyn Widget<_, _, _>)]
}
fn diff(&self, tree: &mut Tree) {
tree.diff_children(&[&self.c as &dyn Widget<_, _, _>]);
}
fn size(&self) -> Size<Length> {
self.c.size()
}
fn layout(
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let tmp = layout::Node::container(
self.c.layout(&mut tree.children[0], renderer, limits),
Padding::new(0.),
);
let s: &mut State = tree.state.downcast_mut();
s.cur_size = tmp.size();
tmp
}
fn operate(
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn Operation,
) {
operation.container(None, layout.bounds());
self.c
.operate(&mut tree.children[0], layout.child(0), renderer, operation);
}
fn update(
&mut self,
tree: &mut Tree,
event: &Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) {
if let Some(m) = &self.on_resize {
let s: &mut State = tree.state.downcast_mut();
if s.cur_size != s.last_size {
shell.publish(m.resized(s.cur_size));
}
s.last_size = s.cur_size;
}
self.c.update(
&mut tree.children[0],
event,
layout.child(0),
cursor,
renderer,
clipboard,
shell,
viewport,
);
}
fn mouse_interaction(
&self,
tree: &Tree,
layout: Layout<'_>,
cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &Renderer,
) -> mouse::Interaction {
self.c.mouse_interaction(
&tree.children[0],
layout.child(0),
cursor,
viewport,
renderer,
)
}
fn draw(
&self,
tree: &Tree,
renderer: &mut Renderer,
theme: &Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor: mouse::Cursor,
viewport: &Rectangle,
) {
self.c.draw(
&tree.children[0],
renderer,
theme,
style,
layout.child(0),
cursor,
viewport,
);
}
fn overlay<'b>(
&'b mut self,
tree: &'b mut Tree,
layout: Layout<'b>,
renderer: &Renderer,
viewport: &Rectangle,
translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
self.c.overlay(
&mut tree.children[0],
layout.child(0),
renderer,
viewport,
translation,
)
}
}
impl<'a, Message, Theme, Renderer> From<ResizeWatcher<'a, Message, Theme, Renderer>>
for Element<'a, Message, Theme, Renderer>
where
Message: 'a,
Theme: Catalog + 'a,
Renderer: iced::advanced::Renderer + 'a,
{
fn from(
container: ResizeWatcher<'a, Message, Theme, Renderer>,
) -> Element<'a, Message, Theme, Renderer> {
Element::new(container)
}
}