Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(197)

Side by Side Diff: Source/core/rendering/RenderMultiColumnSet.cpp

Issue 883293004: [New Multicolumn] Preparatory work for nested multicol support. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: no find copies, please Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2012 Apple Inc. All rights reserved. 2 * Copyright (C) 2012 Apple Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution. 11 * documentation and/or other materials provided with the distribution.
12 * 12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */ 24 */
25 25
26 #include "config.h" 26 #include "config.h"
27 #include "core/rendering/RenderMultiColumnSet.h" 27 #include "core/rendering/RenderMultiColumnSet.h"
28 28
29 #include "core/paint/MultiColumnSetPainter.h" 29 #include "core/paint/MultiColumnSetPainter.h"
30 #include "core/rendering/MultiColumnRow.h"
30 #include "core/rendering/RenderMultiColumnFlowThread.h" 31 #include "core/rendering/RenderMultiColumnFlowThread.h"
31 32
32 namespace blink { 33 namespace blink {
33 34
34 RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread) 35 RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread)
35 : RenderRegion(0, flowThread) 36 : RenderRegion(0, flowThread)
36 , m_columnHeight(0) 37 , m_columnRow(*this)
37 , m_maxColumnHeight(RenderFlowThread::maxLogicalHeight())
38 , m_minSpaceShortage(RenderFlowThread::maxLogicalHeight())
39 , m_minimumColumnHeight(0)
40 { 38 {
41 } 39 }
42 40
43 RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* fl owThread, RenderStyle* parentStyle) 41 RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* fl owThread, RenderStyle* parentStyle)
44 { 42 {
45 Document& document = flowThread->document(); 43 Document& document = flowThread->document();
46 RenderMultiColumnSet* renderer = new RenderMultiColumnSet(flowThread); 44 RenderMultiColumnSet* renderer = new RenderMultiColumnSet(flowThread);
47 renderer->setDocumentForAnonymous(&document); 45 renderer->setDocumentForAnonymous(&document);
48 renderer->setStyle(RenderStyle::createAnonymousStyleWithDisplay(parentStyle, BLOCK)); 46 renderer->setStyle(RenderStyle::createAnonymousStyleWithDisplay(parentStyle, BLOCK));
49 return renderer; 47 return renderer;
50 } 48 }
51 49
50 LayoutUnit RenderMultiColumnSet::pageLogicalHeight() const
51 {
52 // FIXME: pageLogicalHeight() needs to take a flow thread offset parameter, so that we can
53 // locate the right row.
54 return firstColumnRow()->logicalHeight();
55 }
56
52 RenderMultiColumnSet* RenderMultiColumnSet::nextSiblingMultiColumnSet() const 57 RenderMultiColumnSet* RenderMultiColumnSet::nextSiblingMultiColumnSet() const
53 { 58 {
54 for (RenderObject* sibling = nextSibling(); sibling; sibling = sibling->next Sibling()) { 59 for (RenderObject* sibling = nextSibling(); sibling; sibling = sibling->next Sibling()) {
55 if (sibling->isRenderMultiColumnSet()) 60 if (sibling->isRenderMultiColumnSet())
56 return toRenderMultiColumnSet(sibling); 61 return toRenderMultiColumnSet(sibling);
57 } 62 }
58 return 0; 63 return 0;
59 } 64 }
60 65
61 RenderMultiColumnSet* RenderMultiColumnSet::previousSiblingMultiColumnSet() cons t 66 RenderMultiColumnSet* RenderMultiColumnSet::previousSiblingMultiColumnSet() cons t
62 { 67 {
63 for (RenderObject* sibling = previousSibling(); sibling; sibling = sibling-> previousSibling()) { 68 for (RenderObject* sibling = previousSibling(); sibling; sibling = sibling-> previousSibling()) {
64 if (sibling->isRenderMultiColumnSet()) 69 if (sibling->isRenderMultiColumnSet())
65 return toRenderMultiColumnSet(sibling); 70 return toRenderMultiColumnSet(sibling);
66 } 71 }
67 return 0; 72 return 0;
68 } 73 }
69 74
70 void RenderMultiColumnSet::setLogicalTopInFlowThread(LayoutUnit logicalTop) 75 LayoutUnit RenderMultiColumnSet::logicalTopInFlowThread() const
71 { 76 {
72 LayoutRect rect = flowThreadPortionRect(); 77 return firstColumnRow()->logicalTopInFlowThread();
73 if (isHorizontalWritingMode())
74 rect.setY(logicalTop);
75 else
76 rect.setX(logicalTop);
77 setFlowThreadPortionRect(rect);
78 } 78 }
79 79
80 void RenderMultiColumnSet::setLogicalBottomInFlowThread(LayoutUnit logicalBottom ) 80 LayoutUnit RenderMultiColumnSet::logicalBottomInFlowThread() const
81 { 81 {
82 LayoutRect rect = flowThreadPortionRect(); 82 return lastColumnRow()->logicalBottomInFlowThread();
83 if (isHorizontalWritingMode())
84 rect.shiftMaxYEdgeTo(logicalBottom);
85 else
86 rect.shiftMaxXEdgeTo(logicalBottom);
87 setFlowThreadPortionRect(rect);
88 } 83 }
89 84
90 bool RenderMultiColumnSet::heightIsAuto() const 85 bool RenderMultiColumnSet::heightIsAuto() const
91 { 86 {
92 RenderMultiColumnFlowThread* flowThread = multiColumnFlowThread(); 87 RenderMultiColumnFlowThread* flowThread = multiColumnFlowThread();
93 if (!flowThread->isRenderPagedFlowThread()) { 88 if (!flowThread->isRenderPagedFlowThread()) {
94 if (multiColumnBlockFlow()->style()->columnFill() == ColumnFillBalance) 89 if (multiColumnBlockFlow()->style()->columnFill() == ColumnFillBalance)
95 return true; 90 return true;
96 if (RenderBox* next = nextSiblingBox()) { 91 if (RenderBox* next = nextSiblingBox()) {
97 if (next->isRenderMultiColumnSpannerPlaceholder()) { 92 if (next->isRenderMultiColumnSpannerPlaceholder()) {
98 // If we're followed by a spanner, we need to balance. 93 // If we're followed by a spanner, we need to balance.
99 return true; 94 return true;
100 } 95 }
101 } 96 }
102 } 97 }
103 return !flowThread->columnHeightAvailable(); 98 return !flowThread->columnHeightAvailable();
104 } 99 }
105 100
106 LayoutSize RenderMultiColumnSet::flowThreadTranslationAtOffset(LayoutUnit blockO ffset) const 101 LayoutSize RenderMultiColumnSet::flowThreadTranslationAtOffset(LayoutUnit blockO ffset) const
107 { 102 {
108 unsigned columnIndex = columnIndexAtOffset(blockOffset); 103 const MultiColumnRow* row = columnRowAtFlowThreadOffset(blockOffset);
109 LayoutRect portionRect(flowThreadPortionRectAt(columnIndex)); 104 return row->offsetFromColumnSet() + row->flowThreadTranslationAtOffset(block Offset);
110 flipForWritingMode(portionRect);
111 LayoutRect columnRect(columnRectAt(columnIndex));
112 flipForWritingMode(columnRect);
113 return toLayoutPoint(contentBoxOffset()) + columnRect.location() - portionRe ct.location();
114 } 105 }
115 106
116 LayoutUnit RenderMultiColumnSet::heightAdjustedForSetOffset(LayoutUnit height) c onst 107 void RenderMultiColumnSet::updateMinimumColumnHeight(LayoutUnit offsetInFlowThre ad, LayoutUnit height)
117 { 108 {
118 // Adjust for the top offset within the content box of the multicol containe r (containing 109 columnRowAtFlowThreadOffset(offsetInFlowThread)->updateMinimumColumnHeight(h eight);
119 // block), unless this is the first set. We know that the top offset for the first set will be
120 // zero, but if the multicol container has non-zero top border or padding, t he set's top offset
121 // (initially being 0 and relative to the border box) will be negative until it has been laid
122 // out. Had we used this bogus offset, we would calculate the wrong height, and risk performing
123 // a wasted layout iteration. Of course all other sets (if any) have this pr oblem in the first
124 // layout pass too, but there's really nothing we can do there until the flo w thread has been
125 // laid out anyway.
126 if (previousSiblingMultiColumnSet()) {
127 RenderBlockFlow* multicolBlock = multiColumnBlockFlow();
128 LayoutUnit contentLogicalTop = logicalTop() - multicolBlock->borderAndPa ddingBefore();
129 height -= contentLogicalTop;
130 }
131 return max(height, LayoutUnit(1)); // Let's avoid zero height, as that would probably cause an infinite amount of columns to be created.
132 } 110 }
133 111
134 LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) cons t 112 LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) cons t
135 { 113 {
136 unsigned columnIndex = columnIndexAtOffset(offset, AssumeNewColumns); 114 return columnRowAtFlowThreadOffset(offset)->columnLogicalTopForOffset(offset );
137 return logicalTopInFlowThread() + columnIndex * pageLogicalHeight();
138 }
139
140 void RenderMultiColumnSet::setAndConstrainColumnHeight(LayoutUnit newHeight)
141 {
142 m_columnHeight = newHeight;
143 if (m_columnHeight > m_maxColumnHeight)
144 m_columnHeight = m_maxColumnHeight;
145 // FIXME: the height may also be affected by the enclosing pagination contex t, if any.
146 }
147
148 unsigned RenderMultiColumnSet::findRunWithTallestColumns() const
149 {
150 unsigned indexWithLargestHeight = 0;
151 LayoutUnit largestHeight;
152 LayoutUnit previousOffset = logicalTopInFlowThread();
153 size_t runCount = m_contentRuns.size();
154 ASSERT(runCount);
155 for (size_t i = 0; i < runCount; i++) {
156 const ContentRun& run = m_contentRuns[i];
157 LayoutUnit height = run.columnLogicalHeight(previousOffset);
158 if (largestHeight < height) {
159 largestHeight = height;
160 indexWithLargestHeight = i;
161 }
162 previousOffset = run.breakOffset();
163 }
164 return indexWithLargestHeight;
165 }
166
167 void RenderMultiColumnSet::distributeImplicitBreaks()
168 {
169 #if ENABLE(ASSERT)
170 // There should be no implicit breaks assumed at this point.
171 for (unsigned i = 0; i < m_contentRuns.size(); i++)
172 ASSERT(!m_contentRuns[i].assumedImplicitBreaks());
173 #endif // ENABLE(ASSERT)
174
175 // Insert a final content run to encompass all content. This will include ov erflow if this is
176 // the last set.
177 addContentRun(logicalBottomInFlowThread());
178 unsigned columnCount = m_contentRuns.size();
179
180 // If there is room for more breaks (to reach the used value of column-count ), imagine that we
181 // insert implicit breaks at suitable locations. At any given time, the cont ent run with the
182 // currently tallest columns will get another implicit break "inserted", whi ch will increase its
183 // column count by one and shrink its columns' height. Repeat until we have the desired total
184 // number of breaks. The largest column height among the runs will then be t he initial column
185 // height for the balancer to use.
186 while (columnCount < usedColumnCount()) {
187 unsigned index = findRunWithTallestColumns();
188 m_contentRuns[index].assumeAnotherImplicitBreak();
189 columnCount++;
190 }
191 }
192
193 LayoutUnit RenderMultiColumnSet::calculateColumnHeight(BalancedHeightCalculation calculationMode) const
194 {
195 if (calculationMode == GuessFromFlowThreadPortion) {
196 // Initial balancing. Start with the lowest imaginable column height. We use the tallest
197 // content run (after having "inserted" implicit breaks), and find its s tart offset (by
198 // looking at the previous run's end offset, or, if there's no previous run, the set's start
199 // offset in the flow thread).
200 unsigned index = findRunWithTallestColumns();
201 LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffse t() : logicalTopInFlowThread();
202 return std::max<LayoutUnit>(m_contentRuns[index].columnLogicalHeight(sta rtOffset), m_minimumColumnHeight);
203 }
204
205 if (actualColumnCount() <= usedColumnCount()) {
206 // With the current column height, the content fits without creating ove rflowing columns. We're done.
207 return m_columnHeight;
208 }
209
210 if (m_contentRuns.size() >= usedColumnCount()) {
211 // Too many forced breaks to allow any implicit breaks. Initial balancin g should already
212 // have set a good height. There's nothing more we should do.
213 return m_columnHeight;
214 }
215
216 // If the initial guessed column height wasn't enough, stretch it now. Stret ch by the lowest
217 // amount of space shortage found during layout.
218
219 ASSERT(m_minSpaceShortage > 0); // We should never _shrink_ the height!
220 ASSERT(m_minSpaceShortage != RenderFlowThread::maxLogicalHeight()); // If th is happens, we probably have a bug.
221 if (m_minSpaceShortage == RenderFlowThread::maxLogicalHeight())
222 return m_columnHeight; // So bail out rather than looping infinitely.
223
224 return m_columnHeight + m_minSpaceShortage;
225 } 115 }
226 116
227 void RenderMultiColumnSet::addContentRun(LayoutUnit endOffsetFromFirstPage) 117 void RenderMultiColumnSet::addContentRun(LayoutUnit endOffsetFromFirstPage)
228 { 118 {
229 if (!heightIsAuto()) 119 if (!heightIsAuto())
230 return; 120 return;
231 if (!m_contentRuns.isEmpty() && endOffsetFromFirstPage <= m_contentRuns.last ().breakOffset()) 121 columnRowAtFlowThreadOffset(endOffsetFromFirstPage)->addContentRun(endOffset FromFirstPage);
232 return;
233 // Append another item as long as we haven't exceeded used column count. Wha t ends up in the
234 // overflow area shouldn't affect column balancing.
235 if (m_contentRuns.size() < usedColumnCount())
236 m_contentRuns.append(ContentRun(endOffsetFromFirstPage));
237 } 122 }
238 123
239 bool RenderMultiColumnSet::recalculateColumnHeight(BalancedHeightCalculation cal culationMode) 124 bool RenderMultiColumnSet::recalculateColumnHeight(BalancedColumnHeightCalculati on calculationMode)
240 { 125 {
241 LayoutUnit oldColumnHeight = m_columnHeight; 126 bool changed = false;
242 127 for (MultiColumnRow* row = firstColumnRow(); row; row = row->nextRow())
243 m_maxColumnHeight = calculateMaxColumnHeight(); 128 changed = row->recalculateColumnHeight(calculationMode) || changed;
244 129 return changed;
245 if (heightIsAuto()) {
246 if (calculationMode == GuessFromFlowThreadPortion) {
247 // Post-process the content runs and find out where the implicit bre aks will occur.
248 distributeImplicitBreaks();
249 }
250 LayoutUnit newColumnHeight = calculateColumnHeight(calculationMode);
251 setAndConstrainColumnHeight(newColumnHeight);
252 // After having calculated an initial column height, the multicol contai ner typically needs at
253 // least one more layout pass with a new column height, but if a height was specified, we only
254 // need to do this if we think that we need less space than specified. C onversely, if we
255 // determined that the columns need to be as tall as the specified heigh t of the container, we
256 // have already laid it out correctly, and there's no need for another p ass.
257 } else {
258 // The position of the column set may have changed, in which case height available for
259 // columns may have changed as well.
260 setAndConstrainColumnHeight(m_columnHeight);
261 }
262
263 // We can get rid of the content runs now, if we haven't already done so. Th ey are only needed
264 // to calculate the initial balanced column height. In fact, we have to get rid of them before
265 // the next layout pass, since each pass will rebuild this.
266 m_contentRuns.clear();
267
268 if (m_columnHeight == oldColumnHeight)
269 return false; // No change. We're done.
270
271 m_minSpaceShortage = RenderFlowThread::maxLogicalHeight();
272 return true; // Need another pass.
273 } 130 }
274 131
275 void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit spaceShortage) 132 void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit offsetInFlowThread, La youtUnit spaceShortage)
276 { 133 {
277 if (spaceShortage >= m_minSpaceShortage) 134 columnRowAtFlowThreadOffset(offsetInFlowThread)->recordSpaceShortage(spaceSh ortage);
278 return;
279
280 // The space shortage is what we use as our stretch amount. We need a positi ve number here in
281 // order to get anywhere.
282 ASSERT(spaceShortage > 0);
283
284 m_minSpaceShortage = spaceShortage;
285 } 135 }
286 136
287 void RenderMultiColumnSet::resetColumnHeight() 137 void RenderMultiColumnSet::resetColumnHeight()
288 { 138 {
289 // Nuke previously stored minimum column height. Contents may have changed f or all we know. 139 // FIXME: don't we really have to just delete all rows but the first one her e?
290 m_minimumColumnHeight = 0; 140 for (MultiColumnRow* row = firstColumnRow(); row; row = row->nextRow())
141 row->resetColumnHeight();
142 }
291 143
292 m_maxColumnHeight = calculateMaxColumnHeight(); 144 void RenderMultiColumnSet::beginFlow(LayoutUnit offsetInFlowThread)
145 {
146 // At this point layout is exactly at the beginning of this set. Store block offset from flow
147 // thread start.
148 firstColumnRow()->setLogicalTopInFlowThread(offsetInFlowThread);
149 }
293 150
294 LayoutUnit oldColumnHeight = pageLogicalHeight(); 151 void RenderMultiColumnSet::endFlow(LayoutUnit offsetInFlowThread)
295 152 {
296 if (heightIsAuto()) 153 // At this point layout is exactly at the end of this set. Store block offse t from flow thread
297 m_columnHeight = 0; 154 // start. This set is now considered "flowed", although we may have to revis it it later (with
298 else 155 // beginFlow()), e.g. if a subtree in the flow thread has to be laid out ove r again because the
299 setAndConstrainColumnHeight(heightAdjustedForSetOffset(multiColumnFlowTh read()->columnHeightAvailable())); 156 // initial margin collapsing estimates were wrong.
300 157 lastColumnRow()->setLogicalBottomInFlowThread(offsetInFlowThread);
301 if (pageLogicalHeight() != oldColumnHeight)
302 setChildNeedsLayout(MarkOnlyThis);
303
304 // Content runs are only needed in the initial layout pass, in order to find an initial column
305 // height, and should have been deleted afterwards. We're about to rebuild t he content runs, so
306 // the list needs to be empty.
307 ASSERT(m_contentRuns.isEmpty());
308 } 158 }
309 159
310 void RenderMultiColumnSet::expandToEncompassFlowThreadContentsIfNeeded() 160 void RenderMultiColumnSet::expandToEncompassFlowThreadContentsIfNeeded()
311 { 161 {
312 ASSERT(multiColumnFlowThread()->lastMultiColumnSet() == this); 162 ASSERT(multiColumnFlowThread()->lastMultiColumnSet() == this);
313 LayoutRect rect(flowThreadPortionRect()); 163 // FIXME: this may result in the need for creating additional rows, since th ere may not be
314 164 // enough space remaining in the currently last row.
315 // Get the offset within the flow thread in its block progression direction. Then get the 165 lastColumnRow()->expandToEncompassFlowThreadOverflow();
316 // flow thread's remaining logical height including its overflow and expand our rect
317 // to encompass that remaining height and overflow. The idea is that we will generate
318 // additional columns and pages to hold that overflow, since people do write bad
319 // content like <body style="height:0px"> in multi-column layouts.
320 bool isHorizontal = flowThread()->isHorizontalWritingMode();
321 LayoutUnit logicalTopOffset = isHorizontal ? rect.y() : rect.x();
322 LayoutRect layoutRect = flowThread()->layoutOverflowRect();
323 LayoutUnit logicalHeightWithOverflow = (isHorizontal ? layoutRect.maxY() : l ayoutRect.maxX()) - logicalTopOffset;
324 setFlowThreadPortionRect(LayoutRect(rect.x(), rect.y(), isHorizontal ? rect. width() : logicalHeightWithOverflow, isHorizontal ? logicalHeightWithOverflow : rect.height()));
325 } 166 }
326 167
327 void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTo p, LogicalExtentComputedValues& computedValues) const 168 void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTo p, LogicalExtentComputedValues& computedValues) const
328 { 169 {
329 computedValues.m_extent = m_columnHeight; 170 LayoutUnit logicalHeight;
171 for (const MultiColumnRow* row = firstColumnRow(); row; row = row->nextRow() )
172 logicalHeight += row->logicalHeight();
173 computedValues.m_extent = logicalHeight;
330 computedValues.m_position = logicalTop; 174 computedValues.m_position = logicalTop;
331 } 175 }
332 176
333 LayoutUnit RenderMultiColumnSet::calculateMaxColumnHeight() const
334 {
335 RenderBlockFlow* multicolBlock = multiColumnBlockFlow();
336 RenderStyle* multicolStyle = multicolBlock->style();
337 LayoutUnit availableHeight = multiColumnFlowThread()->columnHeightAvailable( );
338 LayoutUnit maxColumnHeight = availableHeight ? availableHeight : RenderFlowT hread::maxLogicalHeight();
339 if (!multicolStyle->logicalMaxHeight().isMaxSizeNone()) {
340 LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight (multicolStyle->logicalMaxHeight(), -1);
341 if (logicalMaxHeight != -1 && maxColumnHeight > logicalMaxHeight)
342 maxColumnHeight = logicalMaxHeight;
343 }
344 return heightAdjustedForSetOffset(maxColumnHeight);
345 }
346
347 LayoutUnit RenderMultiColumnSet::columnGap() const 177 LayoutUnit RenderMultiColumnSet::columnGap() const
348 { 178 {
349 RenderBlockFlow* parentBlock = multiColumnBlockFlow(); 179 RenderBlockFlow* parentBlock = multiColumnBlockFlow();
350 if (parentBlock->style()->hasNormalColumnGap()) 180 if (parentBlock->style()->hasNormalColumnGap())
351 return parentBlock->style()->fontDescription().computedPixelSize(); // " 1em" is recommended as the normal gap setting. Matches <p> margins. 181 return parentBlock->style()->fontDescription().computedPixelSize(); // " 1em" is recommended as the normal gap setting. Matches <p> margins.
352 return parentBlock->style()->columnGap(); 182 return parentBlock->style()->columnGap();
353 } 183 }
354 184
355 unsigned RenderMultiColumnSet::actualColumnCount() const 185 unsigned RenderMultiColumnSet::actualColumnCount() const
356 { 186 {
357 // We must always return a value of 1 or greater. Column count = 0 is a mean ingless situation, 187 // FIXME: remove this method. It's a meaningless question to ask the set "ho w many columns do
358 // and will confuse and cause problems in other parts of the code. 188 // you actually have?", since that may vary for each row.
359 if (!pageLogicalHeight()) 189 return firstColumnRow()->actualColumnCount();
360 return 1;
361
362 // Our portion rect determines our column count. We have as many columns as needed to fit all the content.
363 LayoutUnit logicalHeightInColumns = flowThread()->isHorizontalWritingMode() ? flowThreadPortionRect().height() : flowThreadPortionRect().width();
364 if (!logicalHeightInColumns)
365 return 1;
366
367 unsigned count = ceil(logicalHeightInColumns.toFloat() / pageLogicalHeight() .toFloat());
368 ASSERT(count >= 1);
369 return count;
370 }
371
372 LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const
373 {
374 LayoutUnit colLogicalWidth = pageLogicalWidth();
375 LayoutUnit colLogicalHeight = pageLogicalHeight();
376 LayoutUnit colLogicalTop = borderBefore() + paddingBefore();
377 LayoutUnit colLogicalLeft = borderAndPaddingLogicalLeft();
378 LayoutUnit colGap = columnGap();
379
380 if (multiColumnFlowThread()->progressionIsInline()) {
381 if (style()->isLeftToRightDirection())
382 colLogicalLeft += index * (colLogicalWidth + colGap);
383 else
384 colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (colLogicalWidth + colGap);
385 } else {
386 colLogicalTop += index * (colLogicalHeight + colGap);
387 }
388
389 if (isHorizontalWritingMode())
390 return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLog icalHeight);
391 return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogica lWidth);
392 }
393
394 unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset, ColumnInde xCalculationMode mode) const
395 {
396 LayoutRect portionRect(flowThreadPortionRect());
397
398 // Handle the offset being out of range.
399 LayoutUnit flowThreadLogicalTop = isHorizontalWritingMode() ? portionRect.y( ) : portionRect.x();
400 if (offset < flowThreadLogicalTop)
401 return 0;
402 // If we're laying out right now, we cannot constrain against some logical b ottom, since it
403 // isn't known yet. Otherwise, just return the last column if we're past the logical bottom.
404 if (mode == ClampToExistingColumns) {
405 LayoutUnit flowThreadLogicalBottom = isHorizontalWritingMode() ? portion Rect.maxY() : portionRect.maxX();
406 if (offset >= flowThreadLogicalBottom)
407 return actualColumnCount() - 1;
408 }
409
410 if (LayoutUnit pageLogicalHeight = this->pageLogicalHeight())
411 return (offset - flowThreadLogicalTop).toFloat() / pageLogicalHeight.toF loat();
412
413 return 0;
414 }
415
416 LayoutRect RenderMultiColumnSet::flowThreadPortionRectAt(unsigned index) const
417 {
418 LayoutRect portionRect = flowThreadPortionRect();
419 if (isHorizontalWritingMode())
420 portionRect = LayoutRect(portionRect.x(), portionRect.y() + index * page LogicalHeight(), portionRect.width(), pageLogicalHeight());
421 else
422 portionRect = LayoutRect(portionRect.x() + index * pageLogicalHeight(), portionRect.y(), pageLogicalHeight(), portionRect.height());
423 return portionRect;
424 }
425
426 LayoutRect RenderMultiColumnSet::flowThreadPortionOverflowRect(const LayoutRect& portionRect, unsigned index, unsigned colCount, LayoutUnit colGap) const
427 {
428 // This function determines the portion of the flow thread that paints for t he column. Along the inline axis, columns are
429 // unclipped at outside edges (i.e., the first and last column in the set), and they clip to half the column
430 // gap along interior edges.
431 //
432 // In the block direction, we will not clip overflow out of the top of the f irst column, or out of the bottom of
433 // the last column. This applies only to the true first column and last colu mn across all column sets.
434 //
435 // FIXME: Eventually we will know overflow on a per-column basis, but we can 't do this until we have a painting
436 // mode that understands not to paint contents from a previous column in the overflow area of a following column.
437 // This problem applies to regions and pages as well and is not unique to co lumns.
438 bool isFirstColumn = !index;
439 bool isLastColumn = index == colCount - 1;
440 bool isLeftmostColumn = style()->isLeftToRightDirection() ? isFirstColumn : isLastColumn;
441 bool isRightmostColumn = style()->isLeftToRightDirection() ? isLastColumn : isFirstColumn;
442
443 // Calculate the overflow rectangle, based on the flow thread's, clipped at column logical
444 // top/bottom unless it's the first/last column.
445 LayoutRect overflowRect = overflowRectForFlowThreadPortion(portionRect, isFi rstColumn && isFirstRegion(), isLastColumn && isLastRegion());
446
447 // Avoid overflowing into neighboring columns, by clipping in the middle of adjacent column
448 // gaps. Also make sure that we avoid rounding errors.
449 if (isHorizontalWritingMode()) {
450 if (!isLeftmostColumn)
451 overflowRect.shiftXEdgeTo(portionRect.x() - colGap / 2);
452 if (!isRightmostColumn)
453 overflowRect.shiftMaxXEdgeTo(portionRect.maxX() + colGap - colGap / 2);
454 } else {
455 if (!isLeftmostColumn)
456 overflowRect.shiftYEdgeTo(portionRect.y() - colGap / 2);
457 if (!isRightmostColumn)
458 overflowRect.shiftMaxYEdgeTo(portionRect.maxY() + colGap - colGap / 2);
459 }
460 return overflowRect;
461 } 190 }
462 191
463 void RenderMultiColumnSet::paintObject(const PaintInfo& paintInfo, const LayoutP oint& paintOffset) 192 void RenderMultiColumnSet::paintObject(const PaintInfo& paintInfo, const LayoutP oint& paintOffset)
464 { 193 {
465 MultiColumnSetPainter(*this).paintObject(paintInfo, paintOffset); 194 MultiColumnSetPainter(*this).paintObject(paintInfo, paintOffset);
466 } 195 }
467 196
468 void RenderMultiColumnSet::collectLayerFragments(LayerFragments& fragments, cons t LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) 197 void RenderMultiColumnSet::collectLayerFragments(LayerFragments& fragments, cons t LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect)
469 { 198 {
470 // |layerBoundingBox| is in the flow thread coordinate space, relative to th e top/left edge of 199 for (MultiColumnRow* row = firstColumnRow(); row; row = row->nextRow())
Julien - ping for review 2015/01/29 17:37:47 Could we use C++11 for-loop here? (and probably fo
mstensho (USE GERRIT) 2015/02/02 17:57:24 Done. I was planning to use a home-made linked li
471 // the flow thread, but note that it has been converted with respect to writ ing mode (so that 200 row->collectLayerFragments(fragments, layerBoundingBox, dirtyRect);
472 // it's visual/physical in that sense).
473 //
474 // |dirtyRect| is visual, relative to the multicol container.
475 //
476 // Then there's the output from this method - the stuff we put into the list of fragments. The
477 // fragment.paginationOffset point is the actual visual translation required to get from a
478 // location in the flow thread to a location in a given column. The fragment .paginationClip
479 // rectangle, on the other hand, is in flow thread coordinates.
480 //
481 // All other rectangles in this method are sized physically, and the inline direction coordinate
482 // is physical too, but the block direction coordinate is "logical top". Thi s is the same as
483 // e.g. RenderBox::frameRect(). These rectangles also pretend that there's o nly one long column,
484 // i.e. they are for the flow thread.
485
486 // Put the layer bounds into flow thread-local coordinates by flipping it fi rst. Since we're in
487 // a renderer, most rectangles are represented this way.
488 LayoutRect layerBoundsInFlowThread(layerBoundingBox);
489 flowThread()->flipForWritingMode(layerBoundsInFlowThread);
490
491 // Now we can compare with the flow thread portions owned by each column. Fi rst let's
492 // see if the rect intersects our flow thread portion at all.
493 LayoutRect clippedRect(layerBoundsInFlowThread);
494 clippedRect.intersect(RenderRegion::flowThreadPortionOverflowRect());
495 if (clippedRect.isEmpty())
496 return;
497
498 // Now we know we intersect at least one column. Let's figure out the logica l top and logical
499 // bottom of the area we're checking.
500 LayoutUnit layerLogicalTop = isHorizontalWritingMode() ? layerBoundsInFlowTh read.y() : layerBoundsInFlowThread.x();
501 LayoutUnit layerLogicalBottom = (isHorizontalWritingMode() ? layerBoundsInFl owThread.maxY() : layerBoundsInFlowThread.maxX()) - 1;
502
503 // Figure out the start and end columns and only check within that range so that we don't walk the
504 // entire column set.
505 unsigned startColumn = columnIndexAtOffset(layerLogicalTop);
506 unsigned endColumn = columnIndexAtOffset(layerLogicalBottom);
507
508 LayoutUnit colLogicalWidth = pageLogicalWidth();
509 LayoutUnit colGap = columnGap();
510 unsigned colCount = actualColumnCount();
511
512 RenderMultiColumnFlowThread* flowThread = multiColumnFlowThread();
513 bool progressionIsInline = flowThread->progressionIsInline();
514 bool leftToRight = style()->isLeftToRightDirection();
515
516 LayoutUnit initialBlockOffset = logicalTop() - flowThread->logicalTop();
517
518 for (unsigned i = startColumn; i <= endColumn; i++) {
519 // Get the portion of the flow thread that corresponds to this column.
520 LayoutRect flowThreadPortion = flowThreadPortionRectAt(i);
521
522 // Now get the overflow rect that corresponds to the column.
523 LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flo wThreadPortion, i, colCount, colGap);
524
525 // In order to create a fragment we must intersect the portion painted b y this column.
526 LayoutRect clippedRect(layerBoundsInFlowThread);
527 clippedRect.intersect(flowThreadOverflowPortion);
528 if (clippedRect.isEmpty())
529 continue;
530
531 // We also need to intersect the dirty rect. We have to apply a translat ion and shift based off
532 // our column index.
533 LayoutPoint translationOffset;
534 LayoutUnit inlineOffset = progressionIsInline ? i * (colLogicalWidth + c olGap) : LayoutUnit();
535 if (!leftToRight)
536 inlineOffset = -inlineOffset;
537 translationOffset.setX(inlineOffset);
538 LayoutUnit blockOffset;
539 if (progressionIsInline) {
540 blockOffset = initialBlockOffset + (isHorizontalWritingMode() ? -flo wThreadPortion.y() : -flowThreadPortion.x());
541 } else {
542 // Column gap can apply in the block direction for page fragmentaine rs.
543 // There is currently no spec which calls for column-gap to apply
544 // for page fragmentainers at all, but it's applied here for compati bility
545 // with the old multicolumn implementation.
546 blockOffset = i * colGap;
547 }
548 if (isFlippedBlocksWritingMode(style()->writingMode()))
549 blockOffset = -blockOffset;
550 translationOffset.setY(blockOffset);
551 if (!isHorizontalWritingMode())
552 translationOffset = translationOffset.transposedPoint();
553 // FIXME: The translation needs to include the multicolumn set's content offset within the
554 // multicolumn block as well. This won't be an issue until we start crea ting multiple multicolumn sets.
555
556 // Shift the dirty rect to be in flow thread coordinates with this trans lation applied.
557 LayoutRect translatedDirtyRect(dirtyRect);
558 translatedDirtyRect.moveBy(-translationOffset);
559
560 // See if we intersect the dirty rect.
561 clippedRect = layerBoundingBox;
562 clippedRect.intersect(translatedDirtyRect);
563 if (clippedRect.isEmpty())
564 continue;
565
566 // Something does need to paint in this column. Make a fragment now and supply the physical translation
567 // offset and the clip rect for the column with that offset applied.
568 LayerFragment fragment;
569 fragment.paginationOffset = translationOffset;
570
571 LayoutRect flippedFlowThreadOverflowPortion(flowThreadOverflowPortion);
572 // Flip it into more a physical (RenderLayer-style) rectangle.
573 flowThread->flipForWritingMode(flippedFlowThreadOverflowPortion);
574 fragment.paginationClip = flippedFlowThreadOverflowPortion;
575 fragments.append(fragment);
576 }
577 } 201 }
578 202
579 void RenderMultiColumnSet::addOverflowFromChildren() 203 void RenderMultiColumnSet::addOverflowFromChildren()
580 { 204 {
581 unsigned colCount = actualColumnCount(); 205 LayoutRect overflowRect;
582 if (!colCount) 206 for (MultiColumnRow* row = firstColumnRow(); row; row = row->nextRow()) {
583 return; 207 LayoutRect rect = row->calculateOverflow();
208 rect.move(row->offsetFromColumnSet());
209 overflowRect.unite(rect);
210 }
211 addLayoutOverflow(overflowRect);
212 if (!hasOverflowClip())
213 addVisualOverflow(overflowRect);
214 }
584 215
585 LayoutRect lastRect = columnRectAt(colCount - 1); 216 MultiColumnRow* RenderMultiColumnSet::columnRowAtFlowThreadOffset(LayoutUnit)
586 addLayoutOverflow(lastRect); 217 {
587 if (!hasOverflowClip()) 218 // FIXME: implement this, once we have support for multiple rows.
588 addVisualOverflow(lastRect); 219 return &m_columnRow;
220 }
221
222 const MultiColumnRow* RenderMultiColumnSet::columnRowAtFlowThreadOffset(LayoutUn it) const
223 {
224 // FIXME: implement this, once we have support for multiple rows.
225 return &m_columnRow;
589 } 226 }
590 227
591 const char* RenderMultiColumnSet::renderName() const 228 const char* RenderMultiColumnSet::renderName() const
592 { 229 {
593 return "RenderMultiColumnSet"; 230 return "RenderMultiColumnSet";
594 } 231 }
595 232
596 void RenderMultiColumnSet::insertedIntoTree() 233 void RenderMultiColumnSet::insertedIntoTree()
597 { 234 {
598 RenderRegion::insertedIntoTree(); 235 RenderRegion::insertedIntoTree();
(...skipping 24 matching lines...) Expand all
623 } 260 }
624 261
625 void RenderMultiColumnSet::detachRegion() 262 void RenderMultiColumnSet::detachRegion()
626 { 263 {
627 if (m_flowThread) { 264 if (m_flowThread) {
628 m_flowThread->removeRegionFromThread(this); 265 m_flowThread->removeRegionFromThread(this);
629 m_flowThread = 0; 266 m_flowThread = 0;
630 } 267 }
631 } 268 }
632 269
270 LayoutRect RenderMultiColumnSet::flowThreadPortionRect() const
271 {
272 LayoutRect portionRect(LayoutUnit(), logicalTopInFlowThread(), pageLogicalWi dth(), logicalHeightInFlowThread());
273 if (!isHorizontalWritingMode())
274 return portionRect.transposedRect();
275 return portionRect;
633 } 276 }
277
278 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698