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 "grit/ui_resources.h" 9 #include "ui/base/resource/resource_bundle.h" 10 #include "ui/gfx/canvas.h" 11 #include "ui/gfx/image/image.h" 12 #include "ui/gfx/image/image_skia.h" 13 14 using base::Time; 15 using base::TimeDelta; 16 17 namespace views { 18 19 Throbber::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 29 Throbber::~Throbber() { 30 Stop(); 31 } 32 33 void 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 47 void 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 57 void 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 65 void Throbber::Run() { 66 DCHECK(running_); 67 68 SchedulePaint(); 69 } 70 71 gfx::Size Throbber::GetPreferredSize() { 72 return gfx::Size(frames_->height(), frames_->height()); 73 } 74 75 void 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. 97 static const int kStartDelay = 200; 98 99 // Delay after work stops before stopping, in milliseconds. 100 static const int kStopDelay = 50; 101 102 103 SmoothedThrobber::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 109 SmoothedThrobber::~SmoothedThrobber() {} 110 111 void 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 120 void SmoothedThrobber::StartDelayOver() { 121 Throbber::Start(); 122 } 123 124 void 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 133 void SmoothedThrobber::StopDelayOver() { 134 Throbber::Stop(); 135 } 136 137 // Checkmark throbber --------------------------------------------------------- 138 139 CheckmarkThrobber::CheckmarkThrobber() 140 : Throbber(kFrameTimeMs, false), 141 checked_(false), 142 checkmark_(ui::ResourceBundle::GetSharedInstance().GetImageNamed( 143 IDR_CHECKMARK).ToImageSkia()) { 144 } 145 146 void CheckmarkThrobber::SetChecked(bool checked) { 147 bool changed = checked != checked_; 148 if (changed) { 149 checked_ = checked; 150 SchedulePaint(); 151 } 152 } 153 154 void 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