OLD | NEW |
---|---|
(Empty) | |
1 # Block Layout # | |
2 | |
3 This document can be viewed in formatted form [here](https://chromium.googlesour ce.com/chromium/src/+/master/third_party/WebKit/Source/core/layout/ng/BlockLayou t.md). | |
4 | |
5 ## Floats ## | |
6 | |
7 TODO. | |
8 | |
9 ## An introduction to margin collapsing ## | |
10 | |
11 A simple way to think about margin collapsing is that it takes the maximum margi n between two | |
cbiesinger
2017/06/13 16:45:25
Can we stay with 80 character lines?
ikilpatrick
2017/06/13 20:42:40
Done.
| |
12 elements. For example: | |
13 | |
14 ```html | |
15 <!-- The divs below are 20px apart --> | |
16 <div style="margin-bottom: 10px;">Hi</div> | |
17 <div style="margin-top: 20px;">there</div> | |
18 ``` | |
19 | |
20 This is complicated by _negative_ margins. For example: | |
21 | |
22 ```html | |
23 <!-- The divs below are 10px apart --> | |
24 <div style="margin-bottom: 20px;">Hi</div> | |
25 <div style="margin-top: -10px;">there</div> | |
26 | |
27 <!-- The divs below are -20px apart --> | |
28 <div style="margin-bottom: -20px;">Hi</div> | |
29 <div style="margin-top: -10px;">there</div> | |
30 ``` | |
31 | |
32 The rule here is: `max(pos_margins) + min(neg_margins)`. This rule we'll refer t o as the _margin | |
cbiesinger
2017/06/13 16:45:25
Maybe also link to the CSS spec here (or, at the b
ikilpatrick
2017/06/13 20:42:40
Done.
| |
33 collapsing rule_. If this only happened between top level elements it would be p retty simple, | |
34 however consider the following: | |
35 | |
36 ```html | |
37 <!-- The top-level divs below are -2px apart --> | |
38 <div style="margin-bottom: 3px"> | |
39 <div style="margin-bottom: -5"> | |
40 <div style="margin-bottom: 7px">Hi</div> | |
41 </div> | |
42 </div> | |
43 <div style="margin-top: 11px"> | |
44 <div style="margin-top: -13px">there</div> | |
45 </div> | |
46 ``` | |
47 | |
48 In the above example as there isn't **anything** separating the edges of two fra gments the margins | |
49 stack together (e.g. no borders or padding). There are known as **adjoining marg ins**. If we apply | |
50 our formula to the above we get: `max(3, 7, 11) + min(-5, -13) = -2`. | |
51 | |
52 A useful concept is a **margin strut**. This is a pair of margins consisting of one positive and one | |
53 negative margin. | |
54 | |
55 A margin strut allows us to keep track of the largest positive and smallest nega tive margin. E.g. | |
56 ```cpp | |
57 struct MarginStrut { | |
58 LayoutUnit pos_margin; | |
59 LayoutUnit neg_margin; | |
60 | |
61 void Append(LayoutUnit margin) { | |
62 if (margin < 0) | |
63 neg_margin = std::min(margin, neg_margin); | |
64 else | |
65 pos_margin = std::max(margin, pos_margin); | |
66 } | |
67 | |
68 LayoutUnit Sum() { return pos_margin + neg_margin; } | |
69 } | |
70 ``` | |
71 | |
72 A naïve algorithm for the adjoining margins case would be to _bubble_ up margins . For example each | |
73 fragment would have a **margin strut** at the block-start and block-end edge. If the child fragment | |
74 was **adjoining** to its parent, you simply keep track of the margins by calling `Append` on the | |
75 margin strut. E.g. | |
76 | |
77 ```cpp | |
78 // fragment1 is the first child. | |
79 MarginStrut s1 = fragment1.block_start_margin_strut; | |
80 s1.Append(node1.style.margin_start); | |
81 | |
82 builder.SetStartMarginStrut(s1); | |
83 | |
84 // fragment2 is the last child. | |
85 MarginStrut s2 = fragment2.block_end_margin_strut; | |
86 s2.Append(node2.style.margin_start); | |
87 | |
88 builder.SetEndMarginStrut(s2); | |
89 ``` | |
90 | |
91 When it comes time to collapse the margins you can use the margin collapsing rul e, e.g. | |
92 ```cpp | |
93 MarginStrut s1 = fragment1.block_end_margin_strut; | |
94 MarginStrut s2 = fragment2.block_start_margin_strut; | |
95 LayoutUnit distance = | |
96 std::max(s1.pos_margin, s2.pos_margin) + | |
97 std::min(s1.neg_margin, s2.neg_margin); | |
98 ``` | |
99 | |
100 This would be pretty simple - however it doesn't work. As we discussed in the fl oats section a | |
cbiesinger
2017/06/13 16:45:24
The floats section that just consists of "TODO"? :
ikilpatrick
2017/06/13 20:42:40
Yup, was going to do this in the next patch.
| |
101 _child_ will position _itself_ within the BFC. If we did margin collapsing this way we'd create a | |
102 circular dependency between layout and positioning. E.g. we need to perform layo ut in order to | |
103 determine the block-start margin strut, which would allow us to position the fra gment, which would | |
104 allow us to perform layout. | |
105 | |
106 We **invert** the problem. A fragment now only produces an _end_ margin strut. T he _start_ margin | |
107 strut becomes an input as well as where the margin strut is currently positioned within the BFC. | |
cbiesinger
2017/06/13 16:45:24
I think adding some more details about the bfc off
ikilpatrick
2017/06/13 20:42:40
Yeah I was going to introduce the BFC offset conce
cbiesinger
2017/06/14 21:00:27
OK, I only mentioned it because your code below ma
| |
108 For example: | |
109 | |
110 ```cpp | |
111 Fragment* Layout(LogicalOffset bfc_estimate, MarginStrut input_strut) { | |
112 MarginStrut curr_strut = input_strut; | |
113 LogicalOffset curr_bfc_estimate = bfc_estimate; | |
114 | |
115 // We collapse the margin strut which allows us to compute our BFC offset if | |
116 // we have border or padding. I.e. we don't have an adjoining margin. | |
117 if (border_padding.block_start) { | |
118 curr_bfc_estimate += curr_strut.Sum(); | |
119 curr_strut = MarginStrut(); | |
120 | |
121 fragment_builder.SetBfcOffset(curr_bfc_estimate); | |
122 curr_bfc_estimate += border_padding.block_start; | |
123 } | |
124 | |
125 for (const auto& child : children) { | |
126 curr_strut.Append(child.margins.block_start); | |
127 const auto* fragment = child.Layout(curr_bfc_estimate, curr_strut); | |
128 | |
129 curr_strut = fragment->end_margin_strut; | |
130 curr_strut.Append(child.margins.block_end); | |
131 | |
132 curr_bfc_estimate = fragment->BfcOffset() + fragment->BlockSize(); | |
133 } | |
134 | |
135 fragment_builder.SetEndMarginStrut(curr_strut); | |
136 | |
137 return fragment_builder.ToFragment(); | |
138 } | |
139 ``` | |
140 | |
141 It isn't immediately obvious that this works, but if you try and work through an example manually, | |
142 it'll become clearer. | |
143 | |
144 There are lots of different things which can "resolve" the BFC offset of an elem ent. For example | |
145 inline content (text, atomic inlines), border and padding, if a child _might_ be affected by | |
146 clearance. | |
147 | |
148 ## Zero block-size fragments ## | |
149 | |
150 TODO. | |
151 | |
OLD | NEW |