Skip to content

Commit 6e86410

Browse files
committed
Float positioning logic WIP
Signed-off-by: Nico Burns <[email protected]>
1 parent 8ac1502 commit 6e86410

File tree

5 files changed

+488
-122
lines changed

5 files changed

+488
-122
lines changed

src/compute/block.rs

Lines changed: 155 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ use crate::util::sys::f32_max;
99
use crate::util::sys::Vec;
1010
use crate::util::MaybeMath;
1111
use crate::util::{MaybeResolve, ResolveOrZero};
12-
use crate::{BlockContainerStyle, BlockItemStyle, BoxGenerationMode, BoxSizing, LayoutBlockContainer, TextAlign};
12+
use crate::{
13+
BlockContainerStyle, BlockItemStyle, BoxGenerationMode, BoxSizing, LayoutBlockContainer, RequestedAxis, TextAlign,
14+
};
1315

1416
#[cfg(feature = "float_layout")]
15-
use super::{FloatContext, FloatDirection, FloatedBox};
17+
use super::{FloatContext, FloatDirection};
1618
#[cfg(feature = "float_layout")]
1719
use crate::{Clear, Float};
1820

@@ -30,29 +32,82 @@ impl BlockFormattingContext {
3032
float_context: FloatContext::new(available_space),
3133
}
3234
}
35+
36+
/// Create an initial `BlockContext` for this `BlockFormattingContext`
37+
pub fn root_block_context(&mut self) -> BlockContext<'_> {
38+
BlockContext { bfc: self, y_offset: 0.0, insets: [0.0, 0.0], float_content_contribution: 0.0, is_root: true }
39+
}
40+
}
41+
42+
/// Context for each individual Block within a Block Formatting Context
43+
///
44+
/// Contains a mutable reference to the BlockFormattingContext + block-specific data
45+
pub struct BlockContext<'bfc> {
46+
bfc: &'bfc mut BlockFormattingContext,
47+
/// The y-offset of the border-top of the block node, relative to the to the border-top of the
48+
/// root node of the Block Formatting Context it belongs to.
49+
y_offset: f32,
50+
/// The x-inset in from each side of the block node, relative to the root node of the Block Formatting Context it belongs to.
51+
insets: [f32; 2],
52+
/// The height that floats take up in the element
53+
float_content_contribution: f32,
54+
/// Whether the node is the root of the Block Formatting Context is belongs to.
55+
is_root: bool,
3356
}
3457

3558
#[cfg(feature = "float_layout")]
36-
impl BlockFormattingContext {
59+
impl BlockContext<'_> {
60+
/// Create a sub-`BlockContext` for a child block node
61+
pub fn sub_context(&mut self, additional_y_offset: f32, insets: [f32; 2]) -> BlockContext<'_> {
62+
BlockContext {
63+
bfc: self.bfc,
64+
y_offset: self.y_offset + additional_y_offset,
65+
insets: [self.insets[0] + insets[0], self.insets[1] + insets[1]],
66+
float_content_contribution: 0.0,
67+
is_root: false,
68+
}
69+
}
70+
3771
pub fn set_width(&mut self, available_space: AvailableSpace) {
38-
self.float_context.set_width(available_space);
72+
self.bfc.float_context.set_width(available_space);
3973
}
4074

41-
pub fn place_floated_box(&mut self, floated_box: FloatedBox, float_direction: FloatDirection) -> Point<f32> {
42-
self.float_context.place_floated_box(floated_box, float_direction)
75+
pub fn is_bfc_root(&self) -> bool {
76+
self.is_root
77+
}
78+
79+
pub fn place_floated_box(
80+
&mut self,
81+
floated_box: Size<f32>,
82+
min_y: f32,
83+
direction: FloatDirection,
84+
clear: Clear,
85+
) -> Point<f32> {
86+
let mut pos =
87+
self.bfc.float_context.place_floated_box(floated_box, min_y + self.y_offset, self.insets, direction, clear);
88+
pos.y -= self.y_offset;
89+
pos.x -= self.insets[0];
90+
91+
self.float_content_contribution = self.float_content_contribution.max(pos.y + floated_box.height);
92+
93+
pos
94+
}
95+
96+
fn add_child_floated_content_height_contribution(&mut self, child_contribution: f32) {
97+
self.float_content_contribution = self.float_content_contribution.max(child_contribution);
4398
}
4499

45100
fn floated_content_width_contribution(&self) -> f32 {
46-
self.float_context.content_width()
101+
self.bfc.float_context.content_width()
47102
}
48103

49104
fn floated_content_height_contribution(&self) -> f32 {
50-
self.float_context.content_height()
105+
self.float_content_contribution
51106
}
52107
}
53108

