Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strict types for style values #269

Merged
merged 16 commits into from
Nov 29, 2022

Conversation

nicoburns
Copy link
Collaborator

@nicoburns nicoburns commented Nov 28, 2022

Objective

Prevent invalid values from being on set on style properties.
Fixes #265 and #114
Does not fix #207 or #231

Context

Currently there are a number of properties that use the Dimension enum, and this meant that it had to represent a superset of all the possible value for each of those properties. This problem is anticipated to get worse as we add new values like MinContent, MaxContent and Calc.

Changes

  • Removes Dimension::Undefined. As all CSS values have a default if unspecified, this is used in place of Undefined.
  • Remove Default impl for Dimension. There is no single default: which value is the default depends on context. So to avoid false confidence in the default it is judged better simply not to have a default at all.
  • TaffyZero, TaffyAuto, and FromPoints traits have been added that abstract over all types that can hold a value of zero (f32, Option<f32>, Dimension), auto (Dimension), and points (f32, Option<f32>, Dimensions). They are also implemented for Point, Size, and Rect where the containing types implements the trait. This allows the return-type generic functions zero(), auto(), and points(f32) to be defined which with automagically created an instance of the correct type through type inference.
  • The ResolveOrDefault trait is replaced with ResolveOrZero based on the TaffyZero trait.
  • Add LengthPercentage type which is like Dimension, except with only Points and Percent variants (no Auto). Convert gap, padding, and border to use LengthPercentage instead of Dimension.
  • Add LengthPercentageAuto type. This is currently identical to Dimension, except in future we will likely to want to add MinContent, MaxContent, and FitContent variants to Dimension. And properties using LengthPercentageAuto don't support those properties. Convert position and margin to use LengthPercentageAuto instead of Dimension.

Feedback wanted

  • The CSS spec doesn't allow percentage borders, but Taffy historically has. For this reason, I've made the border property use LengthPercentage rather than f32. We could change this to f32 if people think that's better.
  • Dimension is currently identical to LengthPercentageAuto, so these could be merged into a single type if preferred. However, the properties using Dimension (width, min_width, max_width, flex_basis) support extra values (MinContent, MaxContent, FitContent, and flex_basis additionally supports Content) according the the spec, where the values using LengthPercentageAuto (position and margin) do not. So if and when we and support for those values, we'd need to split the type.
  • How do people feel about the automagical shorthand functions? They make writing styles much easier, but they do make the types less explicit.

@nicoburns nicoburns force-pushed the strict-style-value-types branch from 6e6f96c to 0d957d3 Compare November 28, 2022 17:36
@alice-i-cecile alice-i-cecile added usability Make the library more comfortable to use breaking-change A change that breaks our public interface controversial This work requires a heightened standard of review due to implementation or design complexity labels Nov 28, 2022
Copy link
Collaborator

@Weibye Weibye left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again this is great work!

The CSS spec doesn't allow percentage borders, but Taffy historically has. For this reason, I've made the border property use LengthPercentage rather than f32. We could change this to f32 if people think that's better.

For Bevy I'm sure there will be use-cases for percentage-driven borders. I also think there's value on keeping to spec, at least until we support multiple paradigms. I don't feel strongly either way. Suggest we keep as is for now.

Dimension is currently identical to LengthPercentageAuto, so these could be merged into a single type if preferred. However, the properties using Dimension (width, min_width, max_width, flex_basis) support extra values (MinContent, MaxContent, FitContent, and flex_basis additionally supports Content) according the the spec, where the values using LengthPercentageAuto (position and margin) do not. So if and when we and support for those values, we'd need to split the type.

It feels weird to have two identical types at this point, but I think your argument is a strong one for why we want to keep them separate.

Sidenote: For something like flex_basis: Content, would it make sense to move measure-func into that enum and change how we deal with the measure functions?

How do people feel about the automagical shorthand functions? They make writing styles much easier, but they do make the types less explicit.

I really like them! With the increased "security" of the types, I think we can get away with less expicitness and more ergonomics.

Comment on lines +434 to +437
margin: Rect::zero(),
padding: Rect::zero(),
border: Rect::zero(),
gap: Size::zero(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads so much clearer!

Comment on lines +389 to +398
pub position: Rect<LengthPercentageAuto>,
/// How large should the margin be on each side?
pub margin: Rect<Dimension>,
pub margin: Rect<LengthPercentageAuto>,
/// How large should the padding be on each side?
pub padding: Rect<Dimension>,
pub padding: Rect<LengthPercentage>,
/// How large should the border be on each side?
pub border: Rect<Dimension>,
pub border: Rect<LengthPercentage>,
// Gap
/// How large should the gaps between items in a grid or flex container be?
pub gap: Size<Dimension>,
pub gap: Size<LengthPercentage>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know enough about Rust. Can a type be flags of a different type? Cause that is what we are doing here: We have multiple options with points, percent, auto, undefined, and we want to accept some or many of those options.

Its something I'm curious about but not something that should block the PR.

Copy link
Collaborator Author

@nicoburns nicoburns Nov 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean like a sub-enum that takes a definition from a master one with all the variants? Unfortunately that's not yet a Rust feature, although it may be one day (it's been proposed before).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, good to know :)

@nicoburns
Copy link
Collaborator Author

Sidenote: For something like flex_basis: Content, would it make sense to move measure-func into that enum and change how we deal with the measure functions?

It would not. The reason being that flex-basis: content can also apply to Flex nodes! And grid nodes and any other kind of layout mode we support in future (it means, measure the size of this node without applying any size restriction, however that is done, and use that as the base size).

I think MeasureFuncs are likely to morph into custom layout modes at some point. More to come on this once I've sorted my thoughts out a bit.

@alice-i-cecile
Copy link
Collaborator

For Bevy I'm sure there will be use-cases for percentage-driven borders. I also think there's value on keeping to spec, at least until we support multiple paradigms. I don't feel strongly either way. Suggest we keep as is for now.

I'm comfortable with natural spec extensions, and I think that this one is sensible. I think we should keep it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking-change A change that breaks our public interface controversial This work requires a heightened standard of review due to implementation or design complexity usability Make the library more comfortable to use
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Enforce values that must not be negative at a type level Gap does not support a value of Auto
4 participants