Chromium Code Reviews| 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 |