diff --git a/Cargo.toml b/Cargo.toml index dea5b48..b12683c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ crate-type = ["cdylib"] [dependencies] fontdue = "0.9.3" lazy_static = "1.5.0" +quick-xml = "0.38.3" wasm-bindgen = {version = "0.2.104"} web-sys = {version = "0.3.81", features = ['CanvasRenderingContext2d', 'Document', 'Element', 'HtmlCanvasElement', 'Window', 'ImageData']} diff --git a/pages/main.xml b/pages/main.xml new file mode 100644 index 0000000..b4a58eb --- /dev/null +++ b/pages/main.xml @@ -0,0 +1,2 @@ + + diff --git a/src/app/mod.rs b/src/app/mod.rs index 3da75e1..cb21fb3 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -5,6 +5,7 @@ pub use cursors::{Cursor, set_cursor}; use wasm_bindgen::prelude::wasm_bindgen; use crate::{ + parser::{self, TEST_XML}, render::Renderer, views::{View, default_view}, }; @@ -36,8 +37,10 @@ impl App { pub fn new(renderer: Renderer) -> Self { let (width, height) = (renderer.actual_width, renderer.actual_height); + let root_view = parser::parse(TEST_XML); + let mut this = App { - root_view: Some(default_view()), + root_view: Some(root_view), renderer, // current_activity: Some(0), state: AppState::new(), @@ -49,6 +52,7 @@ impl App { } pub fn draw(&mut self) { + // self.renderer.img.randomize(&mut self.renderer.rand); if let Some(view) = &mut self.root_view { let (width, height) = ( self.renderer.actual_width.clone() as f32, diff --git a/src/lib.rs b/src/lib.rs index 572308b..3c351fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ mod app; mod fonts; +mod parser; mod render; mod views; @@ -12,6 +13,7 @@ use render::rand::Rnd; use crate::{ app::{App, Cursor, set_cursor}, + parser::TEST_XML, render::Renderer, }; diff --git a/src/parser/interpreter.rs b/src/parser/interpreter.rs new file mode 100644 index 0000000..b073eab --- /dev/null +++ b/src/parser/interpreter.rs @@ -0,0 +1,13 @@ +use crate::{ + parser::Tag, + views::{ColorRectView, ConstraintLayout, View}, +}; + +pub fn interpret_tag(tag: Tag) -> Box { + match tag.name.as_str() { + "ColoredRectView" => Box::new(ColorRectView::from_tag(&tag.attributes)), + "ConstraintLayout" => Box::new(ConstraintLayout::from_tag(&tag.attributes)), + + _ => panic!("Unknown tag: {}", tag.name), + } +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs new file mode 100644 index 0000000..100a2cd --- /dev/null +++ b/src/parser/mod.rs @@ -0,0 +1,20 @@ +use std::collections::HashMap; + +use crate::views::View; + +mod interpreter; +mod parser; + +pub const TEST_XML: &'static str = include_str!("../../pages/main.xml"); + +#[derive(Debug)] +struct Tag { + name: String, + children: Vec, + attributes: HashMap, +} + +pub fn parse(xml: &str) -> Box { + let tag = parser::parse_xml(xml); + interpreter::interpret_tag(tag) +} diff --git a/src/parser/parser.rs b/src/parser/parser.rs new file mode 100644 index 0000000..6006a3b --- /dev/null +++ b/src/parser/parser.rs @@ -0,0 +1,112 @@ +use std::collections::HashMap; + +use quick_xml::{ + Reader, + events::{BytesStart, Event}, +}; + +use crate::{log, parser::Tag}; + +pub fn parse_xml(xml: &str) -> Tag { + let mut reader = Reader::from_str(xml); + reader.config_mut().trim_text(true); + + // let mut count = 0; + // let mut txt = Vec::new(); + let mut buf = Vec::new(); + + let mut tag = None; + + loop { + let event = reader.read_event_into(&mut buf); + + match event { + Err(e) => panic!("Error at position {}: {:?}", reader.error_position(), e), + Ok(Event::Eof) => break, + Ok(Event::Start(e)) => { + tag = Some(parse_tag(e, &mut reader)); + } + Ok(Event::Empty(e)) => { + tag = Some(parse_tag_empty(e)); + } + Ok(Event::End(_)) => { + break; + } + _ => {} + }; + buf.clear(); + } + + if tag.is_none() { + log!("No root tag found"); + } + + tag.unwrap() +} + +fn parse_tag(event: BytesStart<'_>, reader: &mut Reader<&[u8]>) -> Tag { + let mut children = Vec::new(); + + let mut buf = Vec::new(); + + loop { + match reader.read_event_into(&mut buf) { + Err(e) => panic!("Error at position {}: {:?}", reader.error_position(), e), + // exits the loop when reaching end of file + Ok(Event::Eof) => panic!("Unexpected end of file"), + + Ok(Event::Start(e)) => { + children.push(parse_tag(e, reader)); + } + Ok(Event::Empty(e)) => { + children.push(parse_tag_empty(e)); + } + Ok(Event::End(e)) => { + println!("End: {:?}", e); + break; + } + _ => (), + } + buf.clear(); + } + + let name = String::from_utf8_lossy(event.name().as_ref()).to_string(); + + let attributes = event + .attributes() + .map(|a| { + let a = a.unwrap(); + ( + String::from_utf8_lossy(a.key.as_ref()).to_string(), + String::from_utf8_lossy(a.value.as_ref()).to_string(), + ) + }) + .collect::>(); + + Tag { + name, + children, + attributes, + } +} + +fn parse_tag_empty(event: BytesStart<'_>) -> Tag { + let name = String::from_utf8_lossy(event.name().as_ref()).to_string(); + + let attributes = event + .attributes() + .map(|a| { + let a = a.unwrap(); + ( + String::from_utf8_lossy(a.key.as_ref()).to_string(), + String::from_utf8_lossy(a.value.as_ref()).to_string(), + ) + }) + .collect::>(); + + Tag { + name, + children: Vec::new(), + attributes, + } +} diff --git a/src/render/buffer.rs b/src/render/buffer.rs index f75c8e7..dff9cd5 100644 --- a/src/render/buffer.rs +++ b/src/render/buffer.rs @@ -1,4 +1,4 @@ -// use crate::render::rand; +use crate::render::rand; pub struct ImgBuffer { pub data: Vec, @@ -15,14 +15,14 @@ impl ImgBuffer { } } - // pub fn randomize(&mut self, rand: &mut rand::Rnd) { - // for pixel in self.data.chunks_mut(4) { - // pixel[0] = rand.next_i32() as u8; - // pixel[1] = pixel[0]; - // pixel[2] = pixel[0]; - // pixel[3] = 255; - // } - // } + pub fn randomize(&mut self, rand: &mut rand::Rnd) { + for pixel in self.data.chunks_mut(4) { + pixel[0] = rand.next_i32() as u8; + pixel[1] = pixel[0]; + pixel[2] = pixel[0]; + pixel[3] = 255; + } + } pub fn overlay_bitmap(&mut self, other: &Bitmap, xoffset: usize, yoffset: usize) { let length = self.data.len(); diff --git a/src/views/color_rect_view.rs b/src/views/color_rect_view.rs index b3de4ec..54e9344 100644 --- a/src/views/color_rect_view.rs +++ b/src/views/color_rect_view.rs @@ -45,7 +45,30 @@ impl View for ColorRectView { // log!("Draw"); } - fn resize(&mut self, x: f32, y: f32, w: f32, h: f32) {} + fn resize(&mut self, _x: f32, _y: f32, _w: f32, _h: f32) {} + + fn from_tag(attributes: &std::collections::HashMap) -> Self + where + Self: Sized, + { + let r = attributes + .get("r") + .unwrap_or(&"0".to_string()) + .parse() + .unwrap(); + let g = attributes + .get("g") + .unwrap_or(&"0".to_string()) + .parse() + .unwrap(); + let b = attributes + .get("b") + .unwrap_or(&"0".to_string()) + .parse() + .unwrap(); + + Self::new(r, g, b) + } } impl LayoutView for ColorRectView { diff --git a/src/views/constraint_layout.rs b/src/views/constraint_layout.rs index 44d8213..903016d 100644 --- a/src/views/constraint_layout.rs +++ b/src/views/constraint_layout.rs @@ -96,46 +96,55 @@ impl ConstraintLayout { crate::views::Bounds::Pixels(x) => x, }; - let y_align = |constraint: &Option| match constraint { - Some(Constraint::TopParent(margin)) => Some(self_top + margin), - Some(Constraint::BottomParent(margin)) => Some(self_bottom - margin), - Some(Constraint::Top(margin, id)) => { - let other = calculated_positions.get(&id).unwrap(); - Some(other.y - margin) - } + let y_align = |constraint: &Option, margin_reverse: bool| { + let margin_mult = if margin_reverse { -1. } else { 1. }; + match constraint { + Some(Constraint::TopParent(margin)) => Some(self_top + margin * margin_mult), + Some(Constraint::BottomParent(margin)) => { + Some(self_bottom + margin * margin_mult) + } + Some(Constraint::Top(margin, id)) => { + let other = calculated_positions.get(&id).unwrap(); + Some(other.y + margin * margin_mult) + } - Some(Constraint::Bottom(margin, id)) => { - let other = calculated_positions.get(&id).unwrap(); - Some(other.y + other.height + margin) + Some(Constraint::Bottom(margin, id)) => { + let other = calculated_positions.get(&id).unwrap(); + Some(other.y + other.height + margin * margin_mult) + } + None => None, + _ => unreachable!(), } - None => None, - _ => unreachable!(), }; - let y = match (y_align(c_top), y_align(c_bottom)) { + let y = match (y_align(c_top, false), y_align(c_bottom, true)) { (Some(top), Some(bottom)) => (bottom + top - c_height) / 2., (Some(top), None) => top, (None, Some(bottom)) => bottom - c_height, (None, None) => unreachable!("Vertically unconstrained"), }; - let x_align = |constraint: &Option| match constraint { - Some(Constraint::LeftParent(margin)) => Some(self_left + margin), - Some(Constraint::RightParent(margin)) => Some(self_right - margin), - Some(Constraint::Left(margin, id)) => { - let other = calculated_positions.get(&id).unwrap(); - Some(other.x - margin) + let x_align = |constraint: &Option, margin_reverse: bool| { + let margin_mult = if margin_reverse { -1. } else { 1. }; + match constraint { + Some(Constraint::LeftParent(margin)) => Some(self_left + margin * margin_mult), + Some(Constraint::RightParent(margin)) => { + Some(self_right + margin * margin_mult) + } + Some(Constraint::Left(margin, id)) => { + let other = calculated_positions.get(&id).unwrap(); + Some(other.x + margin * margin_mult) + } + Some(Constraint::Right(margin, id)) => { + let other = calculated_positions.get(&id).unwrap(); + Some(other.x + other.width + margin * margin_mult) + } + None => None, + _ => unreachable!(), } - - Some(Constraint::Right(margin, id)) => { - let other = calculated_positions.get(&id).unwrap(); - Some(other.x + other.width - margin) - } - None => None, - _ => unreachable!(), }; - let x = match (x_align(c_left), x_align(c_right)) { + let x = match (x_align(c_left, false), x_align(c_right, true)) { (Some(left), Some(right)) => (right + left - c_width) / 2., (Some(left), None) => left, (None, Some(right)) => right - c_width, @@ -170,6 +179,13 @@ impl View for ConstraintLayout { fn resize(&mut self, x: f32, y: f32, w: f32, h: f32) { self.recalculate_positions(x, y, w, h); } + + fn from_tag(attributes: &std::collections::HashMap) -> Self + where + Self: Sized, + { + todo!() + } } #[derive(Clone, Copy, PartialEq)] diff --git a/src/views/mod.rs b/src/views/mod.rs index d1c2ab4..f04d57e 100644 --- a/src/views/mod.rs +++ b/src/views/mod.rs @@ -1,6 +1,8 @@ +use std::collections::HashMap; + use crate::{ render::Renderer, - views::constraint_layout::{Constraint, ConstraintLayout, ConstraintView}, + views::constraint_layout::{Constraint, ConstraintView}, }; mod color_rect_view; @@ -8,13 +10,18 @@ mod constraint_layout; mod text_view; mod vertical_layout; -use color_rect_view::ColorRectView; -use text_view::TextView; -use vertical_layout::VerticalLayout; +pub use color_rect_view::ColorRectView; +pub use constraint_layout::ConstraintLayout; +pub use text_view::TextView; +pub use vertical_layout::VerticalLayout; pub trait View { fn draw(&mut self, renderer: &mut Renderer, x: f32, y: f32, w: f32, h: f32); fn resize(&mut self, x: f32, y: f32, w: f32, h: f32); + + fn from_tag(attributes: &HashMap) -> Self + where + Self: Sized; } #[derive(Debug, Clone, Copy, PartialEq)] @@ -82,16 +89,30 @@ pub fn default_view() -> Box { Some(Constraint::BottomParent(temp_margin)), ); - center.set_bounds(Some(Bounds::Pixels(350.)), Some(Bounds::Pixels(100.))); + center.set_bounds(Some(Bounds::Pixels(350.)), Some(Bounds::Pixels(350.))); let mut center_top = ColorRectView::new(127, 0, 127); center_top.set_constraints( - None, + Some(Constraint::Bottom(0., 4)), + // None, // Some(Constraint::Left(0., 4)), None, - Some(Constraint::Right(0., 4)), - Some(Constraint::Top(20., 4)), + // None, + Some(Constraint::Left(10., 4)), + Some(Constraint::Top(0., 4)), + ); + + let mut inside_top = ColorRectView::new(127, 0, 127); + + inside_top.set_constraints( + Some(Constraint::Bottom(0., 4)), + // None, + // Some(Constraint::Left(0., 4)), + None, + // None, + Some(Constraint::Right(10., 4)), + Some(Constraint::Top(0., 4)), ); Box::new(ConstraintLayout::new(vec![ @@ -101,6 +122,7 @@ pub fn default_view() -> Box { Box::new(d), Box::new(center), Box::new(center_top), + Box::new(inside_top), ])) // Box::new(VerticalLayout::new(vec![ diff --git a/src/views/text_view.rs b/src/views/text_view.rs index de439fe..d872259 100644 --- a/src/views/text_view.rs +++ b/src/views/text_view.rs @@ -78,6 +78,13 @@ impl View for TextView { fn resize(&mut self, x: f32, y: f32, w: f32, h: f32) { // todo!() } + + fn from_tag(attributes: &std::collections::HashMap) -> Self + where + Self: Sized, + { + todo!() + } } impl LayoutView for TextView { diff --git a/src/views/vertical_layout.rs b/src/views/vertical_layout.rs index 5cb1bd4..e788404 100644 --- a/src/views/vertical_layout.rs +++ b/src/views/vertical_layout.rs @@ -42,6 +42,12 @@ impl View for VerticalLayout { } } fn resize(&mut self, x: f32, y: f32, w: f32, h: f32) {} + fn from_tag(attributes: &std::collections::HashMap) -> Self + where + Self: Sized, + { + todo!() + } } impl LayoutView for VerticalLayout {