blob: 8d52bf4d83183de7059c5cafca02c1a0eed2e96c [file] [log] [blame]
// Copyright 2020 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 THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_TRACK_COLLECTION_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_TRACK_COLLECTION_H_
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
// NGGridTrackCollectionBase provides an implementation for some shared
// functionality on track range collections, specifically binary search on
// the collection to get a range index given a track number.
class CORE_EXPORT NGGridTrackCollectionBase {
public:
static constexpr wtf_size_t kInvalidRangeIndex = kNotFound;
class CORE_EXPORT RangeRepeatIterator {
public:
RangeRepeatIterator(const NGGridTrackCollectionBase* collection,
wtf_size_t range_index);
bool IsAtEnd() const;
// Moves iterator to next range, skipping over repeats in a range. Return
// true if the move was successful.
bool MoveToNextRange();
wtf_size_t RepeatCount() const;
// Returns the index of this range in the collection.
wtf_size_t RangeIndex() const;
// Returns the track number for the start of the range.
wtf_size_t RangeTrackStart() const;
// Returns the track number at the end of the range.
wtf_size_t RangeTrackEnd() const;
bool IsRangeCollapsed() const;
private:
bool SetRangeIndex(wtf_size_t range_index);
const NGGridTrackCollectionBase* collection_;
wtf_size_t range_index_;
wtf_size_t range_count_;
// First track number of a range.
wtf_size_t range_track_start_;
// Count of repeated tracks in a range.
wtf_size_t range_track_count_;
};
// Gets the range index for the range that contains the given track number.
wtf_size_t RangeIndexFromTrackNumber(wtf_size_t track_number) const;
RangeRepeatIterator RangeIterator() const;
String ToString() const;
protected:
// Returns the first track number of a range.
virtual wtf_size_t RangeTrackNumber(wtf_size_t range_index) const = 0;
// Returns the number of tracks in a range.
virtual wtf_size_t RangeTrackCount(wtf_size_t range_index) const = 0;
// Returns true if the range at the given index is collapsed.
virtual bool IsRangeCollapsed(wtf_size_t range_index) const = 0;
// Returns the number of track ranges in the collection.
virtual wtf_size_t RangeCount() const = 0;
};
struct CORE_EXPORT TrackSpanProperties {
public:
enum PropertyId : unsigned {
kNone = 0,
kHasIntrinsicTrack = 1 << 0,
kHasFlexibleTrack = 1 << 1,
kHasAutoMinimumTrack = 1 << 2,
kIsCollapsed = 1 << 3,
kIsImplicit = 1 << 4
};
inline bool HasProperty(PropertyId id) const { return bitmask_ & id; }
inline void SetProperty(PropertyId id) { bitmask_ |= id; }
private:
uint8_t bitmask_{kNone};
};
class CORE_EXPORT NGGridBlockTrackCollection
: public NGGridTrackCollectionBase {
public:
struct Range {
bool IsImplicit() const;
bool IsCollapsed() const;
void SetIsImplicit();
void SetIsCollapsed();
wtf_size_t starting_track_number;
wtf_size_t track_count;
wtf_size_t repeater_index;
wtf_size_t repeater_offset;
TrackSpanProperties properties;
};
explicit NGGridBlockTrackCollection(
GridTrackSizingDirection track_direction = kForColumns);
// Sets the specified, implicit tracks, along with a given auto repeat value.
void SetSpecifiedTracks(const NGGridTrackList* explicit_tracks,
const NGGridTrackList* implicit_tracks,
wtf_size_t auto_repeat_count);
// Ensures that after FinalizeRanges is called, a range will start at the
// |track_number|, and a range will end at |track_number| + |span_length|
void EnsureTrackCoverage(wtf_size_t track_number, wtf_size_t span_length);
// Build the collection of ranges based on information provided by
// SetSpecifiedTracks and EnsureTrackCoverage.
void FinalizeRanges();
bool IsRangeImplicit(wtf_size_t range_index) const;
const Range& RangeAtRangeIndex(wtf_size_t range_index) const;
GridTrackSizingDirection Direction() const { return direction_; }
bool IsForColumns() const { return direction_ == kForColumns; }
const NGGridTrackList& ExplicitTracks() const;
const NGGridTrackList& ImplicitTracks() const;
String ToString() const;
protected:
// NGGridTrackCollectionBase overrides.
wtf_size_t RangeTrackNumber(wtf_size_t range_index) const override;
wtf_size_t RangeTrackCount(wtf_size_t range_index) const override;
bool IsRangeCollapsed(wtf_size_t range_index) const override;
wtf_size_t RangeCount() const override;
private:
// Returns true if this collection had implicit tracks provided.
bool HasImplicitTracks() const;
// Returns the repeat size of the implicit tracks.
wtf_size_t ImplicitRepeatSize() const;
bool track_indices_need_sort_ = false;
GridTrackSizingDirection direction_;
wtf_size_t auto_repeat_count_ = 0;
// Stores the specified and implicit tracks specified by SetSpecifiedTracks.
const NGGridTrackList* explicit_tracks_;
const NGGridTrackList* implicit_tracks_;
// Starting and ending tracks mark where ranges will start and end.
// Once the ranges have been built in FinalizeRanges, these are cleared.
Vector<wtf_size_t> starting_tracks_;
Vector<wtf_size_t> ending_tracks_;
Vector<Range> ranges_;
};
// |NGGridBlockTrackCollection::EnsureTrackCoverage| may introduce a range start
// and/or end at the middle of any repeater from the block collection. This will
// affect how some repeated tracks within the same repeater group resolve their
// track sizes; e.g. consider the track list 'repeat(10, auto)' with a grid item
// spanning from the 3rd to the 7th track in the repeater, every track within
// the item's range will grow to fit the content of that item first.
//
// For the track sizing algorithm we want to have separate data (e.g. base size,
// growth limit, etc.) between tracks in different ranges; instead of trivially
// expanding the repeaters, which will limit our implementation to support
// relatively small track counts, we introduce the concept of a "set".
//
// A "set" is a collection of distinct track definitions that compose a range in
// |NGGridLayoutAlgorithmTrackCollection|; each set element stores the number of
// tracks within the range that share its definition. The |NGGridSet| class
// represents a single element from a set.
//
// As an example, consider the following grid definition:
// - 'grid-template-columns: repeat(4, 5px 1fr)'
// - Grid item 1 with 'grid-column: 1 / span 5'
// - Grid item 2 with 'grid-column: 2 / span 1'
// - Grid item 3 with 'grid-column: 6 / span 8'
//
// Expanding the track definitions above we would look at the explicit grid:
// | 5px | 1fr | 5px | 1fr | 5px | 1fr | 5px | 1fr |
//
// This example would produce the following ranges and their respective sets:
// Range 1: [1-1], Set 1: { 5px (1) }
// Range 2: [2-2], Set 2: { 1fr (1) }
// Range 3: [3-5], Set 3: { 5px (2) , 1fr (1) }
// Range 4: [6-8], Set 4: { 1fr (2) , 5px (1) }
// Range 5: [9-13], Set 5: { auto (5) }
//
// Note that, since |NGGridBlockTrackCollection|'s ranges are assured to span a
// single repeater and to not cross any grid item's boundary in the respective
// dimension, tracks within a set are "commutative" and can be sized evenly.
class CORE_EXPORT NGGridSet {
public:
NGGridSet(wtf_size_t track_count, bool is_collapsed);
// |is_content_box_size_indefinite| is used to normalize percentage track
// sizing functions; from https://drafts.csswg.org/css-grid-1/#track-sizes:
// "If the size of the grid container depends on the size of its tracks,
// then the <percentage> must be treated as 'auto'".
NGGridSet(wtf_size_t track_count,
const GridTrackSize& track_size,
bool is_content_box_size_indefinite);
wtf_size_t TrackCount() const { return track_count_; }
const GridTrackSize& TrackSize() const { return track_size_; }
LayoutUnit BaseSize() const;
LayoutUnit GrowthLimit() const;
LayoutUnit PlannedIncrease() const { return planned_increase_; }
LayoutUnit FitContentLimit() const { return fit_content_limit_; }
LayoutUnit ItemIncurredIncrease() const { return item_incurred_increase_; }
bool IsInfinitelyGrowable() const { return is_infinitely_growable_; }
void SetBaseSize(LayoutUnit base_size);
void SetGrowthLimit(LayoutUnit growth_limit);
void SetPlannedIncrease(LayoutUnit planned_increase) {
planned_increase_ = planned_increase;
}
void SetItemIncurredIncrease(LayoutUnit item_incurred_increase) {
item_incurred_increase_ = item_incurred_increase;
}
void SetInfinitelyGrowable(bool infinitely_growable) {
is_infinitely_growable_ = infinitely_growable;
}
private:
bool IsGrowthLimitLessThanBaseSize() const;
void EnsureGrowthLimitIsNotLessThanBaseSize();
wtf_size_t track_count_;
GridTrackSize track_size_;
// Fields used by the track sizing algorithm.
LayoutUnit base_size_;
LayoutUnit growth_limit_;
LayoutUnit planned_increase_;
LayoutUnit fit_content_limit_;
LayoutUnit item_incurred_increase_;
bool is_infinitely_growable_ : 1;
};
class CORE_EXPORT NGGridLayoutAlgorithmTrackCollection
: public NGGridTrackCollectionBase {
public:
struct Range {
// Copies fields that are the same as in |GridBlockTrackCollection::Range|.
Range(const NGGridBlockTrackCollection::Range& block_track_range,
wtf_size_t starting_set_index);
bool IsCollapsed() const;
wtf_size_t starting_track_number;
wtf_size_t track_count;
wtf_size_t starting_set_index;
wtf_size_t set_count;
TrackSpanProperties properties;
};
template <bool is_const>
class CORE_EXPORT SetIteratorBase {
public:
using TrackCollectionPtr =
typename std::conditional<is_const,
const NGGridLayoutAlgorithmTrackCollection*,
NGGridLayoutAlgorithmTrackCollection*>::type;
using NGGridSetRef =
typename std::conditional<is_const, const NGGridSet&, NGGridSet&>::type;
SetIteratorBase(TrackCollectionPtr track_collection,
wtf_size_t begin_set_index,
wtf_size_t end_set_index)
: track_collection_(track_collection),
current_set_index_(begin_set_index),
end_set_index_(end_set_index) {
DCHECK(track_collection_);
DCHECK_LE(current_set_index_, end_set_index_);
DCHECK_LE(end_set_index_, track_collection_->SetCount());
}
bool IsAtEnd() const {
DCHECK_LE(current_set_index_, end_set_index_);
return current_set_index_ == end_set_index_;
}
bool MoveToNextSet() {
current_set_index_ = std::min(current_set_index_ + 1, end_set_index_);
return current_set_index_ < end_set_index_;
}
NGGridSetRef CurrentSet() const {
DCHECK_LT(current_set_index_, end_set_index_);
return track_collection_->SetAt(current_set_index_);
}
private:
TrackCollectionPtr track_collection_;
wtf_size_t current_set_index_;
wtf_size_t end_set_index_;
};
using SetIterator = SetIteratorBase<false>;
using ConstSetIterator = SetIteratorBase<true>;
NGGridLayoutAlgorithmTrackCollection() = default;
// |is_content_box_size_indefinite| is used to normalize percentage track
// sizing functions (see the constructor for |NGGridSet|).
NGGridLayoutAlgorithmTrackCollection(
const NGGridBlockTrackCollection& block_track_collection,
bool is_content_box_size_indefinite);
// Returns the number of sets in the collection.
wtf_size_t SetCount() const;
// Returns a reference to the set located at position |set_index|.
NGGridSet& SetAt(wtf_size_t set_index);
const NGGridSet& SetAt(wtf_size_t set_index) const;
// Returns an iterator for all the sets contained in this collection.
SetIterator GetSetIterator();
ConstSetIterator GetSetIterator() const;
// Returns an iterator for every set in this collection's |sets_| located at
// an index in the interval [begin_set_index, end_set_index).
SetIterator GetSetIterator(wtf_size_t begin_set_index,
wtf_size_t end_set_index);
ConstSetIterator GetSetIterator(wtf_size_t begin_set_index,
wtf_size_t end_set_index) const;
wtf_size_t RangeSetCount(wtf_size_t range_index) const;
wtf_size_t RangeStartingSetIndex(wtf_size_t range_index) const;
// Returns true if the specified property has been set in the track span
// properties bitmask of the range at position |range_index|.
bool RangeHasTrackSpanProperty(
wtf_size_t range_index,
TrackSpanProperties::PropertyId property_id) const;
GridTrackSizingDirection Direction() const { return direction_; }
bool IsForColumns() const { return direction_ == kForColumns; }
protected:
// NGGridTrackCollectionBase overrides.
wtf_size_t RangeTrackNumber(wtf_size_t range_index) const override;
wtf_size_t RangeTrackCount(wtf_size_t range_index) const override;
bool IsRangeCollapsed(wtf_size_t range_index) const override;
wtf_size_t RangeCount() const override;
private:
void AppendTrackRange(
const NGGridBlockTrackCollection::Range& block_track_range,
const NGGridTrackList& specified_track_list,
bool is_content_box_size_indefinite);
GridTrackSizingDirection direction_;
Vector<Range> ranges_;
// A vector of every set element that compose the entire collection's ranges;
// track definitions from the same set are stored in consecutive positions,
// preserving the order in which the definitions appear in their range.
Vector<NGGridSet> sets_;
};
} // namespace blink
WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::NGGridTrackRepeater)
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_TRACK_COLLECTION_H_