Merge branch 'upstream' into simplify-native-core
This commit is contained in:
commit
ecf4ee275a
142 files changed
+2951
-1853
No files matched your search
@@ -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<Dimension>,
|
||||
|
||||
- [x] pub justify_content: JustifyContent,
|
||||
- [ ] pub position: Rect<Dimension>,
|
||||
- [x] pub position: Rect<Dimension>,
|
||||
- [x] pub border: Rect<Dimension>,
|
||||
|
||||
- [ ] pub size: Size<Dimension>, ----> ??? seems to only be relevant for input?
|
||||
- [ ] pub size: Size<Dimension>, ----> seems to only be relevant for input?
|
||||
- [ ] pub min_size: Size<Dimension>,
|
||||
- [ ] pub max_size: Size<Dimension>,
|
||||
|
||||
- [ ] 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,311 @@ 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 {
|
||||
value: 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 {
|
||||
value: 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 {
|
||||
value: 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 {
|
||||
value: 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<LengthValue>,
|
||||
) -> 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 +373,3 @@ pub fn parse_value(value: &str) -> Option<Dimension> {
|
||||
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::<f32>() {
|
||||
style.flex_grow = val;
|
||||
}
|
||||
}
|
||||
"flex-shrink" => {
|
||||
if let Ok(px) = value.parse::<f32>() {
|
||||
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,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
use std::any::Any;
|
||||
use std::hash::BuildHasherDefault;
|
||||
|
||||
pub use node_ref::NodeMask;
|
||||
pub use passes::AnyMapLike;
|
||||
pub use passes::{Dependancy, Pass, TypeErasedPass};
|
||||
use rustc_hash::FxHasher;
|
||||
pub use state::State;
|
||||
pub use tree::NodeId;
|
||||
use tree::NodeId;
|
||||
|
||||
pub mod layout_attributes;
|
||||
pub mod node;
|
||||
@@ -18,7 +18,7 @@ pub mod utils;
|
||||
|
||||
pub type FxDashMap<K, V> = dashmap::DashMap<K, V, BuildHasherDefault<FxHasher>>;
|
||||
pub type FxDashSet<K> = dashmap::DashSet<K, BuildHasherDefault<FxHasher>>;
|
||||
pub type SendAnyMap = anymap::Map<dyn anymap::any::Any + Send + Sync + 'static>;
|
||||
pub type SendAnyMap = anymap::Map<dyn Any + Send + Sync + 'static>;
|
||||
|
||||
/// Used in derived state macros
|
||||
#[derive(Eq, PartialEq)]
|
||||
|
||||
@@ -1,44 +1,45 @@
|
||||
use crate::{state::State, tree::NodeId};
|
||||
use dioxus_core::ElementId;
|
||||
use dioxus_core::{AnyValue, BorrowedAttributeValue, ElementId};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// The node is stored client side and stores only basic data about the node.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Node<S: State> {
|
||||
pub struct Node<S: State<V>, V: FromAnyValue = ()> {
|
||||
/// The transformed state of the node.
|
||||
pub state: S,
|
||||
/// The raw data for the node
|
||||
pub node_data: NodeData,
|
||||
pub node_data: NodeData<V>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NodeData {
|
||||
pub struct NodeData<V: FromAnyValue = ()> {
|
||||
/// The id of the node
|
||||
pub node_id: NodeId,
|
||||
/// The id of the node in the vdom.
|
||||
pub element_id: Option<ElementId>,
|
||||
/// Additional inforation specific to the node type
|
||||
pub node_type: NodeType,
|
||||
pub node_type: NodeType<V>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ElementNode {
|
||||
pub struct ElementNode<V: FromAnyValue> {
|
||||
pub tag: String,
|
||||
pub namespace: Option<String>,
|
||||
pub attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue>,
|
||||
pub attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>>,
|
||||
pub listeners: FxHashSet<String>,
|
||||
}
|
||||
|
||||
/// A type of node with data specific to the node type. The types are a subset of the [VNode] types.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NodeType {
|
||||
pub enum NodeType<V: FromAnyValue = ()> {
|
||||
Text(String),
|
||||
Element(ElementNode),
|
||||
Element(ElementNode<V>),
|
||||
Placeholder,
|
||||
}
|
||||
|
||||
impl<S: State> Node<S> {
|
||||
pub fn new(node_type: NodeType) -> Self {
|
||||
impl<S: State<V>, V: FromAnyValue> Node<S, V> {
|
||||
pub(crate) fn new(node_type: NodeType<V>) -> Self {
|
||||
Node {
|
||||
state: S::default(),
|
||||
node_data: NodeData {
|
||||
@@ -65,24 +66,57 @@ pub struct OwnedAttributeDiscription {
|
||||
/// An attribute on a DOM node, such as `id="my-thing"` or
|
||||
/// `href="https://example.com"`.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct OwnedAttributeView<'a> {
|
||||
pub struct OwnedAttributeView<'a, V: FromAnyValue = ()> {
|
||||
/// The discription of the attribute.
|
||||
pub attribute: &'a OwnedAttributeDiscription,
|
||||
|
||||
/// The value of the attribute.
|
||||
pub value: &'a OwnedAttributeValue,
|
||||
pub value: &'a OwnedAttributeValue<V>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum OwnedAttributeValue {
|
||||
#[derive(Clone)]
|
||||
pub enum OwnedAttributeValue<V: FromAnyValue = ()> {
|
||||
Text(String),
|
||||
Float(f32),
|
||||
Int(i32),
|
||||
Float(f64),
|
||||
Int(i64),
|
||||
Bool(bool),
|
||||
None,
|
||||
Custom(V),
|
||||
}
|
||||
|
||||
impl OwnedAttributeValue {
|
||||
pub trait FromAnyValue: Clone + 'static {
|
||||
fn from_any_value(value: &dyn AnyValue) -> Self;
|
||||
}
|
||||
|
||||
impl FromAnyValue for () {
|
||||
fn from_any_value(_: &dyn AnyValue) -> Self {}
|
||||
}
|
||||
|
||||
impl<V: FromAnyValue> Debug for OwnedAttributeValue<V> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
|
||||
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
|
||||
Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
|
||||
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
|
||||
Self::Custom(_) => f.debug_tuple("Any").finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: FromAnyValue> From<BorrowedAttributeValue<'_>> for OwnedAttributeValue<V> {
|
||||
fn from(value: BorrowedAttributeValue<'_>) -> Self {
|
||||
match value {
|
||||
BorrowedAttributeValue::Text(text) => Self::Text(text.to_string()),
|
||||
BorrowedAttributeValue::Float(float) => Self::Float(float),
|
||||
BorrowedAttributeValue::Int(int) => Self::Int(int),
|
||||
BorrowedAttributeValue::Bool(bool) => Self::Bool(bool),
|
||||
BorrowedAttributeValue::Any(any) => Self::Custom(V::from_any_value(&*any)),
|
||||
BorrowedAttributeValue::None => panic!("None attribute values result in removing the attribute, not converting it to a None value.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: FromAnyValue> OwnedAttributeValue<V> {
|
||||
pub fn as_text(&self) -> Option<&str> {
|
||||
match self {
|
||||
OwnedAttributeValue::Text(text) => Some(text),
|
||||
@@ -90,14 +124,14 @@ impl OwnedAttributeValue {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_float(&self) -> Option<f32> {
|
||||
pub fn as_float(&self) -> Option<f64> {
|
||||
match self {
|
||||
OwnedAttributeValue::Float(float) => Some(*float),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_int(&self) -> Option<i32> {
|
||||
pub fn as_int(&self) -> Option<i64> {
|
||||
match self {
|
||||
OwnedAttributeValue::Int(int) => Some(*int),
|
||||
_ => None,
|
||||
@@ -111,9 +145,9 @@ impl OwnedAttributeValue {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_none(&self) -> Option<()> {
|
||||
pub fn as_custom(&self) -> Option<&V> {
|
||||
match self {
|
||||
OwnedAttributeValue::None => Some(()),
|
||||
OwnedAttributeValue::Custom(custom) => Some(custom),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
use dioxus_core::ElementId;
|
||||
|
||||
use crate::{
|
||||
node::{ElementNode, NodeData, NodeType, OwnedAttributeView},
|
||||
node::{ElementNode, FromAnyValue, NodeData, NodeType, OwnedAttributeView},
|
||||
state::union_ordered_iter,
|
||||
NodeId,
|
||||
};
|
||||
|
||||
/// A view into a [VNode] with limited access.
|
||||
#[derive(Debug)]
|
||||
pub struct NodeView<'a> {
|
||||
inner: &'a NodeData,
|
||||
pub struct NodeView<'a, V: FromAnyValue = ()> {
|
||||
inner: &'a NodeData<V>,
|
||||
mask: NodeMask,
|
||||
}
|
||||
|
||||
impl<'a> NodeView<'a> {
|
||||
impl<'a, V: FromAnyValue> NodeView<'a, V> {
|
||||
/// Create a new NodeView from a VNode, and mask.
|
||||
pub fn new(node: &'a NodeData, view: NodeMask) -> Self {
|
||||
pub fn new(node: &'a NodeData<V>, view: NodeMask) -> Self {
|
||||
Self {
|
||||
inner: node,
|
||||
mask: view,
|
||||
@@ -55,7 +55,9 @@ impl<'a> NodeView<'a> {
|
||||
}
|
||||
|
||||
/// Get any attributes that are enabled in the mask
|
||||
pub fn attributes<'b>(&'b self) -> Option<impl Iterator<Item = OwnedAttributeView<'a>> + 'b> {
|
||||
pub fn attributes<'b>(
|
||||
&'b self,
|
||||
) -> Option<impl Iterator<Item = OwnedAttributeView<'a, V>> + 'b> {
|
||||
match &self.inner.node_type {
|
||||
NodeType::Element(ElementNode { attributes, .. }) => Some(
|
||||
attributes
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::any::{Any, TypeId};
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::node::Node;
|
||||
use crate::node::{FromAnyValue, Node};
|
||||
use crate::node_ref::NodeView;
|
||||
use crate::state::State;
|
||||
use crate::tree::TreeViewMut;
|
||||
@@ -12,7 +12,7 @@ use crate::tree::{Tree, TreeView};
|
||||
use crate::{FxDashMap, FxDashSet, SendAnyMap};
|
||||
use crate::{NodeId, NodeMask};
|
||||
|
||||
pub trait Pass: Any {
|
||||
pub trait Pass<V: FromAnyValue>: Any {
|
||||
/// This is a tuple of (T: Any, ..)
|
||||
type ParentDependencies: Dependancy;
|
||||
/// This is a tuple of (T: Any, ..)
|
||||
@@ -23,7 +23,7 @@ pub trait Pass: Any {
|
||||
|
||||
fn pass<'a>(
|
||||
&mut self,
|
||||
node_view: NodeView,
|
||||
node_view: NodeView<V>,
|
||||
node: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
|
||||
parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
|
||||
children: Option<
|
||||
@@ -58,7 +58,7 @@ pub trait Pass: Any {
|
||||
}
|
||||
}
|
||||
|
||||
fn to_type_erased<T: AnyMapLike + State>() -> TypeErasedPass<T>
|
||||
fn to_type_erased<T: AnyMapLike + State<V>>() -> TypeErasedPass<T, V>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
@@ -72,11 +72,11 @@ pub trait Pass: Any {
|
||||
mask: Self::NODE_MASK,
|
||||
pass_direction: Self::pass_direction(),
|
||||
pass: Box::new(
|
||||
|node_id: NodeId, any_map: &mut Tree<Node<T>>, context: &SendAnyMap| {
|
||||
|node_id: NodeId, any_map: &mut Tree<Node<T, V>>, context: &SendAnyMap| {
|
||||
let (current_node, parent, children) = any_map
|
||||
.node_parent_children_mut(node_id)
|
||||
.expect("tried to run pass on node that does not exist");
|
||||
let current_node_raw = current_node as *mut Node<T>;
|
||||
let current_node_raw = current_node as *mut Node<T, V>;
|
||||
let node = Self::NodeDependencies::borrow_elements_from(¤t_node.state)
|
||||
.expect("tried to get a pass that does not exist");
|
||||
let parent = parent.map(|parent| {
|
||||
@@ -105,7 +105,7 @@ pub trait Pass: Any {
|
||||
)
|
||||
},
|
||||
)
|
||||
as Box<dyn Fn(NodeId, &mut Tree<Node<T>>, &SendAnyMap) -> bool + Send + Sync>,
|
||||
as Box<dyn Fn(NodeId, &mut Tree<Node<T, V>>, &SendAnyMap) -> bool + Send + Sync>,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,21 +145,21 @@ pub trait Pass: Any {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TypeErasedPass<T: AnyMapLike + State> {
|
||||
pub struct TypeErasedPass<T: AnyMapLike + State<V>, V: FromAnyValue = ()> {
|
||||
pub(crate) this_type_id: TypeId,
|
||||
pub(crate) parent_dependant: bool,
|
||||
pub(crate) child_dependant: bool,
|
||||
pub(crate) combined_dependancy_type_ids: FxHashSet<TypeId>,
|
||||
pub(crate) dependants: FxHashSet<TypeId>,
|
||||
pub(crate) mask: NodeMask,
|
||||
pass: PassCallback<T>,
|
||||
pass: PassCallback<T, V>,
|
||||
pub(crate) pass_direction: PassDirection,
|
||||
}
|
||||
|
||||
impl<T: AnyMapLike + State> TypeErasedPass<T> {
|
||||
impl<T: AnyMapLike + State<V>, V: FromAnyValue> TypeErasedPass<T, V> {
|
||||
fn resolve(
|
||||
&self,
|
||||
tree: &mut Tree<Node<T>>,
|
||||
tree: &mut Tree<Node<T, V>>,
|
||||
mut dirty: DirtyNodes,
|
||||
dirty_states: &DirtyNodeStates,
|
||||
nodes_updated: &FxDashSet<NodeId>,
|
||||
@@ -217,7 +217,8 @@ pub enum PassDirection {
|
||||
AnyOrder,
|
||||
}
|
||||
|
||||
type PassCallback<T> = Box<dyn Fn(NodeId, &mut Tree<Node<T>>, &SendAnyMap) -> bool + Send + Sync>;
|
||||
type PassCallback<T, V> =
|
||||
Box<dyn Fn(NodeId, &mut Tree<Node<T, V>>, &SendAnyMap) -> bool + Send + Sync>;
|
||||
|
||||
pub trait AnyMapLike {
|
||||
fn get<T: Any>(&self) -> Option<&T>;
|
||||
@@ -364,10 +365,10 @@ impl DirtyNodeStates {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_passes<T: AnyMapLike + State + Send>(
|
||||
tree: &mut Tree<Node<T>>,
|
||||
pub fn resolve_passes<T: AnyMapLike + State<V> + Send, V: FromAnyValue + Send>(
|
||||
tree: &mut Tree<Node<T, V>>,
|
||||
dirty_nodes: DirtyNodeStates,
|
||||
passes: &[TypeErasedPass<T>],
|
||||
passes: &[TypeErasedPass<T, V>],
|
||||
ctx: SendAnyMap,
|
||||
) -> FxDashSet<NodeId> {
|
||||
let dirty_states = Arc::new(dirty_nodes);
|
||||
@@ -411,7 +412,6 @@ pub fn resolve_passes<T: AnyMapLike + State + Send>(
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
// all passes are resolved at the end of the scope
|
||||
});
|
||||
resolved_passes.extend(resolving.iter().copied());
|
||||
resolving.clear()
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use dioxus_core::{ElementId, Mutations, TemplateNode};
|
||||
use dioxus_core::{BorrowedAttributeValue, ElementId, Mutations, TemplateNode};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use std::fmt::Debug;
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
use crate::node::{
|
||||
ElementNode, Node, NodeData, NodeType, OwnedAttributeDiscription, OwnedAttributeValue,
|
||||
ElementNode, FromAnyValue, Node, NodeData, NodeType, OwnedAttributeDiscription,
|
||||
OwnedAttributeValue,
|
||||
};
|
||||
use crate::node_ref::{AttributeMask, NodeMask};
|
||||
use crate::passes::{resolve_passes, DirtyNodeStates, TypeErasedPass};
|
||||
@@ -15,14 +15,17 @@ use crate::{FxDashSet, SendAnyMap};
|
||||
/// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers.
|
||||
/// The render state passes from parent to children and or accumulates state from children to parents.
|
||||
/// To get started implement [crate::state::ParentDepState], [crate::state::NodeDepState], or [crate::state::ChildDepState] and call [RealDom::apply_mutations] to update the dom and [RealDom::update_state] to update the state of the nodes.
|
||||
pub struct RealDom<S: State + Send> {
|
||||
pub tree: Tree<Node<S>>,
|
||||
///
|
||||
/// # Custom values
|
||||
/// To allow custom values to be passed into attributes implement FromAnyValue on a type that can represent your custom value and specify the V generic to be that type. If you have many different custom values, it can be useful to use a enum type to represent the varients.
|
||||
pub struct RealDom<S: State<V>, V: FromAnyValue = ()> {
|
||||
tree: Tree<Node<S, V>>,
|
||||
/// a map from element id to real node id
|
||||
node_id_mapping: Vec<Option<NodeId>>,
|
||||
nodes_listening: FxHashMap<String, FxHashSet<NodeId>>,
|
||||
stack: Vec<NodeId>,
|
||||
templates: FxHashMap<String, Vec<NodeId>>,
|
||||
pub(crate) passes: Box<[TypeErasedPass<S>]>,
|
||||
pub(crate) passes: Box<[TypeErasedPass<S, V>]>,
|
||||
pub(crate) nodes_updated: FxHashMap<NodeId, NodeMask>,
|
||||
passes_updated: DirtyNodeStates,
|
||||
parent_changed_nodes: FxHashSet<NodeId>,
|
||||
@@ -30,26 +33,14 @@ pub struct RealDom<S: State + Send> {
|
||||
nodes_created: FxHashSet<NodeId>,
|
||||
}
|
||||
|
||||
impl<S: State + Send + Debug> Debug for RealDom<S> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RealDom")
|
||||
.field("tree", &self.tree)
|
||||
.field("node_id_mapping", &self.node_id_mapping)
|
||||
.field("nodes_listening", &self.nodes_listening)
|
||||
.field("stack", &self.stack)
|
||||
.field("templates", &self.templates)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State + Send> Default for RealDom<S> {
|
||||
impl<S: State<V>, V: FromAnyValue> Default for RealDom<S, V> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State + Send> RealDom<S> {
|
||||
pub fn new() -> RealDom<S> {
|
||||
impl<S: State<V>, V: FromAnyValue> RealDom<S, V> {
|
||||
pub fn new() -> RealDom<S, V> {
|
||||
let mut root = Node::new(NodeType::Element(ElementNode {
|
||||
tag: "Root".to_string(),
|
||||
namespace: Some("Root".to_string()),
|
||||
@@ -122,10 +113,6 @@ impl<S: State + Send> RealDom<S> {
|
||||
if self.node_id_mapping.len() <= element_id.0 {
|
||||
self.node_id_mapping.resize(element_id.0 + 1, None);
|
||||
}
|
||||
if let Some(Some(old_id)) = self.node_id_mapping.get(element_id.0) {
|
||||
// free the memory associated with the old node
|
||||
self.tree.remove(*old_id);
|
||||
}
|
||||
self.node_id_mapping[element_id.0] = Some(node_id);
|
||||
}
|
||||
|
||||
@@ -137,7 +124,7 @@ impl<S: State + Send> RealDom<S> {
|
||||
current
|
||||
}
|
||||
|
||||
fn create_node(&mut self, node: Node<S>, mark_dirty: bool) -> NodeId {
|
||||
fn create_node(&mut self, node: Node<S, V>, mark_dirty: bool) -> NodeId {
|
||||
let node_id = self.tree.create_node(node);
|
||||
let node = self.tree.get_mut(node_id).unwrap();
|
||||
node.node_data.node_id = node_id;
|
||||
@@ -263,7 +250,7 @@ impl<S: State + Send> RealDom<S> {
|
||||
for new in new_nodes {
|
||||
self.tree.insert_before(old_node_id, new);
|
||||
}
|
||||
self.tree.remove(old_node_id);
|
||||
self.remove(old_node_id);
|
||||
}
|
||||
ReplacePlaceholder { path, m } => {
|
||||
let new_nodes = self.stack.split_off(self.stack.len() - m);
|
||||
@@ -271,7 +258,7 @@ impl<S: State + Send> RealDom<S> {
|
||||
for new in new_nodes {
|
||||
self.tree.insert_before(old_node_id, new);
|
||||
}
|
||||
self.tree.remove(old_node_id);
|
||||
self.remove(old_node_id);
|
||||
}
|
||||
InsertAfter { id, m } => {
|
||||
let new_nodes = self.stack.split_off(self.stack.len() - m);
|
||||
@@ -295,29 +282,23 @@ impl<S: State + Send> RealDom<S> {
|
||||
} => {
|
||||
let node_id = self.element_to_node_id(id);
|
||||
let mut node = self.get_mut(node_id).unwrap();
|
||||
if let NodeTypeMut::Element(mut element) = node.node_type_mut() {
|
||||
element.set_attribute(
|
||||
OwnedAttributeDiscription {
|
||||
if let NodeTypeMut::Element(element) = &mut node.node_type_mut() {
|
||||
if let BorrowedAttributeValue::None = &value {
|
||||
element.remove_attributes(&OwnedAttributeDiscription {
|
||||
name: name.to_string(),
|
||||
namespace: ns.map(|s| s.to_string()),
|
||||
volatile: false,
|
||||
},
|
||||
crate::node::OwnedAttributeValue::Text(value.to_string()),
|
||||
);
|
||||
}
|
||||
}
|
||||
SetBoolAttribute { name, value, id } => {
|
||||
let node_id = self.element_to_node_id(id);
|
||||
let mut node = self.get_mut(node_id).unwrap();
|
||||
if let NodeTypeMut::Element(mut element) = node.node_type_mut() {
|
||||
element.set_attribute(
|
||||
OwnedAttributeDiscription {
|
||||
name: name.to_string(),
|
||||
namespace: None,
|
||||
volatile: false,
|
||||
},
|
||||
crate::node::OwnedAttributeValue::Bool(value),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
element.set_attribute(
|
||||
OwnedAttributeDiscription {
|
||||
name: name.to_string(),
|
||||
namespace: ns.map(|s| s.to_string()),
|
||||
volatile: false,
|
||||
},
|
||||
OwnedAttributeValue::from(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
SetText { value, id } => {
|
||||
@@ -357,7 +338,7 @@ impl<S: State + Send> RealDom<S> {
|
||||
}
|
||||
Remove { id } => {
|
||||
let node_id = self.element_to_node_id(id);
|
||||
self.tree.remove(node_id);
|
||||
self.remove(node_id);
|
||||
}
|
||||
PushRoot { id } => {
|
||||
let node_id = self.element_to_node_id(id);
|
||||
@@ -367,6 +348,68 @@ impl<S: State + Send> RealDom<S> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Find all nodes that are listening for an event, sorted by there height in the dom progressing starting at the bottom and progressing up.
|
||||
/// This can be useful to avoid creating duplicate events.
|
||||
pub fn get_listening_sorted(&self, event: &'static str) -> Vec<&Node<S, V>> {
|
||||
if let Some(nodes) = self.nodes_listening.get(event) {
|
||||
let mut listening: Vec<_> = nodes.iter().map(|id| self.get(*id).unwrap()).collect();
|
||||
listening.sort_by(|n1, n2| {
|
||||
(self.tree.height(n1.node_data.node_id))
|
||||
.cmp(&self.tree.height(n2.node_data.node_id))
|
||||
.reverse()
|
||||
});
|
||||
listening
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of nodes in the dom.
|
||||
pub fn size(&self) -> usize {
|
||||
// The dom has a root node, ignore it.
|
||||
self.tree.size() - 1
|
||||
}
|
||||
|
||||
/// Returns the id of the root node.
|
||||
pub fn root_id(&self) -> NodeId {
|
||||
self.tree.root()
|
||||
}
|
||||
|
||||
fn clone_node(&mut self, node_id: NodeId) -> NodeId {
|
||||
let node = self.tree.get(node_id).unwrap();
|
||||
let new_node = Node {
|
||||
state: node.state.clone(),
|
||||
node_data: node.node_data.clone(),
|
||||
};
|
||||
let new_id = self.create_node(new_node, true);
|
||||
|
||||
let self_ptr = self as *mut Self;
|
||||
for child in self.tree.children_ids(node_id).unwrap() {
|
||||
unsafe {
|
||||
// this is safe because no node has itself as a child
|
||||
let self_mut = &mut *self_ptr;
|
||||
let child_id = self_mut.clone_node(*child);
|
||||
self_mut.add_child(new_id, child_id);
|
||||
}
|
||||
}
|
||||
new_id
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, id: NodeId) -> Option<NodeMut<'_, V>> {
|
||||
self.tree.get_mut(id).map(|node| NodeMut {
|
||||
node: &mut node.node_data,
|
||||
dirty: NodeMask::new(),
|
||||
nodes_updated: &mut self.nodes_updated,
|
||||
})
|
||||
}
|
||||
|
||||
/// WARNING: This escapes the reactive system that the real dom uses. Any changes made with this method will not trigger updates in states when [RealDom::update_state] is called.
|
||||
pub fn get_mut_raw(&mut self, id: NodeId) -> Option<&mut Node<S, V>> {
|
||||
self.tree.get_mut(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State<V> + Send, V: FromAnyValue + Send> RealDom<S, V> {
|
||||
/// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts.
|
||||
pub fn update_state(
|
||||
&mut self,
|
||||
@@ -421,76 +464,16 @@ impl<S: State + Send> RealDom<S> {
|
||||
nodes_updated,
|
||||
)
|
||||
}
|
||||
|
||||
/// Find all nodes that are listening for an event, sorted by there height in the dom progressing starting at the bottom and progressing up.
|
||||
/// This can be useful to avoid creating duplicate events.
|
||||
pub fn get_listening_sorted(&self, event: &'static str) -> Vec<&Node<S>> {
|
||||
if let Some(nodes) = self.nodes_listening.get(event) {
|
||||
let mut listening: Vec<_> = nodes.iter().map(|id| &self[*id]).collect();
|
||||
listening.sort_by(|n1, n2| {
|
||||
(self.tree.height(n1.node_data.node_id))
|
||||
.cmp(&self.tree.height(n2.node_data.node_id))
|
||||
.reverse()
|
||||
});
|
||||
listening
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of nodes in the dom.
|
||||
pub fn size(&self) -> usize {
|
||||
// The dom has a root node, ignore it.
|
||||
self.tree.size() - 1
|
||||
}
|
||||
|
||||
/// Returns the id of the root node.
|
||||
pub fn root_id(&self) -> NodeId {
|
||||
self.tree.root()
|
||||
}
|
||||
|
||||
fn clone_node(&mut self, node_id: NodeId) -> NodeId {
|
||||
let node = self.tree.get(node_id).unwrap();
|
||||
let new_node = Node {
|
||||
state: node.state.clone(),
|
||||
node_data: node.node_data.clone(),
|
||||
};
|
||||
let new_id = self.create_node(new_node, true);
|
||||
|
||||
let self_ptr = self as *mut Self;
|
||||
for child in self.tree.children_ids(node_id).unwrap() {
|
||||
unsafe {
|
||||
// this is safe because no node has itself as a child
|
||||
let self_mut = &mut *self_ptr;
|
||||
let child_id = self_mut.clone_node(*child);
|
||||
self_mut.add_child(new_id, child_id);
|
||||
}
|
||||
}
|
||||
new_id
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, id: NodeId) -> Option<NodeMut<'_>> {
|
||||
self.tree.get_mut(id).map(|node| NodeMut {
|
||||
node: &mut node.node_data,
|
||||
dirty: NodeMask::new(),
|
||||
nodes_updated: &mut self.nodes_updated,
|
||||
})
|
||||
}
|
||||
|
||||
/// WARNING: This escapes the reactive system that the real dom uses. Any changes made with this method will not trigger updates in states when [RealDom::update_state] is called.
|
||||
pub fn get_mut_raw(&mut self, id: NodeId) -> Option<&mut Node<S>> {
|
||||
self.tree.get_mut(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State + Send> TreeView<Node<S>> for RealDom<S> {
|
||||
type Iterator<'a> = <Tree<Node<S>> as TreeView<Node<S>>>::Iterator<'a>;
|
||||
impl<S: State<V>, V: FromAnyValue> TreeView<Node<S, V>> for RealDom<S, V> {
|
||||
type Iterator<'a> = <Tree<Node<S, V>> as TreeView<Node<S, V>>>::Iterator<'a>;
|
||||
|
||||
fn root(&self) -> NodeId {
|
||||
self.tree.root()
|
||||
}
|
||||
|
||||
fn get(&self, id: NodeId) -> Option<&Node<S>> {
|
||||
fn get(&self, id: NodeId) -> Option<&Node<S, V>> {
|
||||
self.tree.get(id)
|
||||
}
|
||||
|
||||
@@ -502,7 +485,7 @@ impl<S: State + Send> TreeView<Node<S>> for RealDom<S> {
|
||||
self.tree.children_ids(id)
|
||||
}
|
||||
|
||||
fn parent(&self, id: NodeId) -> Option<&Node<S>> {
|
||||
fn parent(&self, id: NodeId) -> Option<&Node<S, V>> {
|
||||
self.tree.parent(id)
|
||||
}
|
||||
|
||||
@@ -519,8 +502,8 @@ impl<S: State + Send> TreeView<Node<S>> for RealDom<S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State + Send> TreeLike<Node<S>> for RealDom<S> {
|
||||
fn create_node(&mut self, node: Node<S>) -> NodeId {
|
||||
impl<S: State<V>, V: FromAnyValue> TreeLike<Node<S, V>> for RealDom<S, V> {
|
||||
fn create_node(&mut self, node: Node<S, V>) -> NodeId {
|
||||
let id = self.tree.create_node(node);
|
||||
self.tree.get_mut(id).unwrap().node_data.node_id = id;
|
||||
self.nodes_created.insert(id);
|
||||
@@ -535,14 +518,14 @@ impl<S: State + Send> TreeLike<Node<S>> for RealDom<S> {
|
||||
self.tree.add_child(parent, child);
|
||||
}
|
||||
|
||||
fn remove(&mut self, id: NodeId) -> Option<Node<S>> {
|
||||
fn remove(&mut self, id: NodeId) -> Option<Node<S, V>> {
|
||||
if let Some(parent_id) = self.tree.parent_id(id) {
|
||||
self.mark_child_changed(parent_id);
|
||||
}
|
||||
self.tree.remove(id)
|
||||
}
|
||||
|
||||
fn remove_all_children(&mut self, id: NodeId) -> Vec<Node<S>> {
|
||||
fn remove_all_children(&mut self, id: NodeId) -> Vec<Node<S, V>> {
|
||||
self.mark_child_changed(id);
|
||||
self.tree.remove_all_children(id)
|
||||
}
|
||||
@@ -572,46 +555,46 @@ impl<S: State + Send> TreeLike<Node<S>> for RealDom<S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State + Send> Index<ElementId> for RealDom<S> {
|
||||
type Output = Node<S>;
|
||||
impl<S: State<V> + Send, V: FromAnyValue> Index<ElementId> for RealDom<S, V> {
|
||||
type Output = Node<S, V>;
|
||||
|
||||
fn index(&self, id: ElementId) -> &Self::Output {
|
||||
self.tree.get(self.element_to_node_id(id)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State + Send> Index<NodeId> for RealDom<S> {
|
||||
type Output = Node<S>;
|
||||
impl<S: State<V> + Send, V: FromAnyValue> Index<NodeId> for RealDom<S, V> {
|
||||
type Output = Node<S, V>;
|
||||
|
||||
fn index(&self, idx: NodeId) -> &Self::Output {
|
||||
self.tree.get(idx).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State + Send> IndexMut<ElementId> for RealDom<S> {
|
||||
impl<S: State<V> + Send, V: FromAnyValue> IndexMut<ElementId> for RealDom<S, V> {
|
||||
fn index_mut(&mut self, id: ElementId) -> &mut Self::Output {
|
||||
self.tree.get_mut(self.element_to_node_id(id)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State + Send> IndexMut<NodeId> for RealDom<S> {
|
||||
impl<S: State<V> + Send, V: FromAnyValue> IndexMut<NodeId> for RealDom<S, V> {
|
||||
fn index_mut(&mut self, idx: NodeId) -> &mut Self::Output {
|
||||
self.tree.get_mut(idx).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NodeMut<'a> {
|
||||
node: &'a mut NodeData,
|
||||
pub struct NodeMut<'a, V: FromAnyValue = ()> {
|
||||
node: &'a mut NodeData<V>,
|
||||
dirty: NodeMask,
|
||||
nodes_updated: &'a mut FxHashMap<NodeId, NodeMask>,
|
||||
}
|
||||
|
||||
impl<'a> NodeMut<'a> {
|
||||
pub fn node_type(&self) -> &NodeType {
|
||||
impl<'a, V: FromAnyValue> NodeMut<'a, V> {
|
||||
pub fn node_type(&self) -> &NodeType<V> {
|
||||
&self.node.node_type
|
||||
}
|
||||
|
||||
pub fn node_type_mut(&mut self) -> NodeTypeMut<'_> {
|
||||
pub fn node_type_mut(&mut self) -> NodeTypeMut<'_, V> {
|
||||
match &mut self.node.node_type {
|
||||
NodeType::Element(element) => NodeTypeMut::Element(ElementNodeMut {
|
||||
element,
|
||||
@@ -626,7 +609,7 @@ impl<'a> NodeMut<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for NodeMut<'_> {
|
||||
impl<V: FromAnyValue> Drop for NodeMut<'_, V> {
|
||||
fn drop(&mut self) {
|
||||
let node_id = self.node.node_id;
|
||||
let mask = std::mem::take(&mut self.dirty);
|
||||
@@ -638,18 +621,18 @@ impl Drop for NodeMut<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum NodeTypeMut<'a> {
|
||||
Element(ElementNodeMut<'a>),
|
||||
pub enum NodeTypeMut<'a, V: FromAnyValue = ()> {
|
||||
Element(ElementNodeMut<'a, V>),
|
||||
Text(&'a mut String),
|
||||
Placeholder,
|
||||
}
|
||||
|
||||
pub struct ElementNodeMut<'a> {
|
||||
element: &'a mut ElementNode,
|
||||
pub struct ElementNodeMut<'a, V: FromAnyValue = ()> {
|
||||
element: &'a mut ElementNode<V>,
|
||||
dirty: &'a mut NodeMask,
|
||||
}
|
||||
|
||||
impl ElementNodeMut<'_> {
|
||||
impl<V: FromAnyValue> ElementNodeMut<'_, V> {
|
||||
pub fn tag(&self) -> &str {
|
||||
&self.element.tag
|
||||
}
|
||||
@@ -668,13 +651,13 @@ impl ElementNodeMut<'_> {
|
||||
&mut self.element.namespace
|
||||
}
|
||||
|
||||
pub fn attributes(&self) -> &FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue> {
|
||||
pub fn attributes(&self) -> &FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>> {
|
||||
&self.element.attributes
|
||||
}
|
||||
|
||||
pub fn attributes_mut(
|
||||
&mut self,
|
||||
) -> &mut FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue> {
|
||||
) -> &mut FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>> {
|
||||
self.dirty.add_attributes(AttributeMask::All);
|
||||
&mut self.element.attributes
|
||||
}
|
||||
@@ -682,16 +665,24 @@ impl ElementNodeMut<'_> {
|
||||
pub fn set_attribute(
|
||||
&mut self,
|
||||
name: OwnedAttributeDiscription,
|
||||
value: OwnedAttributeValue,
|
||||
) -> Option<OwnedAttributeValue> {
|
||||
value: OwnedAttributeValue<V>,
|
||||
) -> Option<OwnedAttributeValue<V>> {
|
||||
self.dirty.add_attributes(AttributeMask::single(&name.name));
|
||||
self.element.attributes.insert(name, value)
|
||||
}
|
||||
|
||||
pub fn remove_attributes(
|
||||
&mut self,
|
||||
name: &OwnedAttributeDiscription,
|
||||
) -> Option<OwnedAttributeValue<V>> {
|
||||
self.dirty.add_attributes(AttributeMask::single(&name.name));
|
||||
self.element.attributes.remove(name)
|
||||
}
|
||||
|
||||
pub fn get_attribute_mut(
|
||||
&mut self,
|
||||
name: &OwnedAttributeDiscription,
|
||||
) -> Option<&mut OwnedAttributeValue> {
|
||||
) -> Option<&mut OwnedAttributeValue<V>> {
|
||||
self.dirty.add_attributes(AttributeMask::single(&name.name));
|
||||
self.element.attributes.get_mut(name)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::passes::{AnyMapLike, TypeErasedPass};
|
||||
use crate::{
|
||||
node::FromAnyValue,
|
||||
passes::{AnyMapLike, TypeErasedPass},
|
||||
};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// Join two sorted iterators
|
||||
@@ -37,7 +40,7 @@ pub(crate) fn union_ordered_iter<'a>(
|
||||
}
|
||||
|
||||
/// Do not implement this trait. It is only meant to be derived and used through [crate::real_dom::RealDom].
|
||||
pub trait State: Default + Clone + AnyMapLike + 'static {
|
||||
pub trait State<V: FromAnyValue = ()>: Default + Clone + AnyMapLike + 'static {
|
||||
#[doc(hidden)]
|
||||
fn create_passes() -> Box<[TypeErasedPass<Self>]>;
|
||||
fn create_passes() -> Box<[TypeErasedPass<Self, V>]>;
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
use slab::Slab;
|
||||
use std::collections::VecDeque;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
@@ -212,57 +211,38 @@ pub trait TreeLike<T> {
|
||||
fn insert_after(&mut self, id: NodeId, new: NodeId);
|
||||
}
|
||||
|
||||
pub struct ChildNodeIterator<'a, T, Tr: TreeView<T>> {
|
||||
tree: &'a Tr,
|
||||
children_ids: &'a [NodeId],
|
||||
pub struct ChildNodeIterator<'a, T> {
|
||||
nodes: &'a Slab<Node<T>>,
|
||||
children_ids: Vec<NodeId>,
|
||||
index: usize,
|
||||
node_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: 'a, Tr: TreeView<T>> Iterator for ChildNodeIterator<'a, T, Tr> {
|
||||
impl<'a, T: 'a> Iterator for ChildNodeIterator<'a, T> {
|
||||
type Item = &'a T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.children_ids.get(self.index).map(|id| {
|
||||
self.index += 1;
|
||||
self.tree.get_unchecked(*id)
|
||||
&self.nodes.get(id.0).unwrap().value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChildNodeIteratorMut<'a, T, Tr: TreeViewMut<T> + 'a> {
|
||||
tree: *mut Tr,
|
||||
children_ids: &'a [NodeId],
|
||||
index: usize,
|
||||
node_type: PhantomData<T>,
|
||||
pub struct ChildNodeIteratorMut<'a, T> {
|
||||
children: Vec<&'a mut Node<T>>,
|
||||
}
|
||||
|
||||
unsafe impl<'a, T, Tr: TreeViewMut<T> + 'a> Sync for ChildNodeIteratorMut<'a, T, Tr> {}
|
||||
|
||||
impl<'a, T, Tr: TreeViewMut<T>> ChildNodeIteratorMut<'a, T, Tr> {
|
||||
fn tree_mut(&mut self) -> &'a mut Tr {
|
||||
unsafe { &mut *self.tree }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a, Tr: TreeViewMut<T>> Iterator for ChildNodeIteratorMut<'a, T, Tr> {
|
||||
impl<'a, T> Iterator for ChildNodeIteratorMut<'a, T> {
|
||||
type Item = &'a mut T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let owned = self.children_ids.get(self.index).copied();
|
||||
match owned {
|
||||
Some(id) => {
|
||||
self.index += 1;
|
||||
|
||||
Some(self.tree_mut().get_mut_unchecked(id))
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
self.children.pop().map(|node| &mut node.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TreeView<T> for Tree<T> {
|
||||
type Iterator<'a> = ChildNodeIterator<'a, T, Tree<T>> where T: 'a;
|
||||
type Iterator<'a> = ChildNodeIterator<'a, T> where T: 'a;
|
||||
|
||||
fn root(&self) -> NodeId {
|
||||
self.root
|
||||
@@ -274,8 +254,8 @@ impl<T> TreeView<T> for Tree<T> {
|
||||
|
||||
fn children(&self, id: NodeId) -> Option<Self::Iterator<'_>> {
|
||||
self.children_ids(id).map(|children_ids| ChildNodeIterator {
|
||||
tree: self,
|
||||
children_ids,
|
||||
nodes: &self.nodes,
|
||||
children_ids: children_ids.to_vec(),
|
||||
index: 0,
|
||||
node_type: PhantomData,
|
||||
})
|
||||
@@ -310,7 +290,7 @@ impl<T> TreeView<T> for Tree<T> {
|
||||
}
|
||||
|
||||
impl<T> TreeViewMut<T> for Tree<T> {
|
||||
type IteratorMut<'a> = ChildNodeIteratorMut<'a, T, Tree<T>> where T: 'a;
|
||||
type IteratorMut<'a> = ChildNodeIteratorMut<'a, T> where T: 'a;
|
||||
|
||||
fn get_mut(&mut self, id: NodeId) -> Option<&mut T> {
|
||||
self.nodes.get_mut(id.0).map(|node| &mut node.value)
|
||||
@@ -331,16 +311,19 @@ impl<T> TreeViewMut<T> for Tree<T> {
|
||||
}
|
||||
|
||||
fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>> {
|
||||
let raw_ptr = self as *mut Self;
|
||||
unsafe {
|
||||
// Safety: No node will appear as a child twice
|
||||
self.children_ids(id)
|
||||
.map(|children_ids| ChildNodeIteratorMut {
|
||||
tree: &mut *raw_ptr,
|
||||
children_ids,
|
||||
index: 0,
|
||||
node_type: PhantomData,
|
||||
})
|
||||
// Safety: No node has itself as a parent.
|
||||
if let Some(children_ids) = self.children_ids(id) {
|
||||
let children_ids = children_ids.to_vec();
|
||||
let children_ids = children_ids.to_vec();
|
||||
Some(ChildNodeIteratorMut {
|
||||
children: unsafe {
|
||||
self.nodes
|
||||
.get_many_mut_unchecked(children_ids.into_iter().rev().map(|id| id.0))
|
||||
.unwrap()
|
||||
},
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,6 +356,29 @@ impl<T> TreeViewMut<T> for Tree<T> {
|
||||
(node, parent, children)
|
||||
})
|
||||
}
|
||||
|
||||
fn parent_child_mut(&mut self, id: NodeId) -> Option<(&mut T, Self::IteratorMut<'_>)> {
|
||||
// Safety: No node will appear as a child twice
|
||||
if let Some(children_ids) = self.children_ids(id) {
|
||||
debug_assert!(!children_ids.iter().any(|child_id| *child_id == id));
|
||||
let mut borrowed = unsafe {
|
||||
let as_vec = children_ids.to_vec();
|
||||
self.nodes
|
||||
.get_many_mut_unchecked(
|
||||
as_vec
|
||||
.into_iter()
|
||||
.rev()
|
||||
.map(|id| id.0)
|
||||
.chain(std::iter::once(id.0)),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
let node = &mut borrowed.pop().unwrap().value;
|
||||
Some((node, ChildNodeIteratorMut { children: borrowed }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TreeLike<T> for Tree<T> {
|
||||
@@ -584,3 +590,124 @@ fn traverse_depth_first() {
|
||||
node_count += 1;
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_node_children_mut() {
|
||||
let mut tree = Tree::new(0);
|
||||
let parent = tree.root();
|
||||
let child1 = tree.create_node(1);
|
||||
tree.add_child(parent, child1);
|
||||
let child2 = tree.create_node(2);
|
||||
tree.add_child(parent, child2);
|
||||
let child3 = tree.create_node(3);
|
||||
tree.add_child(parent, child3);
|
||||
|
||||
let (parent, children) = tree.parent_child_mut(parent).unwrap();
|
||||
for (i, child) in children.enumerate() {
|
||||
assert_eq!(*child, i + 1);
|
||||
}
|
||||
println!("Parent: {:#?}", parent);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_many_mut_unchecked() {
|
||||
let mut slab = Slab::new();
|
||||
let parent = slab.insert(0);
|
||||
let child = slab.insert(1);
|
||||
let grandchild = slab.insert(2);
|
||||
|
||||
let all =
|
||||
unsafe { slab.get_many_mut_unchecked([parent, child, grandchild].into_iter()) }.unwrap();
|
||||
println!("All: {:#?}", all);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Slab<T> {
|
||||
data: Vec<Option<T>>,
|
||||
free: VecDeque<usize>,
|
||||
}
|
||||
|
||||
impl<T> Default for Slab<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Slab<T> {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
data: Vec::new(),
|
||||
free: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, id: usize) -> Option<&T> {
|
||||
self.data.get(id).and_then(|x| x.as_ref())
|
||||
}
|
||||
|
||||
unsafe fn get_unchecked(&self, id: usize) -> &T {
|
||||
self.data.get_unchecked(id).as_ref().unwrap()
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, id: usize) -> Option<&mut T> {
|
||||
self.data.get_mut(id).and_then(|x| x.as_mut())
|
||||
}
|
||||
|
||||
unsafe fn get_unchecked_mut(&mut self, id: usize) -> &mut T {
|
||||
self.data.get_unchecked_mut(id).as_mut().unwrap()
|
||||
}
|
||||
|
||||
fn get2_mut(&mut self, id1: usize, id2: usize) -> Option<(&mut T, &mut T)> {
|
||||
assert!(id1 != id2);
|
||||
let ptr = self.data.as_mut_ptr();
|
||||
let first = unsafe { &mut *ptr.add(id1) };
|
||||
let second = unsafe { &mut *ptr.add(id2) };
|
||||
if let (Some(first), Some(second)) = (first, second) {
|
||||
Some((first, second))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn get_many_mut_unchecked(
|
||||
&mut self,
|
||||
ids: impl Iterator<Item = usize>,
|
||||
) -> Option<Vec<&mut T>> {
|
||||
let ptr = self.data.as_mut_ptr();
|
||||
let mut result = Vec::new();
|
||||
for id in ids {
|
||||
let item = unsafe { &mut *ptr.add(id) };
|
||||
if let Some(item) = item {
|
||||
result.push(item);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn insert(&mut self, value: T) -> usize {
|
||||
if let Some(id) = self.free.pop_front() {
|
||||
self.data[id] = Some(value);
|
||||
id
|
||||
} else {
|
||||
self.data.push(Some(value));
|
||||
self.data.len() - 1
|
||||
}
|
||||
}
|
||||
|
||||
fn try_remove(&mut self, id: usize) -> Option<T> {
|
||||
self.data.get_mut(id).and_then(|x| {
|
||||
self.free.push_back(id);
|
||||
x.take()
|
||||
})
|
||||
}
|
||||
|
||||
fn remove(&mut self, id: usize) -> T {
|
||||
self.try_remove(id).unwrap()
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.data.len() - self.free.len()
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
node::{ElementNode, NodeType},
|
||||
node::{ElementNode, FromAnyValue, NodeType},
|
||||
passes::{AnyMapLike, TypeErasedPass},
|
||||
real_dom::RealDom,
|
||||
state::State,
|
||||
@@ -74,7 +74,11 @@ impl PersistantElementIter {
|
||||
|
||||
/// remove stale element refreneces
|
||||
/// returns true if the focused element is removed
|
||||
pub fn prune<S: State + Send>(&mut self, mutations: &Mutations, rdom: &RealDom<S>) -> bool {
|
||||
pub fn prune<S: State<V>, V: FromAnyValue>(
|
||||
&mut self,
|
||||
mutations: &Mutations,
|
||||
rdom: &RealDom<S, V>,
|
||||
) -> bool {
|
||||
let mut changed = false;
|
||||
let ids_removed: Vec<_> = mutations
|
||||
.edits
|
||||
@@ -100,7 +104,7 @@ impl PersistantElementIter {
|
||||
// if a child is removed or inserted before or at the current element, update the child index
|
||||
for (el_id, child_idx) in self.stack.iter_mut() {
|
||||
if let NodePosition::InChild(child_idx) = child_idx {
|
||||
if let Some(children) = &rdom.tree.children_ids(*el_id) {
|
||||
if let Some(children) = &rdom.children_ids(*el_id) {
|
||||
for m in &mutations.edits {
|
||||
match m {
|
||||
Mutation::Remove { id } => {
|
||||
@@ -131,7 +135,7 @@ impl PersistantElementIter {
|
||||
}
|
||||
|
||||
/// get the next element
|
||||
pub fn next<S: State + Send>(&mut self, rdom: &RealDom<S>) -> ElementProduced {
|
||||
pub fn next<S: State<V>, V: FromAnyValue>(&mut self, rdom: &RealDom<S, V>) -> ElementProduced {
|
||||
if self.stack.is_empty() {
|
||||
let id = NodeId(0);
|
||||
let new = (id, NodePosition::AtNode);
|
||||
@@ -139,10 +143,10 @@ impl PersistantElementIter {
|
||||
ElementProduced::Looped(id)
|
||||
} else {
|
||||
let (last, old_child_idx) = self.stack.last_mut().unwrap();
|
||||
let node = &rdom[*last];
|
||||
let node = rdom.get(*last).unwrap();
|
||||
match &node.node_data.node_type {
|
||||
NodeType::Element(ElementNode { .. }) => {
|
||||
let children = rdom.tree.children_ids(*last).unwrap();
|
||||
let children = rdom.children_ids(*last).unwrap();
|
||||
*old_child_idx = old_child_idx.map(|i| i + 1);
|
||||
// if we have children, go to the next child
|
||||
let child_idx = old_child_idx.get_or_insert(0);
|
||||
@@ -151,7 +155,8 @@ impl PersistantElementIter {
|
||||
self.next(rdom)
|
||||
} else {
|
||||
let id = children[child_idx];
|
||||
if let NodeType::Element(ElementNode { .. }) = &rdom[id].node_data.node_type
|
||||
if let NodeType::Element(ElementNode { .. }) =
|
||||
rdom.get(id).unwrap().node_data.node_type
|
||||
{
|
||||
self.stack.push((id, NodePosition::AtNode));
|
||||
}
|
||||
@@ -168,16 +173,19 @@ impl PersistantElementIter {
|
||||
}
|
||||
|
||||
/// get the previous element
|
||||
pub fn prev<S: State + Send>(&mut self, rdom: &RealDom<S>) -> ElementProduced {
|
||||
pub fn prev<S: State<V> + Send, V: FromAnyValue>(
|
||||
&mut self,
|
||||
rdom: &RealDom<S, V>,
|
||||
) -> ElementProduced {
|
||||
// recursively add the last child element to the stack
|
||||
fn push_back<S: State + Send>(
|
||||
fn push_back<S: State<V> + Send, V: FromAnyValue>(
|
||||
stack: &mut smallvec::SmallVec<[(NodeId, NodePosition); 5]>,
|
||||
new_node: NodeId,
|
||||
rdom: &RealDom<S>,
|
||||
rdom: &RealDom<S, V>,
|
||||
) -> NodeId {
|
||||
match &rdom[new_node].node_data.node_type {
|
||||
NodeType::Element(ElementNode { .. }) => {
|
||||
let children = rdom.tree.children_ids(new_node).unwrap();
|
||||
let children = rdom.children_ids(new_node).unwrap();
|
||||
if children.is_empty() {
|
||||
new_node
|
||||
} else {
|
||||
@@ -196,7 +204,7 @@ impl PersistantElementIter {
|
||||
let node = &rdom[*last];
|
||||
match &node.node_data.node_type {
|
||||
NodeType::Element(ElementNode { .. }) => {
|
||||
let children = rdom.tree.children_ids(*last).unwrap();
|
||||
let children = rdom.children_ids(*last).unwrap();
|
||||
// if we have children, go to the next child
|
||||
if let NodePosition::InChild(0) = old_child_idx {
|
||||
ElementProduced::Progressed(self.pop())
|
||||
|
||||
Reference in new issue
Block a user