54109
#[cfg(not(feature = "float_layout"))]
55-
impl BlockFormattingContext {
110+
impl BlockContext<'_> {
56111
fn floated_content_contribution(&self) -> f32 {
57112
0.0
58113
}
@@ -73,6 +128,9 @@ struct BlockItem {
73128
/// Items that are tables don't have stretch sizing applied to them
74129
is_table: bool,
75130

131+
// Whether the child is a non-independent block or inline node
132+
is_in_same_bfc: bool,
133+
76134
// Float and clear styles
77135
#[cfg(feature = "float_layout")]
78136
float: Float,
@@ -133,7 +191,7 @@ pub fn compute_block_layout(
133191
tree: &mut impl LayoutBlockContainer,
134192
node_id: NodeId,
135193
inputs: LayoutInput,
136-
bfc: Option<&mut BlockFormattingContext>,
194+
block_ctx: Option<&mut BlockContext<'_>>,
137195
) -> LayoutOutput {
138196
let LayoutInput { known_dimensions, parent_size, run_mode, .. } = inputs;
139197
let style = tree.get_block_container_style(node_id);
@@ -187,33 +245,35 @@ pub fn compute_block_layout(
187245
}
188246

189247
// Unwrap the block formatting context if one was passed, or else create a new one
190-
let is_root = bfc.is_none();
191-
let mut new_bfc: BlockFormattingContext;
192-
let bfc_ref: &mut BlockFormattingContext = match bfc {
193-
Some(inherited_bfc) => inherited_bfc,
248+
debug_log!("BLOCK");
249+
match block_ctx {
250+
Some(inherited_bfc) => compute_inner(
251+
tree,
252+
node_id,
253+
LayoutInput { known_dimensions: styled_based_known_dimensions, ..inputs },
254+
inherited_bfc,
255+
),
194256
None => {
195-
new_bfc = BlockFormattingContext::new(inputs.available_space.width);
196-
&mut new_bfc
257+
let mut root_bfc = BlockFormattingContext::new(inputs.available_space.width);
258+
let mut root_ctx = root_bfc.root_block_context();
259+
compute_inner(
260+
tree,
261+
node_id,
262+
LayoutInput { known_dimensions: styled_based_known_dimensions, ..inputs },
263+
&mut root_ctx,
264+
)
197265
}
198-
};
266+
}
199267

200-
debug_log!("BLOCK");
201-
compute_inner(
202-
tree,
203-
node_id,
204-
LayoutInput { known_dimensions: styled_based_known_dimensions, ..inputs },
205-
bfc_ref,
206-
is_root,
207-
)
268+
// compute_inner(tree, node_id, LayoutInput { known_dimensions: styled_based_known_dimensions, ..inputs }, block_ctx)
208269
}
209270

210271
/// Computes the layout of [`LayoutBlockContainer`] according to the block layout algorithm
211272
fn compute_inner(
212273
tree: &mut impl LayoutBlockContainer,
213274
node_id: NodeId,
214275
inputs: LayoutInput,
215-
bfc: &mut BlockFormattingContext,
216-
is_bfc_root: bool,
276+
mut block_ctx: &mut BlockContext<'_>,
217277
) -> LayoutOutput {
218278
let LayoutInput {
219279
known_dimensions, parent_size, available_space, run_mode, vertical_margins_are_collapsible, ..
@@ -261,25 +321,25 @@ fn compute_inner(
261321
.maybe_apply_aspect_ratio(aspect_ratio)
262322
.maybe_add(box_sizing_adjustment);
263323

324+
let overflow = style.overflow();
325+
let is_scroll_container = overflow.x.is_scroll_container() || overflow.y.is_scroll_container();
326+
264327
// Determine margin collapsing behaviour
265328
let own_margins_collapse_with_children = Line {
266329
start: vertical_margins_are_collapsible.start
267-
&& !style.overflow().x.is_scroll_container()
268-
&& !style.overflow().y.is_scroll_container()
330+
&& !is_scroll_container
269331
&& style.position() == Position::Relative
270332
&& padding.top == 0.0
271333
&& border.top == 0.0,
272334
end: vertical_margins_are_collapsible.end
273-
&& !style.overflow().x.is_scroll_container()
274-
&& !style.overflow().y.is_scroll_container()
335+
&& !is_scroll_container
275336
&& style.position() == Position::Relative
276337
&& padding.bottom == 0.0
277338
&& border.bottom == 0.0
278339
&& size.height.is_none(),
279340
};
280341
let has_styles_preventing_being_collapsed_through = !style.is_block()
281-
|| style.overflow().x.is_scroll_container()
282-
|| style.overflow().y.is_scroll_container()
342+
|| is_scroll_container
283343
|| style.position() == Position::Absolute
284344
|| padding.top > 0.0
285345
|| padding.bottom > 0.0
@@ -298,7 +358,7 @@ fn compute_inner(
298358
// 2. Compute container width
299359
let container_outer_width = known_dimensions.width.unwrap_or_else(|| {
300360
let available_width = available_space.width.maybe_sub(content_box_inset.horizontal_axis_sum());
301-
let intrinsic_width = determine_content_based_container_width(tree, bfc, &items, available_width)
361+
let intrinsic_width = determine_content_based_container_width(tree, &mut block_ctx, &items, available_width)
302362
+ content_box_inset.horizontal_axis_sum();
303363
intrinsic_width.maybe_clamp(min_size.width, max_size.width).maybe_max(Some(padding_border_size.width))
304364
});
@@ -321,12 +381,13 @@ fn compute_inner(
321381
resolved_content_box_inset,
322382
text_align,
323383
own_margins_collapse_with_children,
324-
bfc,
384+
&mut block_ctx,
325385
);
326386

327387
// Root BFCs contain floats
328-
if is_bfc_root {
329-
intrinsic_outer_height = intrinsic_outer_height.max(bfc.floated_content_height_contribution());
388+
let contains_floats = block_ctx.is_bfc_root() || is_scroll_container;
389+
if contains_floats {
390+
intrinsic_outer_height = intrinsic_outer_height.max(block_ctx.floated_content_height_contribution());
330391
}
331392

332393
let container_outer_height = known_dimensions
@@ -413,12 +474,22 @@ fn generate_item_list(
413474
let pb_sum = (padding + border).sum_axes();
414475
let box_sizing_adjustment =
415476
if child_style.box_sizing() == BoxSizing::ContentBox { pb_sum } else { Size::ZERO };
477+
478+
let position = child_style.position();
479+
let float = child_style.float();
480+
481+
let is_block = child_style.is_block();
482+
let is_table = child_style.is_table();
483+
484+
let is_in_same_bfc: bool = is_block && !is_table && position != Position::Absolute && float == Float::None;
485+
416486
BlockItem {
417487
node_id: child_node_id,
418488
order: order as u32,
419-
is_table: child_style.is_table(),
489+
is_table,
490+
is_in_same_bfc,
420491
#[cfg(feature = "float_layout")]
421-
float: child_style.float(),
492+
float,
422493
#[cfg(feature = "float_layout")]
423494
clear: child_style.clear(),
424495
size: child_style
@@ -438,7 +509,7 @@ fn generate_item_list(
438509
.maybe_add(box_sizing_adjustment),
439510
overflow: child_style.overflow(),
440511
scrollbar_width: child_style.scrollbar_width(),
441-
position: child_style.position(),
512+
position,
442513
inset: child_style.inset(),
443514
margin: child_style.margin(),
444515
padding,
@@ -458,7 +529,7 @@ fn generate_item_list(
458529
#[inline]
459530
fn determine_content_based_container_width(
460531
tree: &mut impl LayoutPartialTree,
461-
bfc: &mut BlockFormattingContext,
532+
block_ctx: &mut BlockContext<'_>,
462533
items: &[BlockItem],
463534
available_width: AvailableSpace,
464535
) -> f32 {
@@ -495,20 +566,20 @@ fn determine_content_based_container_width(
495566
max_child_width = f32_max(max_child_width, width);
496567
}
497568

498-
max_child_width.max(bfc.floated_content_width_contribution())
569+
max_child_width.max(block_ctx.floated_content_width_contribution())
499570
}
500571

501572
/// Compute each child's final size and position
502573
#[inline]
503574
fn perform_final_layout_on_in_flow_children(
504-
tree: &mut impl LayoutPartialTree,
575+
tree: &mut impl LayoutBlockContainer,
505576
items: &mut [BlockItem],
506577
container_outer_width: f32,
507578
content_box_inset: Rect<f32>,
508579
resolved_content_box_inset: Rect<f32>,
509580
text_align: TextAlign,
510581
own_margins_collapse_with_children: Line<bool>,
511-
bfc: &mut BlockFormattingContext,
582+
block_ctx: &mut BlockContext<'_>,
512583
) -> (Size<f32>, f32, CollapsibleMarginSet, CollapsibleMarginSet) {
513584
// Resolve container_inner_width for sizing child nodes using initial content_box_inset
514585
let container_inner_width = container_outer_width - content_box_inset.horizontal_axis_sum();
@@ -517,12 +588,15 @@ fn perform_final_layout_on_in_flow_children(
517588
Size { width: AvailableSpace::Definite(container_inner_width), height: AvailableSpace::MinContent };
518589

519590
// TODO: handle nested blocks with different widths
520-
bfc.set_width(available_space.width);
591+
if block_ctx.is_bfc_root() {
592+
block_ctx.set_width(available_space.width);
593+
}
521594

522595
#[cfg_attr(not(feature = "content_size"), allow(unused_mut))]
523596
let mut inflow_content_size = Size::ZERO;
524597
let mut committed_y_offset = resolved_content_box_inset.top;
525598
let mut y_offset_for_absolute = resolved_content_box_inset.top;
599+
let mut y_offset_for_float = resolved_content_box_inset.top;
526600
let mut first_child_top_margin_set = CollapsibleMarginSet::ZERO;
527601
let mut active_collapsible_margin_set = CollapsibleMarginSet::ZERO;
528602
let mut is_collapsing_with_first_margin_set = true;
@@ -554,10 +628,8 @@ fn perform_final_layout_on_in_flow_children(
554628
);
555629
let margin_box = item_layout.size + item_non_auto_margin.sum_axes();
556630

557-
let mut location = bfc.place_floated_box(
558-
FloatedBox { id: item.node_id.into(), width: margin_box.width, height: margin_box.height },
559-
float_direction,
560-
);
631+
let mut location =
632+
block_ctx.place_floated_box(margin_box, y_offset_for_float, float_direction, item.clear);
561633

562634
location.y += item_non_auto_margin.top;
563635
location.x += item_non_auto_margin.left;
@@ -613,14 +685,40 @@ fn perform_final_layout_on_in_flow_children(
613685
.maybe_clamp(item.min_size, item.max_size)
614686
};
615687

616-
let item_layout = tree.perform_child_layout(
617-
item.node_id,
688+
//
689+
690+
let inputs = LayoutInput {
691+
run_mode: RunMode::PerformLayout,
692+
sizing_mode: SizingMode::InherentSize,
693+
axis: RequestedAxis::Both,
618694
known_dimensions,
619695
parent_size,
620-
available_space.map_width(|w| w.maybe_sub(item_non_auto_x_margin_sum)),
621-
SizingMode::InherentSize,
622-
Line::TRUE,
623-
);
696+
available_space: available_space.map_width(|w| w.maybe_sub(item_non_auto_x_margin_sum)),
697+
vertical_margins_are_collapsible: if item.is_in_same_bfc { Line::TRUE } else { Line::FALSE },
698+
};
699+
700+
let item_layout = if item.is_in_same_bfc {
701+
let width = known_dimensions
702+
.width
703+
.expect("Same-bfc child will always have defined width due to stretch sizing");
704+
705+
// TODO: account for auto margins
706+
let inset_left = item_non_auto_margin.left + content_box_inset.left;
707+
let inset_right = container_outer_width - width - inset_left;
708+
let insets = [inset_left, inset_right];
709+
710+
// Compute child layout
711+
let mut child_block_ctx = block_ctx.sub_context(y_offset_for_absolute, insets);
712+
let output = tree.compute_block_child_layout(item.node_id, inputs, Some(&mut child_block_ctx));
713+
714+
// Extract float contribution from child block context
715+
let child_contribution = child_block_ctx.floated_content_height_contribution();
716+
block_ctx.add_child_floated_content_height_contribution(y_offset_for_absolute + child_contribution);
717+
718+
output
719+
} else {
720+
tree.compute_child_layout(item.node_id, inputs)
721+
};
624722
let final_size = item_layout.size;
625723

626724
let top_margin_set = item_layout.top_margin.collapse_with_margin(item_margin.top.unwrap_or(0.0));
@@ -734,6 +832,8 @@ fn perform_final_layout_on_in_flow_children(
734832
active_collapsible_margin_set = bottom_margin_set;
735833
y_offset_for_absolute = committed_y_offset + active_collapsible_margin_set.resolve();
736834
}
835+
836+
y_offset_for_float += committed_y_offset + item_layout.size.height + y_margin_offset;
737837
}
738838
}
739839

0 commit comments

Comments
 (0)