Commit 9d068dc8 authored by estade's avatar estade Committed by Commit Bot

Use animated vector icon for app menu notification animation.

1. Expose IconDescription in vector icon code so when we add or
   modify parameters we don't have to update the API.
   TODO: remove the parts of the API that use multiple parameters
         and replace them all with IconDescription.
2. Add AnimatedIconView, which caches the start and end states and
   controls animating in between as well.
3. Add an animated version of browser_tools.icon. This was crafted
   by hand based on the non-animated version and a visual description,
   but in the future designers should deliver Android vector drawables
   which will make the process of creating these icons much easier
   (and perhaps automate-able).
   TODO: actually craft a 2x version instead of reusing 1x.
4. Apply this all to the app menu. This solves the issue of pixel
   perfection in the steady state.

BUG=718549,704786

Review-Url: https://codereview.chromium.org/2892563004
Cr-Commit-Position: refs/heads/master@{#476425}
parent 81ba45d6
......@@ -19,6 +19,8 @@ aggregate_vector_icons("chrome_vector_icons") {
"autologin.icon",
"blocked_badge.icon",
"bluetooth_connected.icon",
"browser_tools_animated.1x.icon",
"browser_tools_animated.icon",
"browser_tools.1x.icon",
"browser_tools.icon",
"browser_tools_error.icon",
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
CANVAS_DIMENSIONS, 16,
// Top dot.
TRANSITION_FROM,
TRANSITION_FROM,
MOVE_TO, 7, 3.5f,
CUBIC_TO, 7, 2.67f, 7.67f, 2, 8.5f, 2,
R_H_LINE_TO, 0,
CUBIC_TO, 9.33f, 2, 10, 2.67f, 10, 3.5f,
CUBIC_TO, 10, 4.33f, 9.33f, 5, 8.5f, 5,
R_H_LINE_TO, 0,
CUBIC_TO, 7.67f, 5, 7, 4.33f, 7, 3.5f,
TRANSITION_TO,
MOVE_TO, 4.68f, 3.5f,
CUBIC_TO, 4.68f, 2.95f, 5.13f, 2.5f, 5.68f, 2.5f,
R_H_LINE_TO, 5.65f,
CUBIC_TO, 11.87f, 2.5f, 12.32f, 2.95f, 12.32f, 3.5f,
CUBIC_TO, 12.32f, 4.05f, 11.87f, 4.5f, 11.32f, 4.5f,
R_H_LINE_TO, -5.65f,
CUBIC_TO, 5.13f, 4.5f, 4.68f, 4.05f, 4.68f, 2.5f,
TRANSITION_END, 133, 150, gfx::Tween::FAST_OUT_SLOW_IN,
TRANSITION_TO,
MOVE_TO, 7, 3.5f,
CUBIC_TO, 7, 2.67f, 7.67f, 2, 8.5f, 2,
LINE_TO, 8.5f, 2,
CUBIC_TO, 9.33f, 2, 10, 2.67f, 10, 3.5f,
CUBIC_TO, 10, 4.33f, 9.33f, 5, 8.5f, 5,
LINE_TO, 8.5f, 5,
CUBIC_TO, 7.67f, 5, 7, 4.33f, 7, 3.5f,
TRANSITION_END, 416, 533, gfx::Tween::FAST_OUT_SLOW_IN,
CLOSE,
NEW_PATH,
// Middle dot.
TRANSITION_FROM,
TRANSITION_FROM,
MOVE_TO, 7, 8.5f,
CUBIC_TO, 7, 7.67f, 7.67f, 7, 8.5f, 7,
R_H_LINE_TO, 0,
CUBIC_TO, 9.33f, 7, 10, 7.67f, 10, 8.5f,
CUBIC_TO, 10, 9.33f, 9.33f, 10, 8.5f, 10,
R_H_LINE_TO, 0,
CUBIC_TO, 7.67f, 10, 7, 9.33f, 7, 8.5f,
TRANSITION_TO,
MOVE_TO, 4.68f, 8.5f,
CUBIC_TO, 4.68f, 7.95f, 5.13f, 7.5f, 5.68f, 7.5f,
R_H_LINE_TO, 5.65f,
CUBIC_TO, 11.87f, 7.5f, 12.32f, 7.95f, 12.32f, 8.5f,
CUBIC_TO, 12.32f, 9.05f, 11.87f, 9.5f, 11.32f, 9.5f,
R_H_LINE_TO, -5.65f,
CUBIC_TO, 5.13f, 9.5f, 4.68f, 9.05f, 4.68f, 7.5f,
TRANSITION_END, 67, 150, gfx::Tween::FAST_OUT_SLOW_IN,
TRANSITION_TO,
MOVE_TO, 7, 8.5f,
CUBIC_TO, 7, 7.67f, 7.67f, 7, 8.5f, 7,
R_H_LINE_TO, 0,
CUBIC_TO, 9.33f, 7, 10, 7.67f, 10, 8.5f,
CUBIC_TO, 10, 9.33f, 9.33f, 10, 8.5f, 10,
R_H_LINE_TO, 0,
CUBIC_TO, 7.67f, 10, 7, 9.33f, 7, 8.5f,
TRANSITION_END, 350, 383, gfx::Tween::FAST_OUT_SLOW_IN,
CLOSE,
NEW_PATH,
// Bottom dot.
TRANSITION_FROM,
TRANSITION_FROM,
MOVE_TO, 7, 13.5f,
CUBIC_TO, 7, 12.67f, 7.67f, 12, 8.5f, 12,
R_H_LINE_TO, 0,
CUBIC_TO, 9.33f, 12, 10, 12.67f, 10, 13.5f,
CUBIC_TO, 10, 14.33f, 9.33f, 15, 8.5f, 15,
R_H_LINE_TO, 0,
CUBIC_TO, 7.67f, 15, 7, 14.33f, 7, 13.5f,
TRANSITION_TO,
MOVE_TO, 4.68f, 13.5f,
CUBIC_TO, 4.68f, 12.95f, 5.13f, 12.5f, 5.68f, 12.5f,
R_H_LINE_TO, 5.65f,
CUBIC_TO, 11.87f, 12.5f, 12.32f, 12.95f, 12.32f, 13.5f,
CUBIC_TO, 12.32f, 14.05f, 11.87f, 14.5f, 11.32f, 14.5f,
R_H_LINE_TO, -5.65f,
CUBIC_TO, 5.13f, 14.5f, 4.68f, 14.05f, 4.68f, 12.5f,
TRANSITION_END, 0, 150, gfx::Tween::FAST_OUT_SLOW_IN,
TRANSITION_TO,
MOVE_TO, 7, 13.5f,
CUBIC_TO, 7, 12.67f, 7.67f, 12, 8.5f, 12,
R_H_LINE_TO, 0,
CUBIC_TO, 9.33f, 12, 10, 12.67f, 10, 13.5f,
CUBIC_TO, 10, 14.33f, 9.33f, 15, 8.5f, 15,
R_H_LINE_TO, 0,
CUBIC_TO, 7.67f, 15, 7, 14.33f, 7, 13.5f,
TRANSITION_END, 283, 400, gfx::Tween::FAST_OUT_SLOW_IN,
CLOSE,
END
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// TODO(estade): this is copied from 1x. Instead, apply motion to the unanimated
// 2x asset.
CANVAS_DIMENSIONS, 16,
// Top dot.
TRANSITION_FROM,
TRANSITION_FROM,
MOVE_TO, 7, 3.5f,
CUBIC_TO, 7, 2.67f, 7.67f, 2, 8.5f, 2,
R_H_LINE_TO, 0,
CUBIC_TO, 9.33f, 2, 10, 2.67f, 10, 3.5f,
CUBIC_TO, 10, 4.33f, 9.33f, 5, 8.5f, 5,
R_H_LINE_TO, 0,
CUBIC_TO, 7.67f, 5, 7, 4.33f, 7, 3.5f,
TRANSITION_TO,
MOVE_TO, 4.68f, 3.5f,
CUBIC_TO, 4.68f, 2.95f, 5.13f, 2.5f, 5.68f, 2.5f,
R_H_LINE_TO, 5.65f,
CUBIC_TO, 11.87f, 2.5f, 12.32f, 2.95f, 12.32f, 3.5f,
CUBIC_TO, 12.32f, 4.05f, 11.87f, 4.5f, 11.32f, 4.5f,
R_H_LINE_TO, -5.65f,
CUBIC_TO, 5.13f, 4.5f, 4.68f, 4.05f, 4.68f, 2.5f,
TRANSITION_END, 133, 150, gfx::Tween::FAST_OUT_SLOW_IN,
TRANSITION_TO,
MOVE_TO, 7, 3.5f,
CUBIC_TO, 7, 2.67f, 7.67f, 2, 8.5f, 2,
LINE_TO, 8.5f, 2,
CUBIC_TO, 9.33f, 2, 10, 2.67f, 10, 3.5f,
CUBIC_TO, 10, 4.33f, 9.33f, 5, 8.5f, 5,
LINE_TO, 8.5f, 5,
CUBIC_TO, 7.67f, 5, 7, 4.33f, 7, 3.5f,
TRANSITION_END, 416, 533, gfx::Tween::FAST_OUT_SLOW_IN,
CLOSE,
NEW_PATH,
// Middle dot.
TRANSITION_FROM,
TRANSITION_FROM,
MOVE_TO, 7, 8.5f,
CUBIC_TO, 7, 7.67f, 7.67f, 7, 8.5f, 7,
R_H_LINE_TO, 0,
CUBIC_TO, 9.33f, 7, 10, 7.67f, 10, 8.5f,
CUBIC_TO, 10, 9.33f, 9.33f, 10, 8.5f, 10,
R_H_LINE_TO, 0,
CUBIC_TO, 7.67f, 10, 7, 9.33f, 7, 8.5f,
TRANSITION_TO,
MOVE_TO, 4.68f, 8.5f,
CUBIC_TO, 4.68f, 7.95f, 5.13f, 7.5f, 5.68f, 7.5f,
R_H_LINE_TO, 5.65f,
CUBIC_TO, 11.87f, 7.5f, 12.32f, 7.95f, 12.32f, 8.5f,
CUBIC_TO, 12.32f, 9.05f, 11.87f, 9.5f, 11.32f, 9.5f,
R_H_LINE_TO, -5.65f,
CUBIC_TO, 5.13f, 9.5f, 4.68f, 9.05f, 4.68f, 7.5f,
TRANSITION_END, 67, 150, gfx::Tween::FAST_OUT_SLOW_IN,
TRANSITION_TO,
MOVE_TO, 7, 8.5f,
CUBIC_TO, 7, 7.67f, 7.67f, 7, 8.5f, 7,
R_H_LINE_TO, 0,
CUBIC_TO, 9.33f, 7, 10, 7.67f, 10, 8.5f,
CUBIC_TO, 10, 9.33f, 9.33f, 10, 8.5f, 10,
R_H_LINE_TO, 0,
CUBIC_TO, 7.67f, 10, 7, 9.33f, 7, 8.5f,
TRANSITION_END, 350, 383, gfx::Tween::FAST_OUT_SLOW_IN,
CLOSE,
NEW_PATH,
// Bottom dot.
TRANSITION_FROM,
TRANSITION_FROM,
MOVE_TO, 7, 13.5f,
CUBIC_TO, 7, 12.67f, 7.67f, 12, 8.5f, 12,
R_H_LINE_TO, 0,
CUBIC_TO, 9.33f, 12, 10, 12.67f, 10, 13.5f,
CUBIC_TO, 10, 14.33f, 9.33f, 15, 8.5f, 15,
R_H_LINE_TO, 0,
CUBIC_TO, 7.67f, 15, 7, 14.33f, 7, 13.5f,
TRANSITION_TO,
MOVE_TO, 4.68f, 13.5f,
CUBIC_TO, 4.68f, 12.95f, 5.13f, 12.5f, 5.68f, 12.5f,
R_H_LINE_TO, 5.65f,
CUBIC_TO, 11.87f, 12.5f, 12.32f, 12.95f, 12.32f, 13.5f,
CUBIC_TO, 12.32f, 14.05f, 11.87f, 14.5f, 11.32f, 14.5f,
R_H_LINE_TO, -5.65f,
CUBIC_TO, 5.13f, 14.5f, 4.68f, 14.05f, 4.68f, 12.5f,
TRANSITION_END, 0, 150, gfx::Tween::FAST_OUT_SLOW_IN,
TRANSITION_TO,
MOVE_TO, 7, 13.5f,
CUBIC_TO, 7, 12.67f, 7.67f, 12, 8.5f, 12,
R_H_LINE_TO, 0,
CUBIC_TO, 9.33f, 12, 10, 12.67f, 10, 13.5f,
CUBIC_TO, 10, 14.33f, 9.33f, 15, 8.5f, 15,
R_H_LINE_TO, 0,
CUBIC_TO, 7.67f, 15, 7, 14.33f, 7, 13.5f,
TRANSITION_END, 283, 400, gfx::Tween::FAST_OUT_SLOW_IN,
CLOSE,
END
......@@ -8,6 +8,7 @@
#include "chrome/app/vector_icons/vector_icons.h"
#include "base/logging.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/vector_icon_types.h"
#define PATH_ELEMENT_TEMPLATE(path_name, ...) \
......
......@@ -879,8 +879,6 @@ split_static_library("ui") {
"task_manager/task_manager_columns.h",
"task_manager/task_manager_table_model.cc",
"task_manager/task_manager_table_model.h",
"toolbar/app_menu_animation.cc",
"toolbar/app_menu_animation.h",
"toolbar/app_menu_icon_controller.cc",
"toolbar/app_menu_icon_controller.h",
"toolbar/app_menu_model.cc",
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/toolbar/app_menu_animation.h"
#include "base/memory/ptr_util.h"
#include "cc/paint/paint_flags.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/skia_util.h"
namespace {
// Duration of the open and close animations in ms.
constexpr float kOpenDurationMs = 733.0f;
constexpr float kCloseDurationMs = 283.0f;
// Duration of the color animation in ms.
constexpr float kColorDurationMs = 100.0f;
// The radius of each dot in the icon.
constexpr float kDotRadius = 2.0f;
// The % the top and bottom dots need to be offset from the middle.
constexpr float kDotYOffset = 0.32f;
// Value of the stroke when the icon is opened or closed.
constexpr float kCloseStroke = 0.204f;
constexpr float kOpenStroke = 0.136f;
// Value of the width when the animation is fully opened.
constexpr float kOpenWidth = 0.52f;
// The delay of the color and dot animations in ms.
constexpr float kColorDelayMs = 33.33f;
constexpr float kDotDelayMs = 66.67f;
// The % of time it takes for each dot to animate to its full width.
constexpr float kTopWidthOpenInterval = 533.3f / kOpenDurationMs;
constexpr float kMiddleWidthOpenInterval = 383.3f / kOpenDurationMs;
constexpr float kBottomWidthOpenInterval = 400.0f / kOpenDurationMs;
// The % of time it takes for each dot to animate to its final stroke.
constexpr float kTopStrokeOpenInterval = 400.0f / kOpenDurationMs;
constexpr float kMiddleStrokeOpenInterval = 283.3f / kOpenDurationMs;
constexpr float kBottomStrokeOpenInterval = 266.7f / kOpenDurationMs;
// The % of time it takes for each dot to animate its width and stroke.
constexpr float kWidthStrokeCloseInterval = 150.0f / kCloseDurationMs;
} // namespace
AppMenuAnimation::AppMenuDot::AppMenuDot(base::TimeDelta delay,
float width_open_interval,
float stroke_open_interval)
: delay_(delay),
width_open_interval_(width_open_interval),
stroke_open_interval_(stroke_open_interval) {}
void AppMenuAnimation::AppMenuDot::Paint(const gfx::PointF& center_point,
SkColor start_color,
SkColor target_color,
gfx::Canvas* canvas,
const gfx::Rect& bounds,
const gfx::SlideAnimation* animation,
AppMenuAnimationDelegate* delegate) {
bool is_opening = animation->IsShowing();
float total_duration = is_opening ? kOpenDurationMs : kCloseDurationMs;
float width_duration =
is_opening ? width_open_interval_ : kWidthStrokeCloseInterval;
float stroke_duration =
is_opening ? stroke_open_interval_ : kWidthStrokeCloseInterval;
// When the animation is closing, each dot uses the remainder of the full
// delay period (2 * kDotDelayMs). The results should be (0->2x, 1x->1x,
// 2x->0).
base::TimeDelta delay =
is_opening ? delay_
: base::TimeDelta::FromMilliseconds(kDotDelayMs * 2) - delay_;
float progress =
animation->GetCurrentValue() - (delay.InMillisecondsF() / total_duration);
float width_progress = 0.0;
float stroke_progress = 0.0;
float color_progress = 0.0;
if (progress > 0) {
width_progress = std::min(1.0f, progress / width_duration);
stroke_progress = std::min(1.0f, progress / stroke_duration);
if (is_opening) {
float color_delay_interval = kColorDelayMs / total_duration;
float color_duration_interval = kColorDurationMs / total_duration;
if (progress > color_delay_interval) {
color_progress = std::min(
1.0f, (progress - color_delay_interval) / color_duration_interval);
}
}
}
float dot_height =
gfx::Tween::FloatValueBetween(stroke_progress, kCloseStroke, kOpenStroke);
dot_height *= bounds.height();
float dot_width =
gfx::Tween::FloatValueBetween(width_progress, kCloseStroke, kOpenWidth);
dot_width *= bounds.width();
gfx::PointF point = center_point;
point.Offset(-dot_width / 2, -dot_height / 2);
SkColor color = is_opening ? gfx::Tween::ColorValueBetween(
color_progress, start_color, target_color)
: target_color;
cc::PaintFlags flags;
flags.setColor(color);
flags.setStrokeWidth(dot_height);
flags.setStrokeCap(cc::PaintFlags::kRound_Cap);
flags.setStyle(cc::PaintFlags::kFill_Style);
flags.setAntiAlias(true);
canvas->DrawRoundRect(gfx::RectF(point, gfx::SizeF(dot_width, dot_height)),
kDotRadius, flags);
}
AppMenuAnimation::AppMenuAnimation(AppMenuAnimationDelegate* delegate,
SkColor initial_color)
: delegate_(delegate),
animation_(base::MakeUnique<gfx::SlideAnimation>(this)),
bottom_dot_(base::TimeDelta(),
kBottomWidthOpenInterval,
kBottomStrokeOpenInterval),
middle_dot_(base::TimeDelta::FromMilliseconds(kDotDelayMs),
kMiddleWidthOpenInterval,
kMiddleStrokeOpenInterval),
top_dot_(base::TimeDelta::FromMilliseconds(kDotDelayMs * 2),
kTopWidthOpenInterval,
kTopStrokeOpenInterval),
start_color_(initial_color),
target_color_(initial_color) {
animation_->SetSlideDuration(kOpenDurationMs);
animation_->SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
}
AppMenuAnimation::~AppMenuAnimation() {}
void AppMenuAnimation::PaintAppMenu(gfx::Canvas* canvas,
const gfx::Rect& bounds) {
gfx::PointF middle_point = gfx::PointF(bounds.CenterPoint());
float y_offset = kDotYOffset * bounds.height();
gfx::PointF top_point = middle_point;
top_point.Offset(0, -y_offset);
gfx::PointF bottom_point = middle_point;
bottom_point.Offset(0, y_offset);
middle_dot_.Paint(middle_point, start_color_, target_color_, canvas, bounds,
animation_.get(), delegate_);
top_dot_.Paint(top_point, start_color_, target_color_, canvas, bounds,
animation_.get(), delegate_);
bottom_dot_.Paint(bottom_point, start_color_, target_color_, canvas, bounds,
animation_.get(), delegate_);
}
void AppMenuAnimation::StartAnimation() {
if (!animation_->is_animating()) {
animation_->SetSlideDuration(kOpenDurationMs);
animation_->Show();
delegate_->AppMenuAnimationStarted();
}
}
void AppMenuAnimation::AnimationEnded(const gfx::Animation* animation) {
if (animation_->IsShowing()) {
animation_->SetSlideDuration(kCloseDurationMs);
animation_->Hide();
} else {
start_color_ = target_color_;
}
delegate_->AppMenuAnimationEnded();
}
void AppMenuAnimation::AnimationProgressed(const gfx::Animation* animation) {
delegate_->InvalidateIcon();
}
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_TOOLBAR_APP_MENU_ANIMATION_H_
#define CHROME_BROWSER_UI_TOOLBAR_APP_MENU_ANIMATION_H_
#include "base/time/time.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/gfx/animation/slide_animation.h"
namespace gfx {
class Canvas;
class PointF;
} // namespace gfx
// Delegate class for AppMenuAnimation. The delegate is expected to
// handle animation events and invalidate the platform's view.
class AppMenuAnimationDelegate {
public:
// Called when the animation has started/ended.
virtual void AppMenuAnimationStarted() = 0;
virtual void AppMenuAnimationEnded() = 0;
// Schedules a redraw of the icon.
virtual void InvalidateIcon() = 0;
};
// This class is used for animating and drawing the app menu icon.
class AppMenuAnimation : public gfx::AnimationDelegate {
public:
AppMenuAnimation(AppMenuAnimationDelegate* owner, SkColor initial_color);
~AppMenuAnimation() override;
// Paints the app menu icon.
void PaintAppMenu(gfx::Canvas* canvas, const gfx::Rect& bounds);
// Starts the animation if it's not already running.
void StartAnimation();
// gfx::AnimationDelegate:
void AnimationEnded(const gfx::Animation* animation) override;
void AnimationProgressed(const gfx::Animation* animation) override;
void set_target_color(SkColor target_color) { target_color_ = target_color; }
private:
// This class is used to represent and paint a dot on the app menu.
class AppMenuDot {
public:
AppMenuDot(base::TimeDelta delay,
float width_open_interval,
float stroke_open_interval);
// Paints the dot on the given |canvas| according to the progress of
// |animation|. The size of the dot is calculated to fit in |bounds|.
// |center_point| is the dot's position on the canvas. The dot's color is
// a transition from |start_color| to |final_color|.
void Paint(const gfx::PointF& center_point,
SkColor start_color,
SkColor final_color,
gfx::Canvas* canvas,
const gfx::Rect& bounds,
const gfx::SlideAnimation* animation,
AppMenuAnimationDelegate* delegate);
private:
// The delay before the dot starts animating in ms.
const base::TimeDelta delay_;
// The percentage of the overall animation duration it takes to animate the
// width and stroke to their open state.
const float width_open_interval_;
const float stroke_open_interval_;
DISALLOW_COPY_AND_ASSIGN(AppMenuDot);
};
AppMenuAnimationDelegate* const delegate_;
std::unique_ptr<gfx::SlideAnimation> animation_;
AppMenuDot bottom_dot_;
AppMenuDot middle_dot_;
AppMenuDot top_dot_;
// The starting color of the dots. The animation is expected to transition
// from this color to |target_color_|.
SkColor start_color_;
// The severity color of the dots. This is final color at the end of the
// animation.
SkColor target_color_;
DISALLOW_COPY_AND_ASSIGN(AppMenuAnimation);
};
#endif // CHROME_BROWSER_UI_TOOLBAR_APP_MENU_ANIMATION_H_
......@@ -14,11 +14,12 @@
#include "cc/paint/paint_flags.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_otr_state.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar/app_menu_animation.h"
#include "chrome/browser/ui/toolbar/app_menu_model.h"
#include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
#include "chrome/browser/ui/views/toolbar/app_menu.h"
......@@ -46,12 +47,7 @@ bool AppMenuButton::g_open_app_immediately_for_testing = false;
AppMenuButton::AppMenuButton(ToolbarView* toolbar_view)
: views::MenuButton(base::string16(), toolbar_view, false),
severity_(AppMenuIconController::Severity::NONE),
type_(AppMenuIconController::IconType::NONE),
toolbar_view_(toolbar_view),
should_use_new_icon_(false),
margin_trailing_(0),
weak_factory_(this) {
toolbar_view_(toolbar_view) {
SetInkDropMode(InkDropMode::ON);
SetFocusPainter(nullptr);
......@@ -133,7 +129,8 @@ gfx::Size AppMenuButton::CalculatePreferredSize() const {
}
void AppMenuButton::Layout() {
if (animation_) {
if (new_icon_) {
new_icon_->SetBoundsRect(GetContentsBounds());
ink_drop_container()->SetBoundsRect(GetLocalBounds());
image()->SetBoundsRect(GetLocalBounds());
return;
......@@ -142,15 +139,8 @@ void AppMenuButton::Layout() {
views::MenuButton::Layout();
}
void AppMenuButton::PaintButtonContents(gfx::Canvas* canvas) {
if (!animation_) {
views::MenuButton::PaintButtonContents(canvas);
return;
}
gfx::Rect bounds = GetLocalBounds();
bounds.Inset(gfx::Insets(ToolbarButton::kInteriorPadding));
animation_->PaintAppMenu(canvas, bounds);
void AppMenuButton::OnThemeChanged() {
UpdateIcon(false);
}
void AppMenuButton::TabInsertedAt(TabStripModel* tab_strip_model,
......@@ -160,19 +150,6 @@ void AppMenuButton::TabInsertedAt(TabStripModel* tab_strip_model,
AnimateIconIfPossible();
}
void AppMenuButton::AppMenuAnimationStarted() {
SetPaintToLayer();
layer()->SetFillsBoundsOpaquely(false);
}
void AppMenuButton::AppMenuAnimationEnded() {
DestroyLayer();
}
void AppMenuButton::InvalidateIcon() {
SchedulePaint();
}
void AppMenuButton::UpdateIcon(bool should_animate) {
SkColor severity_color = gfx::kPlaceholderColor;
SkColor toolbar_icon_color =
......@@ -197,10 +174,21 @@ void AppMenuButton::UpdateIcon(bool should_animate) {
}
if (should_use_new_icon_) {
if (!animation_)
animation_ = base::MakeUnique<AppMenuAnimation>(this, toolbar_icon_color);
if (!new_icon_) {
new_icon_ = new views::AnimatedIconView(kBrowserToolsAnimatedIcon);
new_icon_->set_can_process_events_within_subtree(false);
AddChildView(new_icon_);
}
// Only show a special color for severity when using the classic Chrome
// theme. Otherwise, we can't be sure that it contrasts with the toolbar
// background.
new_icon_->set_color(
ThemeServiceFactory::GetForProfile(toolbar_view_->browser()->profile())
->UsingDefaultTheme()
? severity_color
: toolbar_icon_color);
animation_->set_target_color(severity_color);
if (should_animate)
AnimateIconIfPossible();
......@@ -233,12 +221,12 @@ void AppMenuButton::SetTrailingMargin(int margin) {
}
void AppMenuButton::AnimateIconIfPossible() {
if (!animation_ || !should_use_new_icon_ ||
if (!new_icon_ || !should_use_new_icon_ ||
severity_ == AppMenuIconController::Severity::NONE) {
return;
}
animation_->StartAnimation();
new_icon_->Animate(views::AnimatedIconView::END);
}
const char* AppMenuButton::GetClassName() const {
......
......@@ -10,8 +10,8 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "chrome/browser/ui/toolbar/app_menu_animation.h"
#include "chrome/browser/ui/toolbar/app_menu_icon_controller.h"
#include "ui/views/controls/animated_icon_view.h"
#include "ui/views/controls/button/menu_button.h"
#include "ui/views/controls/button/menu_button_listener.h"
#include "ui/views/view.h"
......@@ -26,9 +26,10 @@ class MenuListener;
class ToolbarView;
class AppMenuButton : public views::MenuButton,
public TabStripModelObserver,
public AppMenuAnimationDelegate {
// The app menu button lives in the top right of the main browser window. It
// shows three dots and animates to a hamburger-ish icon when there's a need to
// alert the user. Clicking displays the app menu.
class AppMenuButton : public views::MenuButton, public TabStripModelObserver {
public:
explicit AppMenuButton(ToolbarView* toolbar_view);
~AppMenuButton() override;
......@@ -58,7 +59,7 @@ class AppMenuButton : public views::MenuButton,
// views::MenuButton:
gfx::Size CalculatePreferredSize() const override;
void Layout() override;
void PaintButtonContents(gfx::Canvas* canvas) override;
void OnThemeChanged() override;
// TabStripObserver:
void TabInsertedAt(TabStripModel* tab_strip_model,
......@@ -66,11 +67,6 @@ class AppMenuButton : public views::MenuButton,
int index,
bool foreground) override;
// AppMenuAnimationDelegate:
void AppMenuAnimationStarted() override;
void AppMenuAnimationEnded() override;
void InvalidateIcon() override;
// Updates the presentation according to |severity_| and the theme provider.
// If |should_animate| is true, the icon should animate.
void UpdateIcon(bool should_animate);
......@@ -103,8 +99,9 @@ class AppMenuButton : public views::MenuButton,
void OnDragExited() override;
int OnPerformDrop(const ui::DropTargetEvent& event) override;
AppMenuIconController::Severity severity_;
AppMenuIconController::IconType type_;
AppMenuIconController::Severity severity_ =
AppMenuIconController::Severity::NONE;
AppMenuIconController::IconType type_ = AppMenuIconController::IconType::NONE;
// Our owning toolbar view.
ToolbarView* toolbar_view_;
......@@ -118,18 +115,20 @@ class AppMenuButton : public views::MenuButton,
std::unique_ptr<AppMenuModel> menu_model_;
std::unique_ptr<AppMenu> menu_;
// Used for animating and drawing the app menu icon.
std::unique_ptr<AppMenuAnimation> animation_;
// The view that depicts and animates the icon. TODO(estade): rename to
// |animated_icon_| when |should_use_new_icon_| defaults to true and is
// removed.
views::AnimatedIconView* new_icon_ = nullptr;
// True if the app menu should use the new animated icon.
bool should_use_new_icon_;
bool should_use_new_icon_ = false;
// Any trailing margin to be applied. Used when the browser is in
// a maximized state to extend to the full window width.
int margin_trailing_;
int margin_trailing_ = 0;
// Used to spawn weak pointers for delayed tasks to open the overflow menu.
base::WeakPtrFactory<AppMenuButton> weak_factory_;
base::WeakPtrFactory<AppMenuButton> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(AppMenuButton);
};
......
......@@ -19,6 +19,7 @@
#include "third_party/skia/include/core/SkPath.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/image/canvas_image_source.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/gfx/vector_icon_types.h"
......@@ -27,6 +28,17 @@ namespace gfx {
namespace {
struct CompareIconDescription {
bool operator()(const IconDescription& a, const IconDescription& b) const {
const VectorIcon* a_icon = &a.icon;
const VectorIcon* b_icon = &b.icon;
const VectorIcon* a_badge = &a.badge_icon;
const VectorIcon* b_badge = &b.badge_icon;
return std::tie(a_icon, a.dip_size, a.color, a.elapsed_time, a_badge) <
std::tie(b_icon, b.dip_size, b.color, b.elapsed_time, b_badge);
}
};
// Helper that simplifies iterating over a sequence of PathElements.
class PathParser {
public:
......@@ -437,43 +449,36 @@ void PaintPath(Canvas* canvas,
class VectorIconSource : public CanvasImageSource {
public:
VectorIconSource(const VectorIcon& icon,
int dip_size,
SkColor color,
const VectorIcon& badge_icon)
: CanvasImageSource(Size(dip_size, dip_size), false),
color_(color),
icon_(icon),
badge_(badge_icon) {}
explicit VectorIconSource(const IconDescription& data)
: CanvasImageSource(Size(data.dip_size, data.dip_size), false),
data_(data) {}
VectorIconSource(const std::string& definition, int dip_size, SkColor color)
: CanvasImageSource(Size(dip_size, dip_size), false),
color_(color),
icon_(kNoneIcon),
badge_(kNoneIcon),
data_(kNoneIcon, dip_size, color, base::TimeDelta(), kNoneIcon),
path_(PathFromSource(definition)) {}
~VectorIconSource() override {}
// CanvasImageSource:
bool HasRepresentationAtAllScales() const override {
return !icon_.is_empty();
return !data_.icon.is_empty();
}
void Draw(Canvas* canvas) override {
if (path_.empty()) {
PaintVectorIcon(canvas, icon_, size_.width(), color_);
if (!badge_.is_empty())
PaintVectorIcon(canvas, badge_, size_.width(), color_);
PaintVectorIcon(canvas, data_.icon, size_.width(), data_.color,
data_.elapsed_time);
if (!data_.badge_icon.is_empty())
PaintVectorIcon(canvas, data_.badge_icon, size_.width(), data_.color);
} else {
PaintPath(canvas, path_.data(), size_.width(), color_, base::TimeDelta());
PaintPath(canvas, path_.data(), size_.width(), data_.color,
base::TimeDelta());
}
}
private:
const SkColor color_;
const VectorIcon& icon_;
const VectorIcon& badge_;
const IconDescription data_;
const std::vector<PathElement> path_;
DISALLOW_COPY_AND_ASSIGN(VectorIconSource);
......@@ -487,46 +492,19 @@ class VectorIconCache {
VectorIconCache() {}
~VectorIconCache() {}
ImageSkia GetOrCreateIcon(const VectorIcon& icon,
int dip_size,
SkColor color,
const VectorIcon& badge_icon) {
IconDescription description(&icon, dip_size, color, &badge_icon);
ImageSkia GetOrCreateIcon(const IconDescription& description) {
auto iter = images_.find(description);
if (iter != images_.end())
return iter->second;
ImageSkia icon_image(
new VectorIconSource(icon, dip_size, color, badge_icon),
Size(dip_size, dip_size));
ImageSkia icon_image(new VectorIconSource(description),
Size(description.dip_size, description.dip_size));
images_.insert(std::make_pair(description, icon_image));
return icon_image;
}
private:
struct IconDescription {
IconDescription(const VectorIcon* icon,
int dip_size,
SkColor color,
const VectorIcon* badge_icon)
: icon(icon),
dip_size(dip_size),
color(color),
badge_icon(badge_icon) {}
bool operator<(const IconDescription& other) const {
return std::tie(icon, dip_size, color, badge_icon) <
std::tie(other.icon, other.dip_size, other.color,
other.badge_icon);
}
const VectorIcon* icon;
int dip_size;
SkColor color;
const VectorIcon* badge_icon;
};
std::map<IconDescription, ImageSkia> images_;
std::map<IconDescription, ImageSkia, CompareIconDescription> images_;
DISALLOW_COPY_AND_ASSIGN(VectorIconCache);
};
......@@ -536,6 +514,24 @@ static base::LazyInstance<VectorIconCache>::DestructorAtExit g_icon_cache =
} // namespace
IconDescription::IconDescription(const IconDescription& other) = default;
IconDescription::IconDescription(const VectorIcon& icon,
int dip_size,
SkColor color,
const base::TimeDelta& elapsed_time,
const VectorIcon& badge_icon)
: icon(icon),
dip_size(dip_size),
color(color),
elapsed_time(elapsed_time),
badge_icon(badge_icon) {
if (dip_size == 0)
this->dip_size = GetDefaultSizeOfVectorIcon(icon);
}
IconDescription::~IconDescription() {}
const VectorIcon kNoneIcon = {};
void PaintVectorIcon(Canvas* canvas,
......@@ -557,6 +553,13 @@ void PaintVectorIcon(Canvas* canvas,
PaintPath(canvas, path, dip_size, color, elapsed_time);
}
ImageSkia CreateVectorIcon(const IconDescription& params) {
if (params.icon.is_empty())
return gfx::ImageSkia();
return g_icon_cache.Get().GetOrCreateIcon(params);
}
ImageSkia CreateVectorIcon(const VectorIcon& icon, SkColor color) {
return CreateVectorIcon(icon, GetDefaultSizeOfVectorIcon(icon), color);
}
......@@ -564,16 +567,16 @@ ImageSkia CreateVectorIcon(const VectorIcon& icon, SkColor color) {
ImageSkia CreateVectorIcon(const VectorIcon& icon,
int dip_size,
SkColor color) {
return CreateVectorIconWithBadge(icon, dip_size, color, kNoneIcon);
return CreateVectorIcon(
IconDescription(icon, dip_size, color, base::TimeDelta(), kNoneIcon));
}
ImageSkia CreateVectorIconWithBadge(const VectorIcon& icon,
int dip_size,
SkColor color,
const VectorIcon& badge_icon) {
return icon.is_empty() ? ImageSkia()
: g_icon_cache.Get().GetOrCreateIcon(
icon, dip_size, color, badge_icon);
return CreateVectorIcon(
IconDescription(icon, dip_size, color, base::TimeDelta(), badge_icon));
}
ImageSkia CreateVectorIconFromSource(const std::string& source,
......
......@@ -15,6 +15,26 @@ namespace gfx {
class Canvas;
struct VectorIcon;
// Describes an instance of an icon: an icon definition and a set of drawing
// parameters.
struct GFX_EXPORT IconDescription {
IconDescription(const IconDescription& other);
IconDescription(const VectorIcon& icon,
int dip_size,
SkColor color,
const base::TimeDelta& elapsed_time,
const VectorIcon& badge_icon);
~IconDescription();
const VectorIcon& icon;
int dip_size;
SkColor color;
const base::TimeDelta elapsed_time;
const VectorIcon& badge_icon;
};
GFX_EXPORT extern const VectorIcon kNoneIcon;
// Draws a vector icon identified by |id| onto |canvas| at (0, 0). |color| is
......@@ -36,6 +56,11 @@ GFX_EXPORT void PaintVectorIcon(
SkColor color,
const base::TimeDelta& elapsed_time = base::TimeDelta());
// Creates an ImageSkia which will render the icon on demand.
// TODO(estade): update clients to use this version and remove the other
// CreateVectorIcon()s.
GFX_EXPORT ImageSkia CreateVectorIcon(const IconDescription& params);
// Creates an ImageSkia which will render the icon on demand. The size will come
// from the .icon file (the 1x version, if multiple versions exist).
GFX_EXPORT ImageSkia CreateVectorIcon(const VectorIcon& icon, SkColor color);
......@@ -66,7 +91,7 @@ GFX_EXPORT int GetDefaultSizeOfVectorIcon(const gfx::VectorIcon& icon);
// Calculates and returns the elapsed time at which all animations/transitions
// will be finished.
base::TimeDelta GetDurationOfAnimation(const VectorIcon& icon);
GFX_EXPORT base::TimeDelta GetDurationOfAnimation(const VectorIcon& icon);
} // namespace gfx
......
......@@ -112,6 +112,8 @@ component("views") {
"color_chooser/color_chooser_view.cc",
"color_chooser/color_chooser_view.h",
"context_menu_controller.h",
"controls/animated_icon_view.cc",
"controls/animated_icon_view.h",
"controls/button/blue_button.cc",
"controls/button/blue_button.h",
"controls/button/button.cc",
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/controls/animated_icon_view.h"
#include "ui/compositor/compositor.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/widget/widget.h"
namespace views {
AnimatedIconView::AnimatedIconView(const gfx::VectorIcon& icon)
: icon_(icon),
color_(gfx::kPlaceholderColor),
duration_(gfx::GetDurationOfAnimation(icon)) {
UpdateStaticImage();
}
AnimatedIconView::~AnimatedIconView() {}
void AnimatedIconView::Animate(State target) {
SetState(target);
if (!IsAnimating())
GetWidget()->GetCompositor()->AddAnimationObserver(this);
start_time_ = base::TimeTicks::Now();
}
void AnimatedIconView::SetState(State state) {
state_ = state;
UpdateStaticImage();
}
void AnimatedIconView::OnPaint(gfx::Canvas* canvas) {
if (!IsAnimating()) {
views::ImageView::OnPaint(canvas);
return;
}
auto timestamp = base::TimeTicks::Now();
base::TimeDelta elapsed = timestamp - start_time_;
if (state_ == END)
elapsed = start_time_ + duration_ - timestamp;
canvas->Translate(GetImageBounds().OffsetFromOrigin());
gfx::PaintVectorIcon(canvas, icon_, color_, elapsed);
}
void AnimatedIconView::OnAnimationStep(base::TimeTicks timestamp) {
base::TimeDelta elapsed = timestamp - start_time_;
if (elapsed > duration_) {
GetWidget()->GetCompositor()->RemoveAnimationObserver(this);
start_time_ = base::TimeTicks();
}
SchedulePaint();
}
void AnimatedIconView::OnCompositingShuttingDown(ui::Compositor* compositor) {}
bool AnimatedIconView::IsAnimating() const {
return start_time_ != base::TimeTicks();
}
void AnimatedIconView::UpdateStaticImage() {
gfx::IconDescription description(
icon_, 0, color_, state_ == START ? base::TimeDelta() : duration_,
gfx::kNoneIcon);
SetImage(gfx::CreateVectorIcon(description));
}
} // namespace views
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_VIEWS_CONTROLS_ANIMATED_ICON_VIEW_H_
#define UI_VIEWS_CONTROLS_ANIMATED_ICON_VIEW_H_
#include "base/macros.h"
#include "base/time/time.h"
#include "ui/compositor/compositor_animation_observer.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/views/controls/image_view.h"
namespace views {
// This class hosts a vector icon that defines transitions. It can be in the
// start steady state, the end steady state, or transitioning in between.
class VIEWS_EXPORT AnimatedIconView : public views::ImageView,
public ui::CompositorAnimationObserver {
public:
enum State {
START,
END,
};
explicit AnimatedIconView(const gfx::VectorIcon& icon);
~AnimatedIconView() override;
void set_color(SkColor color) { color_ = color; }
// Animates to the end or start state.
void Animate(State target);
// Jumps to the end or start state.
void SetState(State state);
// views::ImageView
void OnPaint(gfx::Canvas* canvas) override;
// ui::CompositorAnimationObserver
void OnAnimationStep(base::TimeTicks timestamp) override;
void OnCompositingShuttingDown(ui::Compositor* compositor) override;
private:
bool IsAnimating() const;
void UpdateStaticImage();
const gfx::VectorIcon& icon_;
SkColor color_;
// Tracks the last time Animate() was called.
base::TimeTicks start_time_;
// The amount of time that must elapse until all transitions are done, i.e.
// the length of the animation.
const base::TimeDelta duration_;
// The current state, or when transitioning the goal state.
State state_ = START;
DISALLOW_COPY_AND_ASSIGN(AnimatedIconView);
};
} // namespace views
#endif // UI_VIEWS_CONTROLS_ANIMATED_ICON_VIEW_H_
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