mirror of
https://github.com/servo/servo.git
synced 2025-08-11 16:35:33 +01:00
Refactor Position
A specified position is now a struct made of two values of different types, the first one being PositionComponent<X>, and the second one PositionComponent<Y>. A position component is represented by the new enum PositionComponent<Side>, with the three values Center, Length(LengthOrPercentage), and Side(Side, Option<LengthOrPercentage>). Side keywords are represented by the X and Y enums, which don't include a value for the center keyword anymore. They are accompanied by the Side trait, which allows us to determine whether a side keyword is "left" or "top". This refactor simplified the parsing and serialisation code and exposed bugs in it, where it would reject valid <position> values followed by arbitrary tokens, and where it would fail to prefer "left" to "right" when serialising positions in basic shapes.
This commit is contained in:
parent
0040160b38
commit
70ec61cf01
22 changed files with 484 additions and 887 deletions
|
@ -367,17 +367,16 @@ fn color_to_nscolor_zero_currentcolor(color: Color) -> structs::nscolor {
|
|||
<%def name="impl_position(ident, gecko_ffi_name, need_clone=False)">
|
||||
#[allow(non_snake_case)]
|
||||
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
|
||||
${set_gecko_property("%s.mXPosition" % gecko_ffi_name, "v.horizontal.0.into()")}
|
||||
${set_gecko_property("%s.mYPosition" % gecko_ffi_name, "v.vertical.0.into()")}
|
||||
${set_gecko_property("%s.mXPosition" % gecko_ffi_name, "v.horizontal.into()")}
|
||||
${set_gecko_property("%s.mYPosition" % gecko_ffi_name, "v.vertical.into()")}
|
||||
}
|
||||
<%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
|
||||
% if need_clone:
|
||||
#[allow(non_snake_case)]
|
||||
pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
|
||||
use values::generics::position::{HorizontalPosition, Position, VerticalPosition};
|
||||
Position {
|
||||
horizontal: HorizontalPosition(self.gecko.${gecko_ffi_name}.mXPosition.into()),
|
||||
vertical: VerticalPosition(self.gecko.${gecko_ffi_name}.mYPosition.into()),
|
||||
longhands::${ident}::computed_value::T {
|
||||
horizontal: self.gecko.${gecko_ffi_name}.mXPosition.into(),
|
||||
vertical: self.gecko.${gecko_ffi_name}.mYPosition.into(),
|
||||
}
|
||||
}
|
||||
% endif
|
||||
|
@ -2040,8 +2039,8 @@ fn static_assert() {
|
|||
for (gecko, servo) in self.gecko.mScrollSnapCoordinate
|
||||
.iter_mut()
|
||||
.zip(v) {
|
||||
gecko.mXPosition = servo.horizontal.0.into();
|
||||
gecko.mYPosition = servo.vertical.0.into();
|
||||
gecko.mXPosition = servo.horizontal.into();
|
||||
gecko.mYPosition = servo.vertical.into();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2726,12 +2725,12 @@ fn static_assert() {
|
|||
}
|
||||
</%self:simple_image_array_property>
|
||||
|
||||
% for orientation in [("x", "Horizontal"), ("y", "Vertical")]:
|
||||
pub fn copy_${shorthand}_position_${orientation[0]}_from(&mut self, other: &Self) {
|
||||
% for orientation in ["x", "y"]:
|
||||
pub fn copy_${shorthand}_position_${orientation}_from(&mut self, other: &Self) {
|
||||
use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
|
||||
|
||||
self.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count
|
||||
= cmp::min(1, other.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count);
|
||||
self.gecko.${image_layers_field}.mPosition${orientation.upper()}Count
|
||||
= cmp::min(1, other.gecko.${image_layers_field}.mPosition${orientation.upper()}Count);
|
||||
self.gecko.${image_layers_field}.mLayers.mFirstElement.mPosition =
|
||||
other.gecko.${image_layers_field}.mLayers.mFirstElement.mPosition;
|
||||
unsafe {
|
||||
|
@ -2742,20 +2741,19 @@ fn static_assert() {
|
|||
|
||||
for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut()
|
||||
.zip(other.gecko.${image_layers_field}.mLayers.iter()) {
|
||||
layer.mPosition.m${orientation[0].upper()}Position
|
||||
= other.mPosition.m${orientation[0].upper()}Position;
|
||||
layer.mPosition.m${orientation.upper()}Position
|
||||
= other.mPosition.m${orientation.upper()}Position;
|
||||
}
|
||||
self.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count
|
||||
= other.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count;
|
||||
self.gecko.${image_layers_field}.mPosition${orientation.upper()}Count
|
||||
= other.gecko.${image_layers_field}.mPosition${orientation.upper()}Count;
|
||||
}
|
||||
|
||||
pub fn clone_${shorthand}_position_${orientation[0]}(&self)
|
||||
-> longhands::${shorthand}_position_${orientation[0]}::computed_value::T {
|
||||
use values::generics::position::${orientation[1]}Position;
|
||||
longhands::${shorthand}_position_${orientation[0]}::computed_value::T(
|
||||
pub fn clone_${shorthand}_position_${orientation}(&self)
|
||||
-> longhands::${shorthand}_position_${orientation}::computed_value::T {
|
||||
longhands::${shorthand}_position_${orientation}::computed_value::T(
|
||||
self.gecko.${image_layers_field}.mLayers.iter()
|
||||
.take(self.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count as usize)
|
||||
.map(|position| ${orientation[1]}Position(position.mPosition.m${orientation[0].upper()}Position.into()))
|
||||
.take(self.gecko.${image_layers_field}.mPosition${orientation.upper()}Count as usize)
|
||||
.map(|position| position.mPosition.m${orientation.upper()}Position.into())
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
|
@ -2778,7 +2776,7 @@ fn static_assert() {
|
|||
self.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count = v.len() as u32;
|
||||
for (servo, geckolayer) in v.zip(self.gecko.${image_layers_field}
|
||||
.mLayers.iter_mut()) {
|
||||
geckolayer.mPosition.m${orientation[0].upper()}Position = servo.0.into();
|
||||
geckolayer.mPosition.m${orientation[0].upper()}Position = servo.into();
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
|
|
|
@ -41,7 +41,6 @@ use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone
|
|||
use values::computed::{BorderRadiusSize, ClipRect};
|
||||
use values::computed::{CalcLengthOrPercentage, Context, LengthOrPercentage};
|
||||
use values::computed::{MaxLength, MinLength};
|
||||
use values::computed::position::{HorizontalPosition, VerticalPosition};
|
||||
use values::computed::ToComputedValue;
|
||||
use values::generics::position as generic_position;
|
||||
|
||||
|
@ -655,6 +654,7 @@ pub trait Animatable: Sized {
|
|||
/// https://drafts.csswg.org/css-transitions/#animtype-repeatable-list
|
||||
pub trait RepeatableListAnimatable: Animatable {}
|
||||
|
||||
impl RepeatableListAnimatable for LengthOrPercentage {}
|
||||
impl RepeatableListAnimatable for Either<f32, LengthOrPercentage> {}
|
||||
|
||||
impl<T: RepeatableListAnimatable> Animatable for SmallVec<[T; 1]> {
|
||||
|
@ -1336,46 +1336,6 @@ impl<H: Animatable, V: Animatable> Animatable for generic_position::Position<H,
|
|||
impl<H, V> RepeatableListAnimatable for generic_position::Position<H, V>
|
||||
where H: RepeatableListAnimatable, V: RepeatableListAnimatable {}
|
||||
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-simple-list
|
||||
impl Animatable for HorizontalPosition {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
self.0.interpolate(&other.0, progress).map(generic_position::HorizontalPosition)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
self.0.compute_distance(&other.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
self.0.compute_squared_distance(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl RepeatableListAnimatable for HorizontalPosition {}
|
||||
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-simple-list
|
||||
impl Animatable for VerticalPosition {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
self.0.interpolate(&other.0, progress).map(generic_position::VerticalPosition)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
self.0.compute_distance(&other.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
self.0.compute_squared_distance(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl RepeatableListAnimatable for VerticalPosition {}
|
||||
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-rect
|
||||
impl Animatable for ClipRect {
|
||||
#[inline]
|
||||
|
|
|
@ -20,38 +20,13 @@ ${helpers.predefined_type("background-image", "LayerImage",
|
|||
animation_value_type="none",
|
||||
has_uncacheable_values="True" if product == "gecko" else "False")}
|
||||
|
||||
<%helpers:predefined_type name="background-position-x" type="position::HorizontalPosition"
|
||||
initial_value="computed::position::HorizontalPosition::zero()"
|
||||
initial_specified_value="specified::position::HorizontalPosition::left()"
|
||||
spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-x"
|
||||
animation_value_type="ComputedValue" vector="True" delegate_animate="True">
|
||||
#[inline]
|
||||
/// Get the initial value for horizontal position.
|
||||
pub fn get_initial_position_value() -> SpecifiedValue {
|
||||
use values::generics::position::{HorizontalPosition, PositionValue};
|
||||
use values::specified::{LengthOrPercentage, Percentage};
|
||||
HorizontalPosition(PositionValue {
|
||||
keyword: None,
|
||||
position: Some(LengthOrPercentage::Percentage(Percentage(0.0))),
|
||||
})
|
||||
}
|
||||
</%helpers:predefined_type>
|
||||
|
||||
<%helpers:predefined_type name="background-position-y" type="position::VerticalPosition"
|
||||
initial_value="computed::position::VerticalPosition::zero()"
|
||||
initial_specified_value="specified::position::VerticalPosition::top()"
|
||||
spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-y"
|
||||
animation_value_type="ComputedValue" vector="True" delegate_animate="True">
|
||||
/// Get the initial value for vertical position.
|
||||
pub fn get_initial_position_value() -> SpecifiedValue {
|
||||
use values::generics::position::{VerticalPosition, PositionValue};
|
||||
use values::specified::{LengthOrPercentage, Percentage};
|
||||
VerticalPosition(PositionValue {
|
||||
keyword: None,
|
||||
position: Some(LengthOrPercentage::Percentage(Percentage(0.0))),
|
||||
})
|
||||
}
|
||||
</%helpers:predefined_type>
|
||||
% for (axis, direction, initial) in [("x", "Horizontal", "left"), ("y", "Vertical", "top")]:
|
||||
${helpers.predefined_type("background-position-" + axis, "position::" + direction + "Position",
|
||||
initial_value="computed::LengthOrPercentage::zero()",
|
||||
initial_specified_value="SpecifiedValue::initial_specified_value()",
|
||||
spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-" + axis,
|
||||
animation_value_type="ComputedValue", vector=True, delegate_animate=True)}
|
||||
% endfor
|
||||
|
||||
<%helpers:vector_longhand name="background-repeat" animation_value_type="none"
|
||||
spec="https://drafts.csswg.org/css-backgrounds/#the-background-repeat">
|
||||
|
|
|
@ -2143,11 +2143,11 @@ ${helpers.predefined_type("perspective",
|
|||
animation_value_type="ComputedValue")}
|
||||
|
||||
${helpers.predefined_type("perspective-origin",
|
||||
"position::OriginPosition",
|
||||
"computed::position::OriginPosition::center()",
|
||||
"position::Position",
|
||||
"computed::position::Position::center()",
|
||||
boxed="True",
|
||||
extra_prefixes="moz webkit",
|
||||
spec="https://drafts.csswg.org/css-transforms/#perspective-origin-property",
|
||||
spec="https://drafts.csswg.org/css-transforms-2/#perspective-origin-property",
|
||||
animation_value_type="ComputedValue")}
|
||||
|
||||
${helpers.single_keyword("backface-visibility",
|
||||
|
|
|
@ -90,49 +90,14 @@ ${helpers.single_keyword("mask-mode",
|
|||
}
|
||||
</%helpers:vector_longhand>
|
||||
|
||||
<%helpers:vector_longhand name="mask-position-x" products="gecko"
|
||||
animation_value_type="ComputedValue" extra_prefixes="webkit"
|
||||
spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position">
|
||||
pub use properties::longhands::background_position_x::single_value::get_initial_value;
|
||||
pub use properties::longhands::background_position_x::single_value::get_initial_position_value;
|
||||
pub use properties::longhands::background_position_x::single_value::get_initial_specified_value;
|
||||
pub use properties::longhands::background_position_x::single_value::parse;
|
||||
pub use properties::longhands::background_position_x::single_value::SpecifiedValue;
|
||||
pub use properties::longhands::background_position_x::single_value::computed_value;
|
||||
use properties::animated_properties::{Animatable, RepeatableListAnimatable};
|
||||
use properties::longhands::mask_position_x::computed_value::T as MaskPositionX;
|
||||
|
||||
impl Animatable for MaskPositionX {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
Ok(MaskPositionX(try!(self.0.interpolate(&other.0, progress))))
|
||||
}
|
||||
}
|
||||
|
||||
impl RepeatableListAnimatable for MaskPositionX {}
|
||||
</%helpers:vector_longhand>
|
||||
|
||||
<%helpers:vector_longhand name="mask-position-y" products="gecko"
|
||||
animation_value_type="ComputedValue" extra_prefixes="webkit"
|
||||
spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position">
|
||||
pub use properties::longhands::background_position_y::single_value::get_initial_value;
|
||||
pub use properties::longhands::background_position_y::single_value::get_initial_position_value;
|
||||
pub use properties::longhands::background_position_y::single_value::get_initial_specified_value;
|
||||
pub use properties::longhands::background_position_y::single_value::parse;
|
||||
pub use properties::longhands::background_position_y::single_value::SpecifiedValue;
|
||||
pub use properties::longhands::background_position_y::single_value::computed_value;
|
||||
use properties::animated_properties::{Animatable, RepeatableListAnimatable};
|
||||
use properties::longhands::mask_position_y::computed_value::T as MaskPositionY;
|
||||
|
||||
impl Animatable for MaskPositionY {
|
||||
#[inline]
|
||||
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
||||
Ok(MaskPositionY(try!(self.0.interpolate(&other.0, progress))))
|
||||
}
|
||||
}
|
||||
|
||||
impl RepeatableListAnimatable for MaskPositionY {}
|
||||
</%helpers:vector_longhand>
|
||||
% for (axis, direction) in [("x", "Horizontal"), ("y", "Vertical")]:
|
||||
${helpers.predefined_type("mask-position-" + axis, "position::" + direction + "Position",
|
||||
products="gecko", extra_prefixes="webkit",
|
||||
initial_value="computed::LengthOrPercentage::zero()",
|
||||
initial_specified_value="specified::PositionComponent::Center",
|
||||
spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position",
|
||||
animation_value_type="ComputedValue", vector=True, delegate_animate=True)}
|
||||
% endfor
|
||||
|
||||
${helpers.single_keyword("mask-clip",
|
||||
"border-box content-box padding-box",
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
use properties::longhands::background_clip;
|
||||
use properties::longhands::background_clip::single_value::computed_value::T as Clip;
|
||||
use properties::longhands::background_origin::single_value::computed_value::T as Origin;
|
||||
use values::specified::position::Position;
|
||||
use values::specified::{Position, PositionComponent};
|
||||
use parser::Parse;
|
||||
|
||||
impl From<background_origin::single_value::SpecifiedValue> for background_clip::single_value::SpecifiedValue {
|
||||
|
@ -39,7 +39,7 @@
|
|||
let mut background_${name} = background_${name}::SpecifiedValue(Vec::new());
|
||||
% endfor
|
||||
try!(input.parse_comma_separated(|input| {
|
||||
% for name in "image position_x position_y repeat size attachment origin clip".split():
|
||||
% for name in "image position repeat size attachment origin clip".split():
|
||||
let mut ${name} = None;
|
||||
% endfor
|
||||
loop {
|
||||
|
@ -52,10 +52,9 @@
|
|||
return Err(())
|
||||
}
|
||||
}
|
||||
if position_x.is_none() && position_y.is_none() {
|
||||
if position.is_none() {
|
||||
if let Ok(value) = input.try(|input| Position::parse(context, input)) {
|
||||
position_x = Some(value.horizontal);
|
||||
position_y = Some(value.vertical);
|
||||
position = Some(value);
|
||||
|
||||
// Parse background size, if applicable.
|
||||
size = input.try(|input| {
|
||||
|
@ -83,22 +82,17 @@
|
|||
}
|
||||
}
|
||||
let mut any = false;
|
||||
% for name in "image position_x position_y repeat size attachment origin clip".split():
|
||||
% for name in "image position repeat size attachment origin clip".split():
|
||||
any = any || ${name}.is_some();
|
||||
% endfor
|
||||
any = any || background_color.is_some();
|
||||
if any {
|
||||
if position_x.is_some() || position_y.is_some() {
|
||||
% for name in "position_x position_y".split():
|
||||
if let Some(bg_${name}) = ${name} {
|
||||
background_${name}.0.push(bg_${name});
|
||||
}
|
||||
% endfor
|
||||
if let Some(position) = position {
|
||||
background_position_x.0.push(position.horizontal);
|
||||
background_position_y.0.push(position.vertical);
|
||||
} else {
|
||||
% for name in "position_x position_y".split():
|
||||
background_${name}.0.push(background_${name}::single_value
|
||||
::get_initial_position_value());
|
||||
% endfor
|
||||
background_position_x.0.push(PositionComponent::zero());
|
||||
background_position_y.0.push(PositionComponent::zero());
|
||||
}
|
||||
% for name in "image repeat size attachment origin clip".split():
|
||||
if let Some(bg_${name}) = ${name} {
|
||||
|
@ -193,7 +187,7 @@
|
|||
<%helpers:shorthand name="background-position"
|
||||
sub_properties="background-position-x background-position-y"
|
||||
spec="https://drafts.csswg.org/css-backgrounds-4/#the-background-position">
|
||||
use properties::longhands::{background_position_x,background_position_y};
|
||||
use properties::longhands::{background_position_x, background_position_y};
|
||||
use values::specified::AllowQuirks;
|
||||
use values::specified::position::Position;
|
||||
|
||||
|
@ -202,18 +196,13 @@
|
|||
let mut position_y = background_position_y::SpecifiedValue(Vec::new());
|
||||
let mut any = false;
|
||||
|
||||
try!(input.parse_comma_separated(|input| {
|
||||
loop {
|
||||
if let Ok(value) = input.try(|input| Position::parse_quirky(context, input, AllowQuirks::Yes)) {
|
||||
position_x.0.push(value.horizontal);
|
||||
position_y.0.push(value.vertical);
|
||||
any = true;
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
input.parse_comma_separated(|input| {
|
||||
let value = Position::parse_quirky(context, input, AllowQuirks::Yes)?;
|
||||
position_x.0.push(value.horizontal);
|
||||
position_y.0.push(value.vertical);
|
||||
any = true;
|
||||
Ok(())
|
||||
}));
|
||||
})?;
|
||||
if !any {
|
||||
return Err(());
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
use properties::longhands::{mask_mode, mask_repeat, mask_clip, mask_origin, mask_composite, mask_position_x,
|
||||
mask_position_y};
|
||||
use properties::longhands::{mask_size, mask_image};
|
||||
use values::specified::position::Position;
|
||||
use values::specified::{Position, PositionComponent};
|
||||
use parser::Parse;
|
||||
|
||||
impl From<mask_origin::single_value::SpecifiedValue> for mask_clip::single_value::SpecifiedValue {
|
||||
|
@ -41,7 +41,7 @@
|
|||
% endfor
|
||||
|
||||
try!(input.parse_comma_separated(|input| {
|
||||
% for name in "image mode position_x position_y size repeat origin clip composite".split():
|
||||
% for name in "image mode position size repeat origin clip composite".split():
|
||||
let mut ${name} = None;
|
||||
% endfor
|
||||
loop {
|
||||
|
@ -52,10 +52,9 @@
|
|||
continue
|
||||
}
|
||||
}
|
||||
if position_x.is_none() && position_y.is_none() {
|
||||
if position.is_none() {
|
||||
if let Ok(value) = input.try(|input| Position::parse(context, input)) {
|
||||
position_x = Some(value.horizontal);
|
||||
position_y = Some(value.vertical);
|
||||
position = Some(value);
|
||||
|
||||
// Parse mask size, if applicable.
|
||||
size = input.try(|input| {
|
||||
|
@ -83,21 +82,16 @@
|
|||
}
|
||||
}
|
||||
let mut any = false;
|
||||
% for name in "image mode position_x position_y size repeat origin clip composite".split():
|
||||
% for name in "image mode position size repeat origin clip composite".split():
|
||||
any = any || ${name}.is_some();
|
||||
% endfor
|
||||
if any {
|
||||
if position_x.is_some() || position_y.is_some() {
|
||||
% for name in "position_x position_y".split():
|
||||
if let Some(bg_${name}) = ${name} {
|
||||
mask_${name}.0.push(bg_${name});
|
||||
}
|
||||
% endfor
|
||||
if let Some(position) = position {
|
||||
mask_position_x.0.push(position.horizontal);
|
||||
mask_position_y.0.push(position.vertical);
|
||||
} else {
|
||||
% for name in "position_x position_y".split():
|
||||
mask_${name}.0.push(mask_${name}::single_value
|
||||
::get_initial_position_value());
|
||||
% endfor
|
||||
mask_position_x.0.push(PositionComponent::zero());
|
||||
mask_position_y.0.push(PositionComponent::zero());
|
||||
}
|
||||
% for name in "image mode size repeat origin clip composite".split():
|
||||
if let Some(m_${name}) = ${name} {
|
||||
|
@ -191,18 +185,13 @@
|
|||
let mut position_y = mask_position_y::SpecifiedValue(Vec::new());
|
||||
let mut any = false;
|
||||
|
||||
try!(input.parse_comma_separated(|input| {
|
||||
loop {
|
||||
if let Ok(value) = input.try(|input| Position::parse(context, input)) {
|
||||
position_x.0.push(value.horizontal);
|
||||
position_y.0.push(value.vertical);
|
||||
any = true;
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
input.parse_comma_separated(|input| {
|
||||
let value = Position::parse(context, input)?;
|
||||
position_x.0.push(value.horizontal);
|
||||
position_y.0.push(value.vertical);
|
||||
any = true;
|
||||
Ok(())
|
||||
}));
|
||||
})?;
|
||||
if any == false {
|
||||
return Err(());
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue