blob: cc91bcb9a9bea0f6fda6c7dde91bf63323f0d436 [file] [log] [blame]
// Copyright 2019 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/tabs/tab_group_views.h"
#include <tuple>
#include <utility>
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_style.h"
#include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
#include "chrome/browser/ui/views/tabs/tab_group_header.h"
#include "chrome/browser/ui/views/tabs/tab_group_highlight.h"
#include "chrome/browser/ui/views/tabs/tab_group_underline.h"
#include "chrome/browser/ui/views/tabs/tab_slot_controller.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
#include "chrome/browser/ui/views/tabs/tab_strip_types.h"
#include "components/tab_groups/tab_group_color.h"
#include "components/tab_groups/tab_group_id.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/views/view_utils.h"
TabGroupViews::TabGroupViews(views::View* container_view,
views::View* drag_container_view,
TabSlotController& tab_slot_controller,
const tab_groups::TabGroupId& group)
: tab_slot_controller_(tab_slot_controller), group_(group) {
header_ = container_view->AddChildView(
std::make_unique<TabGroupHeader>(*tab_slot_controller_, group_));
underline_ = container_view->AddChildView(
std::make_unique<TabGroupUnderline>(this, group_));
drag_underline_ = drag_container_view->AddChildView(
std::make_unique<TabGroupUnderline>(this, group_));
highlight_ = drag_container_view->AddChildView(
std::make_unique<TabGroupHighlight>(this, group_));
highlight_->SetVisible(false);
}
TabGroupViews::~TabGroupViews() {
header_->parent()->RemoveChildViewT(std::exchange(header_, nullptr));
underline_->parent()->RemoveChildViewT(std::exchange(underline_, nullptr));
drag_underline_->parent()->RemoveChildViewT(
std::exchange(drag_underline_, nullptr));
highlight_->parent()->RemoveChildViewT(std::exchange(highlight_, nullptr));
}
void TabGroupViews::UpdateBounds() {
// If we're tearing down we should ignore events.
if (InTearDown())
return;
auto [leading_group_view, trailing_group_view] =
GetLeadingTrailingGroupViews();
underline_->UpdateBounds(leading_group_view, trailing_group_view);
auto [leading_dragged_group_view, trailing_dragged_group_view] =
GetLeadingTrailingDraggedGroupViews();
drag_underline_->UpdateBounds(leading_dragged_group_view,
trailing_dragged_group_view);
if (underline_->GetVisible() && drag_underline_->GetVisible()) {
// If we are painting `drag_underline_` on top of `underline_`, we may need
// to extend `drag_underline_`'s bounds so the two merge seamlessly.
// Otherwise, `underline_` may be partially occluded by the dragged views.
gfx::RectF underline_bounds_in_drag_coords_f(underline_->GetLocalBounds());
views::View::ConvertRectToTarget(underline_, drag_underline_->parent(),
&underline_bounds_in_drag_coords_f);
gfx::Rect underline_bounds_in_drag_coords =
ToEnclosingRect(underline_bounds_in_drag_coords_f);
// Try to match `underline_`, but don't shrink, and don't expand beyond the
// dragged views unless already beyond them.
int leading_x =
std::clamp(underline_bounds_in_drag_coords.x(),
std::min(drag_underline_->bounds().x(),
leading_dragged_group_view->bounds().x()),
drag_underline_->bounds().x());
int trailing_x =
std::clamp(underline_bounds_in_drag_coords.right(),
drag_underline_->bounds().right(),
std::max(drag_underline_->bounds().right(),
trailing_dragged_group_view->bounds().right()));
drag_underline_->SetBounds(leading_x, drag_underline_->bounds().y(),
trailing_x - leading_x,
drag_underline_->bounds().height());
}
highlight_->UpdateBounds(leading_dragged_group_view,
trailing_dragged_group_view);
}
void TabGroupViews::OnGroupVisualsChanged() {
// If we're tearing down we should ignore events. (We can still receive them
// here if the editor bubble was open when the tab group was closed.)
if (InTearDown())
return;
header_->VisualsChanged();
underline_->SchedulePaint();
drag_underline_->SchedulePaint();
}
gfx::Rect TabGroupViews::GetBounds() const {
auto [leading_group_view, trailing_group_view] =
GetLeadingTrailingGroupViews();
gfx::RectF leading_bounds = gfx::RectF(leading_group_view->GetLocalBounds());
views::View::ConvertRectToTarget(leading_group_view, underline_->parent(),
&leading_bounds);
gfx::RectF trailing_bounds =
gfx::RectF(trailing_group_view->GetLocalBounds());
views::View::ConvertRectToTarget(trailing_group_view, underline_->parent(),
&trailing_bounds);
gfx::Rect bounds = gfx::ToEnclosingRect(leading_bounds);
bounds.UnionEvenIfEmpty(gfx::ToEnclosingRect(trailing_bounds));
return bounds;
}
SkColor TabGroupViews::GetGroupColor() const {
return tab_slot_controller_->GetPaintedGroupColor(
tab_slot_controller_->GetGroupColorId(group_));
}
SkColor TabGroupViews::GetTabBackgroundColor() const {
return tab_slot_controller_->GetTabBackgroundColor(
TabActive::kInactive, BrowserFrameActiveState::kUseCurrent);
}
SkColor TabGroupViews::GetGroupBackgroundColor() const {
const SkColor active_color = tab_slot_controller_->GetTabBackgroundColor(
TabActive::kActive, BrowserFrameActiveState::kUseCurrent);
return SkColorSetA(active_color, gfx::Tween::IntValueBetween(
TabStyle::kSelectedTabOpacity,
SK_AlphaTRANSPARENT, SK_AlphaOPAQUE));
}
bool TabGroupViews::InTearDown() const {
return !header_ || !header_->GetWidget() || !drag_underline_->GetWidget();
}
std::tuple<views::View*, views::View*>
TabGroupViews::GetLeadingTrailingGroupViews() const {
std::vector<views::View*> children = underline_->parent()->children();
std::vector<views::View*> dragged_children =
drag_underline_->parent()->children();
children.insert(children.end(), dragged_children.begin(),
dragged_children.end());
return GetLeadingTrailingGroupViews(children);
}
std::tuple<views::View*, views::View*>
TabGroupViews::GetLeadingTrailingDraggedGroupViews() const {
return GetLeadingTrailingGroupViews(drag_underline_->parent()->children());
}
std::tuple<views::View*, views::View*>
TabGroupViews::GetLeadingTrailingGroupViews(
std::vector<views::View*> children) const {
// Elements of |children| may be in different coordinate spaces. Canonicalize
// to widget space for comparison, since they will be in the same widget.
views::View* leading_child = nullptr;
gfx::Rect leading_child_widget_bounds;
views::View* trailing_child = nullptr;
gfx::Rect trailing_child_widget_bounds;
for (views::View* child : children) {
TabSlotView* tab_slot_view = views::AsViewClass<TabSlotView>(child);
if (!tab_slot_view || tab_slot_view->group() != group_ ||
!tab_slot_view->GetVisible())
continue;
gfx::Rect child_widget_bounds =
child->ConvertRectToWidget(child->GetLocalBounds());
if (!leading_child ||
child_widget_bounds.x() < leading_child_widget_bounds.x()) {
leading_child = child;
leading_child_widget_bounds = child_widget_bounds;
}
if (!trailing_child ||
child_widget_bounds.right() > trailing_child_widget_bounds.right()) {
trailing_child = child;
trailing_child_widget_bounds = child_widget_bounds;
}
}
return {leading_child, trailing_child};
}