OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/layout/MultiColumnFragmentainerGroup.h" | 5 #include "core/layout/MultiColumnFragmentainerGroup.h" |
6 | 6 |
7 #include "core/layout/ColumnBalancer.h" | 7 #include "core/layout/ColumnBalancer.h" |
8 #include "core/layout/FragmentationContext.h" | 8 #include "core/layout/FragmentationContext.h" |
9 #include "core/layout/LayoutMultiColumnSet.h" | 9 #include "core/layout/LayoutMultiColumnSet.h" |
10 | 10 |
(...skipping 28 matching lines...) Expand all Loading... | |
39 | 39 |
40 void MultiColumnFragmentainerGroup::resetColumnHeight() { | 40 void MultiColumnFragmentainerGroup::resetColumnHeight() { |
41 m_maxColumnHeight = calculateMaxColumnHeight(); | 41 m_maxColumnHeight = calculateMaxColumnHeight(); |
42 | 42 |
43 LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(); | 43 LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(); |
44 if (m_columnSet.heightIsAuto()) { | 44 if (m_columnSet.heightIsAuto()) { |
45 FragmentationContext* enclosingFragmentationContext = | 45 FragmentationContext* enclosingFragmentationContext = |
46 flowThread->enclosingFragmentationContext(); | 46 flowThread->enclosingFragmentationContext(); |
47 if (enclosingFragmentationContext && | 47 if (enclosingFragmentationContext && |
48 enclosingFragmentationContext->isFragmentainerLogicalHeightKnown()) { | 48 enclosingFragmentationContext->isFragmentainerLogicalHeightKnown()) { |
49 // Even if height is auto, we set an initial height, in order to tell how much content | 49 // Even if height is auto, we set an initial height, in order to tell how |
50 // this MultiColumnFragmentainerGroup can hold, and when we need to append a new one. | 50 // much content this MultiColumnFragmentainerGroup can hold, and when we |
51 // need to append a new one. | |
51 m_columnHeight = m_maxColumnHeight; | 52 m_columnHeight = m_maxColumnHeight; |
52 } else { | 53 } else { |
53 m_columnHeight = LayoutUnit(); | 54 m_columnHeight = LayoutUnit(); |
54 } | 55 } |
55 } else { | 56 } else { |
56 setAndConstrainColumnHeight( | 57 setAndConstrainColumnHeight( |
57 heightAdjustedForRowOffset(flowThread->columnHeightAvailable())); | 58 heightAdjustedForRowOffset(flowThread->columnHeightAvailable())); |
58 } | 59 } |
59 } | 60 } |
60 | 61 |
61 bool MultiColumnFragmentainerGroup::recalculateColumnHeight( | 62 bool MultiColumnFragmentainerGroup::recalculateColumnHeight( |
62 LayoutMultiColumnSet& columnSet) { | 63 LayoutMultiColumnSet& columnSet) { |
63 LayoutUnit oldColumnHeight = m_columnHeight; | 64 LayoutUnit oldColumnHeight = m_columnHeight; |
64 | 65 |
65 m_maxColumnHeight = calculateMaxColumnHeight(); | 66 m_maxColumnHeight = calculateMaxColumnHeight(); |
66 | 67 |
67 // Only the last row may have auto height, and thus be balanced. There are no good reasons to | 68 // Only the last row may have auto height, and thus be balanced. There are no |
68 // balance the preceding rows, and that could potentially lead to an insane nu mber of layout | 69 // good reasons to balance the preceding rows, and that could potentially lead |
69 // passes as well. | 70 // to an insane number of layout passes as well. |
70 if (isLastGroup() && columnSet.heightIsAuto()) { | 71 if (isLastGroup() && columnSet.heightIsAuto()) { |
71 LayoutUnit newColumnHeight; | 72 LayoutUnit newColumnHeight; |
72 if (!columnSet.isInitialHeightCalculated()) { | 73 if (!columnSet.isInitialHeightCalculated()) { |
73 // Initial balancing: Start with the lowest imaginable column height. Also calculate the | 74 // Initial balancing: Start with the lowest imaginable column height. Also |
74 // height of the tallest piece of unbreakable content. Columns should neve r get any | 75 // calculate the height of the tallest piece of unbreakable content. |
75 // shorter than that (unless constrained by max-height). Propagate this to our | 76 // Columns should never get any shorter than that (unless constrained by |
76 // containing column set, in case there is an outer multicol container tha t also needs | 77 // max-height). Propagate this to our containing column set, in case there |
77 // to balance. After having calculated the initial column height, the mult icol container | 78 // is an outer multicol container that also needs to balance. After having |
78 // needs another layout pass with the column height that we just calculate d. | 79 // calculated the initial column height, the multicol container needs |
80 // another layout pass with the column height that we just calculated. | |
79 InitialColumnHeightFinder initialHeightFinder( | 81 InitialColumnHeightFinder initialHeightFinder( |
80 columnSet, logicalTopInFlowThread(), logicalBottomInFlowThread()); | 82 columnSet, logicalTopInFlowThread(), logicalBottomInFlowThread()); |
81 LayoutUnit tallestUnbreakableLogicalHeight = | 83 LayoutUnit tallestUnbreakableLogicalHeight = |
82 initialHeightFinder.tallestUnbreakableLogicalHeight(); | 84 initialHeightFinder.tallestUnbreakableLogicalHeight(); |
83 columnSet.propagateTallestUnbreakableLogicalHeight( | 85 columnSet.propagateTallestUnbreakableLogicalHeight( |
84 tallestUnbreakableLogicalHeight); | 86 tallestUnbreakableLogicalHeight); |
85 newColumnHeight = | 87 newColumnHeight = |
86 std::max(initialHeightFinder.initialMinimalBalancedHeight(), | 88 std::max(initialHeightFinder.initialMinimalBalancedHeight(), |
87 tallestUnbreakableLogicalHeight); | 89 tallestUnbreakableLogicalHeight); |
88 } else { | 90 } else { |
89 // Rebalancing: After having laid out again, we'll need to rebalance if th e height | 91 // Rebalancing: After having laid out again, we'll need to rebalance if |
90 // wasn't enough and we're allowed to stretch it, and then re-lay out. The re are further | 92 // the height wasn't enough and we're allowed to stretch it, and then |
91 // details on the column balancing machinery in ColumnBalancer and its der ivates. | 93 // re-lay out. There are further details on the column balancing |
94 // machinery in ColumnBalancer and its derivates. | |
92 newColumnHeight = rebalanceColumnHeightIfNeeded(); | 95 newColumnHeight = rebalanceColumnHeightIfNeeded(); |
93 } | 96 } |
94 setAndConstrainColumnHeight(newColumnHeight); | 97 setAndConstrainColumnHeight(newColumnHeight); |
95 } else { | 98 } else { |
96 // The position of the column set may have changed, in which case height ava ilable for | 99 // The position of the column set may have changed, in which case height |
97 // columns may have changed as well. | 100 // available for columns may have changed as well. |
98 setAndConstrainColumnHeight(m_columnHeight); | 101 setAndConstrainColumnHeight(m_columnHeight); |
99 } | 102 } |
100 | 103 |
101 if (m_columnHeight == oldColumnHeight) | 104 if (m_columnHeight == oldColumnHeight) |
102 return false; // No change. We're done. | 105 return false; // No change. We're done. |
103 | 106 |
104 return true; // Need another pass. | 107 return true; // Need another pass. |
105 } | 108 } |
106 | 109 |
107 LayoutSize MultiColumnFragmentainerGroup::flowThreadTranslationAtOffset( | 110 LayoutSize MultiColumnFragmentainerGroup::flowThreadTranslationAtOffset( |
108 LayoutUnit offsetInFlowThread, | 111 LayoutUnit offsetInFlowThread, |
109 LayoutBox::PageBoundaryRule rule, | 112 LayoutBox::PageBoundaryRule rule, |
110 CoordinateSpaceConversion mode) const { | 113 CoordinateSpaceConversion mode) const { |
111 LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(); | 114 LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(); |
112 | 115 |
113 // A column out of range doesn't have a flow thread portion, so we need to cla mp to make sure | 116 // A column out of range doesn't have a flow thread portion, so we need to |
114 // that we stay within the actual columns. This means that content in the over flow area will be | 117 // clamp to make sure that we stay within the actual columns. This means that |
115 // mapped to the last actual column, instead of being mapped to an imaginary c olumn further | 118 // content in the overflow area will be mapped to the last actual column, |
116 // ahead. | 119 // instead of being mapped to an imaginary column further ahead. |
117 unsigned columnIndex = offsetInFlowThread >= logicalBottomInFlowThread() | 120 unsigned columnIndex = offsetInFlowThread >= logicalBottomInFlowThread() |
118 ? actualColumnCount() - 1 | 121 ? actualColumnCount() - 1 |
119 : columnIndexAtOffset(offsetInFlowThread, rule); | 122 : columnIndexAtOffset(offsetInFlowThread, rule); |
120 | 123 |
121 LayoutRect portionRect(flowThreadPortionRectAt(columnIndex)); | 124 LayoutRect portionRect(flowThreadPortionRectAt(columnIndex)); |
122 flowThread->flipForWritingMode(portionRect); | 125 flowThread->flipForWritingMode(portionRect); |
123 portionRect.moveBy(flowThread->topLeftLocation()); | 126 portionRect.moveBy(flowThread->topLeftLocation()); |
124 | 127 |
125 LayoutRect columnRect(columnRectAt(columnIndex)); | 128 LayoutRect columnRect(columnRectAt(columnIndex)); |
126 columnRect.move(offsetFromColumnSet()); | 129 columnRect.move(offsetFromColumnSet()); |
127 m_columnSet.flipForWritingMode(columnRect); | 130 m_columnSet.flipForWritingMode(columnRect); |
128 columnRect.moveBy(m_columnSet.topLeftLocation()); | 131 columnRect.moveBy(m_columnSet.topLeftLocation()); |
129 | 132 |
130 LayoutSize translationRelativeToFlowThread = | 133 LayoutSize translationRelativeToFlowThread = |
131 columnRect.location() - portionRect.location(); | 134 columnRect.location() - portionRect.location(); |
132 if (mode == CoordinateSpaceConversion::Containing) | 135 if (mode == CoordinateSpaceConversion::Containing) |
133 return translationRelativeToFlowThread; | 136 return translationRelativeToFlowThread; |
134 | 137 |
135 LayoutSize enclosingTranslation; | 138 LayoutSize enclosingTranslation; |
136 if (LayoutMultiColumnFlowThread* enclosingFlowThread = | 139 if (LayoutMultiColumnFlowThread* enclosingFlowThread = |
137 flowThread->enclosingFlowThread()) { | 140 flowThread->enclosingFlowThread()) { |
138 const MultiColumnFragmentainerGroup& firstRow = | 141 const MultiColumnFragmentainerGroup& firstRow = |
139 flowThread->firstMultiColumnSet()->firstFragmentainerGroup(); | 142 flowThread->firstMultiColumnSet()->firstFragmentainerGroup(); |
140 // Translation that would map points in the coordinate space of the outermos t flow thread to | 143 // Translation that would map points in the coordinate space of the |
141 // visual points in the first column in the first fragmentainer group (row) in our multicol | 144 // outermost flow thread to visual points in the first column in the first |
142 // container. | 145 // fragmentainer group (row) in our multicol container. |
143 LayoutSize enclosingTranslationOrigin = | 146 LayoutSize enclosingTranslationOrigin = |
144 enclosingFlowThread->flowThreadTranslationAtOffset( | 147 enclosingFlowThread->flowThreadTranslationAtOffset( |
145 firstRow.blockOffsetInEnclosingFragmentationContext(), | 148 firstRow.blockOffsetInEnclosingFragmentationContext(), |
146 LayoutBox::AssociateWithLatterPage, mode); | 149 LayoutBox::AssociateWithLatterPage, mode); |
147 | 150 |
148 // Translation that would map points in the coordinate space of the outermos t flow thread to | 151 // Translation that would map points in the coordinate space of the |
149 // visual points in the first column in this fragmentainer group. | 152 // outermost flow thread to visual points in the first column in this |
153 // fragmentainer group. | |
150 enclosingTranslation = enclosingFlowThread->flowThreadTranslationAtOffset( | 154 enclosingTranslation = enclosingFlowThread->flowThreadTranslationAtOffset( |
151 blockOffsetInEnclosingFragmentationContext(), | 155 blockOffsetInEnclosingFragmentationContext(), |
152 LayoutBox::AssociateWithLatterPage, mode); | 156 LayoutBox::AssociateWithLatterPage, mode); |
153 | 157 |
154 // What we ultimately return from this method is a translation that maps poi nts in the | 158 // What we ultimately return from this method is a translation that maps |
155 // coordinate space of our flow thread to a visual point in a certain column in this | 159 // points in the coordinate space of our flow thread to a visual point in a |
156 // fragmentainer group. We had to go all the way up to the outermost flow th read, since this | 160 // certain column in this fragmentainer group. We had to go all the way up |
157 // fragmentainer group may be in a different outer column than the first out er column that | 161 // to the outermost flow thread, since this fragmentainer group may be in a |
158 // this multicol container lives in. It's the visual distance between the fi rst | 162 // different outer column than the first outer column that this multicol |
159 // fragmentainer group and this fragmentainer group that we need to add to t he translation. | 163 // container lives in. It's the visual distance between the first |
164 // fragmentainer group and this fragmentainer group that we need to add to | |
165 // the translation. | |
160 enclosingTranslation -= enclosingTranslationOrigin; | 166 enclosingTranslation -= enclosingTranslationOrigin; |
161 } | 167 } |
162 | 168 |
163 return enclosingTranslation + translationRelativeToFlowThread; | 169 return enclosingTranslation + translationRelativeToFlowThread; |
164 } | 170 } |
165 | 171 |
166 LayoutUnit MultiColumnFragmentainerGroup::columnLogicalTopForOffset( | 172 LayoutUnit MultiColumnFragmentainerGroup::columnLogicalTopForOffset( |
167 LayoutUnit offsetInFlowThread) const { | 173 LayoutUnit offsetInFlowThread) const { |
168 unsigned columnIndex = columnIndexAtOffset( | 174 unsigned columnIndex = columnIndexAtOffset( |
169 offsetInFlowThread, LayoutBox::AssociateWithLatterPage); | 175 offsetInFlowThread, LayoutBox::AssociateWithLatterPage); |
170 return logicalTopInFlowThreadAt(columnIndex); | 176 return logicalTopInFlowThreadAt(columnIndex); |
171 } | 177 } |
172 | 178 |
173 LayoutPoint MultiColumnFragmentainerGroup::visualPointToFlowThreadPoint( | 179 LayoutPoint MultiColumnFragmentainerGroup::visualPointToFlowThreadPoint( |
174 const LayoutPoint& visualPoint) const { | 180 const LayoutPoint& visualPoint) const { |
175 unsigned columnIndex = columnIndexAtVisualPoint(visualPoint); | 181 unsigned columnIndex = columnIndexAtVisualPoint(visualPoint); |
176 LayoutRect columnRect = columnRectAt(columnIndex); | 182 LayoutRect columnRect = columnRectAt(columnIndex); |
177 LayoutPoint localPoint(visualPoint); | 183 LayoutPoint localPoint(visualPoint); |
178 localPoint.moveBy(-columnRect.location()); | 184 localPoint.moveBy(-columnRect.location()); |
179 // Before converting to a flow thread position, if the block direction coordin ate is outside the | 185 // Before converting to a flow thread position, if the block direction |
180 // column, snap to the bounds of the column, and reset the inline direction co ordinate to the | 186 // coordinate is outside the column, snap to the bounds of the column, and |
181 // start position in the column. The effect of this is that if the block posit ion is before the | 187 // reset the inline direction coordinate to the start position in the column. |
182 // column rectangle, we'll get to the beginning of this column, while if the b lock position is | 188 // The effect of this is that if the block position is before the column |
183 // after the column rectangle, we'll get to the beginning of the next column. | 189 // rectangle, we'll get to the beginning of this column, while if the block |
190 // position is after the column rectangle, we'll get to the beginning of the | |
191 // next column. | |
184 if (!m_columnSet.isHorizontalWritingMode()) { | 192 if (!m_columnSet.isHorizontalWritingMode()) { |
185 LayoutUnit columnStart = m_columnSet.style()->isLeftToRightDirection() | 193 LayoutUnit columnStart = m_columnSet.style()->isLeftToRightDirection() |
186 ? LayoutUnit() | 194 ? LayoutUnit() |
187 : columnRect.height(); | 195 : columnRect.height(); |
188 if (localPoint.x() < 0) | 196 if (localPoint.x() < 0) |
189 localPoint = LayoutPoint(LayoutUnit(), columnStart); | 197 localPoint = LayoutPoint(LayoutUnit(), columnStart); |
190 else if (localPoint.x() > logicalHeight()) | 198 else if (localPoint.x() > logicalHeight()) |
191 localPoint = LayoutPoint(logicalHeight(), columnStart); | 199 localPoint = LayoutPoint(logicalHeight(), columnStart); |
192 return LayoutPoint(localPoint.x() + logicalTopInFlowThreadAt(columnIndex), | 200 return LayoutPoint(localPoint.x() + logicalTopInFlowThreadAt(columnIndex), |
193 localPoint.y()); | 201 localPoint.y()); |
(...skipping 16 matching lines...) Expand all Loading... | |
210 LayoutFlowThread* flowThread = m_columnSet.flowThread(); | 218 LayoutFlowThread* flowThread = m_columnSet.flowThread(); |
211 flowThread->flipForWritingMode(flippedBoundingBoxInFlowThread); | 219 flowThread->flipForWritingMode(flippedBoundingBoxInFlowThread); |
212 bool isHorizontalWritingMode = m_columnSet.isHorizontalWritingMode(); | 220 bool isHorizontalWritingMode = m_columnSet.isHorizontalWritingMode(); |
213 LayoutUnit boundingBoxLogicalTop = isHorizontalWritingMode | 221 LayoutUnit boundingBoxLogicalTop = isHorizontalWritingMode |
214 ? flippedBoundingBoxInFlowThread.y() | 222 ? flippedBoundingBoxInFlowThread.y() |
215 : flippedBoundingBoxInFlowThread.x(); | 223 : flippedBoundingBoxInFlowThread.x(); |
216 LayoutUnit boundingBoxLogicalBottom = | 224 LayoutUnit boundingBoxLogicalBottom = |
217 isHorizontalWritingMode ? flippedBoundingBoxInFlowThread.maxY() | 225 isHorizontalWritingMode ? flippedBoundingBoxInFlowThread.maxY() |
218 : flippedBoundingBoxInFlowThread.maxX(); | 226 : flippedBoundingBoxInFlowThread.maxX(); |
219 if (boundingBoxLogicalBottom <= logicalTopInFlowThread() || | 227 if (boundingBoxLogicalBottom <= logicalTopInFlowThread() || |
220 boundingBoxLogicalTop >= logicalBottomInFlowThread()) | 228 boundingBoxLogicalTop >= logicalBottomInFlowThread()) { |
221 return LayoutRect(); // The bounding box doesn't intersect this fragmentain er group. | 229 // The bounding box doesn't intersect this fragmentainer group. |
230 return LayoutRect(); | |
231 } | |
222 unsigned startColumn; | 232 unsigned startColumn; |
223 unsigned endColumn; | 233 unsigned endColumn; |
224 columnIntervalForBlockRangeInFlowThread( | 234 columnIntervalForBlockRangeInFlowThread( |
225 boundingBoxLogicalTop, boundingBoxLogicalBottom, startColumn, endColumn); | 235 boundingBoxLogicalTop, boundingBoxLogicalBottom, startColumn, endColumn); |
226 | 236 |
227 LayoutRect startColumnFlowThreadOverflowPortion = | 237 LayoutRect startColumnFlowThreadOverflowPortion = |
228 flowThreadPortionOverflowRectAt(startColumn); | 238 flowThreadPortionOverflowRectAt(startColumn); |
229 flowThread->flipForWritingMode(startColumnFlowThreadOverflowPortion); | 239 flowThread->flipForWritingMode(startColumnFlowThreadOverflowPortion); |
230 LayoutRect startColumnRect(boundingBoxInFlowThread); | 240 LayoutRect startColumnRect(boundingBoxInFlowThread); |
231 startColumnRect.intersect(startColumnFlowThreadOverflowPortion); | 241 startColumnRect.intersect(startColumnFlowThreadOverflowPortion); |
232 startColumnRect.move(flowThreadTranslationAtOffset( | 242 startColumnRect.move(flowThreadTranslationAtOffset( |
233 logicalTopInFlowThreadAt(startColumn), LayoutBox::AssociateWithLatterPage, | 243 logicalTopInFlowThreadAt(startColumn), LayoutBox::AssociateWithLatterPage, |
234 CoordinateSpaceConversion::Containing)); | 244 CoordinateSpaceConversion::Containing)); |
235 if (startColumn == endColumn) | 245 if (startColumn == endColumn) |
236 return startColumnRect; // It all takes place in one column. We're done. | 246 return startColumnRect; // It all takes place in one column. We're done. |
237 | 247 |
238 LayoutRect endColumnFlowThreadOverflowPortion = | 248 LayoutRect endColumnFlowThreadOverflowPortion = |
239 flowThreadPortionOverflowRectAt(endColumn); | 249 flowThreadPortionOverflowRectAt(endColumn); |
240 flowThread->flipForWritingMode(endColumnFlowThreadOverflowPortion); | 250 flowThread->flipForWritingMode(endColumnFlowThreadOverflowPortion); |
241 LayoutRect endColumnRect(boundingBoxInFlowThread); | 251 LayoutRect endColumnRect(boundingBoxInFlowThread); |
242 endColumnRect.intersect(endColumnFlowThreadOverflowPortion); | 252 endColumnRect.intersect(endColumnFlowThreadOverflowPortion); |
243 endColumnRect.move(flowThreadTranslationAtOffset( | 253 endColumnRect.move(flowThreadTranslationAtOffset( |
244 logicalTopInFlowThreadAt(endColumn), LayoutBox::AssociateWithLatterPage, | 254 logicalTopInFlowThreadAt(endColumn), LayoutBox::AssociateWithLatterPage, |
245 CoordinateSpaceConversion::Containing)); | 255 CoordinateSpaceConversion::Containing)); |
246 return unionRect(startColumnRect, endColumnRect); | 256 return unionRect(startColumnRect, endColumnRect); |
247 } | 257 } |
248 | 258 |
249 LayoutRect MultiColumnFragmentainerGroup::calculateOverflow() const { | 259 LayoutRect MultiColumnFragmentainerGroup::calculateOverflow() const { |
250 // Note that we just return the bounding rectangle of the column boxes here. W e currently don't | 260 // Note that we just return the bounding rectangle of the column boxes here. |
251 // examine overflow caused by the actual content that ends up in each column. | 261 // We currently don't examine overflow caused by the actual content that ends |
262 // up in each column. | |
252 LayoutRect overflowRect; | 263 LayoutRect overflowRect; |
253 if (unsigned columnCount = actualColumnCount()) { | 264 if (unsigned columnCount = actualColumnCount()) { |
254 overflowRect = columnRectAt(0); | 265 overflowRect = columnRectAt(0); |
255 if (columnCount > 1) | 266 if (columnCount > 1) |
256 overflowRect.uniteEvenIfEmpty(columnRectAt(columnCount - 1)); | 267 overflowRect.uniteEvenIfEmpty(columnRectAt(columnCount - 1)); |
257 } | 268 } |
258 return overflowRect; | 269 return overflowRect; |
259 } | 270 } |
260 | 271 |
261 unsigned MultiColumnFragmentainerGroup::actualColumnCount() const { | 272 unsigned MultiColumnFragmentainerGroup::actualColumnCount() const { |
262 // We must always return a value of 1 or greater. Column count = 0 is a meanin gless situation, | 273 // We must always return a value of 1 or greater. Column count = 0 is a |
263 // and will confuse and cause problems in other parts of the code. | 274 // meaningless situation, and will confuse and cause problems in other parts |
275 // of the code. | |
264 if (!m_columnHeight) | 276 if (!m_columnHeight) |
265 return 1; | 277 return 1; |
266 | 278 |
267 // Our flow thread portion determines our column count. We have as many column s as needed to fit | 279 // Our flow thread portion determines our column count. We have as many |
268 // all the content. | 280 // columns as needed to fit all the content. |
269 LayoutUnit flowThreadPortionHeight = logicalHeightInFlowThread(); | 281 LayoutUnit flowThreadPortionHeight = logicalHeightInFlowThread(); |
270 if (!flowThreadPortionHeight) | 282 if (!flowThreadPortionHeight) |
271 return 1; | 283 return 1; |
272 | 284 |
273 unsigned count = (flowThreadPortionHeight / m_columnHeight).floor(); | 285 unsigned count = (flowThreadPortionHeight / m_columnHeight).floor(); |
274 // flowThreadPortionHeight may be saturated, so detect the remainder manually. | 286 // flowThreadPortionHeight may be saturated, so detect the remainder manually. |
275 if (count * m_columnHeight < flowThreadPortionHeight) | 287 if (count * m_columnHeight < flowThreadPortionHeight) |
276 count++; | 288 count++; |
277 ASSERT(count >= 1); | 289 ASSERT(count >= 1); |
278 return count; | 290 return count; |
279 } | 291 } |
280 | 292 |
281 LayoutUnit MultiColumnFragmentainerGroup::heightAdjustedForRowOffset( | 293 LayoutUnit MultiColumnFragmentainerGroup::heightAdjustedForRowOffset( |
282 LayoutUnit height) const { | 294 LayoutUnit height) const { |
283 // Let's avoid zero height, as that would cause an infinite amount of columns to be created. | 295 // Let's avoid zero height, as that would cause an infinite amount of columns |
296 // to be created. | |
284 return std::max( | 297 return std::max( |
285 height - logicalTop() - m_columnSet.logicalTopFromMulticolContentEdge(), | 298 height - logicalTop() - m_columnSet.logicalTopFromMulticolContentEdge(), |
286 LayoutUnit(1)); | 299 LayoutUnit(1)); |
287 } | 300 } |
288 | 301 |
289 LayoutUnit MultiColumnFragmentainerGroup::calculateMaxColumnHeight() const { | 302 LayoutUnit MultiColumnFragmentainerGroup::calculateMaxColumnHeight() const { |
290 LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(); | 303 LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(); |
291 LayoutUnit maxColumnHeight = flowThread->maxColumnLogicalHeight(); | 304 LayoutUnit maxColumnHeight = flowThread->maxColumnLogicalHeight(); |
292 LayoutUnit maxHeight = heightAdjustedForRowOffset(maxColumnHeight); | 305 LayoutUnit maxHeight = heightAdjustedForRowOffset(maxColumnHeight); |
293 if (FragmentationContext* enclosingFragmentationContext = | 306 if (FragmentationContext* enclosingFragmentationContext = |
294 flowThread->enclosingFragmentationContext()) { | 307 flowThread->enclosingFragmentationContext()) { |
295 if (enclosingFragmentationContext->isFragmentainerLogicalHeightKnown()) { | 308 if (enclosingFragmentationContext->isFragmentainerLogicalHeightKnown()) { |
296 // We're nested inside another fragmentation context whose fragmentainer h eights are | 309 // We're nested inside another fragmentation context whose fragmentainer |
297 // known. This constrains the max height. | 310 // heights are known. This constrains the max height. |
298 LayoutUnit remainingOuterLogicalHeight = | 311 LayoutUnit remainingOuterLogicalHeight = |
299 enclosingFragmentationContext->remainingLogicalHeightAt( | 312 enclosingFragmentationContext->remainingLogicalHeightAt( |
300 blockOffsetInEnclosingFragmentationContext()); | 313 blockOffsetInEnclosingFragmentationContext()); |
301 ASSERT(remainingOuterLogicalHeight > 0); | 314 ASSERT(remainingOuterLogicalHeight > 0); |
302 if (maxHeight > remainingOuterLogicalHeight) | 315 if (maxHeight > remainingOuterLogicalHeight) |
303 maxHeight = remainingOuterLogicalHeight; | 316 maxHeight = remainingOuterLogicalHeight; |
304 } | 317 } |
305 } | 318 } |
306 return maxHeight; | 319 return maxHeight; |
307 } | 320 } |
308 | 321 |
309 void MultiColumnFragmentainerGroup::setAndConstrainColumnHeight( | 322 void MultiColumnFragmentainerGroup::setAndConstrainColumnHeight( |
310 LayoutUnit newHeight) { | 323 LayoutUnit newHeight) { |
311 m_columnHeight = newHeight; | 324 m_columnHeight = newHeight; |
312 if (m_columnHeight > m_maxColumnHeight) | 325 if (m_columnHeight > m_maxColumnHeight) |
313 m_columnHeight = m_maxColumnHeight; | 326 m_columnHeight = m_maxColumnHeight; |
314 } | 327 } |
315 | 328 |
316 LayoutUnit MultiColumnFragmentainerGroup::rebalanceColumnHeightIfNeeded() | 329 LayoutUnit MultiColumnFragmentainerGroup::rebalanceColumnHeightIfNeeded() |
317 const { | 330 const { |
318 if (actualColumnCount() <= m_columnSet.usedColumnCount()) { | 331 if (actualColumnCount() <= m_columnSet.usedColumnCount()) { |
319 // With the current column height, the content fits without creating overflo wing columns. We're done. | 332 // With the current column height, the content fits without creating |
333 // overflowing columns. We're done. | |
320 return m_columnHeight; | 334 return m_columnHeight; |
321 } | 335 } |
322 | 336 |
323 if (m_columnHeight >= m_maxColumnHeight) { | 337 if (m_columnHeight >= m_maxColumnHeight) { |
324 // We cannot stretch any further. We'll just have to live with the overflowi ng columns. This | 338 // We cannot stretch any further. We'll just have to live with the |
325 // typically happens if the max column height is less than the height of the tallest piece | 339 // overflowing columns. This typically happens if the max column height is |
326 // of unbreakable content (e.g. lines). | 340 // less than the height of the tallest piece of unbreakable content (e.g. |
341 // lines). | |
327 return m_columnHeight; | 342 return m_columnHeight; |
328 } | 343 } |
329 | 344 |
330 MinimumSpaceShortageFinder shortageFinder( | 345 MinimumSpaceShortageFinder shortageFinder( |
331 columnSet(), logicalTopInFlowThread(), logicalBottomInFlowThread()); | 346 columnSet(), logicalTopInFlowThread(), logicalBottomInFlowThread()); |
332 | 347 |
333 if (shortageFinder.forcedBreaksCount() + 1 >= m_columnSet.usedColumnCount()) { | 348 if (shortageFinder.forcedBreaksCount() + 1 >= m_columnSet.usedColumnCount()) { |
334 // Too many forced breaks to allow any implicit breaks. Initial balancing sh ould already | 349 // Too many forced breaks to allow any implicit breaks. Initial balancing |
335 // have set a good height. There's nothing more we should do. | 350 // should already have set a good height. There's nothing more we should do. |
336 return m_columnHeight; | 351 return m_columnHeight; |
337 } | 352 } |
338 | 353 |
339 // If the initial guessed column height wasn't enough, stretch it now. Stretch by the lowest | 354 // If the initial guessed column height wasn't enough, stretch it now. Stretch |
340 // amount of space. | 355 // by the lowest amount of space. |
341 LayoutUnit minSpaceShortage = shortageFinder.minimumSpaceShortage(); | 356 LayoutUnit minSpaceShortage = shortageFinder.minimumSpaceShortage(); |
342 | 357 |
343 ASSERT(minSpaceShortage > 0); // We should never _shrink_ the height! | 358 ASSERT(minSpaceShortage > 0); // We should never _shrink_ the height! |
344 ASSERT(minSpaceShortage != | 359 ASSERT(minSpaceShortage != |
345 LayoutUnit::max()); // If this happens, we probably have a bug. | 360 LayoutUnit::max()); // If this happens, we probably have a bug. |
346 if (minSpaceShortage == LayoutUnit::max()) | 361 if (minSpaceShortage == LayoutUnit::max()) |
347 return m_columnHeight; // So bail out rather than looping infinitely. | 362 return m_columnHeight; // So bail out rather than looping infinitely. |
348 | 363 |
349 return m_columnHeight + minSpaceShortage; | 364 return m_columnHeight + minSpaceShortage; |
350 } | 365 } |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
397 LayoutUnit portionLogicalHeight = logicalBottom - logicalTop; | 412 LayoutUnit portionLogicalHeight = logicalBottom - logicalTop; |
398 if (m_columnSet.isHorizontalWritingMode()) | 413 if (m_columnSet.isHorizontalWritingMode()) |
399 return LayoutRect(LayoutUnit(), logicalTop, m_columnSet.pageLogicalWidth(), | 414 return LayoutRect(LayoutUnit(), logicalTop, m_columnSet.pageLogicalWidth(), |
400 portionLogicalHeight); | 415 portionLogicalHeight); |
401 return LayoutRect(logicalTop, LayoutUnit(), portionLogicalHeight, | 416 return LayoutRect(logicalTop, LayoutUnit(), portionLogicalHeight, |
402 m_columnSet.pageLogicalWidth()); | 417 m_columnSet.pageLogicalWidth()); |
403 } | 418 } |
404 | 419 |
405 LayoutRect MultiColumnFragmentainerGroup::flowThreadPortionOverflowRectAt( | 420 LayoutRect MultiColumnFragmentainerGroup::flowThreadPortionOverflowRectAt( |
406 unsigned columnIndex) const { | 421 unsigned columnIndex) const { |
407 // This function determines the portion of the flow thread that paints for the column. Along the inline axis, columns are | 422 // This function determines the portion of the flow thread that paints for the |
408 // unclipped at outside edges (i.e., the first and last column in the set), an d they clip to half the column | 423 // column. Along the inline axis, columns are unclipped at outside edges |
409 // gap along interior edges. | 424 // (i.e., the first and last column in the set), and they clip to half the |
425 // column gap along interior edges. | |
410 // | 426 // |
411 // In the block direction, we will not clip overflow out of the top of the fir st column, or out of the bottom of | 427 // In the block direction, we will not clip overflow out of the top of the |
412 // the last column. This applies only to the true first column and last column across all column sets. | 428 // first column, or out of the bottom of the last column. This applies only to |
429 // the true first column and last column across all column sets. | |
413 // | 430 // |
414 // FIXME: Eventually we will know overflow on a per-column basis, but we can't do this until we have a painting | 431 // FIXME: Eventually we will know overflow on a per-column basis, but we can't |
415 // mode that understands not to paint contents from a previous column in the o verflow area of a following column. | 432 // do this until we have a painting mode that understands not to paint |
433 // contents from a previous column in the overflow area of a following column. | |
416 bool isFirstColumnInRow = !columnIndex; | 434 bool isFirstColumnInRow = !columnIndex; |
417 bool isLastColumnInRow = columnIndex == actualColumnCount() - 1; | 435 bool isLastColumnInRow = columnIndex == actualColumnCount() - 1; |
418 bool isLTR = m_columnSet.style()->isLeftToRightDirection(); | 436 bool isLTR = m_columnSet.style()->isLeftToRightDirection(); |
419 bool isLeftmostColumn = isLTR ? isFirstColumnInRow : isLastColumnInRow; | 437 bool isLeftmostColumn = isLTR ? isFirstColumnInRow : isLastColumnInRow; |
420 bool isRightmostColumn = isLTR ? isLastColumnInRow : isFirstColumnInRow; | 438 bool isRightmostColumn = isLTR ? isLastColumnInRow : isFirstColumnInRow; |
421 | 439 |
422 LayoutRect portionRect = flowThreadPortionRectAt(columnIndex); | 440 LayoutRect portionRect = flowThreadPortionRectAt(columnIndex); |
423 bool isFirstColumnInMulticolContainer = | 441 bool isFirstColumnInMulticolContainer = |
424 isFirstColumnInRow && this == &m_columnSet.firstFragmentainerGroup() && | 442 isFirstColumnInRow && this == &m_columnSet.firstFragmentainerGroup() && |
425 !m_columnSet.previousSiblingMultiColumnSet(); | 443 !m_columnSet.previousSiblingMultiColumnSet(); |
426 bool isLastColumnInMulticolContainer = | 444 bool isLastColumnInMulticolContainer = |
427 isLastColumnInRow && this == &m_columnSet.lastFragmentainerGroup() && | 445 isLastColumnInRow && this == &m_columnSet.lastFragmentainerGroup() && |
428 !m_columnSet.nextSiblingMultiColumnSet(); | 446 !m_columnSet.nextSiblingMultiColumnSet(); |
429 // Calculate the overflow rectangle, based on the flow thread's, clipped at co lumn logical | 447 // Calculate the overflow rectangle, based on the flow thread's, clipped at |
430 // top/bottom unless it's the first/last column. | 448 // column logical top/bottom unless it's the first/last column. |
431 LayoutRect overflowRect = m_columnSet.overflowRectForFlowThreadPortion( | 449 LayoutRect overflowRect = m_columnSet.overflowRectForFlowThreadPortion( |
432 portionRect, isFirstColumnInMulticolContainer, | 450 portionRect, isFirstColumnInMulticolContainer, |
433 isLastColumnInMulticolContainer); | 451 isLastColumnInMulticolContainer); |
434 | 452 |
435 // Avoid overflowing into neighboring columns, by clipping in the middle of ad jacent column | 453 // Avoid overflowing into neighboring columns, by clipping in the middle of |
436 // gaps. Also make sure that we avoid rounding errors. | 454 // adjacent column gaps. Also make sure that we avoid rounding errors. |
437 LayoutUnit columnGap = m_columnSet.columnGap(); | 455 LayoutUnit columnGap = m_columnSet.columnGap(); |
438 if (m_columnSet.isHorizontalWritingMode()) { | 456 if (m_columnSet.isHorizontalWritingMode()) { |
439 if (!isLeftmostColumn) | 457 if (!isLeftmostColumn) |
440 overflowRect.shiftXEdgeTo(portionRect.x() - columnGap / 2); | 458 overflowRect.shiftXEdgeTo(portionRect.x() - columnGap / 2); |
441 if (!isRightmostColumn) | 459 if (!isRightmostColumn) |
442 overflowRect.shiftMaxXEdgeTo(portionRect.maxX() + columnGap - | 460 overflowRect.shiftMaxXEdgeTo(portionRect.maxX() + columnGap - |
443 columnGap / 2); | 461 columnGap / 2); |
444 } else { | 462 } else { |
445 if (!isLeftmostColumn) | 463 if (!isLeftmostColumn) |
446 overflowRect.shiftYEdgeTo(portionRect.y() - columnGap / 2); | 464 overflowRect.shiftYEdgeTo(portionRect.y() - columnGap / 2); |
(...skipping 12 matching lines...) Expand all Loading... | |
459 return 0; | 477 return 0; |
460 | 478 |
461 if (!m_columnHeight) | 479 if (!m_columnHeight) |
462 return 0; | 480 return 0; |
463 unsigned columnIndex = | 481 unsigned columnIndex = |
464 ((offsetInFlowThread - m_logicalTopInFlowThread) / m_columnHeight) | 482 ((offsetInFlowThread - m_logicalTopInFlowThread) / m_columnHeight) |
465 .floor(); | 483 .floor(); |
466 if (pageBoundaryRule == LayoutBox::AssociateWithFormerPage && | 484 if (pageBoundaryRule == LayoutBox::AssociateWithFormerPage && |
467 columnIndex > 0 && | 485 columnIndex > 0 && |
468 logicalTopInFlowThreadAt(columnIndex) == offsetInFlowThread) { | 486 logicalTopInFlowThreadAt(columnIndex) == offsetInFlowThread) { |
469 // We are exactly at a column boundary, and we've been told to associate off sets at column | 487 // We are exactly at a column boundary, and we've been told to associate |
470 // boundaries with the former column, not the latter. | 488 // offsets at column boundaries with the former column, not the latter. |
471 columnIndex--; | 489 columnIndex--; |
472 } | 490 } |
473 return columnIndex; | 491 return columnIndex; |
474 } | 492 } |
475 | 493 |
476 unsigned MultiColumnFragmentainerGroup::columnIndexAtVisualPoint( | 494 unsigned MultiColumnFragmentainerGroup::columnIndexAtVisualPoint( |
477 const LayoutPoint& visualPoint) const { | 495 const LayoutPoint& visualPoint) const { |
478 bool isColumnProgressionInline = | 496 bool isColumnProgressionInline = |
479 m_columnSet.multiColumnFlowThread()->progressionIsInline(); | 497 m_columnSet.multiColumnFlowThread()->progressionIsInline(); |
480 bool isHorizontalWritingMode = m_columnSet.isHorizontalWritingMode(); | 498 bool isHorizontalWritingMode = m_columnSet.isHorizontalWritingMode(); |
(...skipping 25 matching lines...) Expand all Loading... | |
506 unsigned& firstColumn, | 524 unsigned& firstColumn, |
507 unsigned& lastColumn) const { | 525 unsigned& lastColumn) const { |
508 logicalTopInFlowThread = | 526 logicalTopInFlowThread = |
509 std::max(logicalTopInFlowThread, this->logicalTopInFlowThread()); | 527 std::max(logicalTopInFlowThread, this->logicalTopInFlowThread()); |
510 logicalBottomInFlowThread = | 528 logicalBottomInFlowThread = |
511 std::min(logicalBottomInFlowThread, this->logicalBottomInFlowThread()); | 529 std::min(logicalBottomInFlowThread, this->logicalBottomInFlowThread()); |
512 ASSERT(logicalTopInFlowThread <= logicalBottomInFlowThread); | 530 ASSERT(logicalTopInFlowThread <= logicalBottomInFlowThread); |
513 firstColumn = columnIndexAtOffset(logicalTopInFlowThread, | 531 firstColumn = columnIndexAtOffset(logicalTopInFlowThread, |
514 LayoutBox::AssociateWithLatterPage); | 532 LayoutBox::AssociateWithLatterPage); |
515 if (logicalBottomInFlowThread == logicalTopInFlowThread) { | 533 if (logicalBottomInFlowThread == logicalTopInFlowThread) { |
516 // Zero-height block range. There'll be one column in the interval. Set it r ight away. This | 534 // Zero-height block range. There'll be one column in the interval. Set it |
517 // is important if we're at a column boundary, since calling columnIndexAtOf fset() with the | 535 // right away. This is important if we're at a column boundary, since |
518 // end-exclusive bottom offset would actually give us the *previous* column. | 536 // calling columnIndexAtOffset() with the end-exclusive bottom offset would |
537 // actually give us the *previous* column. | |
519 lastColumn = firstColumn; | 538 lastColumn = firstColumn; |
520 } else { | 539 } else { |
521 lastColumn = columnIndexAtOffset(logicalBottomInFlowThread, | 540 lastColumn = columnIndexAtOffset(logicalBottomInFlowThread, |
522 LayoutBox::AssociateWithFormerPage); | 541 LayoutBox::AssociateWithFormerPage); |
523 } | 542 } |
524 } | 543 } |
525 | 544 |
526 void MultiColumnFragmentainerGroup::columnIntervalForVisualRect( | 545 void MultiColumnFragmentainerGroup::columnIntervalForVisualRect( |
527 const LayoutRect& rect, | 546 const LayoutRect& rect, |
528 unsigned& firstColumn, | 547 unsigned& firstColumn, |
(...skipping 22 matching lines...) Expand all Loading... | |
551 } | 570 } |
552 ASSERT(firstColumn <= lastColumn); | 571 ASSERT(firstColumn <= lastColumn); |
553 } | 572 } |
554 | 573 |
555 MultiColumnFragmentainerGroupList::MultiColumnFragmentainerGroupList( | 574 MultiColumnFragmentainerGroupList::MultiColumnFragmentainerGroupList( |
556 LayoutMultiColumnSet& columnSet) | 575 LayoutMultiColumnSet& columnSet) |
557 : m_columnSet(columnSet) { | 576 : m_columnSet(columnSet) { |
558 append(MultiColumnFragmentainerGroup(m_columnSet)); | 577 append(MultiColumnFragmentainerGroup(m_columnSet)); |
559 } | 578 } |
560 | 579 |
561 // An explicit empty destructor of MultiColumnFragmentainerGroupList should be i n | 580 // An explicit empty destructor of MultiColumnFragmentainerGroupList should be |
562 // MultiColumnFragmentainerGroup.cpp, because if an implicit destructor is used, | 581 // in MultiColumnFragmentainerGroup.cpp, because if an implicit destructor is |
563 // msvc 2015 tries to generate its destructor (because the class is dll-exported class) | 582 // used, msvc 2015 tries to generate its destructor (because the class is |
564 // and causes a compile error because of lack of MultiColumnFragmentainerGroup:: operator=. | 583 // dll-exported class) and causes a compile error because of lack of |
565 // Since MultiColumnFragmentainerGroup is non-copyable, we cannot define the ope rator=. | 584 // MultiColumnFragmentainerGroup::operator=. Since |
585 // MultiColumnFragmentainerGroup is non-copyable, we cannot define the | |
586 // operator=. | |
dcheng
2016/10/07 20:42:01
o.O
| |
566 MultiColumnFragmentainerGroupList::~MultiColumnFragmentainerGroupList() {} | 587 MultiColumnFragmentainerGroupList::~MultiColumnFragmentainerGroupList() {} |
567 | 588 |
568 MultiColumnFragmentainerGroup& | 589 MultiColumnFragmentainerGroup& |
569 MultiColumnFragmentainerGroupList::addExtraGroup() { | 590 MultiColumnFragmentainerGroupList::addExtraGroup() { |
570 append(MultiColumnFragmentainerGroup(m_columnSet)); | 591 append(MultiColumnFragmentainerGroup(m_columnSet)); |
571 return last(); | 592 return last(); |
572 } | 593 } |
573 | 594 |
574 void MultiColumnFragmentainerGroupList::deleteExtraGroups() { | 595 void MultiColumnFragmentainerGroupList::deleteExtraGroups() { |
575 shrink(1); | 596 shrink(1); |
576 } | 597 } |
577 | 598 |
578 } // namespace blink | 599 } // namespace blink |
OLD | NEW |