Commit 56877ae5 authored by Manas Verma's avatar Manas Verma Committed by Commit Bot

[Autofill] Text animation to show confirmation of card saved when user saves local card.

This CL introduces a "card saved" animation next to the save credit card icon. The mock can be found here:
  https://docs.google.com/presentation/d/1dLbeUlWKkSQzqbDlktot5ldlcVZu32jcjJvIT27vGb8/edit#slide=id.g35839571aa_0_4 (internal)

bug: 855186
Change-Id: I2b56291d9f18158eca73e2d67f68f3d4efd31b06
Reviewed-on: https://chromium-review.googlesource.com/1162862
Commit-Queue: Manas Verma <manasverma@google.com>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Reviewed-by: default avatarJared Saul <jsaul@google.com>
Reviewed-by: default avatarMathieu Perreault <mathp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#582732}
parent 8de089e6
......@@ -5,6 +5,7 @@
#include "chrome/browser/ui/autofill/save_card_bubble_controller_impl.h"
#include <stddef.h>
#include <utility>
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/profiles/profile.h"
......@@ -249,6 +250,10 @@ bool SaveCardBubbleControllerImpl::ShouldShowSignInPromo() const {
browser_sync::ProfileSyncService::DISABLE_REASON_USER_CHOICE);
}
bool SaveCardBubbleControllerImpl::CanAnimate() const {
return can_animate_;
}
void SaveCardBubbleControllerImpl::OnSyncPromoAccepted(
const AccountInfo& account,
bool is_default_promo_account) {
......@@ -286,6 +291,11 @@ void SaveCardBubbleControllerImpl::OnSaveButton(
}
case BubbleType::LOCAL_SAVE:
DCHECK(!local_save_card_callback_.is_null());
// Show an animated card saved confirmation message next time
// UpdateIcon() is called.
can_animate_ = base::FeatureList::IsEnabled(
features::kAutofillSaveCardSignInAfterLocalSave);
local_save_card_callback_.Run();
local_save_card_callback_.Reset();
break;
......@@ -301,16 +311,14 @@ void SaveCardBubbleControllerImpl::OnSaveButton(
const BubbleType previous_bubble_type = current_bubble_type_;
current_bubble_type_ = BubbleType::INACTIVE;
// If user just saved a card locally, and we can show the sign in promo,
// then show the sign in promo. If we can't show the sign in promo
// then |current_bubble_type_| will be set to |MANAGE_CARDS|.
// If user just saved a card locally, the next bubble can either be a sign-in
// promo or a manage cards view. If we need to show a sign-in promo, that
// will be handled by OnAnimationEnded(), otherwise clicking the icon again
// will show the MANAGE_CARDS bubble, which is set here.
if (previous_bubble_type == BubbleType::LOCAL_SAVE &&
base::FeatureList::IsEnabled(
features::kAutofillSaveCardSignInAfterLocalSave)) {
if (ShouldShowSignInPromo())
ShowBubbleForSignInPromo();
else
current_bubble_type_ = BubbleType::MANAGE_CARDS;
current_bubble_type_ = BubbleType::MANAGE_CARDS;
}
if (previous_bubble_type == BubbleType::LOCAL_SAVE ||
......@@ -383,6 +391,17 @@ void SaveCardBubbleControllerImpl::OnBubbleClosed() {
UpdateIcon();
}
void SaveCardBubbleControllerImpl::OnAnimationEnded() {
// Do not repeat the animation next time UpdateIcon() is called, unless
// explicitly set somewhere else.
can_animate_ = false;
// We do not want to show the promo if the user clicked on the icon and the
// manage cards bubble started to show.
if (!save_card_bubble_view_)
ShowBubbleForSignInPromo();
}
const LegalMessageLines& SaveCardBubbleControllerImpl::GetLegalMessageLines()
const {
return legal_message_lines_;
......
......@@ -86,6 +86,7 @@ class SaveCardBubbleControllerImpl
// to the server -- this should change.
// TODO(crbug.com/864702): Don't show promo if user is a butter user.
bool ShouldShowSignInPromo() const override;
bool CanAnimate() const override;
void OnSyncPromoAccepted(const AccountInfo& account,
bool is_default_promo_account) override;
void OnSaveButton(
......@@ -94,6 +95,7 @@ class SaveCardBubbleControllerImpl
void OnLegalMessageLinkClicked(const GURL& url) override;
void OnManageCardsClicked() override;
void OnBubbleClosed() override;
void OnAnimationEnded() override;
const LegalMessageLines& GetLegalMessageLines() const override;
bool IsUploadSave() const override;
BubbleType GetBubbleType() const override;
......@@ -132,6 +134,9 @@ class SaveCardBubbleControllerImpl
// The web_contents associated with this controller.
content::WebContents* web_contents_;
// Is true only if the card saved animation can be shown.
bool can_animate_ = false;
// Weak reference. Will be nullptr if no bubble is currently shown.
SaveCardBubbleView* save_card_bubble_view_ = nullptr;
......
......@@ -5,6 +5,7 @@
#include "chrome/browser/ui/autofill/save_card_bubble_controller_impl.h"
#include <stddef.h>
#include <string>
#include <utility>
#include "base/json/json_reader.h"
......@@ -41,7 +42,8 @@ class TestSaveCardBubbleControllerImpl : public SaveCardBubbleControllerImpl {
std::make_unique<TestSaveCardBubbleControllerImpl>(web_contents));
}
// Overriding because calling the original function causes unit test to crash.
// Overriding because parent function requires a browser window to redirect
// properly, which is not available in unit tests.
void ShowPaymentsSettingsPage() override{};
explicit TestSaveCardBubbleControllerImpl(content::WebContents* web_contents)
......@@ -130,6 +132,12 @@ class SaveCardBubbleControllerImplTest : public BrowserWithTestWindowTest {
controller()->ReshowBubble();
}
void ClickSaveButton() {
controller()->OnSaveButton();
if (controller()->CanAnimate())
controller()->OnAnimationEnded();
}
protected:
TestSaveCardBubbleControllerImpl* controller() {
return static_cast<TestSaveCardBubbleControllerImpl*>(
......@@ -264,7 +272,7 @@ TEST_F(SaveCardBubbleControllerImplTest, Metrics_Local_FirstShow_SaveButton) {
ShowLocalBubble();
base::HistogramTester histogram_tester;
controller()->OnSaveButton();
ClickSaveButton();
controller()->OnBubbleClosed();
histogram_tester.ExpectUniqueSample(
......@@ -277,7 +285,7 @@ TEST_F(SaveCardBubbleControllerImplTest, Metrics_Local_Reshows_SaveButton) {
CloseAndReshowBubble();
base::HistogramTester histogram_tester;
controller()->OnSaveButton();
ClickSaveButton();
controller()->OnBubbleClosed();
histogram_tester.ExpectUniqueSample(
......@@ -290,7 +298,7 @@ TEST_F(SaveCardBubbleControllerImplTest,
ShowUploadBubble(/*should_request_name_from_user=*/true);
base::HistogramTester histogram_tester;
controller()->OnSaveButton();
ClickSaveButton();
controller()->OnBubbleClosed();
histogram_tester.ExpectUniqueSample(
......@@ -304,7 +312,7 @@ TEST_F(SaveCardBubbleControllerImplTest,
CloseAndReshowBubble();
base::HistogramTester histogram_tester;
controller()->OnSaveButton();
ClickSaveButton();
controller()->OnBubbleClosed();
histogram_tester.ExpectUniqueSample(
......@@ -376,7 +384,7 @@ TEST_F(SaveCardBubbleControllerImplTest,
controller()->OnBubbleClosed();
ShowLocalBubble();
controller()->OnSaveButton();
ClickSaveButton();
controller()->OnBubbleClosed();
ShowLocalBubble();
......@@ -415,7 +423,7 @@ TEST_F(SaveCardBubbleControllerImplTest,
controller()->OnBubbleClosed();
ShowUploadBubble();
controller()->OnSaveButton();
ClickSaveButton();
controller()->OnBubbleClosed();
ShowUploadBubble();
......@@ -923,7 +931,7 @@ TEST_F(SaveCardBubbleControllerImplTest,
features::kAutofillSaveCardSignInAfterLocalSave);
ShowLocalBubble();
controller()->OnSaveButton();
ClickSaveButton();
// Sign-in promo should be shown after accepting local save.
EXPECT_EQ(BubbleType::SIGN_IN_PROMO, controller()->GetBubbleType());
......@@ -935,7 +943,7 @@ TEST_F(SaveCardBubbleControllerImplTest, Local_FirstShow_SaveButton_NoBubble) {
features::kAutofillSaveCardSignInAfterLocalSave);
ShowLocalBubble();
controller()->OnSaveButton();
ClickSaveButton();
// When this flag is disabled, no promo should appear and
// the icon should go away.
......@@ -966,7 +974,7 @@ TEST_F(SaveCardBubbleControllerImplTest,
features::kAutofillSaveCardSignInAfterLocalSave);
ShowLocalBubble();
controller()->OnSaveButton();
ClickSaveButton();
CloseAndReshowBubble();
// After closing the sign-in promo, clicking the icon should bring
......@@ -1018,7 +1026,7 @@ TEST_F(SaveCardBubbleControllerImplTest,
features::kAutofillSaveCardSignInAfterLocalSave);
ShowLocalBubble();
controller()->OnSaveButton();
ClickSaveButton();
CloseAndReshowBubble();
controller()->OnBubbleClosed();
......@@ -1108,7 +1116,7 @@ TEST_F(SaveCardBubbleControllerImplTest,
features::kAutofillSaveCardSignInAfterLocalSave);
ShowUploadBubble();
controller()->OnSaveButton();
ClickSaveButton();
// Icon should disappear after an upload save,
// even when this flag is enabled.
......
......@@ -13,19 +13,24 @@
#include "chrome/browser/ui/view_ids.h"
#include "chrome/browser/ui/views/autofill/save_card_bubble_views.h"
#include "chrome/grit/generated_resources.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
namespace autofill {
SaveCardIconView::SaveCardIconView(CommandUpdater* command_updater,
Browser* browser,
PageActionIconView::Delegate* delegate)
PageActionIconView::Delegate* delegate,
const gfx::FontList& font_list)
: PageActionIconView(command_updater,
IDC_SAVE_CREDIT_CARD_FOR_PAGE,
delegate),
delegate,
font_list),
browser_(browser) {
DCHECK(delegate);
set_id(VIEW_ID_SAVE_CREDIT_CARD_BUTTON);
SetUpForInOutAnimation();
}
SaveCardIconView::~SaveCardIconView() {}
......@@ -51,6 +56,11 @@ bool SaveCardIconView::Update() {
enabled &= SetCommandEnabled(enabled);
SetVisible(enabled);
AnimateInkDrop(views::InkDropState::HIDDEN, nullptr);
if (enabled && controller->CanAnimate())
AnimateIn(IDS_AUTOFILL_CARD_SAVED);
return was_visible != visible();
}
......@@ -75,4 +85,17 @@ SaveCardBubbleControllerImpl* SaveCardIconView::GetController() const {
return autofill::SaveCardBubbleControllerImpl::FromWebContents(web_contents);
}
bool SaveCardIconView::ShouldShowSeparator() const {
return false;
}
void SaveCardIconView::AnimationEnded(const gfx::Animation* animation) {
IconLabelBubbleView::AnimationEnded(animation);
// |controller| may be nullptr due to lazy initialization.
SaveCardBubbleControllerImpl* controller = GetController();
if (controller)
controller->OnAnimationEnded();
}
} // namespace autofill
......@@ -22,13 +22,15 @@ class SaveCardIconView : public PageActionIconView {
public:
SaveCardIconView(CommandUpdater* command_updater,
Browser* browser,
PageActionIconView::Delegate* delegate);
PageActionIconView::Delegate* delegate,
const gfx::FontList& font_list);
~SaveCardIconView() override;
// PageActionIconView:
views::BubbleDialogDelegateView* GetBubble() const override;
bool Update() override;
base::string16 GetTextForTooltipAndAccessibleName() const override;
bool ShouldShowSeparator() const override;
protected:
// PageActionIconView:
......@@ -38,6 +40,9 @@ class SaveCardIconView : public PageActionIconView {
private:
SaveCardBubbleControllerImpl* GetController() const;
// gfx::AnimationDelegate:
void AnimationEnded(const gfx::Animation* animation) override;
// May be nullptr.
Browser* const browser_;
......
......@@ -4,6 +4,8 @@
#include "chrome/browser/ui/views/location_bar/content_setting_image_view.h"
#include <utility>
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/themes/theme_properties.h"
......@@ -21,15 +23,6 @@
#include "ui/views/controls/label.h"
#include "ui/views/widget/widget.h"
namespace {
// Time spent with animation fully open.
const int kStayOpenTimeMS = 3200;
}
// static
const int ContentSettingImageView::kAnimationDurationMS =
(IconLabelBubbleView::kOpenTimeMS * 2) + kStayOpenTimeMS;
ContentSettingImageView::ContentSettingImageView(
std::unique_ptr<ContentSettingImageModel> image_model,
Delegate* delegate,
......@@ -37,19 +30,9 @@ ContentSettingImageView::ContentSettingImageView(
: IconLabelBubbleView(font_list),
delegate_(delegate),
content_setting_image_model_(std::move(image_model)),
slide_animator_(this),
pause_animation_(false),
pause_animation_state_(0.0),
bubble_view_(nullptr) {
DCHECK(delegate_);
SetInkDropMode(InkDropMode::ON);
SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
image()->EnableCanvasFlippingForRTLUI(true);
label()->SetElideBehavior(gfx::NO_ELIDE);
label()->SetVisible(false);
slide_animator_.SetSlideDuration(kAnimationDurationMS);
slide_animator_.SetTweenType(gfx::Tween::LINEAR);
SetUpForInOutAnimation();
}
ContentSettingImageView::~ContentSettingImageView() {
......@@ -83,12 +66,9 @@ void ContentSettingImageView::Update() {
// the user. If this becomes a problem, we could design some sort of queueing
// mechanism to show one after the other, but it doesn't seem important now.
int string_id = content_setting_image_model_->explanatory_string_id();
AnimateInkDrop(views::InkDropState::HIDDEN, nullptr /* event */);
if (string_id && !label()->visible()) {
SetLabel(l10n_util::GetStringUTF16(string_id));
label()->SetVisible(true);
AnimateIn();
}
AnimateInkDrop(views::InkDropState::HIDDEN, nullptr);
if (string_id)
AnimateIn(string_id);
content_setting_image_model_->SetAnimationHasRun(web_contents);
}
......@@ -127,56 +107,12 @@ SkColor ContentSettingImageView::GetTextColor() const {
ui::NativeTheme::kColorId_TextfieldDefaultColor);
}
bool ContentSettingImageView::ShouldShowLabel() const {
return (!IsShrinking() || (width() > image()->GetPreferredSize().width())) &&
(slide_animator_.is_animating() || pause_animation_);
}
bool ContentSettingImageView::ShouldShowSeparator() const {
return false;
}
double ContentSettingImageView::WidthMultiplier() const {
double state = pause_animation_ ? pause_animation_state_
: slide_animator_.GetCurrentValue();
// The fraction of the animation we'll spend animating the string into view,
// which is also the fraction we'll spend animating it closed; total
// animation (slide out, show, then slide in) is 1.0.
const double kOpenFraction =
static_cast<double>(kOpenTimeMS) / kAnimationDurationMS;
double size_fraction = 1.0;
if (state < kOpenFraction)
size_fraction = state / kOpenFraction;
if (state > (1.0 - kOpenFraction))
size_fraction = (1.0 - state) / kOpenFraction;
return size_fraction;
}
bool ContentSettingImageView::IsShrinking() const {
const double kOpenFraction =
static_cast<double>(kOpenTimeMS) / kAnimationDurationMS;
return (!pause_animation_ && slide_animator_.is_animating() &&
slide_animator_.GetCurrentValue() > (1.0 - kOpenFraction));
}
bool ContentSettingImageView::ShowBubble(const ui::Event& event) {
if (slide_animator_.is_animating()) {
// If the user clicks while we're animating, the bubble arrow will be
// pointing to the image, and if we allow the animation to keep running, the
// image will move away from the arrow (or we'll have to move the bubble,
// which is even worse). So we want to stop the animation. We have two
// choices: jump to the final post-animation state (no label visible), or
// pause the animation where we are and continue running after the bubble
// closes. The former looks more jerky, so we avoid it unless the animation
// hasn't even fully exposed the image yet, in which case pausing with half
// an image visible will look broken.
if (!pause_animation_ && ShouldShowLabel()) {
pause_animation_ = true;
pause_animation_state_ = slide_animator_.GetCurrentValue();
}
slide_animator_.Reset();
}
PauseAnimation();
content::WebContents* web_contents =
delegate_->GetContentSettingWebContents();
if (web_contents && !bubble_view_) {
......@@ -194,7 +130,7 @@ bool ContentSettingImageView::ShowBubble(const ui::Event& event) {
// bubble doesn't need an arrow. If the user clicks during an animation,
// the animation simply pauses and no other visible state change occurs, so
// show the arrow in this case.
if (!pause_animation_) {
if (!is_animation_paused()) {
AnimateInkDrop(views::InkDropState::ACTIVATED,
ui::LocatedEvent::FromIfValid(&event));
}
......@@ -220,42 +156,12 @@ SkColor ContentSettingImageView::GetInkDropBaseColor() const {
: IconLabelBubbleView::GetInkDropBaseColor();
}
void ContentSettingImageView::AnimationEnded(const gfx::Animation* animation) {
slide_animator_.Reset();
if (!pause_animation_) {
label()->SetVisible(false);
parent()->Layout();
parent()->SchedulePaint();
}
GetInkDrop()->SetShowHighlightOnHover(true);
GetInkDrop()->SetShowHighlightOnFocus(true);
}
void ContentSettingImageView::AnimationProgressed(
const gfx::Animation* animation) {
if (!pause_animation_) {
parent()->Layout();
parent()->SchedulePaint();
}
}
void ContentSettingImageView::AnimationCanceled(
const gfx::Animation* animation) {
AnimationEnded(animation);
}
void ContentSettingImageView::OnWidgetDestroying(views::Widget* widget) {
DCHECK(bubble_view_);
DCHECK_EQ(bubble_view_->GetWidget(), widget);
widget->RemoveObserver(this);
bubble_view_ = nullptr;
if (pause_animation_) {
slide_animator_.Reset(pause_animation_state_);
pause_animation_ = false;
AnimateIn();
}
UnpauseAnimation();
}
void ContentSettingImageView::OnWidgetVisibilityChanged(views::Widget* widget,
......@@ -272,9 +178,3 @@ void ContentSettingImageView::UpdateImage() {
GetTextColor()))
.AsImageSkia());
}
void ContentSettingImageView::AnimateIn() {
slide_animator_.Show();
GetInkDrop()->SetShowHighlightOnHover(false);
GetInkDrop()->SetShowHighlightOnFocus(false);
}
......@@ -74,25 +74,13 @@ class ContentSettingImageView : public IconLabelBubbleView {
void OnNativeThemeChanged(const ui::NativeTheme* native_theme) override;
SkColor GetInkDropBaseColor() const override;
SkColor GetTextColor() const override;
bool ShouldShowLabel() const override;
bool ShouldShowSeparator() const override;
double WidthMultiplier() const override;
bool IsShrinking() const override;
bool ShowBubble(const ui::Event& event) override;
bool IsBubbleShowing() const override;
ContentSettingImageModel::ImageType GetTypeForTesting() const;
private:
// The total animation time, including open and close as well as an
// intervening "stay open" period.
static const int kAnimationDurationMS;
// gfx::AnimationDelegate:
void AnimationEnded(const gfx::Animation* animation) override;
void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationCanceled(const gfx::Animation* animation) override;
// views::WidgetObserver:
void OnWidgetDestroying(views::Widget* widget) override;
void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override;
......@@ -100,17 +88,8 @@ class ContentSettingImageView : public IconLabelBubbleView {
// Updates the image and tooltip to match the current model state.
void UpdateImage();
// Animates the view in and disables highlighting for hover and focus.
// TODO(bruthig): See crbug.com/669253. Since the ink drop highlight currently
// cannot handle host resizes, the highlight needs to be disabled when the
// animation is running.
void AnimateIn();
Delegate* delegate_; // Weak.
std::unique_ptr<ContentSettingImageModel> content_setting_image_model_;
gfx::SlideAnimation slide_animator_;
bool pause_animation_;
double pause_animation_state_;
views::BubbleDialogDelegateView* bubble_view_;
base::Optional<SkColor> icon_color_;
......
......@@ -4,13 +4,16 @@
#include "chrome/browser/ui/views/location_bar/icon_label_bubble_view.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/omnibox/omnibox_theme.h"
#include "chrome/browser/ui/views/location_bar/background_with_1_px_border.h"
#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/material_design/material_design_controller.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
......@@ -41,6 +44,18 @@ constexpr int kIconLabelBubbleSpaceBesideSeparator = 8;
constexpr int kIconLabelBubbleFadeInDurationMs = 250;
constexpr int kIconLabelBubbleFadeOutDurationMs = 175;
// The type of tweening for the animation.
const gfx::Tween::Type kTweenType = gfx::Tween::EASE_IN_OUT;
// The time for the text to animate out, as well as in.
constexpr int kSlideTimeMS = 600;
// The total time for the in and out text animation.
constexpr int kAnimationDurationMS = 3000;
// The fraction of time taken for the text to animate out, as well as in.
const double kOpenTimeFraction =
static_cast<double>(kSlideTimeMS) / kAnimationDurationMS;
} // namespace
//////////////////////////////////////////////////////////////////
......@@ -112,8 +127,7 @@ IconLabelBubbleView::IconLabelBubbleView(const gfx::FontList& font_list)
image_(new views::ImageView()),
label_(new views::Label(base::string16(), {font_list})),
ink_drop_container_(new views::InkDropContainerView()),
separator_view_(new SeparatorView(this)),
suppress_button_release_(false) {
separator_view_(new SeparatorView(this)) {
// Disable separate hit testing for |image_|. This prevents views treating
// |image_| as a separate mouse hover region from |this|.
image_->set_can_process_events_within_subtree(false);
......@@ -158,6 +172,8 @@ void IconLabelBubbleView::InkDropRippleAnimationEnded(
views::InkDropState state) {}
bool IconLabelBubbleView::ShouldShowLabel() const {
if (slide_animation_.is_animating() || is_animation_paused_)
return !IsShrinking() || (width() > image()->GetPreferredSize().width());
return label_->visible() && !label_->text().empty();
}
......@@ -183,11 +199,22 @@ bool IconLabelBubbleView::ShouldShowExtraEndSpace() const {
}
double IconLabelBubbleView::WidthMultiplier() const {
return 1.0;
if (!slide_animation_.is_animating() && !is_animation_paused_)
return 1.0;
double state = is_animation_paused_ ? pause_animation_state_
: slide_animation_.GetCurrentValue();
double size_fraction = 1.0;
if (state < open_state_fraction_)
size_fraction = state / open_state_fraction_;
if (state > (1.0 - open_state_fraction_))
size_fraction = (1.0 - state) / open_state_fraction_;
return size_fraction;
}
bool IconLabelBubbleView::IsShrinking() const {
return false;
return slide_animation_.is_animating() && !is_animation_paused_ &&
slide_animation_.GetCurrentValue() > (1.0 - open_state_fraction_);
}
bool IconLabelBubbleView::ShowBubble(const ui::Event& event) {
......@@ -381,6 +408,32 @@ void IconLabelBubbleView::OnBlur() {
Button::OnBlur();
}
void IconLabelBubbleView::AnimationEnded(const gfx::Animation* animation) {
slide_animation_.Reset();
if (!is_animation_paused_) {
// If there is no separator to show, then that means we want the text to
// disappear after animating.
if (!ShouldShowSeparator())
label()->SetVisible(false);
parent()->Layout();
parent()->SchedulePaint();
}
GetInkDrop()->SetShowHighlightOnHover(true);
GetInkDrop()->SetShowHighlightOnFocus(true);
}
void IconLabelBubbleView::AnimationProgressed(const gfx::Animation* animation) {
if (!is_animation_paused_) {
parent()->Layout();
parent()->SchedulePaint();
}
}
void IconLabelBubbleView::AnimationCanceled(const gfx::Animation* animation) {
AnimationEnded(animation);
}
void IconLabelBubbleView::OnWidgetDestroying(views::Widget* widget) {
widget->RemoveObserver(this);
}
......@@ -478,3 +531,57 @@ bool IconLabelBubbleView::OnActivate(const ui::Event& event) {
const char* IconLabelBubbleView::GetClassName() const {
return "IconLabelBubbleView";
}
void IconLabelBubbleView::SetUpForInOutAnimation() {
SetInkDropMode(InkDropMode::ON);
SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
image_->EnableCanvasFlippingForRTLUI(true);
label_->SetElideBehavior(gfx::NO_ELIDE);
label_->SetVisible(false);
slide_animation_.SetSlideDuration(kAnimationDurationMS);
slide_animation_.SetTweenType(kTweenType);
open_state_fraction_ =
gfx::Tween::CalculateValue(kTweenType, kOpenTimeFraction);
}
void IconLabelBubbleView::AnimateIn(int string_id) {
if (!label()->visible()) {
SetLabel(l10n_util::GetStringUTF16(string_id));
label()->SetVisible(true);
ShowAnimation();
}
}
void IconLabelBubbleView::PauseAnimation() {
if (slide_animation_.is_animating()) {
// If the user clicks while we're animating, the bubble arrow will be
// pointing to the image, and if we allow the animation to keep running, the
// image will move away from the arrow (or we'll have to move the bubble,
// which is even worse). So we want to stop the animation. We have two
// choices: jump to the final post-animation state (no label visible), or
// pause the animation where we are and continue running after the bubble
// closes. The former looks more jerky, so we avoid it unless the animation
// hasn't even fully exposed the image yet, in which case pausing with half
// an image visible will look broken.
if (!is_animation_paused_ && ShouldShowLabel()) {
is_animation_paused_ = true;
pause_animation_state_ = slide_animation_.GetCurrentValue();
}
slide_animation_.Reset();
}
}
void IconLabelBubbleView::UnpauseAnimation() {
if (is_animation_paused_) {
slide_animation_.Reset(pause_animation_state_);
is_animation_paused_ = false;
ShowAnimation();
}
}
void IconLabelBubbleView::ShowAnimation() {
slide_animation_.Show();
GetInkDrop()->SetShowHighlightOnHover(false);
GetInkDrop()->SetShowHighlightOnFocus(false);
}
......@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_ICON_LABEL_BUBBLE_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_ICON_LABEL_BUBBLE_VIEW_H_
#include <memory>
#include <string>
#include "base/macros.h"
......@@ -145,6 +146,9 @@ class IconLabelBubbleView : public views::InkDropObserver,
void NotifyClick(const ui::Event& event) override;
void OnFocus() override;
void OnBlur() override;
void AnimationEnded(const gfx::Animation* animation) override;
void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationCanceled(const gfx::Animation* animation) override;
// views::WidgetObserver:
void OnWidgetDestroying(views::Widget* widget) override;
......@@ -161,6 +165,21 @@ class IconLabelBubbleView : public views::InkDropObserver,
// this returns 0.
int GetPrefixedSeparatorWidth() const;
// Set up for icons that animate their labels in and then out.
void SetUpForInOutAnimation();
// Animates the view in and disables highlighting for hover and focus.
// TODO(bruthig): See https://crbug.com/669253. Since the ink drop highlight
// currently cannot handle host resizes, the highlight needs to be disabled
// when the animation is running.
void AnimateIn(int string_id);
void PauseAnimation();
void UnpauseAnimation();
// Returns true iff the slide animation has started, has not ended and is
// currently paused.
bool is_animation_paused() const { return is_animation_paused_; }
private:
// Spacing between the image and the label.
int GetInternalSpacing() const;
......@@ -180,6 +199,8 @@ class IconLabelBubbleView : public views::InkDropObserver,
// views::View:
const char* GetClassName() const override;
void ShowAnimation();
// The contents of the bubble.
views::ImageView* image_;
views::Label* label_;
......@@ -195,7 +216,15 @@ class IconLabelBubbleView : public views::InkDropObserver,
// event. If this is true then IsTriggerableEvent() will return false to
// prevent the bubble from reshowing. This flag is necessary because the
// bubble gets dismissed before the button handles the mouse release event.
bool suppress_button_release_;
bool suppress_button_release_ = false;
// Slide animation for label.
gfx::SlideAnimation slide_animation_{this};
// Parameters for the slide animation.
bool is_animation_paused_ = false;
double pause_animation_state_ = 0.0;
double open_state_fraction_ = 0.0;
DISALLOW_COPY_AND_ASSIGN(IconLabelBubbleView);
};
......
......@@ -7,6 +7,7 @@
#include <algorithm>
#include <map>
#include <memory>
#include <utility>
#include "base/feature_list.h"
#include "base/i18n/rtl.h"
......@@ -237,8 +238,8 @@ void LocationBarView::Init() {
page_action_icons_.push_back(manage_passwords_icon_view_);
if (browser_) {
save_credit_card_icon_view_ =
new autofill::SaveCardIconView(command_updater(), browser_, this);
save_credit_card_icon_view_ = new autofill::SaveCardIconView(
command_updater(), browser_, this, font_list);
page_action_icons_.push_back(save_credit_card_icon_view_);
}
translate_icon_view_ = new TranslateIconView(command_updater(), this);
......
......@@ -51,6 +51,9 @@ class SaveCardBubbleController {
// Returns whether or not a sign in / sync promo needs to be shown.
virtual bool ShouldShowSignInPromo() const = 0;
// Returns true iff the card saved animation can be shown.
virtual bool CanAnimate() const = 0;
// Interaction.
// OnSyncPromoAccepted is called when the Dice Sign-in promo is clicked.
virtual void OnSyncPromoAccepted(const AccountInfo& account,
......@@ -63,6 +66,8 @@ class SaveCardBubbleController {
virtual void OnLegalMessageLinkClicked(const GURL& url) = 0;
virtual void OnManageCardsClicked() = 0;
virtual void OnBubbleClosed() = 0;
// Once the animation ends, it shows a new bubble if needed.
virtual void OnAnimationEnded() = 0;
// State.
......
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