Commit 0270befd authored by Connie Wan's avatar Connie Wan Committed by Commit Bot

Polish TabGroupUnderline UI

This addresses #1, #2, #5, and #6 from the attached bug.

#3 and #4 are not addressed because there isn't a good way to do so without relying on TabStrip::GetTabAnimationTargetBounds, which in turn relies on the soon-to-be-deprecated BoundsAnimator. If there is a replacement in the new animation system we can revisit, but otherwise it's likely not worth it to fix. The issue is only visible during the brief period when a tab is animating across the tab strip, and it's not present when dragging.

See the screenshots on the attached bug for updated visuals.

Bug: 1008438
Change-Id: I013f3a8bd141ec8e159773522251f5fb4eadd8d6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1830400
Commit-Queue: Connie Wan <connily@chromium.org>
Reviewed-by: default avatarTaylor Bergquist <tbergquist@chromium.org>
Cr-Commit-Position: refs/heads/master@{#701362}
parent 646a95ad
...@@ -676,7 +676,6 @@ void Tab::SetGroup(base::Optional<TabGroupId> group) { ...@@ -676,7 +676,6 @@ void Tab::SetGroup(base::Optional<TabGroupId> group) {
if (group_ == group) if (group_ == group)
return; return;
group_ = group; group_ = group;
SchedulePaint();
} }
base::Optional<SkColor> Tab::GetGroupColor() const { base::Optional<SkColor> Tab::GetGroupColor() const {
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <utility> #include <utility>
#include "chrome/browser/ui/tabs/tab_group_visual_data.h" #include "chrome/browser/ui/tabs/tab_group_visual_data.h"
#include "chrome/browser/ui/tabs/tab_style.h"
#include "chrome/browser/ui/views/tabs/tab.h" #include "chrome/browser/ui/views/tabs/tab.h"
#include "chrome/browser/ui/views/tabs/tab_group_header.h" #include "chrome/browser/ui/views/tabs/tab_group_header.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h" #include "chrome/browser/ui/views/tabs/tab_strip.h"
...@@ -21,17 +22,27 @@ constexpr int TabGroupUnderline::kStrokeThickness; ...@@ -21,17 +22,27 @@ constexpr int TabGroupUnderline::kStrokeThickness;
TabGroupUnderline::TabGroupUnderline(TabStrip* tab_strip, TabGroupId group) TabGroupUnderline::TabGroupUnderline(TabStrip* tab_strip, TabGroupId group)
: tab_strip_(tab_strip), group_(group) { : tab_strip_(tab_strip), group_(group) {
UpdateVisuals(); UpdateBounds();
} }
void TabGroupUnderline::OnPaint(gfx::Canvas* canvas) { void TabGroupUnderline::OnPaint(gfx::Canvas* canvas) {
UpdateVisuals(); UpdateBounds();
OnPaintBackground(canvas);
SkPath path = GetPath();
cc::PaintFlags flags;
flags.setAntiAlias(true);
flags.setColor(GetColor());
flags.setStyle(cc::PaintFlags::kFill_Style);
canvas->DrawPath(path, flags);
// Ensure the active tab border stroke is repainted.
const int active_index = tab_strip_->controller()->GetActiveIndex();
if (active_index != ui::ListSelectionModel::kUnselectedIndex &&
tab_strip_->tab_at(active_index)->group() == group_)
tab_strip_->tab_at(active_index)->SchedulePaint();
} }
void TabGroupUnderline::UpdateVisuals() { void TabGroupUnderline::UpdateBounds() {
SkColor color = GetColor();
const int start_x = GetStart(); const int start_x = GetStart();
const int end_x = GetEnd(); const int end_x = GetEnd();
...@@ -39,31 +50,52 @@ void TabGroupUnderline::UpdateVisuals() { ...@@ -39,31 +50,52 @@ void TabGroupUnderline::UpdateVisuals() {
SetBounds(start_x, start_y - kStrokeThickness, end_x - start_x, SetBounds(start_x, start_y - kStrokeThickness, end_x - start_x,
kStrokeThickness); kStrokeThickness);
SetBackground(views::CreateSolidBackground(color));
}
SkColor TabGroupUnderline::GetColor() const {
const TabGroupVisualData* data =
tab_strip_->controller()->GetVisualDataForGroup(group_);
return data->color();
} }
int TabGroupUnderline::GetStart() const { int TabGroupUnderline::GetStart() const {
const gfx::Rect group_header_bounds = const TabGroupHeader* group_header = tab_strip_->group_header(group_);
tab_strip_->group_header(group_)->bounds();
constexpr int kInset = 20; constexpr int kInset = 20;
return group_header_bounds.x() + kInset; return group_header->bounds().x() + kInset;
} }
int TabGroupUnderline::GetEnd() const { int TabGroupUnderline::GetEnd() const {
// Fall back to the group header end for any corner cases. This ensures
// that the underline always has a positive width.
const TabGroupHeader* group_header = tab_strip_->group_header(group_);
constexpr int kInset = 20;
const int header_end = group_header->bounds().right() - kInset;
const std::vector<int> tabs_in_group = const std::vector<int> tabs_in_group =
tab_strip_->controller()->ListTabsInGroup(group_); tab_strip_->controller()->ListTabsInGroup(group_);
if (tabs_in_group.size() <= 0)
return header_end;
const int last_tab_index = tabs_in_group[tabs_in_group.size() - 1]; const int last_tab_index = tabs_in_group[tabs_in_group.size() - 1];
const gfx::Rect& last_tab_bounds = const Tab* last_tab = tab_strip_->tab_at(last_tab_index);
tab_strip_->tab_at(last_tab_index)->bounds();
constexpr int kInset = 20; const int tab_end = last_tab->bounds().right() +
return last_tab_bounds.right() - kInset; (last_tab->IsActive() ? kStrokeThickness : -kInset);
return std::max(tab_end, header_end);
}
SkPath TabGroupUnderline::GetPath() const {
SkPath path;
path.moveTo(0, kStrokeThickness);
path.arcTo(kStrokeThickness, kStrokeThickness, 0, SkPath::kSmall_ArcSize,
SkPath::kCW_Direction, kStrokeThickness, 0);
path.lineTo(width() - kStrokeThickness, 0);
path.arcTo(kStrokeThickness, kStrokeThickness, 0, SkPath::kSmall_ArcSize,
SkPath::kCW_Direction, width(), kStrokeThickness);
path.close();
return path;
}
SkColor TabGroupUnderline::GetColor() const {
const TabGroupVisualData* data =
tab_strip_->controller()->GetVisualDataForGroup(group_);
return data->color();
} }
...@@ -15,29 +15,35 @@ class TabStrip; ...@@ -15,29 +15,35 @@ class TabStrip;
// strip flow and positioned across all tabs in the group. // strip flow and positioned across all tabs in the group.
class TabGroupUnderline : public views::View { class TabGroupUnderline : public views::View {
public: public:
static constexpr int kStrokeThickness = 2; static constexpr int kStrokeThickness = 3;
TabGroupUnderline(TabStrip* tab_strip, TabGroupId group); TabGroupUnderline(TabStrip* tab_strip, TabGroupId group);
TabGroupId group() const { return group_; } TabGroupId group() const { return group_; }
// Updates the bounds and color of the underline for painting. // Updates the bounds of the underline for painting.
void UpdateVisuals(); void UpdateBounds();
// views::View: // views::View:
void OnPaint(gfx::Canvas* canvas) override; void OnPaint(gfx::Canvas* canvas) override;
private: private:
// The underline color is the group color.
SkColor GetColor() const;
// The underline starts at the left edge of the header chip. // The underline starts at the left edge of the header chip.
int GetStart() const; int GetStart() const;
// The underline ends at the right edge of the last grouped tab's close // The underline ends at the right edge of the last grouped tab's close
// button. // button. If the last grouped tab is active, the underline ends at the
// right edge of the active tab border stroke.
int GetEnd() const; int GetEnd() const;
// The underline is a straight line with half-rounded endcaps. Since this
// geometry is nontrivial to represent using primitives, it's instead
// represented using a fill path.
SkPath GetPath() const;
// The underline color is the group color.
SkColor GetColor() const;
TabStrip* const tab_strip_; TabStrip* const tab_strip_;
const TabGroupId group_; const TabGroupId group_;
......
...@@ -1136,41 +1136,45 @@ void TabStrip::ChangeTabGroup(int model_index, ...@@ -1136,41 +1136,45 @@ void TabStrip::ChangeTabGroup(int model_index,
base::Optional<TabGroupId> old_group, base::Optional<TabGroupId> old_group,
base::Optional<TabGroupId> new_group) { base::Optional<TabGroupId> new_group) {
tab_at(model_index)->SetGroup(new_group); tab_at(model_index)->SetGroup(new_group);
if (new_group.has_value() && !group_headers_[new_group.value()]) { if (new_group.has_value()) {
auto header = std::make_unique<TabGroupHeader>(this, new_group.value()); if (!group_headers_[new_group.value()]) {
header->set_owned_by_client(); auto header = std::make_unique<TabGroupHeader>(this, new_group.value());
AddChildView(header.get()); header->set_owned_by_client();
layout_helper_->InsertGroupHeader( AddChildView(header.get());
new_group.value(), header.get(), layout_helper_->InsertGroupHeader(
base::BindOnce(&TabStrip::OnGroupCloseAnimationCompleted, new_group.value(), header.get(),
base::Unretained(this), new_group.value())); base::BindOnce(&TabStrip::OnGroupCloseAnimationCompleted,
group_headers_[new_group.value()] = std::move(header); base::Unretained(this), new_group.value()));
group_headers_[new_group.value()] = std::move(header);
auto underline = }
std::make_unique<TabGroupUnderline>(this, new_group.value()); if (!group_underlines_[new_group.value()]) {
underline->set_owned_by_client(); auto underline =
AddChildView(underline.get()); std::make_unique<TabGroupUnderline>(this, new_group.value());
group_underlines_[new_group.value()] = std::move(underline); underline->set_owned_by_client();
AddChildView(underline.get());
group_underlines_[new_group.value()] = std::move(underline);
}
// The group header may be in the wrong place if the tab didn't actually
// move in terms of model indices.
layout_helper_->UpdateGroupHeaderIndex(new_group.value());
group_underlines_[new_group.value()]->SchedulePaint();
} }
if (old_group.has_value()) { if (old_group.has_value()) {
if (controller_->ListTabsInGroup(old_group.value()).size() == 0) { if (controller_->ListTabsInGroup(old_group.value()).size() == 0) {
layout_helper_->RemoveGroupHeader(old_group.value()); layout_helper_->RemoveGroupHeader(old_group.value());
} else { } else {
// The group header may be in the wrong place if the tab didn't actually // As above, ensure the header is in the right place.
// move in terms of model indices.
layout_helper_->UpdateGroupHeaderIndex(old_group.value()); layout_helper_->UpdateGroupHeaderIndex(old_group.value());
group_underlines_[old_group.value()]->SchedulePaint();
} }
} }
if (new_group.has_value()) {
// As above, ensure the header is in the right place.
layout_helper_->UpdateGroupHeaderIndex(new_group.value());
}
UpdateIdealBounds(); UpdateIdealBounds();
AnimateToIdealBounds(); AnimateToIdealBounds();
} }
void TabStrip::GroupVisualsChanged(TabGroupId group) { void TabStrip::GroupVisualsChanged(TabGroupId group) {
group_headers_[group]->VisualsChanged(); group_headers_[group]->VisualsChanged();
group_underlines_[group]->SchedulePaint();
// The group title may have changed size. // The group title may have changed size.
UpdateIdealBounds(); UpdateIdealBounds();
AnimateToIdealBounds(); AnimateToIdealBounds();
......
...@@ -1184,8 +1184,7 @@ TEST_P(TabStripTest, DeleteTabGroupHeaderAndUnderlineWhenEmpty) { ...@@ -1184,8 +1184,7 @@ TEST_P(TabStripTest, DeleteTabGroupHeaderAndUnderlineWhenEmpty) {
TEST_P(TabStripTest, GroupUnderlineBasics) { TEST_P(TabStripTest, GroupUnderlineBasics) {
tab_strip_->SetBounds(0, 0, 1000, 100); tab_strip_->SetBounds(0, 0, 1000, 100);
bounds_animator()->SetAnimationDuration(base::TimeDelta()); bounds_animator()->SetAnimationDuration(base::TimeDelta());
tab_strip_->AddTabAt(0, TabRendererData(), false); controller_->AddTab(0, false);
tab_strip_->AddTabAt(1, TabRendererData(), false);
base::Optional<TabGroupId> group = TabGroupId::GenerateNew(); base::Optional<TabGroupId> group = TabGroupId::GenerateNew();
controller_->MoveTabIntoGroup(0, group); controller_->MoveTabIntoGroup(0, group);
...@@ -1195,13 +1194,25 @@ TEST_P(TabStripTest, GroupUnderlineBasics) { ...@@ -1195,13 +1194,25 @@ TEST_P(TabStripTest, GroupUnderlineBasics) {
EXPECT_EQ(1u, underlines.size()); EXPECT_EQ(1u, underlines.size());
TabGroupUnderline* underline = underlines[0]; TabGroupUnderline* underline = underlines[0];
// Update underline manually in the absence of a real Paint cycle. // Update underline manually in the absence of a real Paint cycle.
underline->UpdateVisuals(); underline->UpdateBounds();
constexpr int kInset = 20; constexpr int kInset = 20;
EXPECT_EQ(underline->x(), kInset); EXPECT_EQ(underline->x(), kInset);
EXPECT_GT(underline->width(), 0); EXPECT_GT(underline->width(), 0);
EXPECT_EQ(underline->bounds().right(), EXPECT_EQ(underline->bounds().right(),
tab_strip_->tab_at(0)->bounds().right() - kInset); tab_strip_->tab_at(0)->bounds().right() - kInset);
EXPECT_EQ(underline->height(), TabGroupUnderline::kStrokeThickness); EXPECT_EQ(underline->height(), TabGroupUnderline::kStrokeThickness);
// Endpoints are different if the last grouped tab is active.
controller_->AddTab(1, true);
controller_->MoveTabIntoGroup(1, group);
underline->UpdateBounds();
EXPECT_EQ(underline->x(), kInset);
EXPECT_EQ(underline->bounds().right(),
tab_strip_->tab_at(1)->bounds().right() +
TabGroupUnderline::kStrokeThickness);
} }
TEST_P(TabStripTest, ChangingLayoutTypeResizesTabs) { TEST_P(TabStripTest, ChangingLayoutTypeResizesTabs) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment