| /* |
| * Copyright (C) 2012 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifndef LayoutMultiColumnSet_h |
| #define LayoutMultiColumnSet_h |
| |
| #include "core/CoreExport.h" |
| #include "core/layout/LayoutMultiColumnFlowThread.h" |
| #include "core/layout/MultiColumnFragmentainerGroup.h" |
| #include "wtf/Vector.h" |
| |
| namespace blink { |
| |
| // A set of columns in a multicol container. A column set is inserted as an |
| // anonymous child of the actual multicol container (i.e. the layoutObject whose |
| // style computes to non-auto column-count and/or column-width), next to the |
| // flow thread. There'll be one column set for each contiguous run of column |
| // content. The only thing that can interrupt a contiguous run of column content |
| // is a column spanner, which means that if there are no spanners, there'll |
| // only be one column set. |
| // |
| // Since a spanner interrupts an otherwise contiguous run of column content, |
| // inserting one may result in the creation of additional new column sets. A |
| // placeholder for the spanning layoutObject has to be placed in between the |
| // column sets that come before and after the spanner, if there's actually |
| // column content both before and after the spanner. |
| // |
| // A column set has no children on its own, but is merely used to slice a |
| // portion of the tall "single-column" flow thread into actual columns visually, |
| // to convert from flow thread coordinates to visual ones. It is in charge of |
| // both positioning columns correctly relatively to the parent multicol |
| // container, and to calculate the correct translation for each column's |
| // contents, and to paint any rules between them. LayoutMultiColumnSet objects |
| // are used for painting, hit testing, and any other type of operation that |
| // requires mapping from flow thread coordinates to visual coordinates. |
| // |
| // Columns are normally laid out in the inline progression direction, but if the |
| // multicol container is inside another fragmentation context (e.g. paged media, |
| // or an another multicol container), we may need to group the columns, so |
| // that we get one MultiColumnFragmentainerGroup for each outer fragmentainer |
| // (page / column) that the inner multicol container lives in. Each |
| // fragmentainer group has its own column height, but the column height is |
| // uniform within a group. |
| class CORE_EXPORT LayoutMultiColumnSet : public LayoutBlockFlow { |
| public: |
| static LayoutMultiColumnSet* createAnonymous( |
| LayoutFlowThread&, |
| const ComputedStyle& parentStyle); |
| |
| const MultiColumnFragmentainerGroup& firstFragmentainerGroup() const { |
| return m_fragmentainerGroups.first(); |
| } |
| const MultiColumnFragmentainerGroup& lastFragmentainerGroup() const { |
| return m_fragmentainerGroups.last(); |
| } |
| unsigned fragmentainerGroupIndexAtFlowThreadOffset(LayoutUnit, |
| PageBoundaryRule) const; |
| MultiColumnFragmentainerGroup& fragmentainerGroupAtFlowThreadOffset( |
| LayoutUnit flowThreadOffset, |
| PageBoundaryRule rule) { |
| return m_fragmentainerGroups[fragmentainerGroupIndexAtFlowThreadOffset( |
| flowThreadOffset, rule)]; |
| } |
| const MultiColumnFragmentainerGroup& fragmentainerGroupAtFlowThreadOffset( |
| LayoutUnit flowThreadOffset, |
| PageBoundaryRule rule) const { |
| return m_fragmentainerGroups[fragmentainerGroupIndexAtFlowThreadOffset( |
| flowThreadOffset, rule)]; |
| } |
| const MultiColumnFragmentainerGroup& fragmentainerGroupAtVisualPoint( |
| const LayoutPoint&) const; |
| const MultiColumnFragmentainerGroupList& fragmentainerGroups() const { |
| return m_fragmentainerGroups; |
| } |
| |
| bool isOfType(LayoutObjectType type) const override { |
| return type == LayoutObjectLayoutMultiColumnSet || |
| LayoutBlockFlow::isOfType(type); |
| } |
| bool canHaveChildren() const final { return false; } |
| |
| // Return the width and height of a single column or page in the set. |
| LayoutUnit pageLogicalWidth() const { return flowThread()->logicalWidth(); } |
| LayoutUnit pageLogicalHeightForOffset(LayoutUnit) const; |
| LayoutUnit pageRemainingLogicalHeightForOffset(LayoutUnit, |
| PageBoundaryRule) const; |
| bool isPageLogicalHeightKnown() const; |
| LayoutUnit tallestUnbreakableLogicalHeight() const { |
| return m_tallestUnbreakableLogicalHeight; |
| } |
| void propagateTallestUnbreakableLogicalHeight(LayoutUnit value) { |
| m_tallestUnbreakableLogicalHeight = |
| std::max(value, m_tallestUnbreakableLogicalHeight); |
| } |
| |
| LayoutUnit nextLogicalTopForUnbreakableContent( |
| LayoutUnit flowThreadOffset, |
| LayoutUnit contentLogicalHeight) const; |
| |
| LayoutFlowThread* flowThread() const { return m_flowThread; } |
| |
| LayoutBlockFlow* multiColumnBlockFlow() const { |
| return toLayoutBlockFlow(parent()); |
| } |
| LayoutMultiColumnFlowThread* multiColumnFlowThread() const { |
| return toLayoutMultiColumnFlowThread(flowThread()); |
| } |
| |
| LayoutMultiColumnSet* nextSiblingMultiColumnSet() const; |
| LayoutMultiColumnSet* previousSiblingMultiColumnSet() const; |
| |
| // Return true if we have a fragmentainer group that can hold a column at the |
| // specified flow thread block offset. |
| bool hasFragmentainerGroupForColumnAt(LayoutUnit bottomOffsetInFlowThread, |
| PageBoundaryRule) const; |
| |
| MultiColumnFragmentainerGroup& appendNewFragmentainerGroup(); |
| |
| // Logical top relative to the content edge of the multicol container. |
| LayoutUnit logicalTopFromMulticolContentEdge() const; |
| |
| LayoutUnit logicalTopInFlowThread() const; |
| LayoutUnit logicalBottomInFlowThread() const; |
| LayoutUnit logicalHeightInFlowThread() const { |
| return logicalBottomInFlowThread() - logicalTopInFlowThread(); |
| } |
| |
| // Return the amount of flow thread contents that the specified fragmentainer |
| // group can hold without overflowing. |
| LayoutUnit fragmentainerGroupCapacity( |
| const MultiColumnFragmentainerGroup& group) const { |
| return group.logicalHeight() * usedColumnCount(); |
| } |
| |
| LayoutRect flowThreadPortionRect() const; |
| LayoutRect flowThreadPortionOverflowRect() const; |
| LayoutRect overflowRectForFlowThreadPortion( |
| const LayoutRect& flowThreadPortionRect, |
| bool isFirstPortion, |
| bool isLastPortion) const; |
| |
| // The used CSS value of column-count, i.e. how many columns there are room |
| // for without overflowing. |
| unsigned usedColumnCount() const { |
| return multiColumnFlowThread()->columnCount(); |
| } |
| |
| bool heightIsAuto() const; |
| |
| // Find the column that contains the given block offset, and return the |
| // translation needed to get from flow thread coordinates to visual |
| // coordinates. |
| LayoutSize flowThreadTranslationAtOffset(LayoutUnit, |
| PageBoundaryRule, |
| CoordinateSpaceConversion) const; |
| |
| LayoutPoint visualPointToFlowThreadPoint( |
| const LayoutPoint& visualPoint) const; |
| |
| // (Re-)calculate the column height if it's auto. This is first and foremost |
| // needed by sets that are to balance the column height, but even when it |
| // isn't to be balanced, this is necessary if the multicol container's height |
| // is constrained. |
| bool recalculateColumnHeight(); |
| |
| // Reset previously calculated column height. Will mark for layout if needed. |
| void resetColumnHeight(); |
| |
| void storeOldPosition() { m_oldLogicalTop = logicalTop(); } |
| bool isInitialHeightCalculated() const { return m_initialHeightCalculated; } |
| |
| // Layout of flow thread content that's to be rendered inside this column set |
| // begins. This happens at the beginning of flow thread layout, and when |
| // advancing from a previous column set or spanner to this one. |
| void beginFlow(LayoutUnit offsetInFlowThread); |
| |
| // Layout of flow thread content that was to be rendered inside this column |
| // set has finished. This happens at end of flow thread layout, and when |
| // advancing to the next column set or spanner. |
| void endFlow(LayoutUnit offsetInFlowThread); |
| |
| void styleDidChange(StyleDifference, const ComputedStyle* oldStyle) override; |
| void layout() override; |
| |
| void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, |
| LayoutUnit& maxLogicalWidth) const final; |
| |
| void attachToFlowThread(); |
| void detachFromFlowThread(); |
| |
| // The top of the page nearest to the specified block offset. All in |
| // flowthread coordinates. |
| LayoutUnit pageLogicalTopForOffset(LayoutUnit offset) const; |
| |
| LayoutRect fragmentsBoundingBox( |
| const LayoutRect& boundingBoxInFlowThread) const; |
| |
| LayoutUnit columnGap() const; |
| |
| // The "CSS actual" value of column-count. This includes overflowing columns, |
| // if any. |
| unsigned actualColumnCount() const; |
| |
| const char* name() const override { return "LayoutMultiColumnSet"; } |
| |
| // Sets |columnRuleBounds| to the bounds of each column rule rect's painted |
| // extent, adjusted by paint offset, before pixel snapping. Returns true if |
| // column rules should be painted at all. |
| bool computeColumnRuleBounds(const LayoutPoint& paintOffset, |
| Vector<LayoutRect>& columnRuleBounds) const; |
| |
| LayoutRect localOverflowRectForPaintInvalidation() const override; |
| |
| protected: |
| LayoutMultiColumnSet(LayoutFlowThread*); |
| |
| private: |
| void insertedIntoTree() final; |
| void willBeRemovedFromTree() final; |
| |
| bool isSelfCollapsingBlock() const override { return false; } |
| |
| void computeLogicalHeight(LayoutUnit logicalHeight, |
| LayoutUnit logicalTop, |
| LogicalExtentComputedValues&) const override; |
| PositionWithAffinity positionForPoint(const LayoutPoint&) override; |
| |
| void paintObject(const PaintInfo&, |
| const LayoutPoint& paintOffset) const override; |
| |
| void addOverflowFromChildren() override; |
| |
| MultiColumnFragmentainerGroupList m_fragmentainerGroups; |
| LayoutFlowThread* m_flowThread; |
| |
| // Height of the tallest piece of unbreakable content. This is the minimum |
| // column logical height required to avoid fragmentation where it shouldn't |
| // occur (inside unbreakable content, between orphans and widows, etc.). |
| // We only store this so that outer fragmentation contexts (if any) can query |
| // this when calculating their own minimum. Note that we don't store this |
| // value in every fragmentainer group (but rather here, in the column set), |
| // since we only need the largest one among them. |
| LayoutUnit m_tallestUnbreakableLogicalHeight; |
| |
| // Logical top in previous layout pass. |
| LayoutUnit m_oldLogicalTop; |
| |
| bool m_initialHeightCalculated; |
| }; |
| |
| DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutMultiColumnSet, isLayoutMultiColumnSet()); |
| |
| } // namespace blink |
| |
| #endif // LayoutMultiColumnSet_h |