mirror of
https://github.com/Astatin3/IntroToWebAuthoring.git
synced 2026-06-09 00:28:00 -06:00
Get constraint layout work with xml
This commit is contained in:
+28
-2
@@ -1,2 +1,28 @@
|
||||
<ColoredRectView r="20" g="40" b="60" />
|
||||
<!-- </ColoredRectView> -->
|
||||
<ConstraintLayout>
|
||||
<BoxView margin="10" align_top_to_top="parent" align_left_to_left="parent">
|
||||
<ColoredRectView r="123" g="12" b="34" />
|
||||
</BoxView>
|
||||
|
||||
<BoxView
|
||||
margin_left="10"
|
||||
margin_right="10"
|
||||
align_top_to_top="parent"
|
||||
align_left_to_left="parent"
|
||||
align_bottom_to_bottom="parent"
|
||||
align_right_to_right="parent"
|
||||
width="400"
|
||||
height="parent"
|
||||
>
|
||||
<ColoredRectView r="30" g="30" b="30" />
|
||||
</BoxView>
|
||||
|
||||
<BoxView
|
||||
margin="10"
|
||||
align_top_to_top="parent"
|
||||
align_left_to_right="2"
|
||||
align_bottom_to_bottom="parent"
|
||||
width="200"
|
||||
>
|
||||
<ColoredRectView r="123" g="125" b="34" />
|
||||
</BoxView>
|
||||
</ConstraintLayout>
|
||||
|
||||
+3
-1
@@ -5,9 +5,10 @@ pub use cursors::{Cursor, set_cursor};
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
use crate::{
|
||||
log,
|
||||
parser::{self, TEST_XML},
|
||||
render::Renderer,
|
||||
views::{View, default_view},
|
||||
views::View,
|
||||
};
|
||||
|
||||
#[wasm_bindgen]
|
||||
@@ -38,6 +39,7 @@ impl App {
|
||||
let (width, height) = (renderer.actual_width, renderer.actual_height);
|
||||
|
||||
let root_view = parser::parse(TEST_XML);
|
||||
log!("root: {:?}", root_view);
|
||||
|
||||
let mut this = App {
|
||||
root_view: Some(root_view),
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
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),
|
||||
}
|
||||
}
|
||||
+51
-8
@@ -1,20 +1,63 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::views::View;
|
||||
use crate::{
|
||||
log,
|
||||
views::{Bounds, BoxView, ColorRectView, ConstraintLayout, 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 struct Tag {
|
||||
pub name: String,
|
||||
pub children: Vec<Tag>,
|
||||
pub attributes: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Tag {
|
||||
pub fn parse(&self) -> Box<dyn View> {
|
||||
match self.name.as_str() {
|
||||
"ColoredRectView" => ColorRectView::from_tag(&self.attributes, &self.children),
|
||||
"ConstraintLayout" => ConstraintLayout::from_tag(&self.attributes, &self.children),
|
||||
"BoxView" => BoxView::from_tag(&self.attributes, &self.children),
|
||||
|
||||
_ => panic!("Unknown tag: {}", self.name),
|
||||
}
|
||||
}
|
||||
pub fn parse_bounds(attributes: &HashMap<String, String>) -> (Option<Bounds>, Option<Bounds>) {
|
||||
let (mut width, mut height) = (None, None);
|
||||
|
||||
let parse_bound = |str: &str| -> Bounds {
|
||||
match str {
|
||||
"parent" => Bounds::MatchParent,
|
||||
_ => {
|
||||
let bound = if let Ok(int) = str.parse::<i32>() {
|
||||
int as f32
|
||||
} else if let Ok(float) = str.parse::<f32>() {
|
||||
float
|
||||
} else {
|
||||
panic!("Invalid String");
|
||||
};
|
||||
|
||||
Bounds::Pixels(bound)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (key, value) in attributes {
|
||||
match key.as_str() {
|
||||
"width" => width = Some(parse_bound(value)),
|
||||
"height" => height = Some(parse_bound(value)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
(width, height)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(xml: &str) -> Box<dyn View> {
|
||||
let tag = parser::parse_xml(xml);
|
||||
interpreter::interpret_tag(tag)
|
||||
parser::parse_xml(xml).parse()
|
||||
}
|
||||
|
||||
@@ -51,9 +51,15 @@ fn parse_tag(event: BytesStart<'_>, reader: &mut Reader<&[u8]>) -> Tag {
|
||||
|
||||
loop {
|
||||
match reader.read_event_into(&mut buf) {
|
||||
Err(e) => panic!("Error at position {}: {:?}", reader.error_position(), e),
|
||||
Err(e) => {
|
||||
log!("Error at position {}: {:?}", reader.error_position(), e);
|
||||
panic!()
|
||||
}
|
||||
// exits the loop when reaching end of file
|
||||
Ok(Event::Eof) => panic!("Unexpected end of file"),
|
||||
Ok(Event::Eof) => {
|
||||
log!("Unexpected end of file");
|
||||
panic!()
|
||||
}
|
||||
|
||||
Ok(Event::Start(e)) => {
|
||||
children.push(parse_tag(e, reader));
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
use fontdue::layout::{CoordinateSystem, Layout, LayoutSettings, TextStyle};
|
||||
use wasm_bindgen::{Clamped, prelude::*};
|
||||
use web_sys::ImageData;
|
||||
|
||||
use crate::render::{
|
||||
RESOLUTION,
|
||||
buffer::{Bitmap, ImgBuffer},
|
||||
calc_resolution,
|
||||
rand::Rnd,
|
||||
};
|
||||
use crate::render::{RESOLUTION, buffer::ImgBuffer, calc_resolution, rand::Rnd};
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct Renderer {
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
use std::any::TypeId;
|
||||
|
||||
use crate::{
|
||||
log,
|
||||
parser::Tag,
|
||||
render::Renderer,
|
||||
views::{Bounds, Constraint, View},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BoxView {
|
||||
view: Box<dyn View>,
|
||||
bounds: (Option<Bounds>, Option<Bounds>),
|
||||
constraints: (
|
||||
Option<Constraint>,
|
||||
Option<Constraint>,
|
||||
Option<Constraint>,
|
||||
Option<Constraint>,
|
||||
),
|
||||
}
|
||||
|
||||
impl BoxView {
|
||||
pub fn new(view: Box<dyn View>) -> Self {
|
||||
Self {
|
||||
view,
|
||||
bounds: (None, None),
|
||||
constraints: (None, None, None, None),
|
||||
}
|
||||
}
|
||||
pub fn set_bounds(&mut self, width: Option<Bounds>, height: Option<Bounds>) {
|
||||
self.bounds = (width, height);
|
||||
}
|
||||
pub fn set_constraints(
|
||||
&mut self,
|
||||
top: Option<Constraint>,
|
||||
left: Option<Constraint>,
|
||||
right: Option<Constraint>,
|
||||
bottom: Option<Constraint>,
|
||||
) {
|
||||
self.constraints = (top, left, right, bottom)
|
||||
}
|
||||
pub fn bounds(&self, _ph: f32, _pw: f32) -> (Bounds, Bounds) {
|
||||
(
|
||||
*self.bounds.0.as_ref().unwrap_or(&Bounds::Pixels(100.)),
|
||||
*self.bounds.1.as_ref().unwrap_or(&Bounds::Pixels(100.)),
|
||||
)
|
||||
}
|
||||
pub fn get_constraints(
|
||||
&self,
|
||||
) -> (
|
||||
&Option<Constraint>,
|
||||
&Option<Constraint>,
|
||||
&Option<Constraint>,
|
||||
&Option<Constraint>,
|
||||
) {
|
||||
(
|
||||
&self.constraints.0,
|
||||
&self.constraints.1,
|
||||
&self.constraints.2,
|
||||
&self.constraints.3,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl View for BoxView {
|
||||
fn draw(&mut self, renderer: &mut Renderer, x: f32, y: f32, w: f32, h: f32) {
|
||||
self.view.draw(renderer, x, y, w, h);
|
||||
}
|
||||
|
||||
fn resize(&mut self, x: f32, y: f32, w: f32, h: f32) {
|
||||
self.view.resize(x, y, w, h);
|
||||
}
|
||||
|
||||
fn from_tag(
|
||||
attributes: &std::collections::HashMap<String, String>,
|
||||
children: &Vec<Tag>,
|
||||
) -> Box<dyn View> {
|
||||
assert!(children.len() == 1, "BoxView must have exactly one child");
|
||||
|
||||
let (mut margin_top, mut margin_left, mut margin_right, mut margin_bottom) =
|
||||
(None, None, None, None);
|
||||
|
||||
let (mut align_top, mut align_left, mut align_right, mut align_bottom) =
|
||||
(None, None, None, None);
|
||||
|
||||
let parse_float = |var: &mut Option<f32>, value: &str| {
|
||||
if let Ok(float) = value.parse::<f32>() {
|
||||
*var = Some(float);
|
||||
} else if let Ok(int) = value.parse::<i32>() {
|
||||
let float = int as f32;
|
||||
*var = Some(float);
|
||||
} else {
|
||||
panic!("Could not parse margin!")
|
||||
}
|
||||
};
|
||||
|
||||
let parse_align_refrence = |value: &String| -> usize {
|
||||
match value.as_str() {
|
||||
"parent" => 0,
|
||||
_ => {
|
||||
if let Ok(parent) = value.parse::<usize>() {
|
||||
if parent >= children.len() {
|
||||
// panic!("Child {} does not exist!", parent);
|
||||
}
|
||||
parent
|
||||
} else {
|
||||
panic!("Could not parse refrence");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (key, value) in attributes {
|
||||
match key.as_str() {
|
||||
// Set margin on all sides
|
||||
"margin" => {
|
||||
if let Ok(float) = value.parse::<f32>() {
|
||||
(margin_top, margin_left, margin_right, margin_bottom) =
|
||||
(Some(float), Some(float), Some(float), Some(float));
|
||||
} else if let Ok(int) = value.parse::<i32>() {
|
||||
let float = int as f32;
|
||||
(margin_top, margin_left, margin_right, margin_bottom) =
|
||||
(Some(float), Some(float), Some(float), Some(float));
|
||||
} else {
|
||||
panic!("Could not parse margin!")
|
||||
}
|
||||
}
|
||||
|
||||
// Margin for specific directions
|
||||
"margin_top" => parse_float(&mut margin_top, value),
|
||||
"margin_left" => parse_float(&mut margin_left, value),
|
||||
"margin_right" => parse_float(&mut margin_right, value),
|
||||
"margin_bottom" => parse_float(&mut margin_bottom, value),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
for (key, value) in attributes {
|
||||
match key.as_str() {
|
||||
// Alignment
|
||||
"align_top_to_top" => {
|
||||
align_top = Some(match parse_align_refrence(value) {
|
||||
0 => Constraint::TopParent(margin_top.unwrap_or(0.)),
|
||||
n => Constraint::Top(margin_top.unwrap_or(0.), n - 1),
|
||||
})
|
||||
}
|
||||
"align_top_to_bottom" => {
|
||||
align_top = Some(match parse_align_refrence(value) {
|
||||
0 => Constraint::BottomParent(margin_top.unwrap_or(0.)),
|
||||
n => Constraint::Bottom(margin_top.unwrap_or(0.), n - 1),
|
||||
})
|
||||
}
|
||||
|
||||
"align_left_to_left" => {
|
||||
align_left = Some(match parse_align_refrence(value) {
|
||||
0 => Constraint::LeftParent(margin_left.unwrap_or(0.)),
|
||||
n => Constraint::Left(margin_left.unwrap_or(0.), n - 1),
|
||||
})
|
||||
}
|
||||
"align_left_to_right" => {
|
||||
align_left = Some(match parse_align_refrence(value) {
|
||||
0 => Constraint::RightParent(margin_left.unwrap_or(0.)),
|
||||
n => Constraint::Right(margin_left.unwrap_or(0.), n - 1),
|
||||
})
|
||||
}
|
||||
|
||||
"align_right_to_right" => {
|
||||
align_right = Some(match parse_align_refrence(value) {
|
||||
0 => Constraint::RightParent(margin_right.unwrap_or(0.)),
|
||||
n => Constraint::Right(margin_right.unwrap_or(0.), n - 1),
|
||||
})
|
||||
}
|
||||
"align_right_to_left" => {
|
||||
align_right = Some(match parse_align_refrence(value) {
|
||||
0 => Constraint::LeftParent(margin_right.unwrap_or(0.)),
|
||||
n => Constraint::Left(margin_right.unwrap_or(0.), n - 1),
|
||||
})
|
||||
}
|
||||
|
||||
"align_bottom_to_bottom" => {
|
||||
align_bottom = Some(match parse_align_refrence(value) {
|
||||
0 => Constraint::BottomParent(margin_bottom.unwrap_or(0.)),
|
||||
n => Constraint::Bottom(margin_bottom.unwrap_or(0.), n - 1),
|
||||
})
|
||||
}
|
||||
"align_bottom_to_top" => {
|
||||
align_bottom = Some(match parse_align_refrence(value) {
|
||||
0 => Constraint::TopParent(margin_bottom.unwrap_or(0.)),
|
||||
n => Constraint::Top(margin_bottom.unwrap_or(0.), n - 1),
|
||||
})
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut b = Self::new(children[0].parse());
|
||||
|
||||
b.constraints = (align_top, align_left, align_right, align_bottom);
|
||||
b.bounds = Tag::parse_bounds(attributes);
|
||||
|
||||
Box::new(b)
|
||||
}
|
||||
|
||||
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,17 @@
|
||||
use crate::{
|
||||
parser::Tag,
|
||||
render::Renderer,
|
||||
views::{
|
||||
Bounds, LayoutView, View,
|
||||
constraint_layout::{Constraint, ConstraintView},
|
||||
},
|
||||
views::{Bounds, Constraint, View},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ColorRectView {
|
||||
color: (u8, u8, u8),
|
||||
bounds: (Option<Bounds>, Option<Bounds>),
|
||||
constraints: (
|
||||
Option<Constraint>,
|
||||
Option<Constraint>,
|
||||
Option<Constraint>,
|
||||
Option<Constraint>,
|
||||
),
|
||||
}
|
||||
|
||||
impl ColorRectView {
|
||||
pub fn new(r: u8, g: u8, b: u8) -> Self {
|
||||
Self {
|
||||
color: (r, g, b),
|
||||
bounds: (None, None),
|
||||
constraints: (None, None, None, None),
|
||||
}
|
||||
}
|
||||
pub fn set_bounds(&mut self, width: Option<Bounds>, height: Option<Bounds>) {
|
||||
self.bounds = (width, height);
|
||||
}
|
||||
pub fn set_constraints(
|
||||
&mut self,
|
||||
top: Option<Constraint>,
|
||||
left: Option<Constraint>,
|
||||
right: Option<Constraint>,
|
||||
bottom: Option<Constraint>,
|
||||
) {
|
||||
self.constraints = (top, left, right, bottom)
|
||||
Self { color: (r, g, b) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,10 +23,18 @@ impl View for ColorRectView {
|
||||
|
||||
fn resize(&mut self, _x: f32, _y: f32, _w: f32, _h: f32) {}
|
||||
|
||||
fn from_tag(attributes: &std::collections::HashMap<String, String>) -> Self
|
||||
fn from_tag(
|
||||
attributes: &std::collections::HashMap<String, String>,
|
||||
children: &Vec<Tag>,
|
||||
) -> Box<dyn View>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
assert!(
|
||||
children.is_empty(),
|
||||
"ColorRectView does not support children"
|
||||
);
|
||||
|
||||
let r = attributes
|
||||
.get("r")
|
||||
.unwrap_or(&"0".to_string())
|
||||
@@ -67,33 +51,10 @@ impl View for ColorRectView {
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
Self::new(r, g, b)
|
||||
Box::new(Self::new(r, g, b))
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutView for ColorRectView {
|
||||
fn bounds(&self, _ph: f32, _pw: f32) -> (Bounds, Bounds) {
|
||||
(
|
||||
*self.bounds.0.as_ref().unwrap_or(&Bounds::Pixels(100.)),
|
||||
*self.bounds.1.as_ref().unwrap_or(&Bounds::Pixels(100.)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstraintView for ColorRectView {
|
||||
fn get_constraints(
|
||||
&self,
|
||||
) -> (
|
||||
&Option<Constraint>,
|
||||
&Option<Constraint>,
|
||||
&Option<Constraint>,
|
||||
&Option<Constraint>,
|
||||
) {
|
||||
(
|
||||
&self.constraints.0,
|
||||
&self.constraints.1,
|
||||
&self.constraints.2,
|
||||
&self.constraints.3,
|
||||
)
|
||||
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,23 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
render::Renderer,
|
||||
views::{LayoutView, View},
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
collections::HashMap,
|
||||
};
|
||||
|
||||
pub enum Constraint {
|
||||
Left(f32, usize),
|
||||
Right(f32, usize),
|
||||
Top(f32, usize),
|
||||
Bottom(f32, usize),
|
||||
|
||||
LeftParent(f32),
|
||||
RightParent(f32),
|
||||
TopParent(f32),
|
||||
BottomParent(f32),
|
||||
}
|
||||
|
||||
pub trait ConstraintView: LayoutView {
|
||||
fn get_constraints(
|
||||
&self,
|
||||
) -> (
|
||||
&Option<Constraint>,
|
||||
&Option<Constraint>,
|
||||
&Option<Constraint>,
|
||||
&Option<Constraint>,
|
||||
);
|
||||
}
|
||||
use crate::{
|
||||
log,
|
||||
parser::Tag,
|
||||
render::Renderer,
|
||||
views::{Constraint, View, box_view::BoxView},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConstraintLayout {
|
||||
pub children: Vec<Box<dyn ConstraintView>>,
|
||||
pub children: Vec<BoxView>,
|
||||
child_positions: Option<HashMap<usize, Rect>>,
|
||||
child_order: Option<Vec<usize>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Rect {
|
||||
x: f32,
|
||||
y: f32,
|
||||
@@ -41,7 +26,7 @@ struct Rect {
|
||||
}
|
||||
|
||||
impl ConstraintLayout {
|
||||
pub fn new(children: Vec<Box<dyn ConstraintView>>) -> Self {
|
||||
pub fn new(children: Vec<BoxView>) -> Self {
|
||||
for child in &children {
|
||||
let (top, left, right, bottom) = child.get_constraints();
|
||||
|
||||
@@ -55,10 +40,15 @@ impl ConstraintLayout {
|
||||
);
|
||||
}
|
||||
|
||||
ConstraintLayout {
|
||||
let mut this = ConstraintLayout {
|
||||
children,
|
||||
child_positions: None,
|
||||
}
|
||||
child_order: None,
|
||||
};
|
||||
|
||||
this.recalculate_child_render_order();
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
pub fn recalculate_positions(&mut self, x: f32, y: f32, self_w: f32, self_h: f32) {
|
||||
@@ -69,19 +59,11 @@ impl ConstraintLayout {
|
||||
|
||||
let mut calculated_positions: HashMap<usize, Rect> = HashMap::new();
|
||||
|
||||
let dependencies = self
|
||||
.children
|
||||
.iter()
|
||||
.map(|c| {
|
||||
let constraints = c.get_constraints();
|
||||
[constraints.0, constraints.1, constraints.2, constraints.3]
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let child_order = self.child_order.as_ref().unwrap();
|
||||
for idx in child_order {
|
||||
let child: &mut BoxView = &mut self.children[*idx];
|
||||
|
||||
let sorted = topological_sort(&dependencies);
|
||||
|
||||
for idx in sorted {
|
||||
let child = &mut self.children[idx];
|
||||
log!("{:?}", child.get_constraints());
|
||||
|
||||
let (c_top, c_left, c_right, c_bottom) = child.get_constraints();
|
||||
let (c_width, c_height) = child.bounds(self_w, self_h);
|
||||
@@ -152,7 +134,7 @@ impl ConstraintLayout {
|
||||
};
|
||||
|
||||
calculated_positions.insert(
|
||||
idx,
|
||||
*idx,
|
||||
Rect {
|
||||
x: x,
|
||||
y: y,
|
||||
@@ -164,6 +146,19 @@ impl ConstraintLayout {
|
||||
|
||||
self.child_positions = Some(calculated_positions);
|
||||
}
|
||||
|
||||
pub fn recalculate_child_render_order(&mut self) {
|
||||
let dependencies = self
|
||||
.children
|
||||
.iter()
|
||||
.map(|c| {
|
||||
let constraints = c.get_constraints();
|
||||
[constraints.0, constraints.1, constraints.2, constraints.3]
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
self.child_order = Some(topological_sort(&dependencies));
|
||||
}
|
||||
}
|
||||
|
||||
impl View for ConstraintLayout {
|
||||
@@ -180,11 +175,30 @@ impl View for ConstraintLayout {
|
||||
self.recalculate_positions(x, y, w, h);
|
||||
}
|
||||
|
||||
fn from_tag(attributes: &std::collections::HashMap<String, String>) -> Self
|
||||
fn from_tag(
|
||||
attributes: &std::collections::HashMap<String, String>,
|
||||
children: &Vec<Tag>,
|
||||
) -> Box<dyn View>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
todo!()
|
||||
let mut parsed_children = Vec::new();
|
||||
|
||||
for child in children {
|
||||
let child = child.parse();
|
||||
|
||||
if let Ok(b) = child.as_any().downcast::<BoxView>() {
|
||||
parsed_children.push(*b);
|
||||
} else {
|
||||
panic!("Constraint Layout must contain only BoxView!")
|
||||
}
|
||||
}
|
||||
|
||||
Box::new(Self::new(parsed_children))
|
||||
}
|
||||
|
||||
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,7 +233,8 @@ fn dfs(
|
||||
result: &mut Vec<usize>,
|
||||
) {
|
||||
if states[node] == VisitState::Visiting {
|
||||
panic!("Cycle detected in constraint graph at node {}", node);
|
||||
log!("Cycle detected in constraint graph at node {}", node);
|
||||
panic!();
|
||||
}
|
||||
|
||||
if states[node] == VisitState::Visited {
|
||||
|
||||
+40
-28
@@ -1,27 +1,30 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
render::Renderer,
|
||||
views::constraint_layout::{Constraint, ConstraintView},
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
collections::HashMap,
|
||||
fmt::Debug,
|
||||
};
|
||||
|
||||
use crate::{parser::Tag, render::Renderer};
|
||||
|
||||
mod box_view;
|
||||
mod color_rect_view;
|
||||
mod constraint_layout;
|
||||
mod text_view;
|
||||
mod vertical_layout;
|
||||
|
||||
pub use box_view::BoxView;
|
||||
pub use color_rect_view::ColorRectView;
|
||||
pub use constraint_layout::ConstraintLayout;
|
||||
pub use text_view::TextView;
|
||||
pub use vertical_layout::VerticalLayout;
|
||||
|
||||
pub trait View {
|
||||
pub trait View: Debug {
|
||||
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
|
||||
fn from_tag(attributes: &HashMap<String, String>, children: &Vec<Tag>) -> Box<dyn View>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn as_any(self: Box<Self>) -> Box<dyn Any>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
@@ -30,17 +33,32 @@ pub enum Bounds {
|
||||
Pixels(f32),
|
||||
}
|
||||
|
||||
pub trait LayoutView: View {
|
||||
// fn set_bounds(&mut self, width: Option<Bounds>, height: Option<Bounds>);
|
||||
fn bounds(&self, pw: f32, ph: f32) -> (Bounds, Bounds);
|
||||
#[derive(Debug)]
|
||||
pub enum Constraint {
|
||||
Left(f32, usize),
|
||||
Right(f32, usize),
|
||||
Top(f32, usize),
|
||||
Bottom(f32, usize),
|
||||
|
||||
LeftParent(f32),
|
||||
RightParent(f32),
|
||||
TopParent(f32),
|
||||
BottomParent(f32),
|
||||
}
|
||||
|
||||
// pub trait ViewPosition: Drawable {
|
||||
// // fn draw(&self)
|
||||
// pub trait ConstraintView: View {
|
||||
// fn get_constraints(
|
||||
// &self,
|
||||
// ) -> (
|
||||
// &Option<Constraint>,
|
||||
// &Option<Constraint>,
|
||||
// &Option<Constraint>,
|
||||
// &Option<Constraint>,
|
||||
// );
|
||||
// }
|
||||
|
||||
pub fn default_view() -> Box<dyn View> {
|
||||
let mut a = ColorRectView::new(127, 0, 0);
|
||||
let mut a = BoxView::new(Box::new(ColorRectView::new(127, 0, 0)));
|
||||
|
||||
let temp_margin = 10.0;
|
||||
|
||||
@@ -53,7 +71,7 @@ pub fn default_view() -> Box<dyn View> {
|
||||
);
|
||||
a.set_bounds(Some(Bounds::Pixels(200.)), Some(Bounds::Pixels(100.)));
|
||||
|
||||
let mut b = ColorRectView::new(0, 127, 0);
|
||||
let mut b = BoxView::new(Box::new(ColorRectView::new(0, 127, 0)));
|
||||
|
||||
b.set_constraints(
|
||||
Some(Constraint::TopParent(temp_margin)),
|
||||
@@ -62,7 +80,7 @@ pub fn default_view() -> Box<dyn View> {
|
||||
None,
|
||||
);
|
||||
|
||||
let mut c = ColorRectView::new(0, 0, 127);
|
||||
let mut c = BoxView::new(Box::new(ColorRectView::new(0, 0, 127)));
|
||||
|
||||
c.set_constraints(
|
||||
None,
|
||||
@@ -71,7 +89,7 @@ pub fn default_view() -> Box<dyn View> {
|
||||
Some(Constraint::BottomParent(temp_margin)),
|
||||
);
|
||||
|
||||
let mut d = ColorRectView::new(127, 127, 0);
|
||||
let mut d = BoxView::new(Box::new(ColorRectView::new(127, 127, 0)));
|
||||
|
||||
d.set_constraints(
|
||||
None,
|
||||
@@ -80,7 +98,7 @@ pub fn default_view() -> Box<dyn View> {
|
||||
Some(Constraint::BottomParent(temp_margin)),
|
||||
);
|
||||
|
||||
let mut center = ColorRectView::new(127, 127, 0);
|
||||
let mut center = BoxView::new(Box::new(ColorRectView::new(127, 127, 0)));
|
||||
|
||||
center.set_constraints(
|
||||
Some(Constraint::TopParent(temp_margin)),
|
||||
@@ -91,7 +109,7 @@ pub fn default_view() -> Box<dyn View> {
|
||||
|
||||
center.set_bounds(Some(Bounds::Pixels(350.)), Some(Bounds::Pixels(350.)));
|
||||
|
||||
let mut center_top = ColorRectView::new(127, 0, 127);
|
||||
let mut center_top = BoxView::new(Box::new(ColorRectView::new(127, 0, 127)));
|
||||
|
||||
center_top.set_constraints(
|
||||
Some(Constraint::Bottom(0., 4)),
|
||||
@@ -103,7 +121,7 @@ pub fn default_view() -> Box<dyn View> {
|
||||
Some(Constraint::Top(0., 4)),
|
||||
);
|
||||
|
||||
let mut inside_top = ColorRectView::new(127, 0, 127);
|
||||
let mut inside_top = BoxView::new(Box::new(ColorRectView::new(127, 0, 127)));
|
||||
|
||||
inside_top.set_constraints(
|
||||
Some(Constraint::Bottom(0., 4)),
|
||||
@@ -116,13 +134,7 @@ pub fn default_view() -> Box<dyn View> {
|
||||
);
|
||||
|
||||
Box::new(ConstraintLayout::new(vec![
|
||||
Box::new(a),
|
||||
Box::new(b),
|
||||
Box::new(c),
|
||||
Box::new(d),
|
||||
Box::new(center),
|
||||
Box::new(center_top),
|
||||
Box::new(inside_top),
|
||||
a, b, c, d, center, center_top, inside_top,
|
||||
]))
|
||||
|
||||
// Box::new(VerticalLayout::new(vec![
|
||||
|
||||
+34
-16
@@ -1,9 +1,12 @@
|
||||
use fontdue::layout::{CoordinateSystem, Layout, LayoutSettings, TextStyle};
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::{
|
||||
fonts::{FONTS, FontHandle},
|
||||
parser::Tag,
|
||||
render::{Renderer, buffer::Bitmap},
|
||||
views::{Bounds, LayoutView, View},
|
||||
views::{Bounds, View},
|
||||
};
|
||||
|
||||
pub struct TextView {
|
||||
@@ -11,7 +14,6 @@ pub struct TextView {
|
||||
text: String,
|
||||
scale: f32,
|
||||
font: FontHandle,
|
||||
bounds: (Option<Bounds>, Option<Bounds>),
|
||||
}
|
||||
|
||||
impl TextView {
|
||||
@@ -27,7 +29,6 @@ impl TextView {
|
||||
text,
|
||||
scale: 20.,
|
||||
font: FontHandle::AtiksonHyperlegibleRegular,
|
||||
bounds: (None, None),
|
||||
};
|
||||
|
||||
let font = (s.font as usize).clone();
|
||||
@@ -39,9 +40,6 @@ impl TextView {
|
||||
|
||||
s
|
||||
}
|
||||
pub fn set_bounds(&mut self, width: Option<Bounds>, height: Option<Bounds>) {
|
||||
self.bounds = (width, height);
|
||||
}
|
||||
}
|
||||
|
||||
impl View for TextView {
|
||||
@@ -79,24 +77,44 @@ impl View for TextView {
|
||||
// todo!()
|
||||
}
|
||||
|
||||
fn from_tag(attributes: &std::collections::HashMap<String, String>) -> Self
|
||||
fn from_tag(
|
||||
attributes: &std::collections::HashMap<String, String>,
|
||||
children: &Vec<Tag>,
|
||||
) -> Box<dyn View>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
assert!(children.is_empty(), "TextView does not support children");
|
||||
todo!()
|
||||
// Self::new(attributes)
|
||||
}
|
||||
|
||||
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutView for TextView {
|
||||
fn bounds(&self, _ph: f32, _pw: f32) -> (Bounds, Bounds) {
|
||||
// let (x, y) = renderer.undistort(x as f32, y as f32);
|
||||
// impl LayoutView for TextView {
|
||||
// fn bounds(&self, _ph: f32, _pw: f32) -> (Bounds, Bounds) {
|
||||
// // let (x, y) = renderer.undistort(x as f32, y as f32);
|
||||
|
||||
let (mut width, mut height): (usize, usize) = (0, 0);
|
||||
for glyph in self.layout.glyphs() {
|
||||
width = width.max(glyph.x as usize + glyph.width);
|
||||
height = height.max(glyph.y as usize + glyph.height);
|
||||
}
|
||||
// let (mut width, mut height): (usize, usize) = (0, 0);
|
||||
// for glyph in self.layout.glyphs() {
|
||||
// width = width.max(glyph.x as usize + glyph.width);
|
||||
// height = height.max(glyph.y as usize + glyph.height);
|
||||
// }
|
||||
|
||||
(Bounds::Pixels(width as f32), Bounds::Pixels(height as f32))
|
||||
// (Bounds::Pixels(width as f32), Bounds::Pixels(height as f32))
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Debug for TextView {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("TextView")
|
||||
// .field("layout", &self.layout)
|
||||
.field("text", &self.text)
|
||||
.field("scale", &self.scale)
|
||||
.field("font", &self.font)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
use crate::views::{Bounds, LayoutView, View};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
parser::Tag,
|
||||
views::{Bounds, View, box_view::BoxView},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VerticalLayout {
|
||||
pub views: Vec<Box<dyn LayoutView>>,
|
||||
pub views: Vec<BoxView>,
|
||||
pub bounds: (Option<Bounds>, Option<Bounds>),
|
||||
}
|
||||
|
||||
impl VerticalLayout {
|
||||
pub fn new(views: Vec<Box<dyn LayoutView>>) -> Self {
|
||||
pub fn new(views: Vec<BoxView>) -> Self {
|
||||
Self {
|
||||
views,
|
||||
bounds: (None, None),
|
||||
@@ -42,34 +48,43 @@ 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
|
||||
|
||||
fn from_tag(attributes: &HashMap<String, String>, children: &Vec<Tag>) -> Box<dyn View>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
todo!()
|
||||
todo!("");
|
||||
// Box::new(Self {
|
||||
// views: children.iter().map(|tag| tag.parse()).collect(),
|
||||
// bounds: (None, None),
|
||||
// })
|
||||
}
|
||||
|
||||
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutView for VerticalLayout {
|
||||
fn bounds(&self, pw: f32, ph: f32) -> (Bounds, Bounds) {
|
||||
let (mut maxx, mut totaly): (f32, f32) = (0., 0.);
|
||||
for view in &self.views {
|
||||
let (vx, vy) = view.bounds(pw, ph);
|
||||
// impl LayoutView for VerticalLayout {
|
||||
// fn bounds(&self, pw: f32, ph: f32) -> (Bounds, Bounds) {
|
||||
// let (mut maxx, mut totaly): (f32, f32) = (0., 0.);
|
||||
// for view in &self.views {
|
||||
// let (vx, vy) = view.bounds(pw, ph);
|
||||
|
||||
let vx = match vx {
|
||||
super::Bounds::MatchParent => pw,
|
||||
super::Bounds::Pixels(x) => x,
|
||||
};
|
||||
// let vx = match vx {
|
||||
// super::Bounds::MatchParent => pw,
|
||||
// super::Bounds::Pixels(x) => x,
|
||||
// };
|
||||
|
||||
let vy = match vy {
|
||||
super::Bounds::MatchParent => ph,
|
||||
super::Bounds::Pixels(x) => x,
|
||||
};
|
||||
// let vy = match vy {
|
||||
// super::Bounds::MatchParent => ph,
|
||||
// super::Bounds::Pixels(x) => x,
|
||||
// };
|
||||
|
||||
maxx = maxx.max(vx);
|
||||
totaly += vy;
|
||||
}
|
||||
// maxx = maxx.max(vx);
|
||||
// totaly += vy;
|
||||
// }
|
||||
|
||||
(Bounds::Pixels(maxx), Bounds::Pixels(totaly))
|
||||
}
|
||||
}
|
||||
// (Bounds::Pixels(maxx), Bounds::Pixels(totaly))
|
||||
// }
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user