Commit 13bb448d authored by Dana Fried's avatar Dana Fried Committed by Commit Bot

Cross-fade text between hover cards.

We now use an overlay with opacity to show the hover card transition
between adjacent tabs as the hover card slide-animates between them.

This is done with a second title and domain label that starts out
displaying the old title and domain (obscuring the title and domain
labels displaying the new values) and fades away as the hover card
animates.

We also looked at using layer opacity but the results were less
attractive.

Change-Id: I5098838596ea1e674cdeb884520ac7ea3a328623
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1895945
Commit-Queue: Dana Fried <dfried@chromium.org>
Reviewed-by: default avatarCaroline Rising <corising@chromium.org>
Cr-Commit-Position: refs/heads/master@{#712479}
parent 1dba3ee4
...@@ -250,6 +250,7 @@ class TabHoverCardBubbleView::WidgetSlideAnimationDelegate ...@@ -250,6 +250,7 @@ class TabHoverCardBubbleView::WidgetSlideAnimationDelegate
if (desired_anchor_view_) if (desired_anchor_view_)
bubble_delegate_->SetAnchorView(desired_anchor_view_); bubble_delegate_->SetAnchorView(desired_anchor_view_);
} }
bubble_delegate_->UpdateTextFade(value);
bubble_delegate_->GetWidget()->SetBounds(current_bubble_bounds_); bubble_delegate_->GetWidget()->SetBounds(current_bubble_bounds_);
} }
...@@ -272,6 +273,41 @@ class TabHoverCardBubbleView::WidgetSlideAnimationDelegate ...@@ -272,6 +273,41 @@ class TabHoverCardBubbleView::WidgetSlideAnimationDelegate
DISALLOW_COPY_AND_ASSIGN(WidgetSlideAnimationDelegate); DISALLOW_COPY_AND_ASSIGN(WidgetSlideAnimationDelegate);
}; };
// This is a label with two tweaks:
// - a solid background color, which can have alpha
// - a function to make the foreground and background color fade away (via
// alpha) to zero as an animation progresses
//
// It is used to overlay the old title and domain values as a hover card slide
// animation happens.
class TabHoverCardBubbleView::FadeLabel : public views::Label {
public:
using Label::Label;
~FadeLabel() override = default;
// Sets the fade-out of the label as |percent| in the range [0, 1]. Since
// FadeLabel is designed to mask new text with the old and then fade away, the
// higher the percentage the less opaque the label.
void SetFade(double percent) {
if (percent >= 1.0) {
SetVisible(false);
return;
}
const SkAlpha alpha = base::saturated_cast<SkAlpha>(
std::numeric_limits<SkAlpha>::max() * (1.0 - percent));
SetBackgroundColor(SkColorSetA(GetBackgroundColor(), alpha));
SetEnabledColor(SkColorSetA(GetEnabledColor(), alpha));
SetVisible(true);
}
protected:
// views::Label:
void OnPaintBackground(gfx::Canvas* canvas) override {
canvas->DrawColor(GetBackgroundColor());
}
};
TabHoverCardBubbleView::TabHoverCardBubbleView(Tab* tab) TabHoverCardBubbleView::TabHoverCardBubbleView(Tab* tab)
: BubbleDialogDelegateView(tab, views::BubbleBorder::TOP_LEFT) { : BubbleDialogDelegateView(tab, views::BubbleBorder::TOP_LEFT) {
// We'll do all of our own layout inside the bubble, so no need to inset this // We'll do all of our own layout inside the bubble, so no need to inset this
...@@ -291,9 +327,9 @@ TabHoverCardBubbleView::TabHoverCardBubbleView(Tab* tab) ...@@ -291,9 +327,9 @@ TabHoverCardBubbleView::TabHoverCardBubbleView(Tab* tab)
// navigating through the tab strip. // navigating through the tab strip.
set_focus_traversable_from_anchor_view(false); set_focus_traversable_from_anchor_view(false);
title_label_ = title_label_ = AddChildView(std::make_unique<views::Label>(
new views::Label(base::string16(), CONTEXT_TAB_HOVER_CARD_TITLE, base::string16(), CONTEXT_TAB_HOVER_CARD_TITLE,
views::style::STYLE_PRIMARY); views::style::STYLE_PRIMARY));
title_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); title_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
title_label_->SetVerticalAlignment(gfx::ALIGN_TOP); title_label_->SetVerticalAlignment(gfx::ALIGN_TOP);
title_label_->SetMultiLine(true); title_label_->SetMultiLine(true);
...@@ -303,26 +339,38 @@ TabHoverCardBubbleView::TabHoverCardBubbleView(Tab* tab) ...@@ -303,26 +339,38 @@ TabHoverCardBubbleView::TabHoverCardBubbleView(Tab* tab)
views::MinimumFlexSizeRule::kPreferred, views::MinimumFlexSizeRule::kPreferred,
views::MaximumFlexSizeRule::kPreferred, views::MaximumFlexSizeRule::kPreferred,
/* adjust_height_for_width */ true)); /* adjust_height_for_width */ true));
AddChildView(title_label_);
domain_label_ = new views::Label( title_fade_label_ = AddChildView(std::make_unique<FadeLabel>(
base::string16(), CONTEXT_TAB_HOVER_CARD_TITLE,
views::style::STYLE_PRIMARY));
title_fade_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
title_fade_label_->SetVerticalAlignment(gfx::ALIGN_TOP);
title_fade_label_->SetMultiLine(true);
title_fade_label_->SetMaxLines(kTitleMaxLines);
domain_label_ = AddChildView(std::make_unique<views::Label>(
base::string16(), CONTEXT_BODY_TEXT_LARGE, views::style::STYLE_SECONDARY, base::string16(), CONTEXT_BODY_TEXT_LARGE, views::style::STYLE_SECONDARY,
gfx::DirectionalityMode::DIRECTIONALITY_AS_URL); gfx::DirectionalityMode::DIRECTIONALITY_AS_URL));
domain_label_->SetElideBehavior(gfx::ELIDE_HEAD); domain_label_->SetElideBehavior(gfx::ELIDE_HEAD);
domain_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); domain_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
domain_label_->SetMultiLine(false); domain_label_->SetMultiLine(false);
AddChildView(domain_label_);
domain_fade_label_ = AddChildView(std::make_unique<FadeLabel>(
base::string16(), CONTEXT_BODY_TEXT_LARGE, views::style::STYLE_SECONDARY,
gfx::DirectionalityMode::DIRECTIONALITY_AS_URL));
domain_fade_label_->SetElideBehavior(gfx::ELIDE_HEAD);
domain_fade_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
domain_fade_label_->SetMultiLine(false);
if (AreHoverCardImagesEnabled()) { if (AreHoverCardImagesEnabled()) {
using Alignment = views::ImageView::Alignment; using Alignment = views::ImageView::Alignment;
const gfx::Size preview_size = TabStyle::GetPreviewImageSize(); const gfx::Size preview_size = TabStyle::GetPreviewImageSize();
preview_image_ = new views::ImageView(); preview_image_ = AddChildView(std::make_unique<views::ImageView>());
preview_image_->SetVisible(AreHoverCardImagesEnabled()); preview_image_->SetVisible(AreHoverCardImagesEnabled());
preview_image_->SetHorizontalAlignment(Alignment::kCenter); preview_image_->SetHorizontalAlignment(Alignment::kCenter);
preview_image_->SetVerticalAlignment(Alignment::kCenter); preview_image_->SetVerticalAlignment(Alignment::kCenter);
preview_image_->SetImageSize(preview_size); preview_image_->SetImageSize(preview_size);
preview_image_->SetPreferredSize(preview_size); preview_image_->SetPreferredSize(preview_size);
AddChildView(preview_image_);
} }
views::FlexLayout* const layout = views::FlexLayout* const layout =
...@@ -331,6 +379,8 @@ TabHoverCardBubbleView::TabHoverCardBubbleView(Tab* tab) ...@@ -331,6 +379,8 @@ TabHoverCardBubbleView::TabHoverCardBubbleView(Tab* tab)
layout->SetMainAxisAlignment(views::LayoutAlignment::kStart); layout->SetMainAxisAlignment(views::LayoutAlignment::kStart);
layout->SetCrossAxisAlignment(views::LayoutAlignment::kStretch); layout->SetCrossAxisAlignment(views::LayoutAlignment::kStretch);
layout->SetCollapseMargins(true); layout->SetCollapseMargins(true);
layout->SetChildViewIgnoredByLayout(title_fade_label_, true);
layout->SetChildViewIgnoredByLayout(domain_fade_label_, true);
constexpr int kVerticalMargin = 10; constexpr int kVerticalMargin = 10;
constexpr int kHorizontalMargin = 18; constexpr int kHorizontalMargin = 18;
...@@ -421,6 +471,11 @@ void TabHoverCardBubbleView::UpdateAndShow(Tab* tab) { ...@@ -421,6 +471,11 @@ void TabHoverCardBubbleView::UpdateAndShow(Tab* tab) {
if (widget_->IsVisible() && !disable_animations_for_testing_) { if (widget_->IsVisible() && !disable_animations_for_testing_) {
slide_animation_delegate_->AnimateToAnchorView(tab); slide_animation_delegate_->AnimateToAnchorView(tab);
// Reset the text fade, but only if we're not already animating. If we are,
// it will be less disruptive to just continue the animation with the new
// text.
if (!slide_animation_delegate_->is_animating())
UpdateTextFade(0.0);
} else { } else {
if (!anchor_view_set) if (!anchor_view_set)
SetAnchorView(tab); SetAnchorView(tab);
...@@ -523,6 +578,12 @@ std::unique_ptr<views::View> TabHoverCardBubbleView::CreateFootnoteView() { ...@@ -523,6 +578,12 @@ std::unique_ptr<views::View> TabHoverCardBubbleView::CreateFootnoteView() {
return alert_state_label; return alert_state_label;
} }
void TabHoverCardBubbleView::Layout() {
View::Layout();
title_fade_label_->SetBoundsRect(title_label_->bounds());
domain_fade_label_->SetBoundsRect(domain_label_->bounds());
}
base::TimeDelta TabHoverCardBubbleView::GetDelay(int tab_width) const { base::TimeDelta TabHoverCardBubbleView::GetDelay(int tab_width) const {
// Delay is calculated as a logarithmic scale and bounded by a minimum width // Delay is calculated as a logarithmic scale and bounded by a minimum width
// based on the width of a pinned tab and a maximum of the standard width. // based on the width of a pinned tab and a maximum of the standard width.
...@@ -593,11 +654,13 @@ void TabHoverCardBubbleView::UpdateCardContent(const Tab* tab) { ...@@ -593,11 +654,13 @@ void TabHoverCardBubbleView::UpdateCardContent(const Tab* tab) {
url_formatter::kFormatUrlTrimAfterHost, url_formatter::kFormatUrlTrimAfterHost,
net::UnescapeRule::NORMAL, nullptr, nullptr, nullptr); net::UnescapeRule::NORMAL, nullptr, nullptr, nullptr);
} }
title_fade_label_->SetText(title_label_->GetText());
title_label_->SetText(title); title_label_->SetText(title);
if (alert_state_ != old_alert_state) if (alert_state_ != old_alert_state)
GetBubbleFrameView()->SetFootnoteView(CreateFootnoteView()); GetBubbleFrameView()->SetFootnoteView(CreateFootnoteView());
domain_fade_label_->SetText(domain_label_->GetText());
domain_label_->SetText(domain); domain_label_->SetText(domain);
// If the preview image feature is not enabled, |preview_image_| will be null. // If the preview image feature is not enabled, |preview_image_| will be null.
...@@ -606,6 +669,11 @@ void TabHoverCardBubbleView::UpdateCardContent(const Tab* tab) { ...@@ -606,6 +669,11 @@ void TabHoverCardBubbleView::UpdateCardContent(const Tab* tab) {
} }
} }
void TabHoverCardBubbleView::UpdateTextFade(double percent) {
title_fade_label_->SetFade(percent);
domain_fade_label_->SetFade(percent);
}
void TabHoverCardBubbleView::RegisterToThumbnailImageUpdates( void TabHoverCardBubbleView::RegisterToThumbnailImageUpdates(
scoped_refptr<ThumbnailImage> thumbnail_image) { scoped_refptr<ThumbnailImage> thumbnail_image) {
if (thumbnail_image_ == thumbnail_image) if (thumbnail_image_ == thumbnail_image)
...@@ -658,6 +726,10 @@ void TabHoverCardBubbleView::ClearPreviewImage() { ...@@ -658,6 +726,10 @@ void TabHoverCardBubbleView::ClearPreviewImage() {
} }
void TabHoverCardBubbleView::OnHoverCardLanded() { void TabHoverCardBubbleView::OnHoverCardLanded() {
// Make sure we're displaying the new text at 100% opacity, and none of the
// old text.
UpdateTextFade(1.0);
// If we were waiting for a preview image with data to load, we don't want to // If we were waiting for a preview image with data to load, we don't want to
// keep showing the old image while hovering on the new tab, so clear it. This // keep showing the old image while hovering on the new tab, so clear it. This
// shouldn't happen very often for slide animations, but could on slower // shouldn't happen very often for slide animations, but could on slower
......
...@@ -58,6 +58,7 @@ class TabHoverCardBubbleView : public views::BubbleDialogDelegateView, ...@@ -58,6 +58,7 @@ class TabHoverCardBubbleView : public views::BubbleDialogDelegateView,
ax::mojom::Role GetAccessibleWindowRole() override; ax::mojom::Role GetAccessibleWindowRole() override;
int GetDialogButtons() const override; int GetDialogButtons() const override;
std::unique_ptr<views::View> CreateFootnoteView() override; std::unique_ptr<views::View> CreateFootnoteView() override;
void Layout() override;
void set_last_mouse_exit_timestamp( void set_last_mouse_exit_timestamp(
base::TimeTicks last_mouse_exit_timestamp) { base::TimeTicks last_mouse_exit_timestamp) {
...@@ -69,6 +70,7 @@ class TabHoverCardBubbleView : public views::BubbleDialogDelegateView, ...@@ -69,6 +70,7 @@ class TabHoverCardBubbleView : public views::BubbleDialogDelegateView,
friend class TabHoverCardBubbleViewInteractiveUiTest; friend class TabHoverCardBubbleViewInteractiveUiTest;
class WidgetFadeAnimationDelegate; class WidgetFadeAnimationDelegate;
class WidgetSlideAnimationDelegate; class WidgetSlideAnimationDelegate;
class FadeLabel;
// Get delay in milliseconds based on tab width. // Get delay in milliseconds based on tab width.
base::TimeDelta GetDelay(int tab_width) const; base::TimeDelta GetDelay(int tab_width) const;
...@@ -78,6 +80,9 @@ class TabHoverCardBubbleView : public views::BubbleDialogDelegateView, ...@@ -78,6 +80,9 @@ class TabHoverCardBubbleView : public views::BubbleDialogDelegateView,
// Updates and formats title, alert state, domain, and preview image. // Updates and formats title, alert state, domain, and preview image.
void UpdateCardContent(const Tab* tab); void UpdateCardContent(const Tab* tab);
// Update the text fade to the given percent, which should be between 0 and 1.
void UpdateTextFade(double percent);
void RegisterToThumbnailImageUpdates( void RegisterToThumbnailImageUpdates(
scoped_refptr<ThumbnailImage> thumbnail_image); scoped_refptr<ThumbnailImage> thumbnail_image);
...@@ -116,8 +121,10 @@ class TabHoverCardBubbleView : public views::BubbleDialogDelegateView, ...@@ -116,8 +121,10 @@ class TabHoverCardBubbleView : public views::BubbleDialogDelegateView,
views::Widget* widget_ = nullptr; views::Widget* widget_ = nullptr;
views::Label* title_label_ = nullptr; views::Label* title_label_ = nullptr;
FadeLabel* title_fade_label_ = nullptr;
base::Optional<TabAlertState> alert_state_; base::Optional<TabAlertState> alert_state_;
views::Label* domain_label_ = nullptr; views::Label* domain_label_ = nullptr;
FadeLabel* domain_fade_label_ = nullptr;
views::ImageView* preview_image_ = nullptr; views::ImageView* preview_image_ = nullptr;
// Counter used to keep track of the number of tab hover cards seen before a // Counter used to keep track of the number of tab hover cards seen before a
......
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