implement `direction` property
diff is adopted from #736 originally written by @tjamaan
230ab9359bba75fdda0ff35319d082e79189046f · ruwaq Kane Wang · (9 killa ñawpaq) · tayta 25759dad4 · kay commitpi sach'ata qhaway
Semántico sayk'uy · 45
src/compute/grid/alignment.rs
src/compute/grid/track_sizing.rs
src/compute/grid/types/grid_item.rs
src/tree/layout.rs
- silq'uyninpi t'ikrakurqan def HIDDEN pub
- silq'uyninpi t'ikrakurqan struct LayoutInput pub
diff --git a/src/compute/block.rs b/src/compute/block.rs
index b18c134b3..9b7680bc8 100644
--- a/src/compute/block.rs
+++ b/src/compute/block.rs
@@ -10,7 +10,8 @@ use crate::util::sys::Vec;
use crate::util::MaybeMath;
use crate::util::{MaybeResolve, ResolveOrZero};
use crate::{
- BlockContainerStyle, BlockItemStyle, BoxGenerationMode, BoxSizing, LayoutBlockContainer, RequestedAxis, TextAlign,
+ BlockContainerStyle, BlockItemStyle, BoxGenerationMode, BoxSizing, Direction, LayoutBlockContainer, RequestedAxis,
+ TextAlign,
};
#[cfg(feature = "float_layout")]
@@ -206,6 +207,8 @@ struct BlockItem {
#[cfg(feature = "float_layout")]
/// The `clear` style of the node
clear: Clear,
+ /// Direction (LTR or RTL)
+ direction: Direction,
/// The base size of this item
size: Size<Option<f32>>,
@@ -476,7 +479,10 @@ fn compute_inner(
let len = tree.child_count(node_id);
for order in 0..len {
let child = tree.get_child_id(node_id, order);
- if tree.get_block_child_style(child).box_generation_mode() == BoxGenerationMode::None {
+ let child_style = tree.get_block_child_style(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 as u32));
tree.perform_child_layout(
child,
@@ -484,6 +490,7 @@ fn compute_inner(
Size::NONE,
Size::MAX_CONTENT,
SizingMode::InherentSize,
+ direction,
Line::FALSE,
);
}
@@ -566,6 +573,8 @@ fn generate_item_list(
float,
#[cfg(feature = "float_layout")]
clear: child_style.clear(),
+
+ direction: child_style.direction(),
size: child_style
.size()
.maybe_resolve(node_inner_size, |val, basis| tree.calc(val, basis))
@@ -625,6 +634,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,
);
@@ -721,6 +731,7 @@ fn perform_final_layout_on_in_flow_children(
// available_space,
Size::MAX_CONTENT,
SizingMode::InherentSize,
+ item.direction,
Line::TRUE,
);
let margin_box = item_layout.size + item_non_auto_margin.sum_axes();
@@ -829,6 +840,7 @@ fn perform_final_layout_on_in_flow_children(
axis: RequestedAxis::Both,
known_dimensions,
parent_size,
+ direction: item.direction,
available_space: available_space.map_width(|_| AvailableSpace::Definite(stretch_width)),
vertical_margins_are_collapsible: if item.is_in_same_bfc { Line::TRUE } else { Line::FALSE },
};
@@ -864,6 +876,7 @@ fn perform_final_layout_on_in_flow_children(
} else {
tree.compute_child_layout(item.node_id, inputs)
};
+
let final_size = item_layout.size;
let top_margin_set = item_layout.top_margin.collapse_with_margin(item_margin.top.unwrap_or(0.0));
@@ -1104,6 +1117,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,
);
@@ -1118,6 +1132,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,
);
diff --git a/src/compute/flexbox.rs b/src/compute/flexbox.rs
index 2cf25ae4b..aa0e044f7 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<Option<f32>>,
/// 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 layout direction of the current segment being laid out
+ layout_direction: Direction,
/// Is this segment a row
is_row: bool,
/// Is this segment a column
@@ -376,7 +381,10 @@ 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);
+ 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 as u32));
tree.perform_child_layout(
child,
@@ -384,6 +392,7 @@ fn compute_preliminary(tree: &mut impl LayoutFlexboxContainer, node: NodeId, inp
Size::NONE,
Size::MAX_CONTENT,
SizingMode::InherentSize,
+ direction,
Line::FALSE,
);
}
@@ -421,6 +430,7 @@ fn compute_constants(
parent_size: Size<Option<f32>>,
) -> 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);
@@ -436,7 +446,13 @@ fn compute_constants(
let align_items = style.align_items().unwrap_or(AlignItems::Stretch);
let align_content = style.align_content().unwrap_or(AlignContent::Stretch);
- let justify_content = style.justify_content();
+ let mut justify_content = style.justify_content();
+
+ if layout_direction.is_rtl() {
+ if let Some(justify_content) = justify_content.as_mut() {
+ justify_content.flip();
+ }
+ }
// Scrollbar gutters are reserved when the `overflow` property is set to `Overflow::Scroll`.
// However, the axis are switched (transposed) because a node that scrolls vertically needs
@@ -445,11 +461,15 @@ fn compute_constants(
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;
content_box_inset.bottom += scrollbar_gutter.y;
+ match layout_direction {
+ Direction::Ltr => content_box_inset.right += scrollbar_gutter.x,
+ Direction::Rtl => content_box_inset.left += scrollbar_gutter.x,
+ };
+
let node_outer_size = known_dimensions;
let node_inner_size = node_outer_size.maybe_sub(content_box_inset.sum_axes());
let gap = style.gap().resolve_or_zero(node_inner_size.or(Size::zero()), |val, basis| tree.calc(val, basis));
@@ -459,6 +479,7 @@ fn compute_constants(
AlgoConstants {
dir,
+ layout_direction,
is_row,
is_column,
is_wrap,
@@ -518,6 +539,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, |val, basis| tree.calc(val, basis))
@@ -764,6 +786,7 @@ fn determine_flex_base_size(
child_available_space,
SizingMode::ContentSize,
dir.main_axis(),
+ child.direction,
Line::FALSE,
);
};
@@ -807,6 +830,7 @@ fn determine_flex_base_size(
child_available_space,
SizingMode::ContentSize,
dir.main_axis(),
+ child.direction,
Line::FALSE,
)
};
@@ -1062,6 +1086,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);
@@ -1397,6 +1422,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))
@@ -1467,6 +1493,7 @@ fn calculate_children_base_lines(
},
},
SizingMode::ContentSize,
+ child.direction,
Line::FALSE,
);
@@ -1771,10 +1798,12 @@ 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
@@ -1782,7 +1811,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
@@ -1795,7 +1824,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
@@ -1803,7 +1832,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
@@ -1887,6 +1916,7 @@ fn calculate_flex_item(
container_size: Size<f32>,
node_inner_size: Size<Option<f32>>,
direction: FlexDirection,
+ layout_direction: Direction,
) {
let layout_output = tree.perform_child_layout(
item.node,
@@ -1894,6 +1924,7 @@ fn calculate_flex_item(
node_inner_size,
container_size.map(|s| s.into()),
SizingMode::ContentSize,
+ item.direction,
Line::FALSE,
);
let LayoutOutput {
@@ -1903,16 +1934,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 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);
@@ -1924,14 +1970,22 @@ 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 = if direction.is_row() {
+ Point {
+ x: if layout_direction.is_rtl() {
+ container_size.width - (offset_main + size.width) + scrollbar_size.width
+ } else {
+ offset_main
+ },
+ y: offset_cross,
+ }
+ } else {
+ Point { x: offset_cross, y: offset_main }
+ };
tree.set_unrounded_layout(
item.node,
@@ -1968,10 +2022,20 @@ fn calculate_layout_line(
node_inner_size: Size<Option<f32>>,
padding_border: Rect<f32>,
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(
@@ -1985,6 +2049,7 @@ fn calculate_layout_line(
container_size,
node_inner_size,
direction,
+ layout_direction,
);
}
} else {
@@ -2000,11 +2065,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.
@@ -2014,7 +2082,11 @@ fn final_layout_pass(
flex_lines: &mut [FlexLine],
constants: &AlgoConstants,
) -> Size<f32> {
- 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;
@@ -2031,6 +2103,7 @@ fn final_layout_pass(
constants.node_inner_size,
constants.content_box_inset,
constants.dir,
+ constants.layout_direction,
);
}
} else {
@@ -2045,6 +2118,7 @@ fn final_layout_pass(
constants.node_inner_size,
constants.content_box_inset,
constants.dir,
+ constants.layout_direction,
);
}
}
@@ -2124,6 +2198,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);
@@ -2154,6 +2229,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 final_size = known_dimensions.unwrap_or(measured_size).maybe_clamp(min_size, max_size);
@@ -2167,6 +2243,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,
);
diff --git a/src/compute/grid/alignment.rs b/src/compute/grid/alignment.rs
index 9ec256adb..09db9d32d 100644
--- a/src/compute/grid/alignment.rs
+++ b/src/compute/grid/alignment.rs
@@ -198,6 +198,7 @@ pub(super) fn align_and_position_item(
let Size { width, height } = Size { width, height }.maybe_clamp(min_size, max_size);
// Layout node
+ let direction = style.direction();
drop(style);
let size = if position == Position::Absolute && (width.is_none() || height.is_none()) {
@@ -207,6 +208,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,
)
.map(Some)
@@ -220,6 +222,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 a9a3ab089..47f4bc6c0 100644
--- a/src/compute/grid/mod.rs
+++ b/src/compute/grid/mod.rs
@@ -9,8 +9,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, AutoRepeatStrategy};
@@ -45,7 +45,7 @@ pub fn compute_grid_layout<Tree: LayoutGridContainer>(
node: NodeId,
inputs: LayoutInput,
) -> LayoutOutput {
- let LayoutInput { known_dimensions, parent_size, available_space, run_mode, .. } = inputs;
+ let LayoutInput { known_dimensions, parent_size, available_space, run_mode, direction, .. } = inputs;
let style = tree.get_grid_container_style(node);
@@ -86,15 +86,26 @@ pub fn compute_grid_layout<Tree: LayoutGridContainer>(
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;
content_box_inset.bottom += scrollbar_gutter.y;
+ match direction {
+ Direction::Ltr => content_box_inset.right += scrollbar_gutter.x,
+ Direction::Rtl => content_box_inset.left += scrollbar_gutter.x,
+ };
+
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();
+
+ if direction.is_rtl() {
+ justify_content.flip();
+
+ if let Some(justify_items) = justify_items.as_mut() {
+ justify_items.flip();
+ }
+ }
// Note: we avoid accessing the grid rows/columns methods more than once as this can
// cause an expensive-ish computation
@@ -501,11 +512,22 @@ pub fn compute_grid_layout<Tree: LayoutGridContainer>(
// 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(
@@ -533,6 +555,7 @@ pub fn compute_grid_layout<Tree: LayoutGridContainer>(
// 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(
@@ -541,6 +564,7 @@ pub fn compute_grid_layout<Tree: LayoutGridContainer>(
Size::NONE,
Size::MAX_CONTENT,
SizingMode::InherentSize,
+ direction,
Line::FALSE,
);
order += 1;
@@ -568,17 +592,30 @@ pub fn compute_grid_layout<Tree: LayoutGridContainer>(
maybe_grid_line.and_then(|line: OriginZeroLine| line.try_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 2773cefd2..804390890 100644
--- a/src/compute/grid/track_sizing.rs
+++ b/src/compute/grid/track_sizing.rs
@@ -495,6 +495,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 5746303c4..a94962acc 100644
--- a/src/compute/grid/types/grid_item.rs
+++ b/src/compute/grid/types/grid_item.rs
@@ -6,7 +6,7 @@ use crate::geometry::{Line, Point, Rect, Size};
use crate::style::{AlignItems, AlignSelf, AvailableSpace, Dimension, LengthPercentageAuto, Overflow};
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
@@ -20,6 +20,8 @@ pub(in super::super) struct GridItem {
/// We sort the list of grid items during track sizing. This field allows us to sort back the original order
/// for final positioning
pub source_order: u16,
+ /// The item's direction
+ pub direction: Direction,
/// The item's definite row-start and row-end, as resolved by the placement algorithm
/// (in origin-zero coordinates)
@@ -105,6 +107,7 @@ impl GridItem {
GridItem {
node,
source_order,
+ direction: style.direction(),
row: row_span,
column: col_span,
is_compressible_replaced: style.is_compressible_replaced(),
@@ -389,6 +392,7 @@ impl GridItem {
}),
SizingMode::InherentSize,
axis.as_abs_naive(),
+ self.direction,
Line::FALSE,
)
}
@@ -428,6 +432,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 9a13a2ee5..7a3e630fe 100644
--- a/src/compute/mod.rs
+++ b/src/compute/mod.rs
@@ -120,16 +120,6 @@ pub fn compute_root_layout(tree: &mut impl LayoutPartialTree, root: NodeId, avai
}
}
- // Recursively compute node layout
- let output = tree.perform_child_layout(
- root,
- known_dimensions,
- available_space.into_options(),
- available_space,
- SizingMode::InherentSize,
- Line::FALSE,
- );
-
let style = tree.get_core_container_style(root);
let padding =
style.padding().resolve_or_zero(available_space.width.into_option(), |val, basis| tree.calc(val, basis));
@@ -141,8 +131,20 @@ pub fn compute_root_layout(tree: &mut impl LayoutPartialTree, root: NodeId, avai
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(
+ root,
+ known_dimensions,
+ available_space.into_options(),
+ available_space,
+ SizingMode::InherentSize,
+ direction,
+ Line::FALSE,
+ );
+
tree.set_unrounded_layout(
root,
&Layout {
diff --git a/src/style/alignment.rs b/src/style/alignment.rs
index f15e18cb9..d27440295 100644
--- a/src/style/alignment.rs
+++ b/src/style/alignment.rs
@@ -29,6 +29,21 @@ pub enum AlignItems {
/// Stretch to fill the container
Stretch,
}
+
+impl AlignItems {
+ /// Flips the alignment value for the given direction
+ #[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,
+ }
+ }
+}
+
/// Used to control how child nodes are aligned.
/// Does not apply to Flexbox, and will be ignored if specified on a flex container
/// For Grid it controls alignment in the inline axis
@@ -95,3 +110,17 @@ pub enum AlignContent {
///
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content)
pub type JustifyContent = AlignContent;
+
+impl AlignContent {
+ /// Flips the alignment value for the given direction
+ #[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 fe60e0411..aff44cde5 100644
--- a/src/style/mod.rs
+++ b/src/style/mod.rs
@@ -102,6 +102,12 @@ pub trait CoreStyle {
BoxSizing::BorderBox
}
+ /// The direction of text, table and grid columns, and horizontal overflow.
+ #[inline(always)]
+ fn direction(&self) -> Direction {
+ Direction::Ltr
+ }
+
// Overflow properties
/// How children overflowing their container should affect layout
#[inline(always)]
@@ -347,6 +353,26 @@ impl Overflow {
}
}
+/// Sets the direction of text, table and grid columns, and horizontal overflow.
+/// <https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/direction>
+#[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 {
+ /// Returns true if the direction is right-to-left
+ #[inline]
+ pub(crate) fn is_rtl(&self) -> bool {
+ matches!(self, Direction::Rtl)
+ }
+}
+
/// A typed representation of the CSS style information for a single node.
///
/// The most important idea in flexbox is the notion of a "main" and "cross" axis, which are always perpendicular to each other.
@@ -378,6 +404,8 @@ pub struct Style<S: CheapCloneStr = DefaultCheapStr> {
pub item_is_replaced: bool,
/// Should size styles apply to the content box or the border box of the node
pub box_sizing: BoxSizing,
+ /// Sets the direction of text, table and grid columns, and horizontal overflow.
+ pub direction: Direction,
// Overflow properties
/// How children overflowing their container should affect layout
@@ -524,6 +552,7 @@ impl<S: CheapCloneStr> Style<S> {
item_is_table: false,
item_is_replaced: false,
box_sizing: BoxSizing::BorderBox,
+ direction: Direction::Ltr,
overflow: Point { x: Overflow::Visible, y: Overflow::Visible },
scrollbar_width: 0.0,
#[cfg(feature = "float_layout")]
@@ -622,6 +651,10 @@ impl<S: CheapCloneStr> CoreStyle for Style<S> {
self.box_sizing
}
#[inline(always)]
+ fn direction(&self) -> Direction {
+ self.direction
+ }
+ #[inline(always)]
fn overflow(&self) -> Point<Overflow> {
self.overflow
}
@@ -687,6 +720,10 @@ impl<T: CoreStyle> CoreStyle for &'_ T {
(*self).box_sizing()
}
#[inline(always)]
+ fn direction(&self) -> Direction {
+ (*self).direction()
+ }
+ #[inline(always)]
fn overflow(&self) -> Point<Overflow> {
(*self).overflow()
}
@@ -1126,6 +1163,7 @@ mod tests {
float: Default::default(),
#[cfg(feature = "float_layout")]
clear: 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 304953708..2d7d4ca43 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,
+ /// The direction of layout
+ 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:
@@ -141,6 +144,7 @@ impl LayoutInput {
// The important property for hidden layout
run_mode: RunMode::PerformHiddenLayout,
// The rest will be ignored
+ direction: Direction::Ltr,
known_dimensions: Size::NONE,
parent_size: Size::NONE,
available_space: Size::MAX_CONTENT,
diff --git a/src/tree/traits.rs b/src/tree/traits.rs
index 194110ef5..0b03efda5 100644
--- a/src/tree/traits.rs
+++ b/src/tree/traits.rs
@@ -136,6 +136,7 @@ use crate::style::{FlexboxContainerStyle, FlexboxItemStyle};
#[cfg(feature = "grid")]
use crate::style::{GridContainerStyle, GridItemStyle};
use crate::CheapCloneStr;
+use crate::Direction;
#[cfg(feature = "block_layout")]
use crate::{BlockContainerStyle, BlockContext, BlockItemStyle};
@@ -343,6 +344,7 @@ pub(crate) trait LayoutPartialTreeExt: LayoutPartialTree {
available_space: Size<AvailableSpace>,
sizing_mode: SizingMode,
axis: AbsoluteAxis,
+ direction: Direction,
vertical_margins_are_collapsible: Line<bool>,
) -> f32 {
self.compute_child_layout(
@@ -353,6 +355,7 @@ pub(crate) trait LayoutPartialTreeExt: LayoutPartialTree {
available_space,
sizing_mode,
axis: axis.into(),
+ direction,
run_mode: RunMode::ComputeSize,
vertical_margins_are_collapsible,
},
@@ -371,6 +374,7 @@ pub(crate) trait LayoutPartialTreeExt: LayoutPartialTree {
parent_size: Size<Option<f32>>,
available_space: Size<AvailableSpace>,
sizing_mode: SizingMode,
+ direction: Direction,
vertical_margins_are_collapsible: Line<bool>,
) -> Size<f32> {
self.compute_child_layout(
@@ -381,6 +385,7 @@ pub(crate) trait LayoutPartialTreeExt: LayoutPartialTree {
available_space,
sizing_mode,
axis: RequestedAxis::Both,
+ direction,
run_mode: RunMode::ComputeSize,
vertical_margins_are_collapsible,
},
@@ -390,6 +395,7 @@ pub(crate) trait LayoutPartialTreeExt: LayoutPartialTree {
/// Perform a full layout on the node given the specified constraints
#[inline(always)]
+ #[allow(clippy::too_many_arguments)]
fn perform_child_layout(
&mut self,
node_id: NodeId,
@@ -397,6 +403,7 @@ pub(crate) trait LayoutPartialTreeExt: LayoutPartialTree {
parent_size: Size<Option<f32>>,
available_space: Size<AvailableSpace>,
sizing_mode: SizingMode,
+ direction: Direction,
vertical_margins_are_collapsible: Line<bool>,
) -> LayoutOutput {
self.compute_child_layout(
@@ -407,6 +414,7 @@ pub(crate) trait LayoutPartialTreeExt: LayoutPartialTree {
available_space,
sizing_mode,
axis: RequestedAxis::Both,
+ direction,
run_mode: RunMode::PerformLayout,
vertical_margins_are_collapsible,
},