mirror of
https://github.com/Astatin3/IntroToWebAuthoring.git
synced 2026-06-09 00:28:00 -06:00
Add XML Parsing
This commit is contained in:
@@ -9,6 +9,7 @@ crate-type = ["cdylib"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
fontdue = "0.9.3"
|
fontdue = "0.9.3"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
|
quick-xml = "0.38.3"
|
||||||
wasm-bindgen = {version = "0.2.104"}
|
wasm-bindgen = {version = "0.2.104"}
|
||||||
web-sys = {version = "0.3.81", features = ['CanvasRenderingContext2d', 'Document', 'Element', 'HtmlCanvasElement', 'Window', 'ImageData']}
|
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 wasm_bindgen::prelude::wasm_bindgen;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
parser::{self, TEST_XML},
|
||||||
render::Renderer,
|
render::Renderer,
|
||||||
views::{View, default_view},
|
views::{View, default_view},
|
||||||
};
|
};
|
||||||
@@ -36,8 +37,10 @@ impl App {
|
|||||||
pub fn new(renderer: Renderer) -> Self {
|
pub fn new(renderer: Renderer) -> Self {
|
||||||
let (width, height) = (renderer.actual_width, renderer.actual_height);
|
let (width, height) = (renderer.actual_width, renderer.actual_height);
|
||||||
|
|
||||||
|
let root_view = parser::parse(TEST_XML);
|
||||||
|
|
||||||
let mut this = App {
|
let mut this = App {
|
||||||
root_view: Some(default_view()),
|
root_view: Some(root_view),
|
||||||
renderer,
|
renderer,
|
||||||
// current_activity: Some(0),
|
// current_activity: Some(0),
|
||||||
state: AppState::new(),
|
state: AppState::new(),
|
||||||
@@ -49,6 +52,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&mut self) {
|
pub fn draw(&mut self) {
|
||||||
|
// self.renderer.img.randomize(&mut self.renderer.rand);
|
||||||
if let Some(view) = &mut self.root_view {
|
if let Some(view) = &mut self.root_view {
|
||||||
let (width, height) = (
|
let (width, height) = (
|
||||||
self.renderer.actual_width.clone() as f32,
|
self.renderer.actual_width.clone() as f32,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
mod app;
|
mod app;
|
||||||
mod fonts;
|
mod fonts;
|
||||||
|
mod parser;
|
||||||
mod render;
|
mod render;
|
||||||
mod views;
|
mod views;
|
||||||
|
|
||||||
@@ -12,6 +13,7 @@ use render::rand::Rnd;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{App, Cursor, set_cursor},
|
app::{App, Cursor, set_cursor},
|
||||||
|
parser::TEST_XML,
|
||||||
render::Renderer,
|
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 struct ImgBuffer {
|
||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
@@ -15,14 +15,14 @@ impl ImgBuffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn randomize(&mut self, rand: &mut rand::Rnd) {
|
pub fn randomize(&mut self, rand: &mut rand::Rnd) {
|
||||||
// for pixel in self.data.chunks_mut(4) {
|
for pixel in self.data.chunks_mut(4) {
|
||||||
// pixel[0] = rand.next_i32() as u8;
|
pixel[0] = rand.next_i32() as u8;
|
||||||
// pixel[1] = pixel[0];
|
pixel[1] = pixel[0];
|
||||||
// pixel[2] = pixel[0];
|
pixel[2] = pixel[0];
|
||||||
// pixel[3] = 255;
|
pixel[3] = 255;
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
pub fn overlay_bitmap(&mut self, other: &Bitmap, xoffset: usize, yoffset: usize) {
|
pub fn overlay_bitmap(&mut self, other: &Bitmap, xoffset: usize, yoffset: usize) {
|
||||||
let length = self.data.len();
|
let length = self.data.len();
|
||||||
|
|||||||
@@ -45,7 +45,30 @@ impl View for ColorRectView {
|
|||||||
// log!("Draw");
|
// 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 {
|
impl LayoutView for ColorRectView {
|
||||||
|
|||||||
@@ -96,46 +96,55 @@ impl ConstraintLayout {
|
|||||||
crate::views::Bounds::Pixels(x) => x,
|
crate::views::Bounds::Pixels(x) => x,
|
||||||
};
|
};
|
||||||
|
|
||||||
let y_align = |constraint: &Option<Constraint>| match constraint {
|
let y_align = |constraint: &Option<Constraint>, margin_reverse: bool| {
|
||||||
Some(Constraint::TopParent(margin)) => Some(self_top + margin),
|
let margin_mult = if margin_reverse { -1. } else { 1. };
|
||||||
Some(Constraint::BottomParent(margin)) => Some(self_bottom - margin),
|
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)) => {
|
Some(Constraint::Top(margin, id)) => {
|
||||||
let other = calculated_positions.get(&id).unwrap();
|
let other = calculated_positions.get(&id).unwrap();
|
||||||
Some(other.y - margin)
|
Some(other.y + margin * margin_mult)
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Constraint::Bottom(margin, id)) => {
|
Some(Constraint::Bottom(margin, id)) => {
|
||||||
let other = calculated_positions.get(&id).unwrap();
|
let other = calculated_positions.get(&id).unwrap();
|
||||||
Some(other.y + other.height + margin)
|
Some(other.y + other.height + margin * margin_mult)
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
_ => unreachable!(),
|
_ => 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), Some(bottom)) => (bottom + top - c_height) / 2.,
|
||||||
(Some(top), None) => top,
|
(Some(top), None) => top,
|
||||||
(None, Some(bottom)) => bottom - c_height,
|
(None, Some(bottom)) => bottom - c_height,
|
||||||
(None, None) => unreachable!("Vertically unconstrained"),
|
(None, None) => unreachable!("Vertically unconstrained"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let x_align = |constraint: &Option<Constraint>| match constraint {
|
let x_align = |constraint: &Option<Constraint>, margin_reverse: bool| {
|
||||||
Some(Constraint::LeftParent(margin)) => Some(self_left + margin),
|
let margin_mult = if margin_reverse { -1. } else { 1. };
|
||||||
Some(Constraint::RightParent(margin)) => Some(self_right - margin),
|
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)) => {
|
Some(Constraint::Left(margin, id)) => {
|
||||||
let other = calculated_positions.get(&id).unwrap();
|
let other = calculated_positions.get(&id).unwrap();
|
||||||
Some(other.x - margin)
|
Some(other.x + margin * margin_mult)
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Constraint::Right(margin, id)) => {
|
Some(Constraint::Right(margin, id)) => {
|
||||||
let other = calculated_positions.get(&id).unwrap();
|
let other = calculated_positions.get(&id).unwrap();
|
||||||
Some(other.x + other.width - margin)
|
Some(other.x + other.width + margin * margin_mult)
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
_ => unreachable!(),
|
_ => 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), Some(right)) => (right + left - c_width) / 2.,
|
||||||
(Some(left), None) => left,
|
(Some(left), None) => left,
|
||||||
(None, Some(right)) => right - c_width,
|
(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) {
|
fn resize(&mut self, x: f32, y: f32, w: f32, h: f32) {
|
||||||
self.recalculate_positions(x, y, w, h);
|
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)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
|||||||
+30
-8
@@ -1,6 +1,8 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
render::Renderer,
|
render::Renderer,
|
||||||
views::constraint_layout::{Constraint, ConstraintLayout, ConstraintView},
|
views::constraint_layout::{Constraint, ConstraintView},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod color_rect_view;
|
mod color_rect_view;
|
||||||
@@ -8,13 +10,18 @@ mod constraint_layout;
|
|||||||
mod text_view;
|
mod text_view;
|
||||||
mod vertical_layout;
|
mod vertical_layout;
|
||||||
|
|
||||||
use color_rect_view::ColorRectView;
|
pub use color_rect_view::ColorRectView;
|
||||||
use text_view::TextView;
|
pub use constraint_layout::ConstraintLayout;
|
||||||
use vertical_layout::VerticalLayout;
|
pub use text_view::TextView;
|
||||||
|
pub use vertical_layout::VerticalLayout;
|
||||||
|
|
||||||
pub trait View {
|
pub trait View {
|
||||||
fn draw(&mut self, renderer: &mut Renderer, x: f32, y: f32, w: f32, h: f32);
|
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 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)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
@@ -82,16 +89,30 @@ pub fn default_view() -> Box<dyn View> {
|
|||||||
Some(Constraint::BottomParent(temp_margin)),
|
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);
|
let mut center_top = ColorRectView::new(127, 0, 127);
|
||||||
|
|
||||||
center_top.set_constraints(
|
center_top.set_constraints(
|
||||||
None,
|
Some(Constraint::Bottom(0., 4)),
|
||||||
|
// None,
|
||||||
// Some(Constraint::Left(0., 4)),
|
// Some(Constraint::Left(0., 4)),
|
||||||
None,
|
None,
|
||||||
Some(Constraint::Right(0., 4)),
|
// None,
|
||||||
Some(Constraint::Top(20., 4)),
|
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![
|
Box::new(ConstraintLayout::new(vec![
|
||||||
@@ -101,6 +122,7 @@ pub fn default_view() -> Box<dyn View> {
|
|||||||
Box::new(d),
|
Box::new(d),
|
||||||
Box::new(center),
|
Box::new(center),
|
||||||
Box::new(center_top),
|
Box::new(center_top),
|
||||||
|
Box::new(inside_top),
|
||||||
]))
|
]))
|
||||||
|
|
||||||
// Box::new(VerticalLayout::new(vec![
|
// 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) {
|
fn resize(&mut self, x: f32, y: f32, w: f32, h: f32) {
|
||||||
// todo!()
|
// todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn from_tag(attributes: &std::collections::HashMap<String, String>) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutView for TextView {
|
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 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 {
|
impl LayoutView for VerticalLayout {
|
||||||
|
|||||||
Reference in New Issue
Block a user