blob: b55ed5a14e5929b9d36023fa7bed6a895213ff4c [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MultiColumnFragmentainerGroup_h
#define MultiColumnFragmentainerGroup_h
#include "core/layout/LayoutMultiColumnFlowThread.h"
#include "platform/wtf/Allocator.h"
namespace blink {
// A group of columns, that are laid out in the inline progression direction,
// all with the same column height.
//
// When a multicol container is inside another fragmentation context, and said
// multicol container lives in multiple outer fragmentainers (pages / columns),
// we need to put these inner columns into separate groups, with one group per
// outer fragmentainer. Such a group of columns is what comprises a "row of
// column boxes" in spec lingo.
//
// Column balancing, when enabled, takes place within a column fragmentainer
// group.
//
// Each fragmentainer group may have its own actual column count (if there are
// unused columns because of forced breaks, for example). If there are multiple
// fragmentainer groups, the actual column count must not exceed the used column
// count (the one calculated based on column-count and column-width from CSS),
// or they'd overflow the outer fragmentainer in the inline direction. If we
// need more columns than what a group has room for, we'll create another group
// and put them there (and make them appear in the next outer fragmentainer).
class MultiColumnFragmentainerGroup {
DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
public:
MultiColumnFragmentainerGroup(const LayoutMultiColumnSet&);
const LayoutMultiColumnSet& ColumnSet() const { return column_set_; }
bool IsFirstGroup() const;
bool IsLastGroup() const;
// Position within the LayoutMultiColumnSet.
LayoutUnit LogicalTop() const { return logical_top_; }
void SetLogicalTop(LayoutUnit logical_top) { logical_top_ = logical_top; }
// Return the amount of block space that this fragmentainer group takes up in
// its containing LayoutMultiColumnSet.
LayoutUnit GroupLogicalHeight() const {
DCHECK(IsLogicalHeightKnown());
return logical_height_;
}
// Return the block size of a column (or fragmentainer) in this fragmentainer
// group. The spec says that this value must always be >= 1px, to ensure
// progress.
LayoutUnit ColumnLogicalHeight() const {
DCHECK(IsLogicalHeightKnown());
return std::max(LayoutUnit(1), logical_height_);
}
// Return whether we have some column height to work with. This doesn't have
// to be the final height. It will only return false in the first layout pass,
// and even then only if column height is auto and there's no way to even make
// a guess (i.e. when there are no usable constraints).
bool IsLogicalHeightKnown() const { return is_logical_height_known_; }
LayoutSize OffsetFromColumnSet() const;
// Return the block offset from the enclosing fragmentation context, if
// nested. In the coordinate space of the enclosing fragmentation context.
LayoutUnit BlockOffsetInEnclosingFragmentationContext() const;
// The top of our flow thread portion
LayoutUnit LogicalTopInFlowThread() const {
return logical_top_in_flow_thread_;
}
void SetLogicalTopInFlowThread(LayoutUnit logical_top_in_flow_thread) {
logical_top_in_flow_thread_ = logical_top_in_flow_thread;
}
// The bottom of our flow thread portion
LayoutUnit LogicalBottomInFlowThread() const {
return logical_bottom_in_flow_thread_;
}
void SetLogicalBottomInFlowThread(LayoutUnit logical_bottom_in_flow_thread) {
logical_bottom_in_flow_thread_ = logical_bottom_in_flow_thread;
}
// The height of the flow thread portion for the entire fragmentainer group.
LayoutUnit LogicalHeightInFlowThread() const {
// Due to negative margins, logical bottom may actually end up above logical
// top, but we never want to return negative logical heights.
return (logical_bottom_in_flow_thread_ - logical_top_in_flow_thread_)
.ClampNegativeToZero();
}
// The height of the flow thread portion for the specified fragmentainer.
// The last fragmentainer may not be using all available space.
LayoutUnit LogicalHeightInFlowThreadAt(unsigned column_index) const;
void ResetColumnHeight();
bool RecalculateColumnHeight(LayoutMultiColumnSet&);
LayoutSize FlowThreadTranslationAtOffset(LayoutUnit,
LayoutBox::PageBoundaryRule,
CoordinateSpaceConversion) const;
LayoutUnit ColumnLogicalTopForOffset(LayoutUnit offset_in_flow_thread) const;
// If SnapToColumnPolicy is SnapToColumn, visualPointToFlowThreadPoint() won't
// return points that lie outside the bounds of the columns: Before converting
// to a flow thread position, if the block direction coordinate is outside the
// column, snap to the bounds of the column, and reset the inline direction
// coordinate to the start position in the column. The effect of this is that
// if the block position is before the column rectangle, we'll get to the
// beginning of this column, while if the block position is after the column
// rectangle, we'll get to the beginning of the next column. This is behavior
// that positionForPoint() depends on.
enum SnapToColumnPolicy { kDontSnapToColumn, kSnapToColumn };
LayoutPoint VisualPointToFlowThreadPoint(
const LayoutPoint& visual_point,
SnapToColumnPolicy = kDontSnapToColumn) const;
LayoutRect FragmentsBoundingBox(
const LayoutRect& bounding_box_in_flow_thread) const;
LayoutRect FlowThreadPortionRectAt(unsigned column_index) const;
enum ClipRectAxesSelector {
// Only limit the clip rectangle in the block direction. Leave inline
// position and length at infinity. Certain operations require this. Those
// operations would typically ideally want no clipping at all, but in our
// implementation we have to clip in the block direction, in order to slice
// the flow thread properly into columns.
kBlockDirectionAxis,
// Limit the clip rectangle along both axes. This is what to use for
// painting and hit testing.
kBothAxes
};
LayoutRect FlowThreadPortionOverflowRectAt(
unsigned column_index,
ClipRectAxesSelector = kBothAxes) const;
// Get the first and the last column intersecting the specified block range.
// Note that |logicalBottomInFlowThread| is an exclusive endpoint.
void ColumnIntervalForBlockRangeInFlowThread(
LayoutUnit logical_top_in_flow_thread,
LayoutUnit logical_bottom_in_flow_thread,
unsigned& first_column,
unsigned& last_column) const;
// Get the first and the last column intersecting the specified visual
// rectangle.
void ColumnIntervalForVisualRect(const LayoutRect&,
unsigned& first_column,
unsigned& last_column) const;
LayoutRect CalculateOverflow() const;
unsigned ColumnIndexAtOffset(LayoutUnit offset_in_flow_thread,
LayoutBox::PageBoundaryRule) const;
// The "CSS actual" value of column-count. This includes overflowing columns,
// if any.
// Returns 1 or greater, never 0.
unsigned ActualColumnCount() const;
void UpdateFromNG(LayoutUnit logical_height);
private:
LayoutUnit HeightAdjustedForRowOffset(LayoutUnit height) const;
LayoutUnit CalculateMaxColumnHeight() const;
void SetAndConstrainColumnHeight(LayoutUnit);
LayoutUnit RebalanceColumnHeightIfNeeded() const;
LayoutRect ColumnRectAt(unsigned column_index) const;
LayoutUnit LogicalTopInFlowThreadAt(unsigned column_index) const {
return logical_top_in_flow_thread_ + column_index * ColumnLogicalHeight();
}
// Return the column that the specified visual point belongs to. Only the
// coordinate on the column progression axis is relevant. Every point belongs
// to a column, even if said point is not inside any of the columns.
unsigned ColumnIndexAtVisualPoint(const LayoutPoint& visual_point) const;
const LayoutMultiColumnSet& column_set_;
LayoutUnit logical_top_;
LayoutUnit logical_top_in_flow_thread_;
LayoutUnit logical_bottom_in_flow_thread_;
// Logical height of the group. This will also be the height of each column
// in this group, with the difference that, while the logical height can be
// 0, the height of a column must be >= 1px.
LayoutUnit logical_height_;
// Maximum logical height allowed.
LayoutUnit max_logical_height_;
bool is_logical_height_known_ = false;
};
// List of all fragmentainer groups within a column set. There will always be at
// least one group. Deleting the one group is not allowed (or possible). There
// will be more than one group if the owning column set lives in multiple outer
// fragmentainers (e.g. multicol inside paged media).
class CORE_EXPORT MultiColumnFragmentainerGroupList {
DISALLOW_NEW();
public:
MultiColumnFragmentainerGroupList(LayoutMultiColumnSet&);
~MultiColumnFragmentainerGroupList();
// Add an additional fragmentainer group to the end of the list, and return
// it.
MultiColumnFragmentainerGroup& AddExtraGroup();
// Remove all fragmentainer groups but the first one.
void DeleteExtraGroups();
MultiColumnFragmentainerGroup& First() { return groups_.front(); }
const MultiColumnFragmentainerGroup& First() const { return groups_.front(); }
MultiColumnFragmentainerGroup& Last() { return groups_.back(); }
const MultiColumnFragmentainerGroup& Last() const { return groups_.back(); }
typedef Vector<MultiColumnFragmentainerGroup, 1>::iterator iterator;
typedef Vector<MultiColumnFragmentainerGroup, 1>::const_iterator
const_iterator;
iterator begin() { return groups_.begin(); }
const_iterator begin() const { return groups_.begin(); }
iterator end() { return groups_.end(); }
const_iterator end() const { return groups_.end(); }
size_t size() const { return groups_.size(); }
MultiColumnFragmentainerGroup& operator[](size_t i) { return groups_.at(i); }
const MultiColumnFragmentainerGroup& operator[](size_t i) const {
return groups_.at(i);
}
void Append(const MultiColumnFragmentainerGroup& group) {
groups_.push_back(group);
}
void Shrink(size_t size) { groups_.Shrink(size); }
private:
LayoutMultiColumnSet& column_set_;
Vector<MultiColumnFragmentainerGroup, 1> groups_;
};
} // namespace blink
#endif // MultiColumnFragmentainerGroup_h