Chromium Code Reviews| 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/FlowLayout .md). | |
|
atotic
2017/06/10 00:26:51
s/FlowLayout/BlockLayout
ikilpatrick
2017/06/12 22:58:39
Done.
| |
| 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 | |
| 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 | |
| 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 10px apart --> | |
|
atotic
2017/06/10 00:26:51
It'd be clearer if there was only 1 way to get mat
ikilpatrick
2017/06/12 22:58:38
Done.
| |
| 38 <div style="margin-bottom: 10px"> | |
| 39 <div style="margin-bottom: -20px"> | |
| 40 <div style="margin-bottom: 20px">Hi</div> | |
| 41 </div> | |
| 42 </div> | |
| 43 <div style="margin-top: 30px"> | |
| 44 <div style="margin-top: -10px">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 | |
|
dgrogan
2017/06/12 18:54:40
Could you add a parenthetical after **anything** t
ikilpatrick
2017/06/12 22:58:39
Done.
| |
| 49 stack together. There are known as **adjoining margins**. If we apply our formu la to the above we | |
| 50 get: `max(10, 20, 30) + min(-20, -10) = 10`. | |
| 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 above case would be to _bubble_ up margins. For exampl e each fragment | |
|
dgrogan
2017/06/12 18:54:40
Which "above case" do you mean? I suspect the adjo
ikilpatrick
2017/06/12 22:58:38
Done.
| |
| 73 would have a **margin strut** at the block-start and block-end edge. If the chil d fragment was | |
| 74 **adjoining** to its parent, you simply keep track of the margins by calling `Ap pend` on the margin | |
|
dgrogan
2017/06/12 18:54:40
Could you say which of the 4 margin struts (block-
ikilpatrick
2017/06/12 22:58:38
Done? - added example code for bubbling. PTAL
| |
| 75 strut. | |
| 76 | |
| 77 When it comes time to collapse the margins you can use the margin collapsing rul e, e.g. | |
| 78 ```cpp | |
| 79 MarginStrut s1 = fragment1.block_end_margin_strut; | |
| 80 MarginStrut s2 = fragment2.block_start_margin_strut; | |
| 81 LayoutUnit distance = | |
| 82 std::max(s1.pos_margin, s2.pos_margin) + | |
| 83 std::min(s1.neg_margin, s2.neg_margin); | |
| 84 ``` | |
| 85 | |
| 86 This would be pretty simple - however it doesn't work. As we discussed in the fl oats section a | |
| 87 _child_ will position _itself_ within the BFC. If we did margin collapsing this way way we'd create | |
|
dgrogan
2017/06/12 18:54:40
"way way"
ikilpatrick
2017/06/12 22:58:39
Done.
| |
| 88 a circular dependency between layout and positioning. E.g. we need to perform la yout in order to | |
| 89 determine the block-start margin strut, which would allow us to position the fra gment, which would | |
| 90 allow us to perform layout. | |
| 91 | |
| 92 We **invert** the problem. A fragment now only produces an _end_ margin strut. T he _start_ margin | |
| 93 strut becomes an input as well as where the margin strut is currently positioned within the BFC. | |
| 94 For example: | |
| 95 | |
| 96 ```cpp | |
| 97 Fragment* Layout(LogicalOffset bfc_estimate, MarginStrut input_strut) { | |
|
kojii
2017/06/10 05:12:13
Is "bfc_estimate" the "offset to BFC without the l
ikilpatrick
2017/06/12 22:58:38
Thats correct, another way of thinking about this
| |
| 98 MarginStrut curr_strut = input_strut; | |
| 99 LogicalOffset curr_bfc_estimate = bfc_estimate; | |
| 100 | |
| 101 // We collapse the margin strut which allows us to compute our BFC offset if | |
| 102 // we have border or padding. I.e. we don't have an adjoining margin. | |
| 103 if (border_padding.block_start) { | |
| 104 curr_bfc_estimate += curr_strut.Sum(); | |
| 105 curr_strut = MarginStrut(); | |
| 106 | |
| 107 fragment_builder.SetBfcOffset(curr_bfc_estimate); | |
| 108 curr_bfc_estimate += border_padding.block_start; | |
| 109 } | |
| 110 | |
|
kojii
2017/06/10 05:12:13
IIUC, up until here is common to all algorithms, a
ikilpatrick
2017/06/12 22:58:38
This is only for block and inline, but yeah.
| |
| 111 for (const auto& child : children) { | |
| 112 curr_strut.Append(child.margins.block_start); | |
| 113 const auto* fragment = child.Layout(curr_strut); | |
|
kojii
2017/06/10 05:12:13
child.Layout(curr_bfc_estimate, curr_strut) ?
ikilpatrick
2017/06/12 22:58:38
Done.
| |
| 114 | |
| 115 curr_strut = fragment->end_margin_strut; | |
| 116 curr_strut.Append(child.margins.block_end); | |
| 117 | |
| 118 curr_bfc_estimate = fragment->BfcOffset() + fragment->BlockSize(); | |
| 119 } | |
| 120 | |
| 121 fragment_builder.SetEndMarginStrut(curr_strut); | |
| 122 | |
| 123 return fragment_builder.ToFragment(); | |
| 124 } | |
| 125 ``` | |
| 126 | |
| 127 It isn't immediately obvious that this works, but if you try and work through an example manually, | |
| 128 it'll become clearer. | |
| 129 | |
| 130 There are lots of different things which can "resolve" the BFC offset of an elem ent. For example | |
| 131 inline content (text, atomic inlines), border and padding, if a child _might_ be affected by | |
| 132 clearance. | |
| 133 | |
| 134 ## Zero block-size fragments ## | |
| 135 | |
| 136 TODO. | |
| 137 | |
| OLD | NEW |