Merge branch 'upstream' into simplify-native-core

This commit is contained in:
Evan Almloff committed 2023-01-08 14:03:44 -06:00
commit ecf4ee275a
142 files changed
+2951 -1853

No files matched your search

+317 -533
View File
@@ -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,
_ => {}
}
}
}
+3 -3
View File
@@ -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)]
+57 -23
View File
@@ -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,
}
}
+8 -6
View File
@@ -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
+16 -16
View File
@@ -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(&current_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()
+130 -139
View File
@@ -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)
}
+6 -3
View File
@@ -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>]>;
}
+170 -43
View File
@@ -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())