Get constraint layout work with xml

This commit is contained in:
Michael Mikovsky
2025-10-31 12:55:51 -06:00
parent 48067c3eef
commit da608d0dca
12 changed files with 489 additions and 203 deletions
+28 -2
View File
@@ -1,2 +1,28 @@
<ColoredRectView r="20" g="40" b="60" /> <ConstraintLayout>
<!-- </ColoredRectView> --> <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
View File
@@ -5,9 +5,10 @@ pub use cursors::{Cursor, set_cursor};
use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::prelude::wasm_bindgen;
use crate::{ use crate::{
log,
parser::{self, TEST_XML}, parser::{self, TEST_XML},
render::Renderer, render::Renderer,
views::{View, default_view}, views::View,
}; };
#[wasm_bindgen] #[wasm_bindgen]
@@ -38,6 +39,7 @@ impl App {
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 root_view = parser::parse(TEST_XML);
log!("root: {:?}", root_view);
let mut this = App { let mut this = App {
root_view: Some(root_view), root_view: Some(root_view),
-13
View File
@@ -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
View File
@@ -1,20 +1,63 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::views::View; use crate::{
log,
views::{Bounds, BoxView, ColorRectView, ConstraintLayout, View},
};
mod interpreter;
mod parser; mod parser;
pub const TEST_XML: &'static str = include_str!("../../pages/main.xml"); pub const TEST_XML: &'static str = include_str!("../../pages/main.xml");
#[derive(Debug)] #[derive(Debug)]
struct Tag { pub struct Tag {
name: String, pub name: String,
children: Vec<Tag>, pub children: Vec<Tag>,
attributes: HashMap<String, String>, 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> { pub fn parse(xml: &str) -> Box<dyn View> {
let tag = parser::parse_xml(xml); parser::parse_xml(xml).parse()
interpreter::interpret_tag(tag)
} }
+8 -2
View File
@@ -51,9 +51,15 @@ fn parse_tag(event: BytesStart<'_>, reader: &mut Reader<&[u8]>) -> Tag {
loop { loop {
match reader.read_event_into(&mut buf) { 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 // 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)) => { Ok(Event::Start(e)) => {
children.push(parse_tag(e, reader)); children.push(parse_tag(e, reader));
+1 -7
View File
@@ -1,13 +1,7 @@
use fontdue::layout::{CoordinateSystem, Layout, LayoutSettings, TextStyle};
use wasm_bindgen::{Clamped, prelude::*}; use wasm_bindgen::{Clamped, prelude::*};
use web_sys::ImageData; use web_sys::ImageData;
use crate::render::{ use crate::render::{RESOLUTION, buffer::ImgBuffer, calc_resolution, rand::Rnd};
RESOLUTION,
buffer::{Bitmap, ImgBuffer},
calc_resolution,
rand::Rnd,
};
#[wasm_bindgen] #[wasm_bindgen]
pub struct Renderer { pub struct Renderer {
+207
View File
@@ -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
}
}
+16 -55
View File
@@ -1,41 +1,17 @@
use crate::{ use crate::{
parser::Tag,
render::Renderer, render::Renderer,
views::{ views::{Bounds, Constraint, View},
Bounds, LayoutView, View,
constraint_layout::{Constraint, ConstraintView},
},
}; };
#[derive(Debug)]
pub struct ColorRectView { pub struct ColorRectView {
color: (u8, u8, u8), color: (u8, u8, u8),
bounds: (Option<Bounds>, Option<Bounds>),
constraints: (
Option<Constraint>,
Option<Constraint>,
Option<Constraint>,
Option<Constraint>,
),
} }
impl ColorRectView { impl ColorRectView {
pub fn new(r: u8, g: u8, b: u8) -> Self { pub fn new(r: u8, g: u8, b: u8) -> Self {
Self { Self { color: (r, g, b) }
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)
} }
} }
@@ -47,10 +23,18 @@ impl View for ColorRectView {
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 fn from_tag(
attributes: &std::collections::HashMap<String, String>,
children: &Vec<Tag>,
) -> Box<dyn View>
where where
Self: Sized, Self: Sized,
{ {
assert!(
children.is_empty(),
"ColorRectView does not support children"
);
let r = attributes let r = attributes
.get("r") .get("r")
.unwrap_or(&"0".to_string()) .unwrap_or(&"0".to_string())
@@ -67,33 +51,10 @@ impl View for ColorRectView {
.parse() .parse()
.unwrap(); .unwrap();
Self::new(r, g, b) Box::new(Self::new(r, g, b))
}
} }
impl LayoutView for ColorRectView { fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> {
fn bounds(&self, _ph: f32, _pw: f32) -> (Bounds, Bounds) { self
(
*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,
)
} }
} }
+62 -47
View File
@@ -1,38 +1,23 @@
use std::collections::HashMap; use std::{
any::{Any, TypeId},
use crate::{ collections::HashMap,
render::Renderer,
views::{LayoutView, View},
}; };
pub enum Constraint { use crate::{
Left(f32, usize), log,
Right(f32, usize), parser::Tag,
Top(f32, usize), render::Renderer,
Bottom(f32, usize), views::{Constraint, View, box_view::BoxView},
};
LeftParent(f32),
RightParent(f32),
TopParent(f32),
BottomParent(f32),
}
pub trait ConstraintView: LayoutView {
fn get_constraints(
&self,
) -> (
&Option<Constraint>,
&Option<Constraint>,
&Option<Constraint>,
&Option<Constraint>,
);
}
#[derive(Debug)]
pub struct ConstraintLayout { pub struct ConstraintLayout {
pub children: Vec<Box<dyn ConstraintView>>, pub children: Vec<BoxView>,
child_positions: Option<HashMap<usize, Rect>>, child_positions: Option<HashMap<usize, Rect>>,
child_order: Option<Vec<usize>>,
} }
#[derive(Debug)]
struct Rect { struct Rect {
x: f32, x: f32,
y: f32, y: f32,
@@ -41,7 +26,7 @@ struct Rect {
} }
impl ConstraintLayout { impl ConstraintLayout {
pub fn new(children: Vec<Box<dyn ConstraintView>>) -> Self { pub fn new(children: Vec<BoxView>) -> Self {
for child in &children { for child in &children {
let (top, left, right, bottom) = child.get_constraints(); let (top, left, right, bottom) = child.get_constraints();
@@ -55,10 +40,15 @@ impl ConstraintLayout {
); );
} }
ConstraintLayout { let mut this = ConstraintLayout {
children, children,
child_positions: None, 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) { 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 mut calculated_positions: HashMap<usize, Rect> = HashMap::new();
let dependencies = self let child_order = self.child_order.as_ref().unwrap();
.children for idx in child_order {
.iter() let child: &mut BoxView = &mut self.children[*idx];
.map(|c| {
let constraints = c.get_constraints();
[constraints.0, constraints.1, constraints.2, constraints.3]
})
.collect::<Vec<_>>();
let sorted = topological_sort(&dependencies); log!("{:?}", child.get_constraints());
for idx in sorted {
let child = &mut self.children[idx];
let (c_top, c_left, c_right, c_bottom) = 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); let (c_width, c_height) = child.bounds(self_w, self_h);
@@ -152,7 +134,7 @@ impl ConstraintLayout {
}; };
calculated_positions.insert( calculated_positions.insert(
idx, *idx,
Rect { Rect {
x: x, x: x,
y: y, y: y,
@@ -164,6 +146,19 @@ impl ConstraintLayout {
self.child_positions = Some(calculated_positions); 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 { impl View for ConstraintLayout {
@@ -180,11 +175,30 @@ impl View for ConstraintLayout {
self.recalculate_positions(x, y, w, h); 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 where
Self: Sized, 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>, result: &mut Vec<usize>,
) { ) {
if states[node] == VisitState::Visiting { 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 { if states[node] == VisitState::Visited {
+40 -28
View File
@@ -1,27 +1,30 @@
use std::collections::HashMap; use std::{
any::{Any, TypeId},
use crate::{ collections::HashMap,
render::Renderer, fmt::Debug,
views::constraint_layout::{Constraint, ConstraintView},
}; };
use crate::{parser::Tag, render::Renderer};
mod box_view;
mod color_rect_view; mod color_rect_view;
mod constraint_layout; mod constraint_layout;
mod text_view; mod text_view;
mod vertical_layout; mod vertical_layout;
pub use box_view::BoxView;
pub use color_rect_view::ColorRectView; pub use color_rect_view::ColorRectView;
pub use constraint_layout::ConstraintLayout; 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 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 fn from_tag(attributes: &HashMap<String, String>, children: &Vec<Tag>) -> Box<dyn View>
where where
Self: Sized; Self: Sized;
fn as_any(self: Box<Self>) -> Box<dyn Any>;
} }
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
@@ -30,17 +33,32 @@ pub enum Bounds {
Pixels(f32), Pixels(f32),
} }
pub trait LayoutView: View { #[derive(Debug)]
// fn set_bounds(&mut self, width: Option<Bounds>, height: Option<Bounds>); pub enum Constraint {
fn bounds(&self, pw: f32, ph: f32) -> (Bounds, Bounds); Left(f32, usize),
Right(f32, usize),
Top(f32, usize),
Bottom(f32, usize),
LeftParent(f32),
RightParent(f32),
TopParent(f32),
BottomParent(f32),
} }
// pub trait ViewPosition: Drawable { // pub trait ConstraintView: View {
// // fn draw(&self) // fn get_constraints(
// &self,
// ) -> (
// &Option<Constraint>,
// &Option<Constraint>,
// &Option<Constraint>,
// &Option<Constraint>,
// );
// } // }
pub fn default_view() -> Box<dyn View> { 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; 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.))); 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( b.set_constraints(
Some(Constraint::TopParent(temp_margin)), Some(Constraint::TopParent(temp_margin)),
@@ -62,7 +80,7 @@ pub fn default_view() -> Box<dyn View> {
None, None,
); );
let mut c = ColorRectView::new(0, 0, 127); let mut c = BoxView::new(Box::new(ColorRectView::new(0, 0, 127)));
c.set_constraints( c.set_constraints(
None, None,
@@ -71,7 +89,7 @@ pub fn default_view() -> Box<dyn View> {
Some(Constraint::BottomParent(temp_margin)), 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( d.set_constraints(
None, None,
@@ -80,7 +98,7 @@ pub fn default_view() -> Box<dyn View> {
Some(Constraint::BottomParent(temp_margin)), 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( center.set_constraints(
Some(Constraint::TopParent(temp_margin)), 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.))); 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( center_top.set_constraints(
Some(Constraint::Bottom(0., 4)), Some(Constraint::Bottom(0., 4)),
@@ -103,7 +121,7 @@ pub fn default_view() -> Box<dyn View> {
Some(Constraint::Top(0., 4)), 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( inside_top.set_constraints(
Some(Constraint::Bottom(0., 4)), Some(Constraint::Bottom(0., 4)),
@@ -116,13 +134,7 @@ pub fn default_view() -> Box<dyn View> {
); );
Box::new(ConstraintLayout::new(vec![ Box::new(ConstraintLayout::new(vec![
Box::new(a), a, b, c, d, center, center_top, inside_top,
Box::new(b),
Box::new(c),
Box::new(d),
Box::new(center),
Box::new(center_top),
Box::new(inside_top),
])) ]))
// Box::new(VerticalLayout::new(vec![ // Box::new(VerticalLayout::new(vec![
+34 -16
View File
@@ -1,9 +1,12 @@
use fontdue::layout::{CoordinateSystem, Layout, LayoutSettings, TextStyle}; use fontdue::layout::{CoordinateSystem, Layout, LayoutSettings, TextStyle};
use std::fmt::Debug;
use crate::{ use crate::{
fonts::{FONTS, FontHandle}, fonts::{FONTS, FontHandle},
parser::Tag,
render::{Renderer, buffer::Bitmap}, render::{Renderer, buffer::Bitmap},
views::{Bounds, LayoutView, View}, views::{Bounds, View},
}; };
pub struct TextView { pub struct TextView {
@@ -11,7 +14,6 @@ pub struct TextView {
text: String, text: String,
scale: f32, scale: f32,
font: FontHandle, font: FontHandle,
bounds: (Option<Bounds>, Option<Bounds>),
} }
impl TextView { impl TextView {
@@ -27,7 +29,6 @@ impl TextView {
text, text,
scale: 20., scale: 20.,
font: FontHandle::AtiksonHyperlegibleRegular, font: FontHandle::AtiksonHyperlegibleRegular,
bounds: (None, None),
}; };
let font = (s.font as usize).clone(); let font = (s.font as usize).clone();
@@ -39,9 +40,6 @@ impl TextView {
s s
} }
pub fn set_bounds(&mut self, width: Option<Bounds>, height: Option<Bounds>) {
self.bounds = (width, height);
}
} }
impl View for TextView { impl View for TextView {
@@ -79,24 +77,44 @@ impl View for TextView {
// todo!() // 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 where
Self: Sized, Self: Sized,
{ {
assert!(children.is_empty(), "TextView does not support children");
todo!() todo!()
// Self::new(attributes)
}
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
} }
} }
impl LayoutView for TextView { // impl LayoutView for TextView {
fn bounds(&self, _ph: f32, _pw: f32) -> (Bounds, Bounds) { // fn bounds(&self, _ph: f32, _pw: f32) -> (Bounds, Bounds) {
// let (x, y) = renderer.undistort(x as f32, y as f32); // // let (x, y) = renderer.undistort(x as f32, y as f32);
let (mut width, mut height): (usize, usize) = (0, 0); // let (mut width, mut height): (usize, usize) = (0, 0);
for glyph in self.layout.glyphs() { // for glyph in self.layout.glyphs() {
width = width.max(glyph.x as usize + glyph.width); // width = width.max(glyph.x as usize + glyph.width);
height = height.max(glyph.y as usize + glyph.height); // 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()
} }
} }
+39 -24
View File
@@ -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 struct VerticalLayout {
pub views: Vec<Box<dyn LayoutView>>, pub views: Vec<BoxView>,
pub bounds: (Option<Bounds>, Option<Bounds>), pub bounds: (Option<Bounds>, Option<Bounds>),
} }
impl VerticalLayout { impl VerticalLayout {
pub fn new(views: Vec<Box<dyn LayoutView>>) -> Self { pub fn new(views: Vec<BoxView>) -> Self {
Self { Self {
views, views,
bounds: (None, None), bounds: (None, None),
@@ -42,34 +48,43 @@ 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
fn from_tag(attributes: &HashMap<String, String>, children: &Vec<Tag>) -> Box<dyn View>
where where
Self: Sized, 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 { // impl LayoutView for VerticalLayout {
fn bounds(&self, pw: f32, ph: f32) -> (Bounds, Bounds) { // fn bounds(&self, pw: f32, ph: f32) -> (Bounds, Bounds) {
let (mut maxx, mut totaly): (f32, f32) = (0., 0.); // let (mut maxx, mut totaly): (f32, f32) = (0., 0.);
for view in &self.views { // for view in &self.views {
let (vx, vy) = view.bounds(pw, ph); // let (vx, vy) = view.bounds(pw, ph);
let vx = match vx { // let vx = match vx {
super::Bounds::MatchParent => pw, // super::Bounds::MatchParent => pw,
super::Bounds::Pixels(x) => x, // super::Bounds::Pixels(x) => x,
}; // };
let vy = match vy { // let vy = match vy {
super::Bounds::MatchParent => ph, // super::Bounds::MatchParent => ph,
super::Bounds::Pixels(x) => x, // super::Bounds::Pixels(x) => x,
}; // };
maxx = maxx.max(vx); // maxx = maxx.max(vx);
totaly += vy; // totaly += vy;
} // }
(Bounds::Pixels(maxx), Bounds::Pixels(totaly)) // (Bounds::Pixels(maxx), Bounds::Pixels(totaly))
} // }
} // }