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

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

Issue 16943008: Column balancing support in the region based multicol implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 7 years, 6 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
« no previous file with comments | « Source/core/rendering/RenderMultiColumnSet.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
(...skipping 22 matching lines...) Expand all
33 33
34 using namespace std; 34 using namespace std;
35 35
36 namespace WebCore { 36 namespace WebCore {
37 37
38 RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread) 38 RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread)
39 : RenderRegionSet(0, flowThread) 39 : RenderRegionSet(0, flowThread)
40 , m_computedColumnCount(1) 40 , m_computedColumnCount(1)
41 , m_computedColumnWidth(0) 41 , m_computedColumnWidth(0)
42 , m_computedColumnHeight(0) 42 , m_computedColumnHeight(0)
43 , m_requiresBalancing(false) 43 , m_maxColumnHeight(LayoutUnit::max())
44 , m_minSpaceShortage(LayoutUnit::max())
44 , m_minimumColumnHeight(0) 45 , m_minimumColumnHeight(0)
45 , m_forcedBreaksCount(0) 46 , m_forcedBreaksCount(0)
46 , m_maximumDistanceBetweenForcedBreaks(0) 47 , m_maximumDistanceBetweenForcedBreaks(0)
47 , m_forcedBreakOffset(0) 48 , m_forcedBreakOffset(0)
48 { 49 {
49 } 50 }
50 51
51 RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* fl owThread) 52 RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* fl owThread)
52 { 53 {
53 Document* document = flowThread->document(); 54 Document* document = flowThread->document();
54 RenderMultiColumnSet* renderer = new (document->renderArena()) RenderMultiCo lumnSet(flowThread); 55 RenderMultiColumnSet* renderer = new (document->renderArena()) RenderMultiCo lumnSet(flowThread);
55 renderer->setDocumentForAnonymous(document); 56 renderer->setDocumentForAnonymous(document);
56 return renderer; 57 return renderer;
57 } 58 }
58 59
60 LayoutUnit RenderMultiColumnSet::heightAdjustedForSetOffset(LayoutUnit height) c onst
61 {
62 RenderMultiColumnBlock* multicolBlock = toRenderMultiColumnBlock(parent());
63 LayoutUnit contentLogicalTop = logicalTop() - multicolBlock->borderBefore() - multicolBlock->paddingBefore();
64
65 height -= contentLogicalTop;
66 return max(height, LayoutUnit(1)); // Let's avoid zero height, as that would probably cause an infinite amount of columns to be created.
67 }
68
59 LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) cons t 69 LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) cons t
60 { 70 {
61 LayoutUnit portionLogicalTop = (isHorizontalWritingMode() ? flowThreadPortio nRect().y() : flowThreadPortionRect().x()); 71 LayoutUnit portionLogicalTop = (isHorizontalWritingMode() ? flowThreadPortio nRect().y() : flowThreadPortionRect().x());
62 unsigned columnIndex = (offset - portionLogicalTop) / computedColumnHeight() ; 72 unsigned columnIndex = columnIndexAtOffset(offset, AssumeNewColumns);
63 return portionLogicalTop + columnIndex * computedColumnHeight(); 73 return portionLogicalTop + columnIndex * computedColumnHeight();
64 } 74 }
65 75
76 void RenderMultiColumnSet::setAndConstrainColumnHeight(LayoutUnit newHeight)
77 {
78 m_computedColumnHeight = newHeight;
79 if (m_computedColumnHeight > m_maxColumnHeight)
80 m_computedColumnHeight = m_maxColumnHeight;
81 // FIXME: the height may also be affected by the enclosing pagination contex t, if any.
82 }
83
84 bool RenderMultiColumnSet::calculateBalancedHeight(bool initial)
85 {
86 ASSERT(toRenderMultiColumnBlock(parent())->requiresBalancing());
87 LayoutUnit oldColumnHeight = m_computedColumnHeight;
88 LayoutUnit currentMinSpaceShortage = m_minSpaceShortage;
89 m_minSpaceShortage = LayoutUnit::max();
90
91 if (initial) {
92 // Start with the lowest imaginable column height.
93 LayoutUnit logicalHeightGuess = ceilf(float(flowThread()->logicalHeight( )) / float(m_computedColumnCount));
94 logicalHeightGuess = max(logicalHeightGuess, m_minimumColumnHeight);
95 setAndConstrainColumnHeight(logicalHeightGuess);
96
97 // The multicol container now typically needs at least one more layout p ass with a new
98 // column height, but if height was specified, we only need to do this i f we found that we
99 // might need less space than that. On the other hand, if we determined that the columns
100 // need to be as tall as the specified height of the container, we have already laid it out
101 // correctly, and there's no need for another pass.
102 return m_computedColumnHeight != oldColumnHeight;
103 }
104
105 if (columnCount() <= computedColumnCount()) {
106 // With the current column height, the content fits without creating ove rflowing columns. We're done.
107 return false;
108 }
109
110 // If the initial guessed column height wasn't enough, stretch it now. Stret ch by the lowest
111 // amount of space shortage found during layout.
112
113 ASSERT(currentMinSpaceShortage != LayoutUnit::max()); // If this can actuall y happen, we probably have a bug.
114 if (currentMinSpaceShortage == LayoutUnit::max())
115 return false; // So bail out rather than looping infinitely.
116
117 setAndConstrainColumnHeight(m_computedColumnHeight + currentMinSpaceShortage );
118
119 // If we reach the maximum column height (typically set by the height or max -height property),
120 // we may not be allowed to stretch further. Return true only if stretching
121 // succeeded. Otherwise, we're done.
122 ASSERT(m_computedColumnHeight >= oldColumnHeight); // We shouldn't be able t o shrink the height!
123 return m_computedColumnHeight > oldColumnHeight;
124 }
125
126 void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit spaceShortage)
127 {
128 if (spaceShortage >= m_minSpaceShortage)
129 return;
130
131 // The space shortage is what we use as our stretch amount. We need a positi ve number here in
132 // order to get anywhere.
133 ASSERT(spaceShortage > 0);
134
135 m_minSpaceShortage = spaceShortage;
136 }
137
66 void RenderMultiColumnSet::updateLogicalWidth() 138 void RenderMultiColumnSet::updateLogicalWidth()
67 { 139 {
68 RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); 140 RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent());
69 setComputedColumnWidthAndCount(parentBlock->columnWidth(), parentBlock->colu mnCount()); // FIXME: This will eventually vary if we are contained inside regio ns. 141 setComputedColumnWidthAndCount(parentBlock->columnWidth(), parentBlock->colu mnCount()); // FIXME: This will eventually vary if we are contained inside regio ns.
70 142
71 // FIXME: When we add regions support, we'll start it off at the width of th e multi-column 143 // FIXME: When we add regions support, we'll start it off at the width of th e multi-column
72 // block in that particular region. 144 // block in that particular region.
73 setLogicalWidth(parentBox()->contentLogicalWidth()); 145 setLogicalWidth(parentBox()->contentLogicalWidth());
74 146
75 // If we overflow, increase our logical width. 147 // If we overflow, increase our logical width.
76 unsigned colCount = columnCount(); 148 unsigned colCount = columnCount();
77 if (!colCount)
78 return;
79
80 LayoutUnit colGap = columnGap(); 149 LayoutUnit colGap = columnGap();
81 LayoutUnit minimumContentLogicalWidth = colCount * computedColumnWidth() + ( colCount - 1) * colGap; 150 LayoutUnit minimumContentLogicalWidth = colCount * computedColumnWidth() + ( colCount - 1) * colGap;
82 LayoutUnit currentContentLogicalWidth = contentLogicalWidth(); 151 LayoutUnit currentContentLogicalWidth = contentLogicalWidth();
83 LayoutUnit delta = max(LayoutUnit(), minimumContentLogicalWidth - currentCon tentLogicalWidth); 152 LayoutUnit delta = max(LayoutUnit(), minimumContentLogicalWidth - currentCon tentLogicalWidth);
84 if (!delta) 153 if (!delta)
85 return; 154 return;
86 155
87 // Increase our logical width by the delta. 156 // Increase our logical width by the delta.
88 setLogicalWidth(logicalWidth() + delta); 157 setLogicalWidth(logicalWidth() + delta);
89 } 158 }
90 159
91 void RenderMultiColumnSet::updateLogicalHeight() 160 void RenderMultiColumnSet::prepareForLayout()
92 { 161 {
93 // FIXME: This is the only class that overrides updateLogicalHeight. If we d idn't have to set computedColumnHeight, 162 RenderMultiColumnBlock* multicolBlock = toRenderMultiColumnBlock(parent());
94 // we could remove this and make updateLogicalHeight non-virtual. https://bu gs.webkit.org/show_bug.cgi?id=96804 163 RenderStyle* multicolStyle = multicolBlock->style();
95 // Make sure our column height is up to date. 164
96 LogicalExtentComputedValues computedValues; 165 // Set box logical top.
97 computeLogicalHeight(0, 0, computedValues); 166 ASSERT(!previousSiblingBox() || !previousSiblingBox()->isRenderMultiColumnSe t()); // FIXME: multiple set not implemented; need to examine previous set to ca lculate the correct logical top.
98 setComputedColumnHeight(computedValues.m_extent); // FIXME: Once we make mor e than one column set, this will become variable. 167 setLogicalTop(multicolBlock->borderBefore() + multicolBlock->paddingBefore() );
99 168
100 // Our logical height is always just the height of our columns. 169 // Set box width.
101 setLogicalHeight(computedColumnHeight()); 170 updateLogicalWidth();
171
172 if (multicolBlock->requiresBalancing()) {
173 // Set maximum column height. We will not stretch beyond this.
174 m_maxColumnHeight = LayoutUnit::max();
175 if (!multicolStyle->logicalHeight().isAuto())
176 m_maxColumnHeight = multicolBlock->computeContentLogicalHeight(multi colStyle->logicalHeight(), -1);
177 if (!multicolStyle->logicalMaxHeight().isUndefined()) {
178 LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHe ight(multicolStyle->logicalMaxHeight(), -1);
179 if (m_maxColumnHeight > logicalMaxHeight)
180 m_maxColumnHeight = logicalMaxHeight;
181 }
182 m_maxColumnHeight = heightAdjustedForSetOffset(m_maxColumnHeight);
183 m_computedColumnHeight = 0; // Restart balancing.
184 } else {
185 setAndConstrainColumnHeight(heightAdjustedForSetOffset(multicolBlock->co lumnHeightAvailable()));
186 }
187
188 // Nuke previously stored minimum column height. Contents may have changed f or all we know.
189 m_minimumColumnHeight = 0;
102 } 190 }
103 191
104 void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit, LogicalE xtentComputedValues& computedValues) const 192 void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTo p, LogicalExtentComputedValues& computedValues) const
105 { 193 {
106 RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); 194 computedValues.m_extent = m_computedColumnHeight;
107 computedValues.m_extent = parentBlock->columnHeight(); 195 computedValues.m_position = logicalTop;
108 } 196 }
109 197
110 LayoutUnit RenderMultiColumnSet::columnGap() const 198 LayoutUnit RenderMultiColumnSet::columnGap() const
111 { 199 {
112 // FIXME: Eventually we will cache the column gap when the widths of columns start varying, but for now we just 200 // FIXME: Eventually we will cache the column gap when the widths of columns start varying, but for now we just
113 // go to the parent block to get the gap. 201 // go to the parent block to get the gap.
114 RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); 202 RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent());
115 if (parentBlock->style()->hasNormalColumnGap()) 203 if (parentBlock->style()->hasNormalColumnGap())
116 return parentBlock->style()->fontDescription().computedPixelSize(); // " 1em" is recommended as the normal gap setting. Matches <p> margins. 204 return parentBlock->style()->fontDescription().computedPixelSize(); // " 1em" is recommended as the normal gap setting. Matches <p> margins.
117 return parentBlock->style()->columnGap(); 205 return parentBlock->style()->columnGap();
118 } 206 }
119 207
120 unsigned RenderMultiColumnSet::columnCount() const 208 unsigned RenderMultiColumnSet::columnCount() const
121 { 209 {
210 // We must always return a value of 1 or greater. Column count = 0 is a mean ingless situation,
211 // and will confuse and cause problems in other parts of the code.
122 if (!computedColumnHeight()) 212 if (!computedColumnHeight())
123 return 0; 213 return 1;
124 214
125 // Our portion rect determines our column count. We have as many columns as needed to fit all the content. 215 // Our portion rect determines our column count. We have as many columns as needed to fit all the content.
126 LayoutUnit logicalHeightInColumns = flowThread()->isHorizontalWritingMode() ? flowThreadPortionRect().height() : flowThreadPortionRect().width(); 216 LayoutUnit logicalHeightInColumns = flowThread()->isHorizontalWritingMode() ? flowThreadPortionRect().height() : flowThreadPortionRect().width();
127 return ceil(static_cast<float>(logicalHeightInColumns) / computedColumnHeigh t()); 217 unsigned count = ceil(static_cast<float>(logicalHeightInColumns) / computedC olumnHeight());
218 ASSERT(count >= 1);
219 return count;
128 } 220 }
129 221
130 LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const 222 LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const
131 { 223 {
132 LayoutUnit colLogicalWidth = computedColumnWidth(); 224 LayoutUnit colLogicalWidth = computedColumnWidth();
133 LayoutUnit colLogicalHeight = computedColumnHeight(); 225 LayoutUnit colLogicalHeight = computedColumnHeight();
134 LayoutUnit colLogicalTop = borderBefore() + paddingBefore(); 226 LayoutUnit colLogicalTop = borderBefore() + paddingBefore();
135 LayoutUnit colLogicalLeft = borderAndPaddingLogicalLeft(); 227 LayoutUnit colLogicalLeft = borderAndPaddingLogicalLeft();
136 LayoutUnit colGap = columnGap(); 228 LayoutUnit colGap = columnGap();
137 if (style()->isLeftToRightDirection()) 229 if (style()->isLeftToRightDirection())
138 colLogicalLeft += index * (colLogicalWidth + colGap); 230 colLogicalLeft += index * (colLogicalWidth + colGap);
139 else 231 else
140 colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (col LogicalWidth + colGap); 232 colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (col LogicalWidth + colGap);
141 233
142 if (isHorizontalWritingMode()) 234 if (isHorizontalWritingMode())
143 return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLog icalHeight); 235 return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLog icalHeight);
144 return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogica lWidth); 236 return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogica lWidth);
145 } 237 }
146 238
147 unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset) const 239 unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset, ColumnInde xCalculationMode mode) const
148 { 240 {
149 LayoutRect portionRect(flowThreadPortionRect()); 241 LayoutRect portionRect(flowThreadPortionRect());
242
243 // Handle the offset being out of range.
150 LayoutUnit flowThreadLogicalTop = isHorizontalWritingMode() ? portionRect.y( ) : portionRect.x(); 244 LayoutUnit flowThreadLogicalTop = isHorizontalWritingMode() ? portionRect.y( ) : portionRect.x();
151 LayoutUnit flowThreadLogicalBottom = isHorizontalWritingMode() ? portionRect .maxY() : portionRect.maxX();
152
153 // Handle the offset being out of range.
154 if (offset < flowThreadLogicalTop) 245 if (offset < flowThreadLogicalTop)
155 return 0; 246 return 0;
156 if (offset >= flowThreadLogicalBottom) 247 // If we're laying out right now, we cannot constrain against some logical b ottom, since it
157 return columnCount() - 1; 248 // isn't known yet. Otherwise, just return the last column if we're past the logical bottom.
158 249 if (mode == ClampToExistingColumns) {
250 LayoutUnit flowThreadLogicalBottom = isHorizontalWritingMode() ? portion Rect.maxY() : portionRect.maxX();
251 if (offset >= flowThreadLogicalBottom)
252 return columnCount() - 1;
253 }
254
159 // Just divide by the column height to determine the correct column. 255 // Just divide by the column height to determine the correct column.
160 return static_cast<float>(offset - flowThreadLogicalTop) / computedColumnHei ght(); 256 return static_cast<float>(offset - flowThreadLogicalTop) / computedColumnHei ght();
161 } 257 }
162 258
163 LayoutRect RenderMultiColumnSet::flowThreadPortionRectAt(unsigned index) const 259 LayoutRect RenderMultiColumnSet::flowThreadPortionRectAt(unsigned index) const
164 { 260 {
165 LayoutRect portionRect = flowThreadPortionRect(); 261 LayoutRect portionRect = flowThreadPortionRect();
166 if (isHorizontalWritingMode()) 262 if (isHorizontalWritingMode())
167 portionRect = LayoutRect(portionRect.x(), portionRect.y() + index * comp utedColumnHeight(), portionRect.width(), computedColumnHeight()); 263 portionRect = LayoutRect(portionRect.x(), portionRect.y() + index * comp utedColumnHeight(), portionRect.width(), computedColumnHeight());
168 else 264 else
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after
412 fragments.append(fragment); 508 fragments.append(fragment);
413 } 509 }
414 } 510 }
415 511
416 const char* RenderMultiColumnSet::renderName() const 512 const char* RenderMultiColumnSet::renderName() const
417 { 513 {
418 return "RenderMultiColumnSet"; 514 return "RenderMultiColumnSet";
419 } 515 }
420 516
421 } 517 }
OLDNEW
« no previous file with comments | « Source/core/rendering/RenderMultiColumnSet.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698