1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/views/controls/throbber.h"
6
7#include "base/time/time.h"
8#include "ui/base/resource/resource_bundle.h"
9#include "ui/gfx/canvas.h"
10#include "ui/gfx/image/image.h"
11#include "ui/gfx/image/image_skia.h"
12#include "ui/resources/grit/ui_resources.h"
13
14using base::Time;
15using base::TimeDelta;
16
17namespace views {
18
19Throbber::Throbber(int frame_time_ms,
20                   bool paint_while_stopped)
21    : running_(false),
22      paint_while_stopped_(paint_while_stopped),
23      frames_(NULL),
24      frame_time_(TimeDelta::FromMilliseconds(frame_time_ms)) {
25  SetFrames(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
26      IDR_THROBBER).ToImageSkia());
27}
28
29Throbber::~Throbber() {
30  Stop();
31}
32
33void Throbber::Start() {
34  if (running_)
35    return;
36
37  start_time_ = Time::Now();
38
39  timer_.Start(FROM_HERE, frame_time_ - TimeDelta::FromMilliseconds(10),
40               this, &Throbber::Run);
41
42  running_ = true;
43
44  SchedulePaint();  // paint right away
45}
46
47void Throbber::Stop() {
48  if (!running_)
49    return;
50
51  timer_.Stop();
52
53  running_ = false;
54  SchedulePaint();  // Important if we're not painting while stopped
55}
56
57void Throbber::SetFrames(const gfx::ImageSkia* frames) {
58  frames_ = frames;
59  DCHECK(frames_->width() > 0 && frames_->height() > 0);
60  DCHECK(frames_->width() % frames_->height() == 0);
61  frame_count_ = frames_->width() / frames_->height();
62  PreferredSizeChanged();
63}
64
65void Throbber::Run() {
66  DCHECK(running_);
67
68  SchedulePaint();
69}
70
71gfx::Size Throbber::GetPreferredSize() const {
72  return gfx::Size(frames_->height(), frames_->height());
73}
74
75void Throbber::OnPaint(gfx::Canvas* canvas) {
76  if (!running_ && !paint_while_stopped_)
77    return;
78
79  const TimeDelta elapsed_time = Time::Now() - start_time_;
80  const int current_frame =
81      static_cast<int>(elapsed_time / frame_time_) % frame_count_;
82
83  int image_size = frames_->height();
84  int image_offset = current_frame * image_size;
85  canvas->DrawImageInt(*frames_,
86                       image_offset, 0, image_size, image_size,
87                       0, 0, image_size, image_size,
88                       false);
89}
90
91
92
93// Smoothed throbber ---------------------------------------------------------
94
95
96// Delay after work starts before starting throbber, in milliseconds.
97static const int kStartDelay = 200;
98
99// Delay after work stops before stopping, in milliseconds.
100static const int kStopDelay = 50;
101
102
103SmoothedThrobber::SmoothedThrobber(int frame_time_ms)
104    : Throbber(frame_time_ms, /* paint_while_stopped= */ false),
105      start_delay_ms_(kStartDelay),
106      stop_delay_ms_(kStopDelay) {
107}
108
109SmoothedThrobber::~SmoothedThrobber() {}
110
111void SmoothedThrobber::Start() {
112  stop_timer_.Stop();
113
114  if (!running_ && !start_timer_.IsRunning()) {
115    start_timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(start_delay_ms_),
116                       this, &SmoothedThrobber::StartDelayOver);
117  }
118}
119
120void SmoothedThrobber::StartDelayOver() {
121  Throbber::Start();
122}
123
124void SmoothedThrobber::Stop() {
125  if (!running_)
126    start_timer_.Stop();
127
128  stop_timer_.Stop();
129  stop_timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(stop_delay_ms_),
130                    this, &SmoothedThrobber::StopDelayOver);
131}
132
133void SmoothedThrobber::StopDelayOver() {
134  Throbber::Stop();
135}
136
137// Checkmark throbber ---------------------------------------------------------
138
139CheckmarkThrobber::CheckmarkThrobber()
140    : Throbber(kFrameTimeMs, false),
141      checked_(false),
142      checkmark_(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
143          IDR_CHECKMARK).ToImageSkia()) {
144}
145
146void CheckmarkThrobber::SetChecked(bool checked) {
147  bool changed = checked != checked_;
148  if (changed) {
149    checked_ = checked;
150    SchedulePaint();
151  }
152}
153
154void CheckmarkThrobber::OnPaint(gfx::Canvas* canvas) {
155  if (running_) {
156    // Let the throbber throb...
157    Throbber::OnPaint(canvas);
158    return;
159  }
160  // Otherwise we paint our tick mark or nothing depending on our state.
161  if (checked_) {
162    int checkmark_x = (width() - checkmark_->width()) / 2;
163    int checkmark_y = (height() - checkmark_->height()) / 2;
164    canvas->DrawImageInt(*checkmark_, checkmark_x, checkmark_y);
165  }
166}
167
168}  // namespace views
169