1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/toolbar/pinned_toolbar_actions_container_layout.h"
#include "base/containers/adapters.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/views/toolbar/pinned_action_toolbar_button.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/views/view_class_properties.h"
views::ProposedLayout
PinnedToolbarActionsContainerLayout::CalculateProposedLayout(
const views::SizeBounds& size_bounds) const {
views::ProposedLayout layout;
const bool size_bounded = size_bounds.is_fully_bounded();
int remaining_width =
size_bounded ? (size_bounds.width().value() - interior_margin_.width())
: 0;
bool has_found_button_forced_visible = false;
int mid_priority_width = 0;
bool divider_space_preallocated = false;
bool divider_included_for_mid_priority = false;
// 1. Allocate space for forced visible views and calculate additional space
// needed for medium priority views.
if (size_bounded) {
for (views::View* child : host_view()->children()) {
if (!IsChildIncludedInLayout(child)) {
continue;
}
const PinnedToolbarActionFlexPriority priority =
static_cast<PinnedToolbarActionFlexPriority>(
child->GetProperty(kToolbarButtonFlexPriorityKey));
gfx::Size preferred_size = child->GetPreferredSize();
preferred_size.Enlarge(child->GetProperty(views::kMarginsKey)->width(),
0);
// Include forced visible views.
if (priority == PinnedToolbarActionFlexPriority::kHigh) {
has_found_button_forced_visible = true;
remaining_width -= preferred_size.width();
}
// Track the total width of items with medium flex priority.
if (views::Button::AsButton(child) &&
priority == PinnedToolbarActionFlexPriority::kMedium) {
mid_priority_width += preferred_size.width();
}
// Include the divider if any previous buttons are forced visible.
if (!views::Button::AsButton(child)) {
if (has_found_button_forced_visible) {
divider_space_preallocated = true;
remaining_width -= preferred_size.width();
} else if (mid_priority_width > 0) {
divider_included_for_mid_priority = true;
mid_priority_width += preferred_size.width();
}
}
}
}
// 2. Check if all medium priority items will fit in the remaining space. If
// so, allocate space for them in the remaining width.
const bool fits_all_medium_priority =
!size_bounded || (remaining_width - mid_priority_width > 0);
divider_space_preallocated =
divider_space_preallocated ||
(fits_all_medium_priority && divider_included_for_mid_priority);
if (size_bounded && fits_all_medium_priority) {
remaining_width -= mid_priority_width;
}
// 3. Allocate space for remaining views, and get their indices. The divider
// should only be included if buttons after it should be included.
std::vector<size_t> visible_child_indices;
size_t index = host_view()->children().size() - 1;
size_t divider_index = 0;
int divider_width = 0;
for (const auto& i : base::Reversed(host_view()->children())) {
if (!IsChildIncludedInLayout(i)) {
index--;
continue;
}
// If the next child is the divider, skip it. It only is included in the
// layout if one of the following children is visible.
if (!views::Button::AsButton(i)) {
divider_index = index;
divider_width = i->GetPreferredSize().width() +
i->GetProperty(views::kMarginsKey)->width();
index--;
continue;
}
// Get the preferred size and include the divider width if this view is
// causing the divider to need to be shown.
const int margin_width = i->GetProperty(views::kMarginsKey)->width();
gfx::Size preferred_size = i->GetPreferredSize();
preferred_size.Enlarge(margin_width, 0);
if (divider_index > index && !divider_space_preallocated) {
preferred_size.Enlarge(divider_width, 0);
}
const PinnedToolbarActionFlexPriority priority =
static_cast<PinnedToolbarActionFlexPriority>(
i->GetProperty(kToolbarButtonFlexPriorityKey));
const bool has_preallocated_space_in_layout =
priority == PinnedToolbarActionFlexPriority::kHigh ||
(fits_all_medium_priority &&
priority == PinnedToolbarActionFlexPriority::kMedium);
const bool fits_in_remaining_space =
remaining_width >= preferred_size.width();
const bool should_take_remaining_space =
fits_all_medium_priority ||
priority == PinnedToolbarActionFlexPriority::kMedium;
// Determine whether the child should be visible in the layout and add it to
// |visible_child_indices| if so.
if (has_preallocated_space_in_layout ||
(!size_bounded ||
(fits_in_remaining_space && should_take_remaining_space))) {
// Include the divider if necessary.
if (divider_index != 0) {
visible_child_indices.push_back(divider_index);
divider_index = 0;
}
if (has_preallocated_space_in_layout) {
visible_child_indices.push_back(index);
} else {
visible_child_indices.push_back(index);
if (size_bounded) {
remaining_width -= preferred_size.width();
}
}
}
index--;
}
// 4. Create layout based on prior calculations.
int total_width = interior_margin_.width();
int left = interior_margin_.left();
index = 0;
const int height = GetLayoutConstant(LayoutConstant::TOOLBAR_BUTTON_HEIGHT);
for (auto view : host_view()->children()) {
if (visible_child_indices.size() && index == visible_child_indices.back()) {
const gfx::Size preferred_size = view->GetPreferredSize();
const int y = (height - preferred_size.height()) / 2;
left += view->GetProperty(views::kMarginsKey)->left();
layout.child_layouts.emplace_back(views::ChildLayout{
view, true,
gfx::Rect(left, y, preferred_size.width(), preferred_size.height())});
left += preferred_size.width() +
view->GetProperty(views::kMarginsKey)->right();
total_width += preferred_size.width() +
view->GetProperty(views::kMarginsKey)->width();
visible_child_indices.pop_back();
} else {
layout.child_layouts.emplace_back(views::ChildLayout{view, false});
}
index++;
}
layout.host_size = gfx::Size(total_width, height);
return layout;
}
void PinnedToolbarActionsContainerLayout::SetInteriorMargin(
const gfx::Insets& interior_margin) {
if (interior_margin_ != interior_margin) {
interior_margin_ = interior_margin;
InvalidateHost(true);
}
}
|