@@ -9,10 +9,12 @@ use crate::util::sys::f32_max;
99use crate :: util:: sys:: Vec ;
1010use crate :: util:: MaybeMath ;
1111use 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" ) ]
1719use 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
211272fn 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]
459530fn 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]
503574fn 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