Index: chrome/browser/chromeos/setting_level_bubble.cc |
diff --git a/chrome/browser/chromeos/setting_level_bubble.cc b/chrome/browser/chromeos/setting_level_bubble.cc |
index 3f467aec3ef9c2b75a495030a69c7ce1a7238c48..4320ea180bd7c104ae2d8d0375988151fac3e9a8 100644 |
--- a/chrome/browser/chromeos/setting_level_bubble.cc |
+++ b/chrome/browser/chromeos/setting_level_bubble.cc |
@@ -4,9 +4,10 @@ |
#include "chrome/browser/chromeos/setting_level_bubble.h" |
+#include <algorithm> |
+ |
#include <gdk/gdk.h> |
-#include "base/timer.h" |
#include "chrome/browser/chromeos/login/background_view.h" |
#include "chrome/browser/chromeos/login/login_utils.h" |
#include "chrome/browser/chromeos/login/webui_login_display.h" |
@@ -19,10 +20,19 @@ |
#include "ui/gfx/screen.h" |
#include "views/widget/root_view.h" |
+using base::TimeDelta; |
+using base::TimeTicks; |
+using std::max; |
+using std::min; |
+ |
namespace { |
-const int kBubbleShowTimeoutSec = 2; |
-const int kAnimationDurationMs = 200; |
+// How long should the bubble be shown onscreen whenever the setting changes? |
+const int kBubbleShowTimeoutMs = 1000; |
+ |
+// How long should the level initially take to move up or down when it changes? |
+// (The rate adapts to handle keyboard autorepeat.) |
+const int64 kInitialAnimationDurationMs = 200; |
// Horizontal position of the center of the bubble on the screen: 0 is left |
// edge, 0.5 is center, 1 is right edge. |
@@ -31,12 +41,12 @@ const double kBubbleXRatio = 0.5; |
// Vertical gap from the bottom of the screen in pixels. |
const int kBubbleBottomGap = 30; |
-int LimitPercent(int percent) { |
- if (percent < 0) |
- percent = 0; |
- else if (percent > 100) |
- percent = 100; |
- return percent; |
+// Duration between animation frames. |
+// Chosen to match ui::SlideAnimation's kDefaultFramerateHz. |
+const int kAnimationIntervalMs = 1000 / 50; |
+ |
+double LimitPercent(double percent) { |
+ return min(max(percent, 0.0), 100.0); |
} |
} // namespace |
@@ -75,30 +85,26 @@ static views::Widget* GetToplevelWidget() { |
SettingLevelBubble::SettingLevelBubble(SkBitmap* increase_icon, |
SkBitmap* decrease_icon, |
SkBitmap* disabled_icon) |
- : previous_percent_(-1), |
- current_percent_(-1), |
+ : current_percent_(-1.0), |
+ target_percent_(-1.0), |
increase_icon_(increase_icon), |
decrease_icon_(decrease_icon), |
disabled_icon_(disabled_icon), |
bubble_(NULL), |
view_(NULL), |
- animation_(this) { |
- animation_.SetSlideDuration(kAnimationDurationMs); |
- animation_.SetTweenType(ui::Tween::LINEAR); |
+ is_animating_(false) { |
} |
SettingLevelBubble::~SettingLevelBubble() {} |
-void SettingLevelBubble::ShowBubble(int percent, bool enabled) { |
- percent = LimitPercent(percent); |
- if (previous_percent_ == -1) |
- previous_percent_ = percent; |
- current_percent_ = percent; |
+void SettingLevelBubble::ShowBubble(double percent, bool enabled) { |
+ const double old_target_percent = target_percent_; |
+ UpdateTargetPercent(percent); |
SkBitmap* icon = increase_icon_; |
- if (!enabled || current_percent_ == 0) |
+ if (!enabled || target_percent_ == 0) |
icon = disabled_icon_; |
- else if (current_percent_ < previous_percent_) |
+ else if (old_target_percent >= 0 && target_percent_ < old_target_percent) |
icon = decrease_icon_; |
if (!bubble_) { |
@@ -109,7 +115,7 @@ void SettingLevelBubble::ShowBubble(int percent, bool enabled) { |
} |
DCHECK(view_ == NULL); |
view_ = new SettingLevelBubbleView; |
- view_->Init(icon, previous_percent_, enabled); |
+ view_->Init(icon, current_percent_, enabled); |
// Calculate the position in screen coordinates that the bubble should |
// "point" at (since we use BubbleBorder::FLOAT, this position actually |
@@ -123,29 +129,27 @@ void SettingLevelBubble::ShowBubble(int percent, bool enabled) { |
monitor_area.bottom() - view_size.height() / 2 - kBubbleBottomGap, |
0, 0); |
- // ShowFocusless doesn't set ESC accelerator. |
bubble_ = Bubble::ShowFocusless(parent_widget, |
position_relative_to, |
BubbleBorder::FLOAT, |
view_, // contents |
this, // delegate |
true); // show while screen is locked |
- bubble_->set_fade_away_on_close(true); |
+ // TODO(derat): We probably shouldn't be using Bubble. It'd be nice to call |
+ // bubble_->set_fade_away_on_close(true) here, but then, if ShowBubble() |
+ // gets called while the bubble is fading away, we end up just adjusting the |
+ // value on the disappearing bubble; ideally we'd have a way to cancel the |
+ // fade and show the bubble at full opacity for another |
+ // kBubbleShowTimeoutMs. |
} else { |
DCHECK(view_); |
- timeout_timer_.Stop(); |
+ hide_timer_.Stop(); |
view_->SetIcon(icon); |
+ view_->SetEnabled(enabled); |
} |
- view_->SetEnabled(enabled); |
- |
- if (animation_.is_animating()) |
- animation_.End(); |
- animation_.Reset(); |
- animation_.Show(); |
- |
- timeout_timer_.Start(base::TimeDelta::FromSeconds(kBubbleShowTimeoutSec), |
- this, &SettingLevelBubble::OnTimeout); |
+ hide_timer_.Start(base::TimeDelta::FromMilliseconds(kBubbleShowTimeoutMs), |
+ this, &SettingLevelBubble::OnHideTimeout); |
} |
void SettingLevelBubble::HideBubble() { |
@@ -153,36 +157,51 @@ void SettingLevelBubble::HideBubble() { |
bubble_->Close(); |
} |
-void SettingLevelBubble::UpdateWithoutShowingBubble(int percent, bool enabled) { |
+void SettingLevelBubble::UpdateWithoutShowingBubble(double percent, |
+ bool enabled) { |
+ UpdateTargetPercent(percent); |
if (view_) |
view_->SetEnabled(enabled); |
+} |
- percent = LimitPercent(percent); |
+void SettingLevelBubble::OnHideTimeout() { |
+ HideBubble(); |
+} |
- previous_percent_ = |
- animation_.is_animating() ? |
- animation_.GetCurrentValue() : |
- current_percent_; |
- if (previous_percent_ < 0) |
- previous_percent_ = percent; |
- current_percent_ = percent; |
+void SettingLevelBubble::OnAnimationTimeout() { |
+ const TimeTicks now = TimeTicks::Now(); |
+ const int64 remaining_ms = (target_time_ - now).InMilliseconds(); |
- if (animation_.is_animating()) |
- animation_.End(); |
- animation_.Reset(); |
- animation_.Show(); |
-} |
+ if (remaining_ms <= 0) { |
+ current_percent_ = target_percent_; |
+ StopAnimation(); |
+ } else { |
+ // Figure out what fraction of the total time until we want to reach the |
+ // target has elapsed since the last update. |
+ const double remaining_percent = target_percent_ - current_percent_; |
+ const int64 elapsed_ms = |
+ (now - last_animation_update_time_).InMilliseconds(); |
+ current_percent_ += |
+ remaining_percent * |
+ (static_cast<double>(elapsed_ms) / (elapsed_ms + remaining_ms)); |
+ } |
+ last_animation_update_time_ = now; |
-void SettingLevelBubble::OnTimeout() { |
- HideBubble(); |
+ if (view_) |
+ view_->SetLevel(current_percent_); |
} |
void SettingLevelBubble::BubbleClosing(Bubble* bubble, bool) { |
DCHECK(bubble == bubble_); |
- timeout_timer_.Stop(); |
- animation_.Stop(); |
+ hide_timer_.Stop(); |
+ StopAnimation(); |
bubble_ = NULL; |
view_ = NULL; |
+ current_percent_ = -1.0; |
+ target_percent_ = -1.0; |
+ target_time_ = TimeTicks(); |
+ last_animation_update_time_ = TimeTicks(); |
+ last_target_update_time_ = TimeTicks(); |
} |
bool SettingLevelBubble::CloseOnEscape() { |
@@ -193,17 +212,41 @@ bool SettingLevelBubble::FadeInOnShow() { |
return false; |
} |
-void SettingLevelBubble::AnimationEnded(const ui::Animation* animation) { |
- previous_percent_ = current_percent_; |
-} |
+void SettingLevelBubble::UpdateTargetPercent(double percent) { |
+ target_percent_ = LimitPercent(percent); |
+ const TimeTicks now = TimeTicks::Now(); |
-void SettingLevelBubble::AnimationProgressed(const ui::Animation* animation) { |
- if (view_) { |
- view_->SetLevel( |
- ui::Tween::ValueBetween(animation->GetCurrentValue(), |
- previous_percent_, |
- current_percent_)); |
+ if (current_percent_ < 0.0) { |
+ // If we're setting the level for the first time, no need to animate. |
+ current_percent_ = target_percent_; |
+ if (view_) |
+ view_->SetLevel(current_percent_); |
+ } else { |
+ // Use the time since the last request as a hint for the duration of the |
+ // animation. This makes us automatically adapt to the repeat rate if a key |
+ // is being held down to change a setting (which prevents us from lagging |
+ // behind when the key is finally released). |
+ int64 duration_ms = kInitialAnimationDurationMs; |
+ if (!last_target_update_time_.is_null()) |
+ duration_ms = min(kInitialAnimationDurationMs, |
+ (now - last_target_update_time_).InMilliseconds()); |
+ target_time_ = now + TimeDelta::FromMilliseconds(duration_ms); |
+ |
+ if (!is_animating_) { |
+ animation_timer_.Start(TimeDelta::FromMilliseconds(kAnimationIntervalMs), |
+ this, |
+ &SettingLevelBubble::OnAnimationTimeout); |
+ is_animating_ = true; |
+ last_animation_update_time_ = now; |
+ } |
} |
+ |
+ last_target_update_time_ = now; |
+} |
+ |
+void SettingLevelBubble::StopAnimation() { |
+ animation_timer_.Stop(); |
+ is_animating_ = false; |
} |
} // namespace chromeos |