diff --git a/packages/native-core/Cargo.toml b/packages/native-core/Cargo.toml index 9958b8a02..ae0027d1a 100644 --- a/packages/native-core/Cargo.toml +++ b/packages/native-core/Cargo.toml @@ -24,6 +24,9 @@ parking_lot = "0.12.1" crossbeam-deque = "0.8.2" dashmap = "5.4.0" +# for parsing attributes +lightningcss = "1.0.0-alpha.38" + [dev-dependencies] rand = "0.8.5" dioxus = { path = "../dioxus", version = "^0.2.1" } diff --git a/packages/native-core/src/layout_attributes.rs b/packages/native-core/src/layout_attributes.rs index b59177e4e..7d722d5de 100644 --- a/packages/native-core/src/layout_attributes.rs +++ b/packages/native-core/src/layout_attributes.rs @@ -1,7 +1,7 @@ /* -- [ ] pub display: Display, -- [x] pub position_type: PositionType, --> kinda, taffy doesnt support everything -- [ ] pub direction: Direction, +- [ ] pub display: Display, ----> taffy doesnt support all display types +- [x] pub position_type: PositionType, --> taffy doesnt support everything +- [x] pub direction: Direction, - [x] pub flex_direction: FlexDirection, - [x] pub flex_wrap: FlexWrap, @@ -9,7 +9,7 @@ - [x] pub flex_shrink: f32, - [x] pub flex_basis: Dimension, -- [x] pub overflow: Overflow, ---> kinda implemented... taffy doesnt have support for directional overflow +- [x] pub overflow: Overflow, ---> taffy doesnt have support for directional overflow - [x] pub align_items: AlignItems, - [x] pub align_self: AlignSelf, @@ -19,16 +19,27 @@ - [x] pub padding: Rect, - [x] pub justify_content: JustifyContent, -- [ ] pub position: Rect, +- [x] pub position: Rect, - [x] pub border: Rect, -- [ ] pub size: Size, ----> ??? seems to only be relevant for input? +- [ ] pub size: Size, ----> seems to only be relevant for input? - [ ] pub min_size: Size, - [ ] pub max_size: Size, -- [ ] pub aspect_ratio: Number, +- [ ] pub aspect_ratio: Number, ----> parsing is done, but taffy doesnt support it */ +use lightningcss::properties::{align, display, flex, position, size}; +use lightningcss::{ + properties::{align::GapValue, border::BorderSideWidth, Property, PropertyId}, + stylesheet::ParserOptions, + traits::Parse, + values::{ + length::{Length, LengthPercentageOrAuto, LengthValue}, + percentage::DimensionPercentage, + ratio::Ratio, + }, +}; use taffy::{ prelude::*, style::{FlexDirection, PositionType}, @@ -36,229 +47,303 @@ use taffy::{ /// applies the entire html namespace defined in dioxus-html pub fn apply_layout_attributes(name: &str, value: &str, style: &mut Style) { - match name { - "align-content" - | "align-items" - | "align-self" => apply_align(name, value, style), - - "animation" - | "animation-delay" - | "animation-direction" - | "animation-duration" - | "animation-fill-mode" - | "animation-iteration-count" - | "animation-name" - | "animation-play-state" - | "animation-timing-function" => apply_animation(name, value, style), - - "backface-visibility" => {} - - "border" - | "border-bottom" - | "border-bottom-color" - | "border-bottom-left-radius" - | "border-bottom-right-radius" - | "border-bottom-style" - | "border-bottom-width" - | "border-collapse" - | "border-color" - | "border-image" - | "border-image-outset" - | "border-image-repeat" - | "border-image-slice" - | "border-image-source" - | "border-image-width" - | "border-left" - | "border-left-color" - | "border-left-style" - | "border-left-width" - | "border-radius" - | "border-right" - | "border-right-color" - | "border-right-style" - | "border-right-width" - | "border-spacing" - | "border-style" - | "border-top" - | "border-top-color" - | "border-top-left-radius" - | "border-top-right-radius" - | "border-top-style" - | "border-top-width" - | "border-width" => apply_border(name, value, style), - - "bottom" => {} - "box-shadow" => {} - "box-sizing" => {} - "caption-side" => {} - "clear" => {} - "clip" => {} - - "column-count" - | "column-fill" - | "column-gap" - | "column-rule" - | "column-rule-color" - | "column-rule-style" - | "column-rule-width" - | "column-span" - // add column-width - | "column-width" => apply_column(name, value, style), - - "columns" => {} - - "content" => {} - "counter-increment" => {} - "counter-reset" => {} - - "cursor" => {} - "direction" => {} - - "display" => apply_display(name, value, style), - - "empty-cells" => {} - - "flex" - | "flex-basis" - | "flex-direction" - | "flex-flow" - | "flex-grow" - | "flex-shrink" - | "flex-wrap" => apply_flex(name, value, style), - - "float" => {}, - - "font-style" - | "font-variant" - | "font-weight" - | "font-size" - | "line-height" - | "font-family" => apply_font(name, value, style), - - "height" => { - if let Some(v) = parse_value(value){ - style.size.height = v; + if let Ok(property) = + Property::parse_string(PropertyId::from(name), value, ParserOptions::default()) + { + match property { + Property::Display(display) => match display { + display::Display::Keyword(display::DisplayKeyword::None) => { + style.display = Display::None + } + display::Display::Pair(pair) => { + if let display::DisplayInside::Flex(_) = pair.inside { + style.display = Display::Flex + } + } + _ => (), + }, + Property::Position(position) => { + style.position_type = match position { + position::Position::Relative => PositionType::Relative, + position::Position::Absolute => PositionType::Absolute, + _ => return, + } + } + Property::Top(top) => style.position.top = convert_length_percentage_or_auto(top), + Property::Bottom(bottom) => { + style.position.bottom = convert_length_percentage_or_auto(bottom) + } + Property::Left(left) => style.position.left = convert_length_percentage_or_auto(left), + Property::Right(right) => { + style.position.right = convert_length_percentage_or_auto(right) + } + Property::Inset(inset) => { + style.position.top = convert_length_percentage_or_auto(inset.top); + style.position.bottom = convert_length_percentage_or_auto(inset.bottom); + style.position.left = convert_length_percentage_or_auto(inset.left); + style.position.right = convert_length_percentage_or_auto(inset.right); + } + Property::BorderTopWidth(width) => { + style.border.top = convert_border_side_width(width); + } + Property::BorderBottomWidth(width) => { + style.border.bottom = convert_border_side_width(width); + } + Property::BorderLeftWidth(width) => { + style.border.left = convert_border_side_width(width); + } + Property::BorderRightWidth(width) => { + style.border.right = convert_border_side_width(width); + } + Property::BorderWidth(width) => { + style.border.top = convert_border_side_width(width.top); + style.border.bottom = convert_border_side_width(width.bottom); + style.border.left = convert_border_side_width(width.left); + style.border.right = convert_border_side_width(width.right); + } + Property::Border(border) => { + let width = convert_border_side_width(border.width); + style.border.top = width; + style.border.bottom = width; + style.border.left = width; + style.border.right = width; + } + Property::BorderTop(top) => { + style.border.top = convert_border_side_width(top.width); + } + Property::BorderBottom(bottom) => { + style.border.bottom = convert_border_side_width(bottom.width); + } + Property::BorderLeft(left) => { + style.border.left = convert_border_side_width(left.width); + } + Property::BorderRight(right) => { + style.border.right = convert_border_side_width(right.width); + } + Property::FlexDirection(flex_direction, _) => { + use FlexDirection::*; + style.flex_direction = match flex_direction { + flex::FlexDirection::Row => Row, + flex::FlexDirection::RowReverse => RowReverse, + flex::FlexDirection::Column => Column, + flex::FlexDirection::ColumnReverse => ColumnReverse, + } + } + Property::FlexWrap(wrap, _) => { + use FlexWrap::*; + style.flex_wrap = match wrap { + flex::FlexWrap::NoWrap => NoWrap, + flex::FlexWrap::Wrap => Wrap, + flex::FlexWrap::WrapReverse => WrapReverse, + } + } + Property::FlexGrow(grow, _) => { + style.flex_grow = grow; + } + Property::FlexShrink(shrink, _) => { + style.flex_shrink = shrink; + } + Property::FlexBasis(basis, _) => { + style.flex_basis = convert_length_percentage_or_auto(basis); + } + Property::Flex(flex, _) => { + style.flex_grow = flex.grow; + style.flex_shrink = flex.shrink; + style.flex_basis = convert_length_percentage_or_auto(flex.basis); + } + Property::AlignContent(align, _) => { + use AlignContent::*; + style.align_content = match align { + align::AlignContent::ContentDistribution(distribution) => match distribution { + align::ContentDistribution::SpaceBetween => SpaceBetween, + align::ContentDistribution::SpaceAround => SpaceAround, + align::ContentDistribution::SpaceEvenly => SpaceEvenly, + align::ContentDistribution::Stretch => Stretch, + }, + align::AlignContent::ContentPosition(_, position) => match position { + align::ContentPosition::Center => Center, + align::ContentPosition::Start | align::ContentPosition::FlexStart => { + FlexStart + } + align::ContentPosition::End | align::ContentPosition::FlexEnd => FlexEnd, + }, + _ => return, + }; + } + Property::JustifyContent(justify, _) => { + use JustifyContent::*; + style.justify_content = match justify { + align::JustifyContent::ContentDistribution(distribution) => { + match distribution { + align::ContentDistribution::SpaceBetween => SpaceBetween, + align::ContentDistribution::SpaceAround => SpaceAround, + align::ContentDistribution::SpaceEvenly => SpaceEvenly, + _ => return, + } + } + align::JustifyContent::ContentPosition(_, position) => match position { + align::ContentPosition::Center => Center, + // start ignores -reverse flex-direction but there is no way to specify that in Taffy + align::ContentPosition::Start | align::ContentPosition::FlexStart => { + FlexStart + } + // end ignores -reverse flex-direction but there is no way to specify that in Taffy + align::ContentPosition::End | align::ContentPosition::FlexEnd => FlexEnd, + }, + _ => return, + }; + } + Property::AlignSelf(align, _) => { + use AlignSelf::*; + style.align_self = match align { + align::AlignSelf::Auto => Auto, + align::AlignSelf::Stretch => Stretch, + align::AlignSelf::BaselinePosition(_) => Baseline, + align::AlignSelf::SelfPosition(_overflow, position) => match position { + align::SelfPosition::Center => Center, + align::SelfPosition::Start + | align::SelfPosition::SelfStart + | align::SelfPosition::FlexStart => FlexStart, + align::SelfPosition::End + | align::SelfPosition::SelfEnd + | align::SelfPosition::FlexEnd => FlexEnd, + }, + _ => return, + }; + } + Property::AlignItems(align, _) => { + use AlignItems::*; + style.align_items = match align { + align::AlignItems::BaselinePosition(_) => Baseline, + align::AlignItems::Stretch => Stretch, + align::AlignItems::SelfPosition(_overflow, position) => match position { + align::SelfPosition::Center => Center, + align::SelfPosition::FlexStart => FlexStart, + align::SelfPosition::FlexEnd => FlexEnd, + _ => return, + }, + _ => return, + }; + } + Property::RowGap(row_gap) => { + style.gap.width = convert_gap_value(row_gap); + } + Property::ColumnGap(column_gap) => { + style.gap.height = convert_gap_value(column_gap); + } + Property::Gap(gap) => { + style.gap = Size { + width: convert_gap_value(gap.row), + height: convert_gap_value(gap.column), + }; + } + Property::MarginTop(margin) => { + style.margin.top = convert_length_percentage_or_auto(margin); + } + Property::MarginBottom(margin) => { + style.margin.bottom = convert_length_percentage_or_auto(margin); + } + Property::MarginLeft(margin) => { + style.margin.left = convert_length_percentage_or_auto(margin); + } + Property::MarginRight(margin) => { + style.margin.right = convert_length_percentage_or_auto(margin); + } + Property::Margin(margin) => { + style.margin = Rect { + top: convert_length_percentage_or_auto(margin.top), + bottom: convert_length_percentage_or_auto(margin.bottom), + left: convert_length_percentage_or_auto(margin.left), + right: convert_length_percentage_or_auto(margin.right), + }; + } + Property::PaddingTop(padding) => { + style.padding.top = convert_length_percentage_or_auto(padding); + } + Property::PaddingBottom(padding) => { + style.padding.bottom = convert_length_percentage_or_auto(padding); + } + Property::PaddingLeft(padding) => { + style.padding.left = convert_length_percentage_or_auto(padding); + } + Property::PaddingRight(padding) => { + style.padding.right = convert_length_percentage_or_auto(padding); + } + Property::Padding(padding) => { + style.padding = Rect { + top: convert_length_percentage_or_auto(padding.top), + bottom: convert_length_percentage_or_auto(padding.bottom), + left: convert_length_percentage_or_auto(padding.left), + right: convert_length_percentage_or_auto(padding.right), + }; + } + Property::Width(width) => { + style.size.width = convert_size(width); + } + Property::Height(height) => { + style.size.height = convert_size(height); + } + _ => (), + } + // currently not implemented in lightningcss + if name == "aspect-ratio" { + if let Ok(ratio) = Ratio::parse_string(value) { + style.aspect_ratio = Some(ratio.0 / ratio.1); } } - "justify-content" => { - use JustifyContent::*; - style.justify_content = match value { - "flex-start" => FlexStart, - "flex-end" => FlexEnd, - "center" => Center, - "space-between" => SpaceBetween, - "space-around" => SpaceAround, - "space-evenly" => SpaceEvenly, - _ => FlexStart, - }; - } - "left" => {} - "letter-spacing" => {} - - "list-style" - | "list-style-image" - | "list-style-position" - | "list-style-type" => {} - - "margin" - | "margin-bottom" - | "margin-left" - | "margin-right" - | "margin-top" => apply_margin(name, value, style), - - "max-height" => {} - "max-width" => {} - "min-height" => {} - "min-width" => {} - - "opacity" => {} - "order" => {} - "outline" => {} - - "outline-color" - | "outline-offset" - | "outline-style" - | "outline-width" => {} - - "overflow" - | "overflow-x" - | "overflow-y" => apply_overflow(name, value, style), - - "padding" - | "padding-bottom" - | "padding-left" - | "padding-right" - | "padding-top" => apply_padding(name, value, style), - - "page-break-after" - | "page-break-before" - | "page-break-inside" => {} - - "perspective" - | "perspective-origin" => {} - - "position" => { - match value { - "static" => {} - "relative" => style.position_type = PositionType::Relative, - "fixed" => {} - "absolute" => style.position_type = PositionType::Absolute, - "sticky" => {} - _ => {} - } - - } - - "pointer-events" => {} - - "quotes" => {} - "resize" => {} - "right" => {} - "tab-size" => {} - "table-layout" => {} - - "top" => {} - - "transform" - | "transform-origin" - | "transform-style" => apply_transform(name, value, style), - - "transition" - | "transition-delay" - | "transition-duration" - | "transition-property" - | "transition-timing-function" => apply_transition(name, value, style), - - "vertical-align" => {} - "visibility" => {} - "white-space" => {} - "width" => { - if let Some(v) = parse_value(value){ - style.size.width = v; - } - } - "word-break" => {} - "word-spacing" => {} - "word-wrap" => {} - "z-index" => {} - _ => {} } } -fn apply_font(name: &str, value: &str, style: &mut Style) { - match name { - "font-style" => {} - "font-variant" => {} - "font-weight" => {} - "font-size" => {} - "line-height" => { - style.size = Size { - width: style.size.width, - height: parse_value(value).unwrap_or(Dimension::Points(12.0)), - } +fn convert_length_value(length_value: LengthValue) -> Dimension { + match length_value { + LengthValue::Px(value) => Dimension::Points(value), + _ => todo!(), + } +} + +fn convert_dimension_percentage( + dimension_percentage: DimensionPercentage, +) -> Dimension { + match dimension_percentage { + DimensionPercentage::Dimension(value) => convert_length_value(value), + DimensionPercentage::Percentage(percentage) => Dimension::Percent(percentage.0), + _ => todo!(), + } +} + +fn convert_length_percentage_or_auto( + length_percentage_or_auto: LengthPercentageOrAuto, +) -> Dimension { + match length_percentage_or_auto { + LengthPercentageOrAuto::Auto => Dimension::Auto, + LengthPercentageOrAuto::LengthPercentage(percentage) => { + convert_dimension_percentage(percentage) } - "font-family" => {} - _ => {} + } +} + +fn convert_border_side_width(border_side_width: BorderSideWidth) -> Dimension { + match border_side_width { + BorderSideWidth::Length(Length::Value(value)) => convert_length_value(value), + BorderSideWidth::Thick => Dimension::Points(5.0), + BorderSideWidth::Medium => Dimension::Points(3.0), + BorderSideWidth::Thin => Dimension::Points(1.0), + _ => todo!(), + } +} + +fn convert_gap_value(gap_value: GapValue) -> Dimension { + match gap_value { + GapValue::LengthPercentage(dim) => convert_dimension_percentage(dim), + GapValue::Normal => Dimension::Auto, + } +} + +fn convert_size(size: size::Size) -> Dimension { + match size { + size::Size::Auto => Dimension::Auto, + size::Size::LengthPercentage(length) => convert_dimension_percentage(length), + _ => todo!(), } } @@ -280,312 +365,3 @@ pub fn parse_value(value: &str) -> Option { None } } - -fn apply_overflow(_name: &str, _value: &str, _style: &mut Style) { - // todo: add overflow support to taffy -} - -fn apply_display(_name: &str, value: &str, style: &mut Style) { - style.display = match value { - "flex" => Display::Flex, - "block" => Display::None, - _ => Display::Flex, - } - - // TODO: there are way more variants - // taffy needs to be updated to handle them - // - // "block" => Display::Block, - // "inline" => Display::Inline, - // "inline-block" => Display::InlineBlock, - // "inline-table" => Display::InlineTable, - // "list-item" => Display::ListItem, - // "run-in" => Display::RunIn, - // "table" => Display::Table, - // "table-caption" => Display::TableCaption, - // "table-cell" => Display::TableCell, - // "table-column" => Display::TableColumn, - // "table-column-group" => Display::TableColumnGroup, - // "table-footer-group" => Display::TableFooterGroup, - // "table-header-group" => Display::TableHeaderGroup, - // "table-row" => Display::TableRow, - // "table-row-group" => Display::TableRowGroup, - // "none" => Display::None, - // _ => Display::Inline, -} - -fn apply_border(name: &str, value: &str, style: &mut Style) { - match name { - "border" => {} - "border-bottom" => {} - "border-bottom-color" => {} - "border-bottom-left-radius" => {} - "border-bottom-right-radius" => {} - "border-bottom-style" => { - if style.border.bottom == Dimension::default() { - let v = Dimension::Points(1.0); - style.border.bottom = v; - } - } - "border-bottom-width" => { - if let Some(v) = parse_value(value) { - style.border.bottom = v; - } - } - "border-collapse" => {} - "border-color" => {} - "border-image" => {} - "border-image-outset" => {} - "border-image-repeat" => {} - "border-image-slice" => {} - "border-image-source" => {} - "border-image-width" => {} - "border-left" => {} - "border-left-color" => {} - "border-left-style" => { - if style.border.left == Dimension::default() { - let v = Dimension::Points(1.0); - style.border.left = v; - } - } - "border-left-width" => { - if let Some(v) = parse_value(value) { - style.border.left = v; - } - } - "border-radius" => {} - "border-right" => {} - "border-right-color" => {} - "border-right-style" => { - let v = Dimension::Points(1.0); - style.border.right = v; - } - "border-right-width" => { - if let Some(v) = parse_value(value) { - style.border.right = v; - } - } - "border-spacing" => {} - "border-style" => { - if style.border.top == Dimension::default() { - let v = Dimension::Points(1.0); - style.border.top = v; - } - if style.border.bottom == Dimension::default() { - let v = Dimension::Points(1.0); - style.border.bottom = v; - } - if style.border.left == Dimension::default() { - let v = Dimension::Points(1.0); - style.border.left = v; - } - if style.border.right == Dimension::default() { - let v = Dimension::Points(1.0); - style.border.right = v; - } - } - "border-top" => {} - "border-top-color" => {} - "border-top-left-radius" => {} - "border-top-right-radius" => {} - "border-top-style" => { - if style.border.top == Dimension::default() { - let v = Dimension::Points(1.0); - style.border.top = v; - } - } - "border-top-width" => { - if let Some(v) = parse_value(value) { - style.border.top = v; - } - } - "border-width" => { - let values: Vec<_> = value.split(' ').collect(); - if values.len() == 1 { - if let Some(dim) = parse_value(values[0]) { - style.border = Rect { - right: dim, - left: dim, - top: dim, - bottom: dim, - }; - } - } else { - let border_widths = [ - &mut style.border.top, - &mut style.border.bottom, - &mut style.border.left, - &mut style.border.right, - ]; - for (v, width) in values.into_iter().zip(border_widths) { - if let Some(w) = parse_value(v) { - *width = w; - } - } - } - } - _ => (), - } -} - -fn apply_animation(name: &str, _value: &str, _style: &mut Style) { - match name { - "animation" => {} - "animation-delay" => {} - "animation-direction =>{}" => {} - "animation-duration" => {} - "animation-fill-mode" => {} - "animation-itera =>{}tion-count" => {} - "animation-name" => {} - "animation-play-state" => {} - "animation-timing-function" => {} - _ => {} - } -} - -fn apply_column(name: &str, _value: &str, _style: &mut Style) { - match name { - "column-count" => {} - "column-fill" => {} - "column-gap" => {} - "column-rule" => {} - "column-rule-color" => {} - "column-rule-style" => {} - "column-rule-width" => {} - "column-span" => {} - "column-width" => {} - _ => {} - } -} - -fn apply_flex(name: &str, value: &str, style: &mut Style) { - // - [x] pub flex_direction: FlexDirection, - // - [x] pub flex_wrap: FlexWrap, - // - [x] pub flex_grow: f32, - // - [x] pub flex_shrink: f32, - // - [x] pub flex_basis: Dimension, - - match name { - "flex" => {} - "flex-direction" => { - use FlexDirection::*; - style.flex_direction = match value { - "row" => Row, - "row-reverse" => RowReverse, - "column" => Column, - "column-reverse" => ColumnReverse, - _ => Row, - }; - } - "flex-basis" => { - if let Some(v) = parse_value(value) { - style.flex_basis = v; - } - } - "flex-flow" => {} - "flex-grow" => { - if let Ok(val) = value.parse::() { - style.flex_grow = val; - } - } - "flex-shrink" => { - if let Ok(px) = value.parse::() { - style.flex_shrink = px; - } - } - "flex-wrap" => { - use FlexWrap::*; - style.flex_wrap = match value { - "nowrap" => NoWrap, - "wrap" => Wrap, - "wrap-reverse" => WrapReverse, - _ => NoWrap, - }; - } - _ => {} - } -} - -fn apply_padding(name: &str, value: &str, style: &mut Style) { - if let Some(v) = parse_value(value) { - match name { - "padding" => { - style.padding.top = v; - style.padding.bottom = v; - style.padding.left = v; - style.padding.right = v; - } - "padding-bottom" => style.padding.bottom = v, - "padding-left" => style.padding.left = v, - "padding-right" => style.padding.right = v, - "padding-top" => style.padding.top = v, - _ => {} - } - } -} - -fn apply_transform(_name: &str, _value: &str, _style: &mut Style) { - todo!() -} - -fn apply_transition(_name: &str, _value: &str, _style: &mut Style) { - todo!() -} - -fn apply_align(name: &str, value: &str, style: &mut Style) { - match name { - "align-items" => { - use AlignItems::*; - style.align_items = match value { - "flex-start" => FlexStart, - "flex-end" => FlexEnd, - "center" => Center, - "baseline" => Baseline, - "stretch" => Stretch, - _ => FlexStart, - }; - } - "align-content" => { - use AlignContent::*; - style.align_content = match value { - "flex-start" => FlexStart, - "flex-end" => FlexEnd, - "center" => Center, - "space-between" => SpaceBetween, - "space-around" => SpaceAround, - _ => FlexStart, - }; - } - "align-self" => { - use AlignSelf::*; - style.align_self = match value { - "auto" => Auto, - "flex-start" => FlexStart, - "flex-end" => FlexEnd, - "center" => Center, - "baseline" => Baseline, - "stretch" => Stretch, - _ => Auto, - }; - } - _ => {} - } -} - -fn apply_margin(name: &str, value: &str, style: &mut Style) { - if let Some(dim) = parse_value(value) { - match name { - "margin" => { - style.margin.top = dim; - style.margin.bottom = dim; - style.margin.left = dim; - style.margin.right = dim; - } - "margin-top" => style.margin.top = dim, - "margin-bottom" => style.margin.bottom = dim, - "margin-left" => style.margin.left = dim, - "margin-right" => style.margin.right = dim, - _ => {} - } - } -}