| 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
|
|
|