mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 13:30:48 +00:00
Store layout attributes separately
This commit is contained in:
parent
fe272a6d7c
commit
ed4931f638
3 changed files with 176 additions and 13 deletions
|
@ -20,6 +20,7 @@ impl LayoutAbstractNode {
|
|||
pub struct LayoutAbstractTag {
|
||||
pub namespace: String,
|
||||
pub name: String,
|
||||
pub layout_attributes: LayoutAttributes,
|
||||
pub attributes: Vec<Attribute>,
|
||||
}
|
||||
|
||||
|
@ -28,12 +29,27 @@ impl LayoutAbstractTag {
|
|||
Self {
|
||||
namespace,
|
||||
name,
|
||||
layout_attributes: Default::default(),
|
||||
attributes: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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)]
|
||||
|
@ -54,3 +129,28 @@ pub enum AttributeValue {
|
|||
VariableParameter(VariableParameter),
|
||||
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>),
|
||||
Integer(i64),
|
||||
Decimal(f64),
|
||||
AbsolutePx(f32),
|
||||
Percent(f32),
|
||||
PercentRemainder(f32),
|
||||
Inner,
|
||||
Width,
|
||||
Height,
|
||||
Dimension(Dimension),
|
||||
TemplateString(Vec<TemplateStringSegment>),
|
||||
Color(Color),
|
||||
Bool(bool),
|
||||
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)]
|
||||
pub enum TemplateStringSegment {
|
||||
String(String),
|
||||
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
|
||||
.parse::<f32>()
|
||||
.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: ?%
|
||||
Some([value, "%"]) => {
|
||||
let percent = value
|
||||
.parse::<f32>()
|
||||
.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: ?@
|
||||
Some([value, "@"]) => {
|
||||
let percent_remainder = value
|
||||
.parse::<f32>()
|
||||
.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
|
||||
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
|
||||
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
|
||||
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: `? ... {{?}} ...`
|
||||
Some(["`", string, "`"]) => {
|
||||
let mut segments = Vec::<TemplateStringSegment>::new();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue