diff --git a/.cargo/config.toml b/.cargo/config.toml index 4a305236e..73ee4959a 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,9 @@ +[unstable] +codegen-backend = true + +[profile.dev] +codegen-backend = "cranelift" + [alias] gentest = "run --release --package gentest --" import-yoga-tests = "run --package import-yoga-tests --" diff --git a/src/compute/block.rs b/src/compute/block.rs index eeb2aa009..9be42f627 100644 --- a/src/compute/block.rs +++ b/src/compute/block.rs @@ -9,7 +9,9 @@ use crate::util::sys::f32_max; use crate::util::sys::Vec; use crate::util::MaybeMath; use crate::util::{MaybeResolve, ResolveOrZero}; -use crate::{BlockContainerStyle, BlockItemStyle, BoxGenerationMode, BoxSizing, LayoutBlockContainer, TextAlign}; +use crate::{ + BlockContainerStyle, BlockItemStyle, BoxGenerationMode, BoxSizing, Direction, LayoutBlockContainer, TextAlign, +}; #[cfg(feature = "content_size")] use super::common::content_size::compute_content_size_contribution; @@ -26,6 +28,9 @@ struct BlockItem { /// Items that are tables don't have stretch sizing applied to them is_table: bool, + /// Direction (LTR or RTL) + direction: Direction, + /// The base size of this item size: Size>, /// The minimum allowable size of this item @@ -194,6 +199,7 @@ fn compute_inner(tree: &mut impl LayoutBlockContainer, node_id: NodeId, inputs: || matches!(min_size.height, Some(h) if h > 0.0); let text_align = style.text_align(); + let direction = style.direction(); drop(style); @@ -257,6 +263,7 @@ fn compute_inner(tree: &mut impl LayoutBlockContainer, node_id: NodeId, inputs: Size::NONE, Size::MAX_CONTENT, SizingMode::InherentSize, + direction, Line::FALSE, ); } @@ -314,6 +321,7 @@ fn generate_item_list( node_id: child_node_id, order: order as u32, is_table: child_style.is_table(), + direction: child_style.direction(), size: child_style .size() .maybe_resolve(node_inner_size) @@ -369,6 +377,7 @@ fn determine_content_based_container_width( Size::NONE, available_space.map_width(|w| w.maybe_sub(item_x_margin_sum)), SizingMode::InherentSize, + item.direction, Line::TRUE, ); @@ -434,6 +443,7 @@ fn perform_final_layout_on_in_flow_children( parent_size, available_space.map_width(|w| w.maybe_sub(item_non_auto_x_margin_sum)), SizingMode::InherentSize, + item.direction, Line::TRUE, ); let final_size = item_layout.size; @@ -649,6 +659,7 @@ fn perform_absolute_layout_on_absolute_children( height: AvailableSpace::Definite(area_height.maybe_clamp(min_size.height, max_size.height)), }, SizingMode::ContentSize, + item.direction, Line::FALSE, ); let measured_size = layout_output.size; diff --git a/src/compute/flexbox.rs b/src/compute/flexbox.rs index 9f1fcc1f4..7732b4c69 100644 --- a/src/compute/flexbox.rs +++ b/src/compute/flexbox.rs @@ -13,7 +13,7 @@ use crate::util::debug::debug_log; use crate::util::sys::{f32_max, new_vec_with_capacity, Vec}; use crate::util::MaybeMath; use crate::util::{MaybeResolve, ResolveOrZero}; -use crate::{BoxGenerationMode, BoxSizing}; +use crate::{BoxGenerationMode, BoxSizing, Direction}; use super::common::alignment::apply_alignment_fallback; #[cfg(feature = "content_size")] @@ -27,6 +27,9 @@ struct FlexItem { /// The order of the node relative to it's siblings order: u32, + /// Direction (LTR or RTL) + direction: Direction, + /// The base size of this item size: Size>, /// The minimum allowable size of this item @@ -118,6 +121,8 @@ struct FlexLine<'a> { struct AlgoConstants { /// The direction of the current segment being laid out dir: FlexDirection, + /// The direction of the current layout (left-to-right or right-to-left) + layout_direction: Direction, /// Is this segment a row is_row: bool, /// Is this segment a column @@ -372,7 +377,11 @@ fn compute_preliminary(tree: &mut impl LayoutFlexboxContainer, node: NodeId, inp let len = tree.child_count(node); for order in 0..len { let child = tree.get_child_id(node, order); - if tree.get_flexbox_child_style(child).box_generation_mode() == BoxGenerationMode::None { + let child_style = tree.get_flexbox_child_style(child); + let direction = child_style.direction(); + let box_generation_mode = child_style.box_generation_mode(); + drop(child_style); + if box_generation_mode == BoxGenerationMode::None { tree.set_unrounded_layout(child, &Layout::with_order(order as u32)); tree.perform_child_layout( child, @@ -380,6 +389,7 @@ fn compute_preliminary(tree: &mut impl LayoutFlexboxContainer, node: NodeId, inp Size::NONE, Size::MAX_CONTENT, SizingMode::InherentSize, + direction, Line::FALSE, ); } @@ -416,6 +426,7 @@ fn compute_constants( parent_size: Size>, ) -> AlgoConstants { let dir = style.flex_direction(); + let layout_direction = style.direction(); let is_row = dir.is_row(); let is_column = dir.is_column(); let is_wrap = matches!(style.flex_wrap(), FlexWrap::Wrap | FlexWrap::WrapReverse); @@ -454,6 +465,7 @@ fn compute_constants( AlgoConstants { dir, + layout_direction, is_row, is_column, is_wrap, @@ -509,6 +521,7 @@ fn generate_anonymous_flex_items( FlexItem { node: child, order: index as u32, + direction: child_style.direction(), size: child_style .size() .maybe_resolve(constants.node_inner_size) @@ -729,6 +742,7 @@ fn determine_flex_base_size( child_available_space, SizingMode::ContentSize, dir.main_axis(), + child.direction, Line::FALSE, ); }; @@ -772,6 +786,7 @@ fn determine_flex_base_size( child_available_space, SizingMode::ContentSize, dir.main_axis(), + child.direction, Line::FALSE, ) }; @@ -1027,6 +1042,7 @@ fn determine_container_main_size( child_available_space, SizingMode::InherentSize, dir.main_axis(), + item.direction, Line::FALSE, ) + item.margin.main_axis_sum(constants.dir); @@ -1362,6 +1378,7 @@ fn determine_hypothetical_cross_size( }, SizingMode::ContentSize, constants.dir.cross_axis(), + child.direction, Line::FALSE, ) .maybe_clamp(child.min_size.cross(constants.dir), child.max_size.cross(constants.dir)) @@ -1432,6 +1449,7 @@ fn calculate_children_base_lines( }, }, SizingMode::ContentSize, + child.direction, Line::FALSE, ); @@ -1732,10 +1750,11 @@ fn align_flex_items_along_cross_axis( max_baseline: f32, constants: &AlgoConstants, ) -> f32 { + let cross_axis_should_reverse = constants.is_column && matches!(constants.layout_direction, Direction::Rtl); match child.align_self { AlignSelf::Start => 0.0, AlignSelf::FlexStart => { - if constants.is_wrap_reverse { + if constants.is_wrap_reverse ^ cross_axis_should_reverse { free_space } else { 0.0 @@ -1743,7 +1762,7 @@ fn align_flex_items_along_cross_axis( } AlignSelf::End => free_space, AlignSelf::FlexEnd => { - if constants.is_wrap_reverse { + if constants.is_wrap_reverse ^ cross_axis_should_reverse { 0.0 } else { free_space @@ -1756,7 +1775,7 @@ fn align_flex_items_along_cross_axis( } else { // Until we support vertical writing modes, baseline alignment only makes sense if // the constants.direction is row, so we treat it as flex-start alignment in columns. - if constants.is_wrap_reverse { + if constants.is_wrap_reverse ^ cross_axis_should_reverse { free_space } else { 0.0 @@ -1764,7 +1783,7 @@ fn align_flex_items_along_cross_axis( } } AlignSelf::Stretch => { - if constants.is_wrap_reverse { + if constants.is_wrap_reverse ^ cross_axis_should_reverse { free_space } else { 0.0 @@ -1848,6 +1867,7 @@ fn calculate_flex_item( container_size: Size, node_inner_size: Size>, direction: FlexDirection, + layout_direction: Direction, ) { let layout_output = tree.perform_child_layout( item.node, @@ -1855,6 +1875,7 @@ fn calculate_flex_item( node_inner_size, container_size.map(|s| s.into()), SizingMode::ContentSize, + item.direction, Line::FALSE, ); let LayoutOutput { @@ -1864,16 +1885,31 @@ fn calculate_flex_item( .. } = layout_output; - let offset_main = *total_offset_main - + item.offset_main - + item.margin.main_start(direction) - + (item.inset.main_start(direction).or(item.inset.main_end(direction).map(|pos| -pos)).unwrap_or(0.0)); + let offset_main = if direction.is_row() && layout_direction.is_rtl() { + *total_offset_main + - item.offset_main + - item.margin.main_end(direction) + - (item.inset.main_end(direction).or(item.inset.main_start(direction).map(|pos| -pos)).unwrap_or(0.0)) + } else { + *total_offset_main + + item.offset_main + + item.margin.main_start(direction) + + (item.inset.main_start(direction).or(item.inset.main_end(direction).map(|pos| -pos)).unwrap_or(0.0)) + }; - let offset_cross = total_offset_cross - + item.offset_cross - + line_offset_cross - + item.margin.cross_start(direction) - + (item.inset.cross_start(direction).or(item.inset.cross_end(direction).map(|pos| -pos)).unwrap_or(0.0)); + let offset_cross = if false && direction.is_column() && layout_direction.is_rtl() { + total_offset_cross + - item.offset_cross + - line_offset_cross + - item.margin.cross_end(direction) + - (item.inset.cross_end(direction).or(item.inset.cross_start(direction).map(|pos| -pos)).unwrap_or(0.0)) + } else { + total_offset_cross + + item.offset_cross + + line_offset_cross + + item.margin.cross_start(direction) + + (item.inset.cross_start(direction).or(item.inset.cross_end(direction).map(|pos| -pos)).unwrap_or(0.0)) + }; if direction.is_row() { let baseline_offset_cross = total_offset_cross + item.offset_cross + item.margin.cross_start(direction); @@ -1885,14 +1921,21 @@ fn calculate_flex_item( item.baseline = baseline_offset_main + inner_baseline; } - let location = match direction.is_row() { - true => Point { x: offset_main, y: offset_cross }, - false => Point { x: offset_cross, y: offset_main }, - }; let scrollbar_size = Size { width: if item.overflow.y == Overflow::Scroll { item.scrollbar_width } else { 0.0 }, height: if item.overflow.x == Overflow::Scroll { item.scrollbar_width } else { 0.0 }, }; + let location = match direction.is_row() { + true => Point { + x: if layout_direction.is_rtl() { + container_size.width - (offset_main + size.width) + scrollbar_size.width + } else { + offset_main + }, + y: offset_cross, + }, + false => Point { x: offset_cross, y: offset_main }, + }; tree.set_unrounded_layout( item.node, @@ -1929,10 +1972,20 @@ fn calculate_layout_line( node_inner_size: Size>, padding_border: Rect, direction: FlexDirection, + layout_direction: Direction, ) { - let mut total_offset_main = padding_border.main_start(direction); + let mut total_offset_main = if layout_direction.is_rtl() && direction.is_row() { + padding_border.main_end(direction) + } else { + padding_border.main_start(direction) + }; let line_offset_cross = line.offset_cross; + let is_rtl_column = layout_direction.is_rtl() && direction.is_column(); + if is_rtl_column { + *total_offset_cross -= line_offset_cross + line.cross_size; + } + if direction.is_reverse() { for item in line.items.iter_mut().rev() { calculate_flex_item( @@ -1946,6 +1999,7 @@ fn calculate_layout_line( container_size, node_inner_size, direction, + layout_direction, ); } } else { @@ -1961,11 +2015,14 @@ fn calculate_layout_line( container_size, node_inner_size, direction, + layout_direction, ); } } - *total_offset_cross += line_offset_cross + line.cross_size; + if !is_rtl_column { + *total_offset_cross += line_offset_cross + line.cross_size; + } } /// Do a final layout pass and collect the resulting layouts. @@ -1975,7 +2032,11 @@ fn final_layout_pass( flex_lines: &mut [FlexLine], constants: &AlgoConstants, ) -> Size { - let mut total_offset_cross = constants.content_box_inset.cross_start(constants.dir); + let mut total_offset_cross = if constants.is_column && constants.layout_direction.is_rtl() { + constants.container_size.width - constants.content_box_inset.cross_end(constants.dir) + } else { + constants.content_box_inset.cross_start(constants.dir) + }; #[cfg_attr(not(feature = "content_size"), allow(unused_mut))] let mut content_size = Size::ZERO; @@ -1992,6 +2053,7 @@ fn final_layout_pass( constants.node_inner_size, constants.content_box_inset, constants.dir, + constants.layout_direction, ); } } else { @@ -2006,6 +2068,7 @@ fn final_layout_pass( constants.node_inner_size, constants.content_box_inset, constants.dir, + constants.layout_direction, ); } } @@ -2078,6 +2141,7 @@ fn perform_absolute_layout_on_absolute_children( .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let mut known_dimensions = style_size.maybe_clamp(min_size, max_size); + let direction = child_style.direction(); drop(child_style); @@ -2108,6 +2172,7 @@ fn perform_absolute_layout_on_absolute_children( height: AvailableSpace::Definite(container_height.maybe_clamp(min_size.height, max_size.height)), }, SizingMode::InherentSize, + direction, Line::FALSE, ); let measured_size = layout_output.size; diff --git a/src/compute/grid/alignment.rs b/src/compute/grid/alignment.rs index aeeed4424..51b9141db 100644 --- a/src/compute/grid/alignment.rs +++ b/src/compute/grid/alignment.rs @@ -189,6 +189,9 @@ pub(super) fn align_and_position_item( // Clamp size by min and max width/height let Size { width, height } = Size { width, height }.maybe_clamp(min_size, max_size); + // Direction + let direction = style.direction(); + // Layout node drop(style); let layout_output = tree.perform_child_layout( @@ -197,6 +200,7 @@ pub(super) fn align_and_position_item( grid_area_size.map(Option::Some), grid_area_minus_item_margins_size.map(AvailableSpace::Definite), SizingMode::InherentSize, + direction, Line::FALSE, ); diff --git a/src/compute/grid/mod.rs b/src/compute/grid/mod.rs index 5bba6bdfb..692877407 100644 --- a/src/compute/grid/mod.rs +++ b/src/compute/grid/mod.rs @@ -11,8 +11,8 @@ use crate::util::sys::{f32_max, GridTrackVec, Vec}; use crate::util::MaybeMath; use crate::util::{MaybeResolve, ResolveOrZero}; use crate::{ - style_helpers::*, AlignContent, BoxGenerationMode, BoxSizing, CoreStyle, GridContainerStyle, GridItemStyle, - JustifyContent, LayoutGridContainer, + style_helpers::*, AlignContent, BoxGenerationMode, BoxSizing, CoreStyle, Direction, GridContainerStyle, + GridItemStyle, JustifyContent, LayoutGridContainer, }; use alignment::{align_and_position_item, align_tracks}; use explicit_grid::{compute_explicit_grid_size_in_axis, initialize_grid_tracks}; @@ -40,7 +40,7 @@ mod util; /// - Track (row/column) sizing /// - Alignment & Final item placement pub fn compute_grid_layout(tree: &mut impl LayoutGridContainer, node: NodeId, inputs: LayoutInput) -> LayoutOutput { - let LayoutInput { known_dimensions, parent_size, available_space, run_mode, .. } = inputs; + let LayoutInput { direction, known_dimensions, parent_size, available_space, run_mode, .. } = inputs; let style = tree.get_grid_container_style(node); @@ -81,15 +81,26 @@ pub fn compute_grid_layout(tree: &mut impl LayoutGridContainer, node: NodeId, in Overflow::Scroll => style.scrollbar_width(), _ => 0.0, }); - // TODO: make side configurable based on the `direction` property + let mut content_box_inset = padding_border; - content_box_inset.right += scrollbar_gutter.x; + match direction { + Direction::Ltr => content_box_inset.right += scrollbar_gutter.x, + Direction::Rtl => content_box_inset.left += scrollbar_gutter.x, + }; content_box_inset.bottom += scrollbar_gutter.y; let align_content = style.align_content().unwrap_or(AlignContent::Stretch); - let justify_content = style.justify_content().unwrap_or(JustifyContent::Stretch); + let mut justify_content = style.justify_content().unwrap_or(JustifyContent::Stretch); let align_items = style.align_items(); - let justify_items = style.justify_items(); + let mut justify_items = style.justify_items(); + + match direction { + Direction::Rtl => { + justify_content.flip(); + justify_items.as_mut().map(|x| x.flip()); + } + _ => (), + } // Note: we avoid accessing the grid rows/columns methods more than once as this can // cause an expensive-ish computation @@ -476,11 +487,22 @@ pub fn compute_grid_layout(tree: &mut impl LayoutGridContainer, node: NodeId, in // Position in-flow children (stored in items vector) for (index, item) in items.iter_mut().enumerate() { + let (left, right) = match item.direction { + Direction::Ltr => ( + columns[item.column_indexes.start as usize + 1].offset, + columns[item.column_indexes.end as usize].offset, + ), + Direction::Rtl => ( + container_border_box.width - columns[item.column_indexes.end as usize].offset, + container_border_box.width - columns[item.column_indexes.start as usize + 1].offset, + ), + }; + let grid_area = Rect { top: rows[item.row_indexes.start as usize + 1].offset, bottom: rows[item.row_indexes.end as usize].offset, - left: columns[item.column_indexes.start as usize + 1].offset, - right: columns[item.column_indexes.end as usize].offset, + left, + right, }; #[cfg_attr(not(feature = "content_size"), allow(unused_variables))] let (content_size_contribution, y_position, height) = align_and_position_item( @@ -508,6 +530,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutGridContainer, node: NodeId, in // Position hidden child if child_style.box_generation_mode() == BoxGenerationMode::None { + let direction = child_style.direction(); drop(child_style); tree.set_unrounded_layout(child, &Layout::with_order(order)); tree.perform_child_layout( @@ -516,6 +539,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutGridContainer, node: NodeId, in Size::NONE, Size::MAX_CONTENT, SizingMode::InherentSize, + direction, Line::FALSE, ); order += 1; @@ -543,17 +567,30 @@ pub fn compute_grid_layout(tree: &mut impl LayoutGridContainer, node: NodeId, in maybe_grid_line.map(|line: OriginZeroLine| line.into_track_vec_index(final_row_counts)) }); + let (left, right) = match direction { + Direction::Ltr => ( + maybe_col_indexes.start.map(|index| columns[index].offset).unwrap_or(border.left), + maybe_col_indexes + .end + .map(|index| columns[index].offset) + .unwrap_or(container_border_box.width - border.right - scrollbar_gutter.x), + ), + Direction::Rtl => ( + maybe_col_indexes + .start + .map(|index| columns[index].offset + border.left + scrollbar_gutter.x) + .unwrap_or(border.left), + maybe_col_indexes.end.map(|index| columns[index].offset).unwrap_or(container_border_box.width), + ), + }; let grid_area = Rect { top: maybe_row_indexes.start.map(|index| rows[index].offset).unwrap_or(border.top), bottom: maybe_row_indexes .end .map(|index| rows[index].offset) .unwrap_or(container_border_box.height - border.bottom - scrollbar_gutter.y), - left: maybe_col_indexes.start.map(|index| columns[index].offset).unwrap_or(border.left), - right: maybe_col_indexes - .end - .map(|index| columns[index].offset) - .unwrap_or(container_border_box.width - border.right - scrollbar_gutter.x), + left, + right, }; drop(child_style); diff --git a/src/compute/grid/track_sizing.rs b/src/compute/grid/track_sizing.rs index 622f3a6f9..403eccf58 100644 --- a/src/compute/grid/track_sizing.rs +++ b/src/compute/grid/track_sizing.rs @@ -477,6 +477,7 @@ fn resolve_item_baselines( inner_node_size, Size::MIN_CONTENT, SizingMode::InherentSize, + item.direction, Line::FALSE, ); diff --git a/src/compute/grid/types/grid_item.rs b/src/compute/grid/types/grid_item.rs index 3df863d82..e5554c23a 100644 --- a/src/compute/grid/types/grid_item.rs +++ b/src/compute/grid/types/grid_item.rs @@ -9,7 +9,7 @@ use crate::style::{ }; use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, NodeId, SizingMode}; use crate::util::{MaybeMath, MaybeResolve, ResolveOrZero}; -use crate::{BoxSizing, GridItemStyle, LengthPercentage}; +use crate::{BoxSizing, Direction, GridItemStyle, LengthPercentage}; use core::ops::Range; /// Represents a single grid item @@ -24,6 +24,8 @@ pub(in super::super) struct GridItem { /// for final positioning pub source_order: u16, + pub direction: Direction, + /// The item's definite row-start and row-end, as resolved by the placement algorithm /// (in origin-zero coordinates) pub row: Line, @@ -105,6 +107,7 @@ impl GridItem { GridItem { node, source_order, + direction: style.direction(), row: row_span, column: col_span, overflow: style.overflow(), @@ -382,6 +385,7 @@ impl GridItem { }), SizingMode::InherentSize, axis.as_abs_naive(), + self.direction, Line::FALSE, ) } @@ -421,6 +425,7 @@ impl GridItem { }), SizingMode::InherentSize, axis.as_abs_naive(), + self.direction, Line::FALSE, ) } diff --git a/src/compute/mod.rs b/src/compute/mod.rs index 1c6ba62a5..01cfa9257 100644 --- a/src/compute/mod.rs +++ b/src/compute/mod.rs @@ -111,6 +111,16 @@ pub fn compute_root_layout(tree: &mut impl LayoutPartialTree, root: NodeId, avai known_dimensions = styled_based_known_dimensions; } } + let style = tree.get_core_container_style(root); + let padding = style.padding().resolve_or_zero(available_space.width.into_option()); + let border = style.border().resolve_or_zero(available_space.width.into_option()); + let margin = style.margin().resolve_or_zero(available_space.width.into_option()); + let scrollbar_size = Size { + width: if style.overflow().y == Overflow::Scroll { style.scrollbar_width() } else { 0.0 }, + height: if style.overflow().x == Overflow::Scroll { style.scrollbar_width() } else { 0.0 }, + }; + let direction = style.direction(); + drop(style); // Recursively compute node layout let output = tree.perform_child_layout( @@ -119,19 +129,10 @@ pub fn compute_root_layout(tree: &mut impl LayoutPartialTree, root: NodeId, avai available_space.into_options(), available_space, SizingMode::InherentSize, + direction, Line::FALSE, ); - let style = tree.get_core_container_style(root); - let padding = style.padding().resolve_or_zero(available_space.width.into_option()); - let border = style.border().resolve_or_zero(available_space.width.into_option()); - let margin = style.margin().resolve_or_zero(available_space.width.into_option()); - let scrollbar_size = Size { - width: if style.overflow().y == Overflow::Scroll { style.scrollbar_width() } else { 0.0 }, - height: if style.overflow().x == Overflow::Scroll { style.scrollbar_width() } else { 0.0 }, - }; - drop(style); - tree.set_unrounded_layout( root, &Layout { diff --git a/src/style/alignment.rs b/src/style/alignment.rs index f15e18cb9..972d3386d 100644 --- a/src/style/alignment.rs +++ b/src/style/alignment.rs @@ -95,3 +95,31 @@ pub enum AlignContent { /// /// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content) pub type JustifyContent = AlignContent; + +impl AlignItems { + /// TODO: documentation + #[inline] + pub fn flip(&mut self) { + *self = match self { + Self::Start => Self::End, + Self::End => Self::Start, + Self::FlexStart => Self::FlexEnd, + Self::FlexEnd => Self::FlexStart, + _ => return, + } + } +} + +impl AlignContent { + /// TODO: documentation + #[inline] + pub fn flip(&mut self) { + *self = match self { + Self::Start => Self::End, + Self::End => Self::Start, + Self::FlexStart => Self::FlexEnd, + Self::FlexEnd => Self::FlexStart, + _ => return, + } + } +} diff --git a/src/style/mod.rs b/src/style/mod.rs index 51563c0f2..435e82e02 100644 --- a/src/style/mod.rs +++ b/src/style/mod.rs @@ -55,6 +55,12 @@ pub trait CoreStyle { BoxSizing::BorderBox } + /// Which direction + #[inline(always)] + fn direction(&self) -> Direction { + Style::DEFAULT.direction + } + // Overflow properties /// How children overflowing their container should affect layout #[inline(always)] @@ -256,6 +262,29 @@ impl Default for BoxSizing { } } +/// TODO: Documentation +#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Direction { + #[default] + /// Left-to-right + Ltr, + /// Right-to-left + Rtl, +} + +impl Direction { + #[inline] + pub(crate) fn is_ltr(&self) -> bool { + matches!(self, Direction::Rtl) + } + + #[inline] + pub(crate) fn is_rtl(&self) -> bool { + matches!(self, Direction::Rtl) + } +} + /// How children overflowing their container should affect layout /// /// In CSS the primary effect of this property is to control whether contents of a parent container that overflow that container should @@ -335,6 +364,8 @@ pub struct Style { pub item_is_table: bool, /// Should size styles apply to the content box or the border box of the node pub box_sizing: BoxSizing, + /// The direction of text, table columns, and horizontal overflow + pub direction: Direction, // Overflow properties /// How children overflowing their container should affect layout @@ -461,6 +492,7 @@ impl Style { display: Display::DEFAULT, item_is_table: false, box_sizing: BoxSizing::BorderBox, + direction: Direction::Ltr, overflow: Point { x: Overflow::Visible, y: Overflow::Visible }, scrollbar_width: 0.0, position: Position::Relative, @@ -543,6 +575,10 @@ impl CoreStyle for Style { self.box_sizing } #[inline(always)] + fn direction(&self) -> Direction { + self.direction + } + #[inline(always)] fn overflow(&self) -> Point { self.overflow } @@ -602,6 +638,10 @@ impl CoreStyle for &'_ T { (*self).box_sizing() } #[inline(always)] + fn direction(&self) -> Direction { + (*self).direction() + } + #[inline(always)] fn overflow(&self) -> Point { (*self).overflow() } @@ -777,8 +817,14 @@ impl FlexboxItemStyle for &'_ T { #[cfg(feature = "grid")] impl GridContainerStyle for Style { - type TemplateTrackList<'a> = &'a [TrackSizingFunction] where Self: 'a; - type AutoTrackList<'a> = &'a [NonRepeatedTrackSizingFunction] where Self: 'a; + type TemplateTrackList<'a> + = &'a [TrackSizingFunction] + where + Self: 'a; + type AutoTrackList<'a> + = &'a [NonRepeatedTrackSizingFunction] + where + Self: 'a; #[inline(always)] fn grid_template_rows(&self) -> &[TrackSizingFunction] { @@ -824,8 +870,14 @@ impl GridContainerStyle for Style { #[cfg(feature = "grid")] impl GridContainerStyle for &'_ T { - type TemplateTrackList<'a> = T::TemplateTrackList<'a> where Self: 'a; - type AutoTrackList<'a> = T::AutoTrackList<'a> where Self: 'a; + type TemplateTrackList<'a> + = T::TemplateTrackList<'a> + where + Self: 'a; + type AutoTrackList<'a> + = T::AutoTrackList<'a> + where + Self: 'a; #[inline(always)] fn grid_template_rows(&self) -> Self::TemplateTrackList<'_> { @@ -923,6 +975,7 @@ mod tests { display: Default::default(), item_is_table: false, box_sizing: Default::default(), + direction: Default::default(), overflow: Default::default(), scrollbar_width: 0.0, position: Default::default(), diff --git a/src/tree/layout.rs b/src/tree/layout.rs index 166914bd2..c81d27f0d 100644 --- a/src/tree/layout.rs +++ b/src/tree/layout.rs @@ -3,6 +3,7 @@ use crate::geometry::{AbsoluteAxis, Line, Point, Rect, Size}; use crate::style::AvailableSpace; use crate::style_helpers::TaffyMaxContent; use crate::util::sys::{f32_max, f32_min}; +use crate::Direction; /// Whether we are performing a full layout, or we merely need to size the node #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -115,6 +116,8 @@ pub struct LayoutInput { pub sizing_mode: SizingMode, /// Which axis we need the size of pub axis: RequestedAxis, + /// TODO: documentation + pub direction: Direction, /// Known dimensions represent dimensions (width/height) which should be taken as fixed when performing layout. /// For example, if known_dimensions.width is set to Some(WIDTH) then this means something like: @@ -146,6 +149,7 @@ impl LayoutInput { available_space: Size::MAX_CONTENT, sizing_mode: SizingMode::InherentSize, axis: RequestedAxis::Both, + direction: Direction::Ltr, vertical_margins_are_collapsible: Line::FALSE, }; } diff --git a/src/tree/taffy_tree.rs b/src/tree/taffy_tree.rs index 57144d451..481af3d84 100644 --- a/src/tree/taffy_tree.rs +++ b/src/tree/taffy_tree.rs @@ -166,7 +166,10 @@ impl Iterator for TaffyTreeChildIter<'_> { // TraversePartialTree impl for TaffyTree impl TraversePartialTree for TaffyTree { - type ChildIter<'a> = TaffyTreeChildIter<'a> where Self: 'a; + type ChildIter<'a> + = TaffyTreeChildIter<'a> + where + Self: 'a; #[inline(always)] fn child_ids(&self, parent_node_id: NodeId) -> Self::ChildIter<'_> { @@ -243,7 +246,10 @@ where MeasureFunction: FnMut(Size>, Size, NodeId, Option<&mut NodeContext>, &Style) -> Size, { - type ChildIter<'a> = TaffyTreeChildIter<'a> where Self: 'a; + type ChildIter<'a> + = TaffyTreeChildIter<'a> + where + Self: 'a; #[inline(always)] fn child_ids(&self, parent_node_id: NodeId) -> Self::ChildIter<'_> { @@ -274,8 +280,14 @@ where MeasureFunction: FnMut(Size>, Size, NodeId, Option<&mut NodeContext>, &Style) -> Size, { - type CoreContainerStyle<'a> = &'a Style where Self : 'a; - type CacheMut<'b> = &'b mut Cache where Self : 'b; + type CoreContainerStyle<'a> + = &'a Style + where + Self: 'a; + type CacheMut<'b> + = &'b mut Cache + where + Self: 'b; #[inline(always)] fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> { @@ -349,8 +361,14 @@ where MeasureFunction: FnMut(Size>, Size, NodeId, Option<&mut NodeContext>, &Style) -> Size, { - type BlockContainerStyle<'a> = &'a Style where Self: 'a; - type BlockItemStyle<'a> = &'a Style where Self: 'a; + type BlockContainerStyle<'a> + = &'a Style + where + Self: 'a; + type BlockItemStyle<'a> + = &'a Style + where + Self: 'a; #[inline(always)] fn get_block_container_style(&self, node_id: NodeId) -> Self::BlockContainerStyle<'_> { @@ -369,8 +387,14 @@ where MeasureFunction: FnMut(Size>, Size, NodeId, Option<&mut NodeContext>, &Style) -> Size, { - type FlexboxContainerStyle<'a> = &'a Style where Self: 'a; - type FlexboxItemStyle<'a> = &'a Style where Self: 'a; + type FlexboxContainerStyle<'a> + = &'a Style + where + Self: 'a; + type FlexboxItemStyle<'a> + = &'a Style + where + Self: 'a; #[inline(always)] fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_> { @@ -389,8 +413,14 @@ where MeasureFunction: FnMut(Size>, Size, NodeId, Option<&mut NodeContext>, &Style) -> Size, { - type GridContainerStyle<'a> = &'a Style where Self: 'a; - type GridItemStyle<'a> = &'a Style where Self: 'a; + type GridContainerStyle<'a> + = &'a Style + where + Self: 'a; + type GridItemStyle<'a> + = &'a Style + where + Self: 'a; #[inline(always)] fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_> { diff --git a/src/tree/traits.rs b/src/tree/traits.rs index 2642b0749..b604c90a8 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -133,6 +133,7 @@ use crate::style::{AvailableSpace, CoreStyle}; use crate::style::{FlexboxContainerStyle, FlexboxItemStyle}; #[cfg(feature = "grid")] use crate::style::{GridContainerStyle, GridItemStyle}; +use crate::Direction; #[cfg(feature = "block_layout")] use crate::{BlockContainerStyle, BlockItemStyle}; use core::ops::{Deref, DerefMut}; @@ -287,6 +288,7 @@ pub(crate) trait LayoutPartialTreeExt: LayoutPartialTree { available_space: Size, sizing_mode: SizingMode, axis: AbsoluteAxis, + direction: Direction, vertical_margins_are_collapsible: Line, ) -> f32 { self.compute_child_layout( @@ -297,6 +299,7 @@ pub(crate) trait LayoutPartialTreeExt: LayoutPartialTree { available_space, sizing_mode, axis: axis.into(), + direction, run_mode: RunMode::ComputeSize, vertical_margins_are_collapsible, }, @@ -314,6 +317,7 @@ pub(crate) trait LayoutPartialTreeExt: LayoutPartialTree { parent_size: Size>, available_space: Size, sizing_mode: SizingMode, + direction: Direction, vertical_margins_are_collapsible: Line, ) -> LayoutOutput { self.compute_child_layout( @@ -324,6 +328,7 @@ pub(crate) trait LayoutPartialTreeExt: LayoutPartialTree { available_space, sizing_mode, axis: RequestedAxis::Both, + direction, run_mode: RunMode::PerformLayout, vertical_margins_are_collapsible, },