mirror of
https://github.com/Astatin3/IntroToWebAuthoring.git
synced 2026-06-08 16:18:01 -06:00
Add XML Parsing
This commit is contained in:
@@ -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']}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
<ColoredRectView r="20" g="40" b="60" />
|
||||
<!-- </ColoredRectView> -->
|
||||
+5
-1
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
use crate::{
|
||||
parser::Tag,
|
||||
views::{ColorRectView, ConstraintLayout, View},
|
||||
};
|
||||
|
||||
pub fn interpret_tag(tag: Tag) -> Box<dyn View> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
@@ -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<Tag>,
|
||||
attributes: HashMap<String, String>,
|
||||
}
|
||||
|
||||
pub fn parse(xml: &str) -> Box<dyn View> {
|
||||
let tag = parser::parse_xml(xml);
|
||||
interpreter::interpret_tag(tag)
|
||||
}
|
||||
@@ -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::<HashMap<_, _>>();
|
||||
|
||||
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::<HashMap<_, _>>();
|
||||
|
||||
Tag {
|
||||
name,
|
||||
children: Vec::new(),
|
||||
attributes,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// use crate::render::rand;
|
||||
use crate::render::rand;
|
||||
|
||||
pub struct ImgBuffer {
|
||||
pub data: Vec<u8>,
|
||||
@@ -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();
|
||||
|
||||
@@ -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<String, String>) -> 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 {
|
||||
|
||||
@@ -96,46 +96,55 @@ impl ConstraintLayout {
|
||||
crate::views::Bounds::Pixels(x) => x,
|
||||
};
|
||||
|
||||
let y_align = |constraint: &Option<Constraint>| 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<Constraint>, 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<Constraint>| 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<Constraint>, 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<String, String>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
|
||||
+30
-8
@@ -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<String, String>) -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
@@ -82,16 +89,30 @@ pub fn default_view() -> Box<dyn View> {
|
||||
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<dyn View> {
|
||||
Box::new(d),
|
||||
Box::new(center),
|
||||
Box::new(center_top),
|
||||
Box::new(inside_top),
|
||||
]))
|
||||
|
||||
// Box::new(VerticalLayout::new(vec![
|
||||
|
||||
@@ -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<String, String>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutView for TextView {
|
||||
|
||||
@@ -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<String, String>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutView for VerticalLayout {
|
||||
|
||||
Reference in New Issue
Block a user