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

Side by Side Diff: third_party/WebKit/Source/core/layout/ColumnBalancer.cpp

Issue 1399493002: Column balancing refactoring. Don't propagate data during layout. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 2 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "config.h"
6
7 #include "core/layout/ColumnBalancer.h"
8
9 #include "core/layout/LayoutMultiColumnSet.h"
10
11 namespace blink {
12
13 ColumnBalancer::ColumnBalancer(const MultiColumnFragmentainerGroup& group)
14 : m_group(group)
15 {
16 }
17
18 void ColumnBalancer::traverse()
19 {
20 traverseSubtree(*m_group.columnSet().flowThread());
21 ASSERT(!flowThreadOffset());
22 }
23
24 void ColumnBalancer::traverseSubtree(const LayoutBox& box)
25 {
26 if (box.childrenInline() && box.isLayoutBlockFlow()) {
27 // Look for breaks between lines.
28 for (const RootInlineBox* line = toLayoutBlockFlow(box).firstRootBox(); line; line = line->nextRootBox()) {
29 LayoutUnit lineTopInFlowThread = m_flowThreadOffset + line->lineTopW ithLeading();
30 if (lineTopInFlowThread < group().logicalTopInFlowThread())
31 continue;
32 if (lineTopInFlowThread >= group().logicalBottomInFlowThread())
33 break;
34 examineLine(*line);
35 }
36 return;
37 }
38
39 const LayoutFlowThread* flowThread = group().columnSet().flowThread();
40 bool isHorizontalWritingMode = flowThread->isHorizontalWritingMode();
41
42 // Look for breaks between and inside children.
43 for (const LayoutObject* child = box.slowFirstChild(); child; child = child- >nextSibling()) {
44 if (!child->isBox())
45 continue;
46 const LayoutBox& childBox = toLayoutBox(*child);
47 LayoutRect overflowRect = childBox.layoutOverflowRect();
48 LayoutUnit childLogicalBottomWithOverflow = childBox.logicalTop() + (isH orizontalWritingMode ? overflowRect.maxY() : overflowRect.maxX());
49 if (m_flowThreadOffset + childLogicalBottomWithOverflow <= group().logic alTopInFlowThread()) {
50 // This child is fully above the fragmentainer group we're examining .
51 continue;
52 }
53 LayoutUnit childLogicalTopWithOverflow = childBox.logicalTop() + (isHori zontalWritingMode ? overflowRect.y() : overflowRect.x());
54 if (m_flowThreadOffset + childLogicalTopWithOverflow >= group().logicalB ottomInFlowThread()) {
55 // This child is fully below the fragmentainer group we're examining . We cannot just
56 // stop here, though, thanks to negative margins. So keep looking.
57 continue;
58 }
59 if (childBox.isOutOfFlowPositioned() || childBox.isColumnSpanAll())
60 continue;
61
62 // Tables are wicked. Both table rows and table cells are relative to th eir table section.
63 LayoutUnit offsetForThisChild = childBox.isTableRow() ? LayoutUnit() : c hildBox.logicalTop();
64 m_flowThreadOffset += offsetForThisChild;
65
66 examineBoxAfterEntering(childBox);
67 // Unless the child is unsplittable, or if the child establishes an inne r multicol
68 // container, we descend into its subtree for further examination.
69 if (!childBox.isUnsplittableForPagination()
70 && (!childBox.isLayoutBlockFlow() || !toLayoutBlockFlow(childBox).mu ltiColumnFlowThread()))
71 traverseSubtree(childBox);
72 examineBoxBeforeLeaving(childBox);
73
74 m_flowThreadOffset -= offsetForThisChild;
75 }
76 }
77
78 InitialColumnHeightFinder::InitialColumnHeightFinder(const MultiColumnFragmentai nerGroup& group)
79 : ColumnBalancer(group)
80 {
81 traverse();
82 // We have now found each explicit / forced break, and their location. Now w e need to figure out
83 // how many additional implicit / soft breaks we need and guess where they w ill occur, in order
84 // to provide an initial column height.
85 distributeImplicitBreaks();
86 }
87
88 LayoutUnit InitialColumnHeightFinder::initialMinimalBalancedHeight() const
89 {
90 unsigned index = contentRunIndexWithTallestColumns();
91 LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() : group().logicalTopInFlowThread();
92 return m_contentRuns[index].columnLogicalHeight(startOffset);
93 }
94
95 void InitialColumnHeightFinder::examineBoxAfterEntering(const LayoutBox& box)
96 {
97 ASSERT(isFirstAfterBreak(flowThreadOffset()) || !box.paginationStrut());
98 if (box.hasForcedBreakBefore())
99 addContentRun(flowThreadOffset());
100 if (box.hasForcedBreakAfter())
101 addContentRun(flowThreadOffset() + box.logicalHeight());
102 }
103
104 void InitialColumnHeightFinder::examineBoxBeforeLeaving(const LayoutBox& box)
105 {
106 }
107
108 void InitialColumnHeightFinder::examineLine(const RootInlineBox& line)
109 {
110 }
111
112 void InitialColumnHeightFinder::addContentRun(LayoutUnit endOffsetInFlowThread)
113 {
114 if (!m_contentRuns.isEmpty() && endOffsetInFlowThread <= m_contentRuns.last( ).breakOffset())
115 return;
116 // Append another item as long as we haven't exceeded used column count. Wha t ends up in the
117 // overflow area shouldn't affect column balancing.
118 if (m_contentRuns.size() < group().columnSet().usedColumnCount())
119 m_contentRuns.append(ContentRun(endOffsetInFlowThread));
120 }
121
122 unsigned InitialColumnHeightFinder::contentRunIndexWithTallestColumns() const
123 {
124 unsigned indexWithLargestHeight = 0;
125 LayoutUnit largestHeight;
126 LayoutUnit previousOffset = group().logicalTopInFlowThread();
127 size_t runCount = m_contentRuns.size();
128 ASSERT(runCount);
129 for (size_t i = 0; i < runCount; i++) {
130 const ContentRun& run = m_contentRuns[i];
131 LayoutUnit height = run.columnLogicalHeight(previousOffset);
132 if (largestHeight < height) {
133 largestHeight = height;
134 indexWithLargestHeight = i;
135 }
136 previousOffset = run.breakOffset();
137 }
138 return indexWithLargestHeight;
139 }
140
141 void InitialColumnHeightFinder::distributeImplicitBreaks()
142 {
143 // Insert a final content run to encompass all content. This will include ov erflow if this is
144 // the last group in the multicol container.
145 addContentRun(group().logicalBottomInFlowThread());
146 unsigned columnCount = m_contentRuns.size();
147
148 // If there is room for more breaks (to reach the used value of column-count ), imagine that we
149 // insert implicit breaks at suitable locations. At any given time, the cont ent run with the
150 // currently tallest columns will get another implicit break "inserted", whi ch will increase its
151 // column count by one and shrink its columns' height. Repeat until we have the desired total
152 // number of breaks. The largest column height among the runs will then be t he initial column
153 // height for the balancer to use.
154 while (columnCount < group().columnSet().usedColumnCount()) {
155 unsigned index = contentRunIndexWithTallestColumns();
156 m_contentRuns[index].assumeAnotherImplicitBreak();
157 columnCount++;
158 }
159 }
160
161 MinimumSpaceShortageFinder::MinimumSpaceShortageFinder(const MultiColumnFragment ainerGroup& group)
162 : ColumnBalancer(group)
163 , m_minimumSpaceShortage(LayoutUnit::max())
164 , m_pendingStrut(LayoutUnit::min())
165 , m_forcedBreaksCount(0)
166 {
167 traverse();
168 }
169
170 void MinimumSpaceShortageFinder::examineBoxAfterEntering(const LayoutBox& box)
171 {
172 if (box.hasForcedBreakBefore())
173 m_forcedBreaksCount++;
174 if (box.hasForcedBreakAfter())
175 m_forcedBreaksCount++;
176
177 // Look for breaks before the child box.
178 bool isFirstAfterBreak = this->isFirstAfterBreak(flowThreadOffset());
179 ASSERT(isFirstAfterBreak || !box.paginationStrut());
180 if (isFirstAfterBreak && !box.hasForcedBreakBefore()) {
181 // This box is first after a soft break.
182 LayoutUnit strut = box.paginationStrut();
183 if (box.isUnsplittableForPagination()) {
184 // Since we cannot break inside the box, just figure out how much mo re space we would
185 // need to prevent it from being pushed to the next column.
186 recordSpaceShortage(box.logicalHeight() - strut);
187 } else if (m_pendingStrut == LayoutUnit::min()) {
188 // We now want to look for the first piece of unbreakable content (e .g. a line or a
189 // block-displayed image) inside this block. That ought to be a good candidate for
190 // minimum space shortage; a much better one than reporting space sh ortage for the
191 // entire block (which we'll also do (further down), in case we coul dn't find anything
192 // more suitable).
193 m_pendingStrut = strut;
194 }
195 }
196
197 if (!box.isUnsplittableForPagination()) {
198 // See if this breakable box crosses column boundaries.
199 LayoutUnit bottomInFlowThread = flowThreadOffset() + box.logicalHeight() ;
200 if (isFirstAfterBreak || group().columnLogicalTopForOffset(flowThreadOff set()) != group().columnLogicalTopForOffset(bottomInFlowThread)) {
201 // If the child crosses a column boundary, record space shortage, in case nothing
202 // inside it has already done so. The column balancer needs to know by how much it
203 // has to stretch the columns to make more content fit. If no breaks are reported
204 // (but do occur), the balancer will have no clue. Only measure the space after the
205 // last column boundary, in case it crosses more than one.
206 LayoutUnit spaceUsedInLastColumn = bottomInFlowThread - group().colu mnLogicalTopForOffset(bottomInFlowThread);
207 recordSpaceShortage(spaceUsedInLastColumn);
208 }
209 }
210 }
211
212 void MinimumSpaceShortageFinder::examineBoxBeforeLeaving(const LayoutBox& box)
213 {
214 if (m_pendingStrut == LayoutUnit::min() || !box.isUnsplittableForPagination( ))
215 return;
216
217 // The previous break was before a breakable block. Here's the first piece o f unbreakable
218 // content after / inside that block. We want to record the distance from th e top of the column
219 // to the bottom of this box as space shortage.
220 LayoutUnit logicalOffsetFromCurrentColumn = flowThreadOffset() - group().col umnLogicalTopForOffset(flowThreadOffset());
221 recordSpaceShortage(logicalOffsetFromCurrentColumn + box.logicalHeight() - m _pendingStrut);
222 m_pendingStrut = LayoutUnit::min();
223 }
224
225 void MinimumSpaceShortageFinder::examineLine(const RootInlineBox& line)
226 {
227 LayoutUnit lineTop = line.lineTopWithLeading();
228 LayoutUnit lineTopInFlowThread = flowThreadOffset() + lineTop;
229 LayoutUnit lineHeight = line.lineBottomWithLeading() - lineTop;
230 if (m_pendingStrut != LayoutUnit::min()) {
231 // The previous break was before a breakable block. Here's the first lin e after / inside
232 // that block. We want to record the distance from the top of the column to the bottom of
233 // this box as space shortage.
234 LayoutUnit logicalOffsetFromCurrentColumn = lineTopInFlowThread - group( ).columnLogicalTopForOffset(lineTopInFlowThread);
235 recordSpaceShortage(logicalOffsetFromCurrentColumn + lineHeight - m_pend ingStrut);
236 m_pendingStrut = LayoutUnit::min();
237 return;
238 }
239 ASSERT(isFirstAfterBreak(lineTopInFlowThread) || !line.paginationStrut());
240 if (isFirstAfterBreak(lineTopInFlowThread))
241 recordSpaceShortage(lineHeight - line.paginationStrut());
242 }
243
244 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698