summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorestade <estade@chromium.org>2015-05-03 13:21:26 -0700
committerCommit bot <commit-bot@chromium.org>2015-05-03 20:22:01 +0000
commit4b7d20a22e0c0a06109434005f2d39ac88f51722 (patch)
treed66b32ea0d2bda85e0b00e5806277d0c8d673ba8
parent7d724373197ee4d8b7bc2bc968d9f25333b284fe (diff)
downloadchromium_src-4b7d20a22e0c0a06109434005f2d39ac88f51722.zip
chromium_src-4b7d20a22e0c0a06109434005f2d39ac88f51722.tar.gz
chromium_src-4b7d20a22e0c0a06109434005f2d39ac88f51722.tar.bz2
Material throbber: use in tabstrip
- move paint code to ui/gfx - add color and tinting enums in native_theme and ThemeProvider, calculate tinted color in ThemeService - port tabs to use new throbber - ash system throbber also gets new throbber - merge views::Throbber and view::MaterialThrobber and delete some code that wasn't being used (frame_time_ms was always the same, for example) BUG=473342, 461137 Review URL: https://codereview.chromium.org/1123573002 Cr-Commit-Position: refs/heads/master@{#328079}
-rw-r--r--ash/resources/ash_resources.grd2
-rw-r--r--ash/resources/default_100_percent/cros/common/default_throbber.pngbin6686 -> 0 bytes
-rw-r--r--ash/resources/default_200_percent/cros/common/default_throbber.pngbin16231 -> 0 bytes
-rw-r--r--ash/system/tray/throbber_view.cc10
-rw-r--r--ash/system/tray/throbber_view.h2
-rw-r--r--chrome/browser/themes/theme_properties.h2
-rw-r--r--chrome/browser/themes/theme_service.cc14
-rw-r--r--chrome/browser/ui/libgtk2ui/native_theme_gtk2.cc2
-rw-r--r--chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc2
-rw-r--r--chrome/browser/ui/views/autofill/card_unmask_prompt_views.h6
-rw-r--r--chrome/browser/ui/views/tabs/tab.cc64
-rw-r--r--ui/gfx/BUILD.gn2
-rw-r--r--ui/gfx/gfx.gyp2
-rw-r--r--ui/gfx/paint_throbber.cc114
-rw-r--r--ui/gfx/paint_throbber.h38
-rw-r--r--ui/native_theme/common_theme.cc10
-rw-r--r--ui/native_theme/native_theme.h3
-rw-r--r--ui/views/controls/throbber.cc202
-rw-r--r--ui/views/controls/throbber.h70
-rw-r--r--ui/views/examples/throbber_example.cc16
20 files changed, 260 insertions, 301 deletions
diff --git a/ash/resources/ash_resources.grd b/ash/resources/ash_resources.grd
index 2ff0296..c1eb94e 100644
--- a/ash/resources/ash_resources.grd
+++ b/ash/resources/ash_resources.grd
@@ -69,8 +69,6 @@
<structure type="chrome_scaled_image" name="IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND" file="common/tray_popup_label_button_normal_background.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER" file="common/tray_popup_public_account_logout_button_border.png" />
- <structure type="chrome_scaled_image" name="IDR_AURA_CROS_DEFAULT_THROBBER" file="cros/common/default_throbber.png" />
-
<structure type="chrome_scaled_image" name="IDR_AURA_WARNING_ICON" file="common/alert_small.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_ACCESSIBILITY" file="cros/status/status_accessibility_mode.png" />
diff --git a/ash/resources/default_100_percent/cros/common/default_throbber.png b/ash/resources/default_100_percent/cros/common/default_throbber.png
deleted file mode 100644
index 47eeb2c..0000000
--- a/ash/resources/default_100_percent/cros/common/default_throbber.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/common/default_throbber.png b/ash/resources/default_200_percent/cros/common/default_throbber.png
deleted file mode 100644
index 1538e41..0000000
--- a/ash/resources/default_200_percent/cros/common/default_throbber.png
+++ /dev/null
Binary files differ
diff --git a/ash/system/tray/throbber_view.cc b/ash/system/tray/throbber_view.cc
index 90372d1..6ec1f8d 100644
--- a/ash/system/tray/throbber_view.cc
+++ b/ash/system/tray/throbber_view.cc
@@ -13,16 +13,12 @@
namespace ash {
namespace {
-// Time in ms per throbber frame.
-const int kThrobberFrameMs = 30;
-
// Duration for showing/hiding animation in milliseconds.
const int kThrobberAnimationDurationMs = 200;
} // namespace
-SystemTrayThrobber::SystemTrayThrobber(int frame_delay_ms)
- : views::SmoothedThrobber(frame_delay_ms) {
+SystemTrayThrobber::SystemTrayThrobber() : views::SmoothedThrobber() {
}
SystemTrayThrobber::~SystemTrayThrobber() {
@@ -42,9 +38,7 @@ bool SystemTrayThrobber::GetTooltipText(const gfx::Point& p,
}
ThrobberView::ThrobberView() {
- throbber_ = new SystemTrayThrobber(kThrobberFrameMs);
- throbber_->SetFrames(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
- IDR_AURA_CROS_DEFAULT_THROBBER).ToImageSkia());
+ throbber_ = new SystemTrayThrobber();
throbber_->set_stop_delay_ms(kThrobberAnimationDurationMs);
AddChildView(throbber_);
diff --git a/ash/system/tray/throbber_view.h b/ash/system/tray/throbber_view.h
index 08976d6..f09dd8b 100644
--- a/ash/system/tray/throbber_view.h
+++ b/ash/system/tray/throbber_view.h
@@ -14,7 +14,7 @@ namespace ash {
// A SmoothedThrobber with tooltip.
class SystemTrayThrobber : public views::SmoothedThrobber {
public:
- SystemTrayThrobber(int frame_delay_ms);
+ SystemTrayThrobber();
~SystemTrayThrobber() override;
void SetTooltipText(const base::string16& tooltip_text);
diff --git a/chrome/browser/themes/theme_properties.h b/chrome/browser/themes/theme_properties.h
index 530315c..f2f9b08 100644
--- a/chrome/browser/themes/theme_properties.h
+++ b/chrome/browser/themes/theme_properties.h
@@ -88,6 +88,8 @@ class ThemeProperties {
COLOR_NTP_SECTION_HEADER_RULE,
COLOR_NTP_SECTION_HEADER_RULE_LIGHT,
COLOR_NTP_TEXT_LIGHT,
+ COLOR_THROBBER_SPINNING,
+ COLOR_THROBBER_WAITING,
#if defined(ENABLE_SUPERVISED_USERS)
COLOR_SUPERVISED_USER_LABEL,
COLOR_SUPERVISED_USER_LABEL_BACKGROUND,
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index 0594475..8f61303 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -34,6 +34,8 @@
#include "ui/base/layout.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image_skia.h"
+#include "ui/native_theme/common_theme.h"
+#include "ui/native_theme/native_theme.h"
#if defined(ENABLE_EXTENSIONS)
#include "extensions/browser/extension_registry_observer.h"
@@ -227,6 +229,18 @@ SkColor ThemeService::GetColor(int id) const {
return IncreaseLightness(GetColor(Properties::COLOR_NTP_TEXT), 0.86);
case Properties::COLOR_NTP_TEXT_LIGHT:
return IncreaseLightness(GetColor(Properties::COLOR_NTP_TEXT), 0.40);
+ case Properties::COLOR_THROBBER_SPINNING:
+ case Properties::COLOR_THROBBER_WAITING: {
+ SkColor base_color;
+ bool found_color = ui::CommonThemeGetSystemColor(
+ id == Properties::COLOR_THROBBER_SPINNING
+ ? ui::NativeTheme::kColorId_ThrobberSpinningColor
+ : ui::NativeTheme::kColorId_ThrobberWaitingColor,
+ &base_color);
+ DCHECK(found_color);
+ color_utils::HSL hsl = GetTint(Properties::TINT_BUTTONS);
+ return color_utils::HSLShift(base_color, hsl);
+ }
#if defined(ENABLE_SUPERVISED_USERS)
case Properties::COLOR_SUPERVISED_USER_LABEL:
return color_utils::GetReadableColor(
diff --git a/chrome/browser/ui/libgtk2ui/native_theme_gtk2.cc b/chrome/browser/ui/libgtk2ui/native_theme_gtk2.cc
index b55352a..fc8ba3b 100644
--- a/chrome/browser/ui/libgtk2ui/native_theme_gtk2.cc
+++ b/chrome/browser/ui/libgtk2ui/native_theme_gtk2.cc
@@ -405,6 +405,8 @@ GdkColor NativeThemeGtk2::GetSystemGdkColor(ColorId color_id) const {
return GetReadableColor(kNegativeTextColor,
GetEntryStyle()->base[GTK_STATE_SELECTED]);
}
+ case kColorId_ThrobberSpinningColor:
+ case kColorId_ThrobberWaitingColor:
case kColorId_NumColors:
NOTREACHED();
break;
diff --git a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc
index 20425fb..c3f45ad 100644
--- a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc
+++ b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc
@@ -435,7 +435,7 @@ void CardUnmaskPromptViews::InitIfNecessary() {
progress_overlay_->SetVisible(false);
AddChildView(progress_overlay_);
- progress_throbber_ = new views::MaterialThrobber();
+ progress_throbber_ = new views::Throbber();
progress_overlay_->AddChildView(progress_throbber_);
progress_label_ = new views::Label(l10n_util::GetStringUTF16(
diff --git a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.h b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.h
index b272480..ebf8226 100644
--- a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.h
+++ b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.h
@@ -14,10 +14,10 @@
#include "ui/views/window/dialog_delegate.h"
namespace views {
+class Checkbox;
class ImageView;
class Label;
-class Checkbox;
-class MaterialThrobber;
+class Throbber;
}
namespace autofill {
@@ -135,7 +135,7 @@ class CardUnmaskPromptViews : public CardUnmaskPromptView,
views::Checkbox* storage_checkbox_;
FadeOutView* progress_overlay_;
- views::MaterialThrobber* progress_throbber_;
+ views::Throbber* progress_throbber_;
views::Label* progress_label_;
gfx::SlideAnimation overlay_animation_;
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index 25df3600..cfaad36 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -38,6 +38,7 @@
#include "ui/gfx/favicon_size.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/paint_throbber.h"
#include "ui/gfx/path.h"
#include "ui/gfx/skia_util.h"
#include "ui/resources/grit/ui_resources.h"
@@ -1332,15 +1333,16 @@ void Tab::PaintIcon(gfx::Canvas* canvas) {
if (data().network_state != TabRendererData::NETWORK_STATE_NONE) {
// Paint network activity (aka throbber) animation frame.
ui::ThemeProvider* tp = GetThemeProvider();
- gfx::ImageSkia frames(*tp->GetImageSkiaNamed(
- (data().network_state == TabRendererData::NETWORK_STATE_WAITING) ?
- IDR_THROBBER_WAITING : IDR_THROBBER));
-
- int icon_size = frames.height();
- int image_offset = loading_animation_frame_ * icon_size;
- DrawIconCenter(canvas, frames, image_offset,
- icon_size, icon_size,
- bounds, false, SkPaint());
+ if (data().network_state == TabRendererData::NETWORK_STATE_WAITING) {
+ gfx::PaintThrobberWaitingForFrame(
+ canvas, bounds, tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING),
+ loading_animation_frame_);
+ } else {
+ gfx::PaintThrobberSpinningForFrame(
+ canvas, bounds,
+ tp->GetColor(ThemeProperties::COLOR_THROBBER_SPINNING),
+ loading_animation_frame_);
+ }
} else if (should_display_crashed_favicon_) {
// Paint crash favicon.
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
@@ -1364,55 +1366,15 @@ void Tab::AdvanceLoadingAnimation(TabRendererData::NetworkState old_state,
// TODO(robliao): Remove ScopedTracker below once crbug.com/461137 is fixed.
tracked_objects::ScopedTracker tracking_profile1(
FROM_HERE_WITH_EXPLICIT_FUNCTION("461137 Tab::AdvanceLoadingAnimation1"));
- static bool initialized = false;
- static int loading_animation_frame_count = 0;
- static int waiting_animation_frame_count = 0;
- static int waiting_to_loading_frame_count_ratio = 0;
- if (!initialized) {
- // TODO(robliao): Remove ScopedTracker below once crbug.com/461137 is fixed.
- tracked_objects::ScopedTracker tracking_profile2(
- FROM_HERE_WITH_EXPLICIT_FUNCTION(
- "461137 Tab::AdvanceLoadingAnimation2"));
- initialized = true;
- ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
- gfx::ImageSkia loading_animation(*rb.GetImageSkiaNamed(IDR_THROBBER));
- loading_animation_frame_count =
- loading_animation.width() / loading_animation.height();
- gfx::ImageSkia waiting_animation(*rb.GetImageSkiaNamed(
- IDR_THROBBER_WAITING));
- waiting_animation_frame_count =
- waiting_animation.width() / waiting_animation.height();
- waiting_to_loading_frame_count_ratio =
- waiting_animation_frame_count / loading_animation_frame_count;
-
- base::debug::Alias(&loading_animation_frame_count);
- base::debug::Alias(&waiting_animation_frame_count);
- CHECK_NE(0, waiting_to_loading_frame_count_ratio) <<
- "Number of frames in IDR_THROBBER must be equal to or greater " <<
- "than the number of frames in IDR_THROBBER_WAITING. Please " <<
- "investigate how this happened and update http://crbug.com/132590, " <<
- "this is causing crashes in the wild.";
- }
-
- // The waiting animation is the reverse of the loading animation, but at a
- // different rate - the following reverses and scales the animation_frame_
- // so that the frame is at an equivalent position when going from one
- // animation to the other.
- if (state != old_state) {
- loading_animation_frame_ = loading_animation_frame_count -
- (loading_animation_frame_ / waiting_to_loading_frame_count_ratio);
- }
if (state == TabRendererData::NETWORK_STATE_WAITING) {
- loading_animation_frame_ = (loading_animation_frame_ + 1) %
- waiting_animation_frame_count;
+ ++loading_animation_frame_;
// Waiting steps backwards.
immersive_loading_step_ =
(immersive_loading_step_ - 1 + kImmersiveLoadingStepCount) %
kImmersiveLoadingStepCount;
} else if (state == TabRendererData::NETWORK_STATE_LOADING) {
- loading_animation_frame_ = (loading_animation_frame_ + 1) %
- loading_animation_frame_count;
+ ++loading_animation_frame_;
immersive_loading_step_ = (immersive_loading_step_ + 1) %
kImmersiveLoadingStepCount;
} else {
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index 51a2b49..c7a7801 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -145,6 +145,8 @@ component("gfx") {
"native_widget_types.h",
"nine_image_painter.cc",
"nine_image_painter.h",
+ "paint_throbber.cc",
+ "paint_throbber.h",
"path.cc",
"path.h",
"path_aura.cc",
diff --git a/ui/gfx/gfx.gyp b/ui/gfx/gfx.gyp
index 97bdb8f..f93151e 100644
--- a/ui/gfx/gfx.gyp
+++ b/ui/gfx/gfx.gyp
@@ -224,6 +224,8 @@
'nine_image_painter.cc',
'nine_image_painter.h',
'overlay_transform.h',
+ 'paint_throbber.cc',
+ 'paint_throbber.h',
'path.cc',
'path.h',
'path_aura.cc',
diff --git a/ui/gfx/paint_throbber.cc b/ui/gfx/paint_throbber.cc
new file mode 100644
index 0000000..0495f6a
--- /dev/null
+++ b/ui/gfx/paint_throbber.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2015 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/gfx/paint_throbber.h"
+
+#include "base/time/time.h"
+#include "ui/gfx/animation/tween.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/skia_util.h"
+
+namespace gfx {
+
+namespace {
+
+void PaintArc(Canvas* canvas,
+ const Rect& bounds,
+ SkColor color,
+ SkScalar start_angle,
+ SkScalar sweep) {
+ // Inset by half the stroke width to make sure the whole arc is inside
+ // the visible rect.
+ SkScalar stroke_width = SkIntToScalar(bounds.width()) / 10.0;
+ Rect oval = bounds;
+ int inset = SkScalarCeilToInt(stroke_width / 2.0);
+ oval.Inset(inset, inset);
+
+ SkPath path;
+ path.arcTo(RectToSkRect(oval), start_angle, sweep, true);
+
+ SkPaint paint;
+ paint.setColor(color);
+ paint.setStrokeCap(SkPaint::kRound_Cap);
+ paint.setStrokeWidth(stroke_width);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setAntiAlias(true);
+ canvas->DrawPath(path, paint);
+}
+
+} // namespace
+
+void PaintThrobberSpinning(Canvas* canvas,
+ const Rect& bounds, SkColor color, const base::TimeDelta& elapsed_time) {
+ // This is a Skia port of the MD spinner SVG. The |start_angle| rotation
+ // here corresponds to the 'rotate' animation.
+ base::TimeDelta rotation_time = base::TimeDelta::FromMilliseconds(1568);
+ int64_t start_angle = 270 + 360 * elapsed_time / rotation_time;
+
+ // The sweep angle ranges from -|arc_size| to |arc_size| over 1333ms. CSS
+ // animation timing functions apply in between key frames, so we have to
+ // break up the |arc_time| into two keyframes (-arc_size to 0, then 0 to
+ // arc_size).
+ int64_t arc_size = 270;
+ base::TimeDelta arc_time = base::TimeDelta::FromMilliseconds(666);
+ double arc_size_progress = static_cast<double>(elapsed_time.InMicroseconds() %
+ arc_time.InMicroseconds()) /
+ arc_time.InMicroseconds();
+ // This tween is equivalent to cubic-bezier(0.4, 0.0, 0.2, 1).
+ double sweep =
+ arc_size * Tween::CalculateValue(Tween::FAST_OUT_SLOW_IN,
+ arc_size_progress);
+ int64_t sweep_keyframe = (elapsed_time / arc_time) % 2;
+ if (sweep_keyframe == 0)
+ sweep -= arc_size;
+
+ // This part makes sure the sweep is at least 5 degrees long. Roughly
+ // equivalent to the "magic constants" in SVG's fillunfill animation.
+ const double min_sweep_length = 5.0;
+ if (sweep >= 0.0 && sweep < min_sweep_length) {
+ start_angle -= (min_sweep_length - sweep);
+ sweep = min_sweep_length;
+ } else if (sweep <= 0.0 && sweep > -min_sweep_length) {
+ start_angle += (-min_sweep_length - sweep);
+ sweep = -min_sweep_length;
+ }
+
+ // To keep the sweep smooth, we have an additional rotation after each
+ // |arc_time| period has elapsed. See SVG's 'rot' animation.
+ int64_t rot_keyframe = (elapsed_time / (arc_time * 2)) % 4;
+ PaintArc(canvas, bounds, color,
+ start_angle + rot_keyframe * arc_size, sweep);
+}
+
+void PaintThrobberSpinningForFrame(Canvas* canvas,
+ const Rect& bounds, SkColor color, uint32_t frame) {
+ const uint32_t frame_duration_ms = 30;
+ PaintThrobberSpinning(canvas, bounds, color,
+ base::TimeDelta::FromMilliseconds(frame * frame_duration_ms));
+}
+
+void PaintThrobberWaiting(Canvas* canvas,
+ const Rect& bounds, SkColor color, const base::TimeDelta& elapsed_time) {
+ // Calculate start and end points. The angles are counter-clockwise because
+ // the throbber spins counter-clockwise. The finish angle starts at 12 o'clock
+ // (90 degrees) and rotates steadily. The start angle trails 180 degrees
+ // behind, except for the first half revolution, when it stays at 12 o'clock.
+ base::TimeDelta revolution_time = base::TimeDelta::FromMilliseconds(1320);
+ int64_t twelve_oclock = 90;
+ int64_t finish_angle = twelve_oclock + 360 * elapsed_time / revolution_time;
+ int64_t start_angle = std::max(finish_angle - 180, twelve_oclock);
+
+ // Negate the angles to convert to the clockwise numbers Skia expects.
+ PaintArc(canvas, bounds, color, -start_angle, -(finish_angle - start_angle));
+}
+
+void PaintThrobberWaitingForFrame(Canvas* canvas,
+ const Rect& bounds, SkColor color, uint32_t frame) {
+ const uint32_t frame_duration_ms = 30;
+ PaintThrobberWaiting(canvas, bounds, color,
+ base::TimeDelta::FromMilliseconds(frame * frame_duration_ms));
+}
+
+} // namespace gfx
diff --git a/ui/gfx/paint_throbber.h b/ui/gfx/paint_throbber.h
new file mode 100644
index 0000000..497de74
--- /dev/null
+++ b/ui/gfx/paint_throbber.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2015 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_GFX_PAINT_THROBBER_H_
+#define UI_GFX_PAINT_THROBBER_H_
+
+#include "base/basictypes.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace base {
+class TimeDelta;
+}
+
+namespace gfx {
+
+class Canvas;
+class Rect;
+
+// Paints a single frame of the throbber in the "spinning", aka Material, state.
+GFX_EXPORT void PaintThrobberSpinning(Canvas* canvas,
+ const Rect& bounds, SkColor color, const base::TimeDelta& elapsed_time);
+// As above, but frame is passed in rather than calculated. Frame duration is
+// assumed to be 30ms.
+GFX_EXPORT void PaintThrobberSpinningForFrame(Canvas* canvas,
+ const Rect& bounds, SkColor color, uint32_t frame);
+
+// Paints a throbber in the "waiting" state. Used when waiting on a network
+// response, for example.
+GFX_EXPORT void PaintThrobberWaiting(Canvas* canvas,
+ const Rect& bounds, SkColor color, const base::TimeDelta& elapsed_time);
+GFX_EXPORT void PaintThrobberWaitingForFrame(Canvas* canvas,
+ const Rect& bounds, SkColor color, uint32_t frame);
+
+} // namespace gfx
+
+#endif // UI_GFX_PAINT_THROBBER_H_
diff --git a/ui/native_theme/common_theme.cc b/ui/native_theme/common_theme.cc
index a7a4c5a..43e5a53 100644
--- a/ui/native_theme/common_theme.cc
+++ b/ui/native_theme/common_theme.cc
@@ -40,6 +40,9 @@ const SkColor kBlueButtonDisabledColor = SK_ColorWHITE;
const SkColor kBlueButtonPressedColor = SK_ColorWHITE;
const SkColor kBlueButtonHoverColor = SK_ColorWHITE;
const SkColor kBlueButtonShadowColor = SkColorSetRGB(0x53, 0x8C, 0xEA);
+// Material spinner/throbber:
+const SkColor kThrobberSpinningColor = SkColorSetRGB(0x42, 0x81, 0xF4);
+const SkColor kThrobberWaitingColor = SkColorSetRGB(0xA6, 0xA6, 0xA6);
} // namespace
@@ -106,6 +109,13 @@ bool CommonThemeGetSystemColor(NativeTheme::ColorId color_id, SkColor* color) {
case NativeTheme::kColorId_BlueButtonShadowColor:
*color = kBlueButtonShadowColor;
break;
+ // Material spinner/throbber
+ case NativeTheme::kColorId_ThrobberSpinningColor:
+ *color = kThrobberSpinningColor;
+ break;
+ case NativeTheme::kColorId_ThrobberWaitingColor:
+ *color = kThrobberWaitingColor;
+ break;
default:
return false;
}
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h
index 3f47ad9..cb75406 100644
--- a/ui/native_theme/native_theme.h
+++ b/ui/native_theme/native_theme.h
@@ -325,6 +325,9 @@ class NATIVE_THEME_EXPORT NativeTheme {
kColorId_ResultsTableNegativeHoveredText,
kColorId_ResultsTableNegativeSelectedText,
// TODO(benrg): move other hardcoded colors here.
+ // Colors for the material spinner (aka throbber).
+ kColorId_ThrobberSpinningColor,
+ kColorId_ThrobberWaitingColor,
kColorId_NumColors,
};
diff --git a/ui/views/controls/throbber.cc b/ui/views/controls/throbber.cc
index b2a26fc..1a8e38c 100644
--- a/ui/views/controls/throbber.cc
+++ b/ui/views/controls/throbber.cc
@@ -9,16 +9,14 @@
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/paint_throbber.h"
+#include "ui/native_theme/common_theme.h"
+#include "ui/native_theme/native_theme.h"
#include "ui/resources/grit/ui_resources.h"
namespace views {
-Throbber::Throbber(int frame_time_ms, bool paint_while_stopped)
- : paint_while_stopped_(paint_while_stopped),
- frames_(NULL),
- frame_time_(base::TimeDelta::FromMilliseconds(frame_time_ms)) {
- SetFrames(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
- IDR_THROBBER).ToImageSkia());
+Throbber::Throbber() : checked_(false), checkmark_(nullptr) {
}
Throbber::~Throbber() {
@@ -30,8 +28,9 @@ void Throbber::Start() {
return;
start_time_ = base::TimeTicks::Now();
- timer_.Start(FROM_HERE, frame_time_ - base::TimeDelta::FromMilliseconds(10),
- this, &Throbber::SchedulePaint);
+ const int kFrameTimeMs = 30;
+ timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kFrameTimeMs), this,
+ &Throbber::SchedulePaint);
SchedulePaint(); // paint right away
}
@@ -40,35 +39,44 @@ void Throbber::Stop() {
return;
timer_.Stop();
- SchedulePaint(); // Important if we're not painting while stopped
+ SchedulePaint();
}
-void Throbber::SetFrames(const gfx::ImageSkia* frames) {
- frames_ = frames;
- DCHECK(frames_->width() > 0 && frames_->height() > 0);
- DCHECK(frames_->width() % frames_->height() == 0);
- frame_count_ = frames_->width() / frames_->height();
- PreferredSizeChanged();
+void Throbber::SetChecked(bool checked) {
+ if (checked == checked_)
+ return;
+
+ checked_ = checked;
+ SchedulePaint();
}
gfx::Size Throbber::GetPreferredSize() const {
- return gfx::Size(frames_->height(), frames_->height());
+ const int kDefaultDiameter = 16;
+ return gfx::Size(kDefaultDiameter, kDefaultDiameter);
}
void Throbber::OnPaint(gfx::Canvas* canvas) {
- if (!IsRunning() && !paint_while_stopped_)
- return;
+ if (!IsRunning()) {
+ if (checked_) {
+ if (!checkmark_) {
+ checkmark_ = ui::ResourceBundle::GetSharedInstance()
+ .GetImageNamed(IDR_CHECKMARK)
+ .ToImageSkia();
+ }
- const base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time_;
- const int current_frame =
- static_cast<int>(elapsed_time / frame_time_) % frame_count_;
+ int checkmark_x = (width() - checkmark_->width()) / 2;
+ int checkmark_y = (height() - checkmark_->height()) / 2;
+ canvas->DrawImageInt(*checkmark_, checkmark_x, checkmark_y);
+ }
+ return;
+ }
- int image_size = frames_->height();
- int image_offset = current_frame * image_size;
- canvas->DrawImageInt(*frames_,
- image_offset, 0, image_size, image_size,
- 0, 0, image_size, image_size,
- false);
+ SkColor color;
+ bool found_color = ui::CommonThemeGetSystemColor(
+ ui::NativeTheme::kColorId_ThrobberSpinningColor, &color);
+ DCHECK(found_color);
+ base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time_;
+ gfx::PaintThrobberSpinning(canvas, GetContentsBounds(), color, elapsed_time);
}
bool Throbber::IsRunning() const {
@@ -83,10 +91,8 @@ static const int kStartDelay = 200;
// Delay after work stops before stopping, in milliseconds.
static const int kStopDelay = 50;
-SmoothedThrobber::SmoothedThrobber(int frame_time_ms)
- : Throbber(frame_time_ms, /* paint_while_stopped= */ false),
- start_delay_ms_(kStartDelay),
- stop_delay_ms_(kStopDelay) {
+SmoothedThrobber::SmoothedThrobber()
+ : start_delay_ms_(kStartDelay), stop_delay_ms_(kStopDelay) {
}
SmoothedThrobber::~SmoothedThrobber() {}
@@ -119,138 +125,4 @@ void SmoothedThrobber::StopDelayOver() {
Throbber::Stop();
}
-// Material throbber -----------------------------------------------------------
-
-// The length of a frame in milliseconds.
-// TODO(estade): remove the +10 when the -10 is removed from Throbber::Start().
-static const int kMaterialThrobberFrameTimeMs = 30 + 10;
-
-MaterialThrobber::MaterialThrobber() :
- Throbber(kMaterialThrobberFrameTimeMs, false),
- preferred_diameter_(0),
- checked_(false),
- checkmark_(nullptr) {
-}
-
-MaterialThrobber::~MaterialThrobber() {
-}
-
-void MaterialThrobber::SetChecked(bool checked) {
- if (checked == checked_)
- return;
-
- checked_ = checked;
- SchedulePaint();
-}
-
-gfx::Size MaterialThrobber::GetPreferredSize() const {
- if (preferred_diameter_ == 0)
- return Throbber::GetPreferredSize();
-
- return gfx::Size(preferred_diameter_, preferred_diameter_);
-}
-
-int MaterialThrobber::GetHeightForWidth(int w) const {
- return w;
-}
-
-void MaterialThrobber::OnPaint(gfx::Canvas* canvas) {
- if (!IsRunning()) {
- if (checked_) {
- if (!checkmark_) {
- checkmark_ = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
- IDR_CHECKMARK).ToImageSkia();
- }
-
- int checkmark_x = (width() - checkmark_->width()) / 2;
- int checkmark_y = (height() - checkmark_->height()) / 2;
- canvas->DrawImageInt(*checkmark_, checkmark_x, checkmark_y);
- }
- return;
- }
-
- PaintSpinning(canvas);
-}
-
-void MaterialThrobber::PaintSpinning(gfx::Canvas* canvas) {
- base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time();
-
- // This is a Skia port of the MD spinner SVG. The |start_angle| rotation
- // here corresponds to the 'rotate' animation.
- base::TimeDelta rotation_time = base::TimeDelta::FromMilliseconds(1568);
- int64_t start_angle = 270 + 360 * elapsed_time / rotation_time;
-
- // The sweep angle ranges from -|arc_size| to |arc_size| over 1333ms. CSS
- // animation timing functions apply in between key frames, so we have to
- // break up the |arc_time| into two keyframes (-arc_size to 0, then 0 to
- // arc_size).
- int64_t arc_size = 270;
- base::TimeDelta arc_time = base::TimeDelta::FromMilliseconds(666);
- double arc_size_progress = static_cast<double>(elapsed_time.InMicroseconds() %
- arc_time.InMicroseconds()) /
- arc_time.InMicroseconds();
- // This tween is equivalent to cubic-bezier(0.4, 0.0, 0.2, 1).
- double sweep =
- arc_size * gfx::Tween::CalculateValue(gfx::Tween::FAST_OUT_SLOW_IN,
- arc_size_progress);
- int64_t sweep_keyframe = (elapsed_time / arc_time) % 2;
- if (sweep_keyframe == 0)
- sweep -= arc_size;
-
- // This part makes sure the sweep is at least 5 degrees long. Roughly
- // equivalent to the "magic constants" in SVG's fillunfill animation.
- const double min_sweep_length = 5.0;
- if (sweep >= 0.0 && sweep < min_sweep_length) {
- start_angle -= (min_sweep_length - sweep);
- sweep = min_sweep_length;
- } else if (sweep <= 0.0 && sweep > -min_sweep_length) {
- start_angle += (-min_sweep_length - sweep);
- sweep = -min_sweep_length;
- }
-
- // To keep the sweep smooth, we have an additional rotation after each
- // |arc_time| period has elapsed. See SVG's 'rot' animation.
- int64_t rot_keyframe = (elapsed_time / (arc_time * 2)) % 4;
- PaintArc(canvas, start_angle + rot_keyframe * arc_size, sweep);
-}
-
-void MaterialThrobber::PaintWaiting(gfx::Canvas* canvas) {
- // Calculate start and end points. The angles are counter-clockwise because
- // the throbber spins counter-clockwise. The finish angle starts at 12 o'clock
- // (90 degrees) and rotates steadily. The start angle trails 180 degrees
- // behind, except for the first half revolution, when it stays at 12 o'clock.
- base::TimeDelta revolution_time = base::TimeDelta::FromMilliseconds(1320);
- base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time();
- int64_t twelve_oclock = 90;
- int64_t finish_angle = twelve_oclock + 360 * elapsed_time / revolution_time;
- int64_t start_angle = std::max(finish_angle - 180, twelve_oclock);
-
- // Negate the angles to convert to the clockwise numbers Skia expects.
- PaintArc(canvas, -start_angle, -(finish_angle - start_angle));
-}
-
-void MaterialThrobber::PaintArc(gfx::Canvas* canvas,
- SkScalar start_angle,
- SkScalar sweep) {
- gfx::Rect bounds = GetContentsBounds();
- // Inset by half the stroke width to make sure the whole arc is inside
- // the visible rect.
- SkScalar stroke_width = SkIntToScalar(bounds.width()) / 10.0;
- gfx::Rect oval = bounds;
- int inset = SkScalarCeilToInt(stroke_width / 2.0);
- oval.Inset(inset, inset);
-
- SkPath path;
- path.arcTo(gfx::RectToSkRect(oval), start_angle, sweep, true);
-
- SkPaint paint;
- // TODO(estade): find a place for this color.
- paint.setColor(SkColorSetRGB(0x42, 0x85, 0xF4));
- paint.setStrokeCap(SkPaint::kRound_Cap);
- paint.setStrokeWidth(stroke_width);
- paint.setStyle(SkPaint::kStroke_Style);
- paint.setAntiAlias(true);
- canvas->DrawPath(path, paint);
-}
-
} // namespace views
diff --git a/ui/views/controls/throbber.h b/ui/views/controls/throbber.h
index 8c8bb91..c5bed81 100644
--- a/ui/views/controls/throbber.h
+++ b/ui/views/controls/throbber.h
@@ -21,20 +21,15 @@ namespace views {
class VIEWS_EXPORT Throbber : public View {
public:
- // |frame_time_ms| is the amount of time that should elapse between frames
- // (in milliseconds)
- // If |paint_while_stopped| is false, this view will be invisible when not
- // running.
- Throbber(int frame_time_ms, bool paint_while_stopped);
- Throbber(int frame_time_ms, bool paint_while_stopped, gfx::ImageSkia* frames);
+ Throbber();
~Throbber() override;
- // Start and stop the throbber animation
+ // Start and stop the throbber animation.
virtual void Start();
virtual void Stop();
- // Set custom throbber frames. Otherwise IDR_THROBBER is loaded.
- void SetFrames(const gfx::ImageSkia* frames);
+ // Stop spinning and, if checked is true, display a checkmark.
+ void SetChecked(bool checked);
// Overridden from View:
gfx::Size GetPreferredSize() const override;
@@ -43,16 +38,17 @@ class VIEWS_EXPORT Throbber : public View {
protected:
// Specifies whether the throbber is currently animating or not
bool IsRunning() const;
- base::TimeTicks start_time() const { return start_time_; }
private:
- bool paint_while_stopped_;
- int frame_count_; // How many frames we have.
base::TimeTicks start_time_; // Time when Start was called.
- const gfx::ImageSkia* frames_; // Frames images.
- base::TimeDelta frame_time_; // How long one frame is displayed.
base::RepeatingTimer<Throbber> timer_; // Used to schedule Run calls.
+ // Whether or not we should display a checkmark.
+ bool checked_;
+
+ // The checkmark image. Will be null until it's used (if ever).
+ const gfx::ImageSkia* checkmark_;
+
DISALLOW_COPY_AND_ASSIGN(Throbber);
};
@@ -62,8 +58,7 @@ class VIEWS_EXPORT Throbber : public View {
// a small amount of work time has passed.
class VIEWS_EXPORT SmoothedThrobber : public Throbber {
public:
- explicit SmoothedThrobber(int frame_delay_ms);
- SmoothedThrobber(int frame_delay_ms, gfx::ImageSkia* frames);
+ SmoothedThrobber();
~SmoothedThrobber() override;
void Start() override;
@@ -93,49 +88,6 @@ class VIEWS_EXPORT SmoothedThrobber : public Throbber {
DISALLOW_COPY_AND_ASSIGN(SmoothedThrobber);
};
-// A throbber that follows material design guidelines. This is a specialization
-// of Throbber for now, but should probably be folded into/replace Throbber
-// eventually.
-class VIEWS_EXPORT MaterialThrobber : public Throbber {
- public:
- MaterialThrobber();
- ~MaterialThrobber() override;
-
- // Stop spinning and, if checked is true, display a checkmark.
- void SetChecked(bool checked);
-
- void set_preferred_diameter(int diameter) { preferred_diameter_ = diameter; }
-
- // View implementation.
- gfx::Size GetPreferredSize() const override;
- int GetHeightForWidth(int w) const override;
- void OnPaint(gfx::Canvas* canvas) override;
-
- private:
- // Paints this throbber in the "waiting" state, for example when waiting for
- // an initial network response.
- void PaintWaiting(gfx::Canvas* canvas);
-
- // Paints this throbber in its normal state. Corresponds to MD throbber.
- void PaintSpinning(gfx::Canvas* canvas);
-
- // Common painting code for PaintWaiting and PaintSpinning.
- void PaintArc(gfx::Canvas* canvas, SkScalar start_angle, SkScalar sweep);
-
- // The preferred width and height for this view. Zero indicates the size is
- // set by the parent class (i.e. matches the size of the pre-material
- // sprites).
- int preferred_diameter_;
-
- // Whether or not we should display a checkmark.
- bool checked_;
-
- // The checkmark image. Will be null until it's used (if ever).
- const gfx::ImageSkia* checkmark_;
-
- DISALLOW_COPY_AND_ASSIGN(MaterialThrobber);
-};
-
} // namespace views
#endif // UI_VIEWS_CONTROLS_THROBBER_H_
diff --git a/ui/views/examples/throbber_example.cc b/ui/views/examples/throbber_example.cc
index 8582a2b..3333e5b 100644
--- a/ui/views/examples/throbber_example.cc
+++ b/ui/views/examples/throbber_example.cc
@@ -13,15 +13,10 @@ namespace examples {
namespace {
-// Time in ms per throbber frame.
-const int kThrobberFrameMs = 60;
-
class ThrobberView : public View {
public:
- ThrobberView() {
- throbber_ = new Throbber(kThrobberFrameMs, false);
+ ThrobberView() : throbber_(new Throbber()) {
AddChildView(throbber_);
- throbber_->SetVisible(true);
throbber_->Start();
}
@@ -30,11 +25,10 @@ class ThrobberView : public View {
}
void Layout() override {
- View* child = child_at(0);
- gfx::Size ps = child->GetPreferredSize();
- child->SetBounds((width() - ps.width()) / 2,
- (height() - ps.height()) / 2,
- ps.width(), ps.height());
+ int diameter = 64;
+ throbber_->SetBounds((width() - diameter) / 2,
+ (height() - diameter) / 2,
+ diameter, diameter);
SizeToPreferredSize();
}