| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/bubble/simple_grid_layout.h" |
| |
| #include "ui/views/view.h" |
| |
| namespace ash { |
| |
| SimpleGridLayout::SimpleGridLayout(int column_count, |
| std::optional<int> column_spacing, |
| std::optional<int> row_spacing) |
| : column_count_(column_count), |
| column_spacing_(column_spacing), |
| row_spacing_(row_spacing) {} |
| |
| SimpleGridLayout::~SimpleGridLayout() = default; |
| |
| views::ProposedLayout SimpleGridLayout::CalculateProposedLayout( |
| const views::SizeBounds& size_bounds) const { |
| views::ProposedLayout proposed_layout; |
| |
| auto calculate_spacing = [](const views::SizeBound& bound, int child_size, |
| int child_count) { |
| if (!bound.is_bounded()) { |
| return 0; |
| } |
| return std::max( |
| 0, (bound.value() - child_size * child_count) / (child_count - 1)); |
| }; |
| |
| const gfx::Size child_size = GetChildPreferredSize(); |
| |
| const int column_spacing = column_spacing_.value_or(calculate_spacing( |
| size_bounds.width(), child_size.width(), column_count_)); |
| |
| const int row_spacing = row_spacing_.value_or(0); |
| |
| proposed_layout.host_size = |
| CalculatePreferredSize(column_spacing, row_spacing); |
| |
| int row = 0; |
| int col = 0; |
| for (views::View* child : host_view()->children()) { |
| if (!IsChildIncludedInLayout(child)) |
| continue; |
| |
| int x = col * (column_spacing + child_size.width()); |
| int y = row * (row_spacing + child_size.height()); |
| proposed_layout.child_layouts.push_back(views::ChildLayout{ |
| child, |
| child->GetVisible(), |
| gfx::Rect(x, y, child_size.width(), child_size.height()), |
| {child_size.width(), child_size.height()}}); |
| |
| ++col; |
| if (col % column_count_ == 0) { |
| ++row; |
| col = 0; |
| } |
| } |
| return proposed_layout; |
| } |
| |
| void SimpleGridLayout::OnLayoutChanged() { |
| cached_child_preferred_size_.reset(); |
| LayoutManagerBase::OnLayoutChanged(); |
| } |
| |
| gfx::Size SimpleGridLayout::GetPreferredSize( |
| const views::View* host, |
| const views::SizeBounds& available_bounds) const { |
| // If column spacing is forced, the preferred layout size does not depend on |
| // the `available_bounds`. Using `GetPreferredSize(const view::View*)` will |
| // avoid some calculation, as the base class caches preferred size calculation |
| // results. |
| if (column_spacing_) { |
| return views::LayoutManagerBase::GetPreferredSize(host); |
| } |
| return views::LayoutManagerBase::GetPreferredSize(host, available_bounds); |
| } |
| |
| gfx::Size SimpleGridLayout::GetChildPreferredSize() const { |
| if (cached_child_preferred_size_) |
| return *cached_child_preferred_size_; |
| |
| if (!host_view()->children().size()) |
| return gfx::Size(); |
| |
| for (views::View* child : host_view()->children()) { |
| if (IsChildIncludedInLayout(child)) { |
| cached_child_preferred_size_ = child->GetPreferredSize(); |
| return *cached_child_preferred_size_; |
| } |
| } |
| |
| return gfx::Size(); |
| } |
| |
| gfx::Size SimpleGridLayout::CalculatePreferredSize(int column_spacing, |
| int row_spacing) const { |
| int total_children = 0; |
| for (views::View* child : host_view()->children()) { |
| if (IsChildIncludedInLayout(child)) |
| ++total_children; |
| } |
| // Equivalent to `ceil(children().size() / column_count_)`. |
| const int number_of_rows = |
| (total_children + column_count_ - 1) / column_count_; |
| |
| if (!number_of_rows) |
| return gfx::Size(); |
| |
| // `SimpleGridLayout` assumes all children have identical sizes. |
| const int child_height = GetChildPreferredSize().height(); |
| const int child_width = GetChildPreferredSize().width(); |
| |
| const int height = |
| (number_of_rows * (row_spacing + child_height)) - row_spacing; |
| const int width = |
| (column_count_ * (child_width + column_spacing)) - column_spacing; |
| |
| return gfx::Size(width, height); |
| } |
| |
| } // namespace ash |