Store layout attributes separately
This commit is contained in:
parent
fe272a6d7c
commit
ed4931f638
|
|
@ -20,6 +20,7 @@ impl LayoutAbstractNode {
|
||||||
pub struct LayoutAbstractTag {
|
pub struct LayoutAbstractTag {
|
||||||
pub namespace: String,
|
pub namespace: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub layout_attributes: LayoutAttributes,
|
||||||
pub attributes: Vec<Attribute>,
|
pub attributes: Vec<Attribute>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,12 +29,27 @@ impl LayoutAbstractTag {
|
||||||
Self {
|
Self {
|
||||||
namespace,
|
namespace,
|
||||||
name,
|
name,
|
||||||
|
layout_attributes: Default::default(),
|
||||||
attributes: Vec::new(),
|
attributes: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_attribute(&mut self, attribute: Attribute) {
|
pub fn add_attribute(&mut self, attribute: Attribute) {
|
||||||
self.attributes.push(attribute);
|
match &attribute.name[..] {
|
||||||
|
// Layout attributes, stored separately
|
||||||
|
"width" => self.layout_attributes.width = attribute.dimension(),
|
||||||
|
"height" => self.layout_attributes.height = attribute.dimension(),
|
||||||
|
"x-align" => self.layout_attributes.x_align = attribute.percent(),
|
||||||
|
"y-align" => self.layout_attributes.y_align = attribute.percent(),
|
||||||
|
"x-padding" => self.layout_attributes.padding.set_horizontal(attribute.dimension()),
|
||||||
|
"y-padding" => self.layout_attributes.padding.set_vertical(attribute.dimension()),
|
||||||
|
"padding" => self.layout_attributes.padding = attribute.box_dimensions(),
|
||||||
|
"x-spacing" => self.layout_attributes.spacing.set_horizontal(attribute.dimension()),
|
||||||
|
"y-spacing" => self.layout_attributes.spacing.set_vertical(attribute.dimension()),
|
||||||
|
"spacing" => self.layout_attributes.spacing = attribute.box_dimensions(),
|
||||||
|
// Non-layout attribute
|
||||||
|
_ => self.attributes.push(attribute),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,6 +63,65 @@ impl Attribute {
|
||||||
pub fn new(name: String, value: AttributeValue) -> Self {
|
pub fn new(name: String, value: AttributeValue) -> Self {
|
||||||
Self { name, value }
|
Self { name, value }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extracts this attribute's values as typed values.
|
||||||
|
fn values(self) -> Vec<TypeValue> {
|
||||||
|
if let AttributeValue::TypeValue(values) = self.value {
|
||||||
|
values
|
||||||
|
.into_iter()
|
||||||
|
.map(|value| {
|
||||||
|
if let TypeValueOrArgument::TypeValue(value) = value {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
todo!("variable arguments are note yet supported")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
todo!("variable arguments are not yet supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts this attribute's value into a single dimension.
|
||||||
|
fn dimension(self) -> Dimension {
|
||||||
|
let values = self.values();
|
||||||
|
assert_eq!(values.len(), 1, "expected a single value");
|
||||||
|
values[0].expect_dimension()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts a percentage from this attribute's value.
|
||||||
|
fn percent(self) -> f32 {
|
||||||
|
match self.dimension() {
|
||||||
|
Dimension::Percent(value) => value,
|
||||||
|
_ => panic!("expected a percentage"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts this attribute's values into box dimensions.
|
||||||
|
fn box_dimensions(self) -> BoxDimensions {
|
||||||
|
let values = self.values();
|
||||||
|
match values.len() {
|
||||||
|
1 => {
|
||||||
|
let value = values[0].expect_dimension();
|
||||||
|
BoxDimensions::all(value)
|
||||||
|
},
|
||||||
|
2 => {
|
||||||
|
let vertical = values[0].expect_dimension();
|
||||||
|
let horizontal = values[1].expect_dimension();
|
||||||
|
BoxDimensions::symmetric(vertical, horizontal)
|
||||||
|
},
|
||||||
|
4 => {
|
||||||
|
let top = values[0].expect_dimension();
|
||||||
|
let right = values[1].expect_dimension();
|
||||||
|
let bottom = values[2].expect_dimension();
|
||||||
|
let left = values[3].expect_dimension();
|
||||||
|
BoxDimensions::new(top, right, bottom, left)
|
||||||
|
},
|
||||||
|
_ => panic!("expected 1, 2 or 4 values"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
|
@ -54,3 +129,28 @@ pub enum AttributeValue {
|
||||||
VariableParameter(VariableParameter),
|
VariableParameter(VariableParameter),
|
||||||
TypeValue(Vec<TypeValueOrArgument>),
|
TypeValue(Vec<TypeValueOrArgument>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Layout-specific attributes.
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct LayoutAttributes {
|
||||||
|
pub width: Dimension,
|
||||||
|
pub height: Dimension,
|
||||||
|
pub x_align: f32,
|
||||||
|
pub y_align: f32,
|
||||||
|
pub spacing: BoxDimensions,
|
||||||
|
pub padding: BoxDimensions,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for LayoutAttributes {
|
||||||
|
fn default() -> Self {
|
||||||
|
let zero_box = BoxDimensions::all(Dimension::AbsolutePx(0.0));
|
||||||
|
Self {
|
||||||
|
width: Dimension::Inner,
|
||||||
|
height: Dimension::Inner,
|
||||||
|
x_align: 0.0,
|
||||||
|
y_align: 0.0,
|
||||||
|
spacing: zero_box,
|
||||||
|
padding: zero_box,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,20 +49,80 @@ pub enum TypeValue {
|
||||||
Layout(Vec<ComponentAst>),
|
Layout(Vec<ComponentAst>),
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
Decimal(f64),
|
Decimal(f64),
|
||||||
AbsolutePx(f32),
|
Dimension(Dimension),
|
||||||
Percent(f32),
|
|
||||||
PercentRemainder(f32),
|
|
||||||
Inner,
|
|
||||||
Width,
|
|
||||||
Height,
|
|
||||||
TemplateString(Vec<TemplateStringSegment>),
|
TemplateString(Vec<TemplateStringSegment>),
|
||||||
Color(Color),
|
Color(Color),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TypeValue {
|
||||||
|
/// Converts this to a dimension, panics if not possible.
|
||||||
|
pub fn expect_dimension(&self) -> Dimension {
|
||||||
|
match self {
|
||||||
|
Self::Dimension(dimension) => *dimension,
|
||||||
|
_ => panic!("expected a dimension"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum TemplateStringSegment {
|
pub enum TemplateStringSegment {
|
||||||
String(String),
|
String(String),
|
||||||
Argument(TypeValueOrArgument),
|
Argument(TypeValueOrArgument),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A dimension is a measure along an axis.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
pub enum Dimension {
|
||||||
|
/// Absolute value in pixels.
|
||||||
|
AbsolutePx(f32),
|
||||||
|
/// Percent of parent container size along the same axis.
|
||||||
|
Percent(f32),
|
||||||
|
/// Percent of free space remaining in parent container.
|
||||||
|
PercentRemainder(f32),
|
||||||
|
/// Minimum size required to fit the children.
|
||||||
|
Inner,
|
||||||
|
/// Size relative to the width of this component.
|
||||||
|
Width,
|
||||||
|
/// Size relative to the height of this component.
|
||||||
|
Height,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dimensions along a box's four sides.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
pub struct BoxDimensions {
|
||||||
|
pub top: Dimension,
|
||||||
|
pub right: Dimension,
|
||||||
|
pub bottom: Dimension,
|
||||||
|
pub left: Dimension,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoxDimensions {
|
||||||
|
/// Construct new box dimensions, with values given for each side.
|
||||||
|
pub fn new(top: Dimension, right: Dimension, bottom: Dimension, left: Dimension) -> Self {
|
||||||
|
Self { top, right, bottom, left }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct new box dimensions, with same values used for top-bottom and left-right.
|
||||||
|
pub fn symmetric(vertical: Dimension, horizontal: Dimension) -> Self {
|
||||||
|
Self::new(vertical, horizontal, vertical, horizontal)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct new box dimensions with the same value for all sides.
|
||||||
|
pub fn all(value: Dimension) -> Self {
|
||||||
|
Self::new(value, value, value, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the padding on the top and bottom sides.
|
||||||
|
pub fn set_vertical(&mut self, value: Dimension) {
|
||||||
|
self.top = value;
|
||||||
|
self.bottom = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the padding on the left and right sides.
|
||||||
|
pub fn set_horizontal(&mut self, value: Dimension) {
|
||||||
|
self.left = value;
|
||||||
|
self.right = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,28 +130,31 @@ impl AttributeParser {
|
||||||
let pixels = value
|
let pixels = value
|
||||||
.parse::<f32>()
|
.parse::<f32>()
|
||||||
.expect(&format!("Invalid value `{}` specified in the attribute type`{}` when parsing XML layout", value, attribute_type)[..]);
|
.expect(&format!("Invalid value `{}` specified in the attribute type`{}` when parsing XML layout", value, attribute_type)[..]);
|
||||||
TypeValueOrArgument::TypeValue(TypeValue::AbsolutePx(pixels))
|
let dimension = Dimension::AbsolutePx(pixels);
|
||||||
|
TypeValueOrArgument::TypeValue(TypeValue::Dimension(dimension))
|
||||||
},
|
},
|
||||||
// Percent: ?%
|
// Percent: ?%
|
||||||
Some([value, "%"]) => {
|
Some([value, "%"]) => {
|
||||||
let percent = value
|
let percent = value
|
||||||
.parse::<f32>()
|
.parse::<f32>()
|
||||||
.expect(&format!("Invalid value `{}` specified in the attribute type `{}` when parsing XML layout", value, attribute_type)[..]);
|
.expect(&format!("Invalid value `{}` specified in the attribute type `{}` when parsing XML layout", value, attribute_type)[..]);
|
||||||
TypeValueOrArgument::TypeValue(TypeValue::Percent(percent))
|
let dimension = Dimension::Percent(percent);
|
||||||
|
TypeValueOrArgument::TypeValue(TypeValue::Dimension(dimension))
|
||||||
},
|
},
|
||||||
// PercentRemainder: ?@
|
// PercentRemainder: ?@
|
||||||
Some([value, "@"]) => {
|
Some([value, "@"]) => {
|
||||||
let percent_remainder = value
|
let percent_remainder = value
|
||||||
.parse::<f32>()
|
.parse::<f32>()
|
||||||
.expect(&format!("Invalid value `{}` specified in the attribute type `{}` when parsing XML layout", value, attribute_type)[..]);
|
.expect(&format!("Invalid value `{}` specified in the attribute type `{}` when parsing XML layout", value, attribute_type)[..]);
|
||||||
TypeValueOrArgument::TypeValue(TypeValue::PercentRemainder(percent_remainder))
|
let dimension = Dimension::PercentRemainder(percent_remainder);
|
||||||
|
TypeValueOrArgument::TypeValue(TypeValue::Dimension(dimension))
|
||||||
},
|
},
|
||||||
// Inner: inner
|
// Inner: inner
|
||||||
Some([inner]) if inner.eq_ignore_ascii_case("inner") => TypeValueOrArgument::TypeValue(TypeValue::Inner),
|
Some([inner]) if inner.eq_ignore_ascii_case("inner") => TypeValueOrArgument::TypeValue(TypeValue::Dimension(Dimension::Inner)),
|
||||||
// Width: width
|
// Width: width
|
||||||
Some([width]) if width.eq_ignore_ascii_case("width") => TypeValueOrArgument::TypeValue(TypeValue::Width),
|
Some([width]) if width.eq_ignore_ascii_case("width") => TypeValueOrArgument::TypeValue(TypeValue::Dimension(Dimension::Width)),
|
||||||
// Height: height
|
// Height: height
|
||||||
Some([height]) if height.eq_ignore_ascii_case("height") => TypeValueOrArgument::TypeValue(TypeValue::Height),
|
Some([height]) if height.eq_ignore_ascii_case("height") => TypeValueOrArgument::TypeValue(TypeValue::Dimension(Dimension::Height)),
|
||||||
// TemplateString: `? ... {{?}} ...`
|
// TemplateString: `? ... {{?}} ...`
|
||||||
Some(["`", string, "`"]) => {
|
Some(["`", string, "`"]) => {
|
||||||
let mut segments = Vec::<TemplateStringSegment>::new();
|
let mut segments = Vec::<TemplateStringSegment>::new();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue