1 // Copyright (c) 2011 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.
5 #include "chrome/browser/chromeos/setting_level_bubble.h"
9 #include "chrome/browser/chromeos/login/background_view.h"
10 #include "chrome/browser/chromeos/login/base_login_display_host.h"
11 #include "chrome/browser/chromeos/login/login_display_host.h"
12 #include "chrome/browser/chromeos/login/login_utils.h"
13 #include "chrome/browser/chromeos/login/webui_login_display.h"
14 #include "chrome/browser/chromeos/setting_level_bubble_view.h"
15 #include "chrome/browser/profiles/profile_manager.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/browser_list.h"
18 #include "chrome/browser/ui/browser_window.h"
19 #include "chrome/browser/ui/views/window.h"
20 #include "ui/gfx/screen.h"
21 #include "ui/views/bubble/bubble_delegate.h"
22 #include "ui/views/layout/fill_layout.h"
23 #include "ui/views/widget/root_view.h"
25 using base::TimeDelta
;
26 using base::TimeTicks
;
32 // How long should the bubble be shown onscreen whenever the setting changes?
33 const int kBubbleShowTimeoutMs
= 1000;
35 // How long should the level initially take to move up or down when it changes?
36 // (The rate adapts to handle keyboard autorepeat.)
37 const int64 kInitialAnimationDurationMs
= 200;
39 // Horizontal position of the center of the bubble on the screen: 0 is left
40 // edge, 0.5 is center, 1 is right edge.
41 const double kBubbleXRatio
= 0.5;
43 // Vertical gap from the bottom of the screen in pixels.
44 const int kBubbleBottomGap
= 30;
46 // Duration between animation frames.
47 // Chosen to match ui::SlideAnimation's kDefaultFramerateHz.
48 const int kAnimationIntervalMs
= 1000 / 50;
50 double LimitPercent(double percent
) {
51 return min(max(percent
, 0.0), 100.0);
58 // SettingLevelBubbleDelegateView ----------------------------------------------
59 class SettingLevelBubbleDelegateView
: public views::BubbleDelegateView
{
61 // BubbleDelegate overrides:
62 virtual gfx::Rect
GetAnchorRect() OVERRIDE
;
64 // Create the bubble delegate view.
65 SettingLevelBubbleDelegateView();
66 virtual ~SettingLevelBubbleDelegateView();
68 SettingLevelBubbleView
* view() { return view_
; }
71 // BubbleDelegate overrides:
72 virtual void Init() OVERRIDE
;
75 SettingLevelBubbleView
* view_
;
77 DISALLOW_COPY_AND_ASSIGN(SettingLevelBubbleDelegateView
);
80 gfx::Rect
SettingLevelBubbleDelegateView::GetAnchorRect() {
81 gfx::Size view_size
= GetPreferredSize();
82 // Calculate the position in screen coordinates that the bubble should
83 // "point" at (since we use BubbleBorder::FLOAT, this position actually
84 // specifies the center of the bubble).
85 gfx::Rect monitor_area
= gfx::Screen::GetMonitorAreaNearestWindow(NULL
);
87 monitor_area
.x() + kBubbleXRatio
* monitor_area
.width(),
88 monitor_area
.bottom() - view_size
.height() / 2 - kBubbleBottomGap
, 0, 0));
91 SettingLevelBubbleDelegateView::SettingLevelBubbleDelegateView()
92 : BubbleDelegateView(NULL
, views::BubbleBorder::FLOAT
),
94 set_close_on_esc(false);
95 set_use_focusless(true);
98 SettingLevelBubbleDelegateView::~SettingLevelBubbleDelegateView() {
102 void SettingLevelBubbleDelegateView::Init() {
103 SetLayoutManager(new views::FillLayout());
104 view_
= new SettingLevelBubbleView();
108 // SettingLevelBubble ----------------------------------------------------------
109 void SettingLevelBubble::ShowBubble(double percent
, bool enabled
) {
112 // Set up target percent and icon.
113 const double old_target_percent
= target_percent_
;
114 UpdateTargetPercent(percent
);
115 SkBitmap
* current_icon
= increase_icon_
;
116 if (!enabled
|| target_percent_
== 0)
117 current_icon
= disabled_icon_
;
118 else if (old_target_percent
>= 0 && target_percent_
< old_target_percent
)
119 current_icon
= decrease_icon_
;
122 view_
= CreateView();
123 view_
->Init(current_icon
, percent
, enabled
);
125 // Reset fade sequence, if the bubble is already fading.
126 SettingLevelBubbleDelegateView
* delegate
=
127 static_cast<SettingLevelBubbleDelegateView
*>
128 (view_
->GetWidget()->widget_delegate());
129 delegate
->ResetFade();
130 view_
->SetIcon(current_icon
);
131 view_
->SetEnabled(enabled
);
133 view_
->GetWidget()->Show();
134 // When the timer runs out, start the fade sequence.
135 hide_timer_
.Start(FROM_HERE
,
136 base::TimeDelta::FromMilliseconds(kBubbleShowTimeoutMs
),
137 this, &SettingLevelBubble::OnHideTimeout
);
140 void SettingLevelBubble::HideBubble() {
143 view_
->GetWidget()->Close();
148 void SettingLevelBubble::UpdateWithoutShowingBubble(double percent
,
150 UpdateTargetPercent(percent
);
152 view_
->SetEnabled(enabled
);
155 SettingLevelBubble::SettingLevelBubble(SkBitmap
* increase_icon
,
156 SkBitmap
* decrease_icon
,
157 SkBitmap
* disabled_icon
)
158 : current_percent_(-1.0),
159 target_percent_(-1.0),
160 increase_icon_(increase_icon
),
161 decrease_icon_(decrease_icon
),
162 disabled_icon_(disabled_icon
),
164 is_animating_(false) {
167 SettingLevelBubble::~SettingLevelBubble() {
171 void SettingLevelBubble::OnWidgetClosing(views::Widget
* widget
) {
172 if (view_
&& view_
->GetWidget() == widget
) {
173 view_
->GetWidget()->RemoveObserver(this);
177 current_percent_
= target_percent_
;
178 target_time_
= TimeTicks();
179 last_animation_update_time_
= TimeTicks();
180 last_target_update_time_
= TimeTicks();
185 SettingLevelBubbleView
* SettingLevelBubble::CreateView() {
186 SettingLevelBubbleDelegateView
* delegate
= new SettingLevelBubbleDelegateView
;
187 views::Widget
* widget
= browser::CreateViewsBubbleAboveLockScreen(delegate
);
188 widget
->AddObserver(this);
189 // Hold on to the content view.
190 return delegate
->view();
193 void SettingLevelBubble::OnHideTimeout() {
194 // Start fading away.
196 SettingLevelBubbleDelegateView
* delegate
=
197 static_cast<SettingLevelBubbleDelegateView
*>
198 (view_
->GetWidget()->widget_delegate());
199 delegate
->StartFade(false);
203 void SettingLevelBubble::OnAnimationTimeout() {
204 const TimeTicks now
= TimeTicks::Now();
205 const int64 remaining_ms
= (target_time_
- now
).InMilliseconds();
207 if (remaining_ms
<= 0) {
208 current_percent_
= target_percent_
;
211 // Figure out what fraction of the total time until we want to reach the
212 // target has elapsed since the last update.
213 const double remaining_percent
= target_percent_
- current_percent_
;
214 const int64 elapsed_ms
=
215 (now
- last_animation_update_time_
).InMilliseconds();
218 (static_cast<double>(elapsed_ms
) / (elapsed_ms
+ remaining_ms
));
220 last_animation_update_time_
= now
;
223 view_
->SetLevel(current_percent_
);
226 void SettingLevelBubble::UpdateTargetPercent(double percent
) {
227 target_percent_
= LimitPercent(percent
);
228 const TimeTicks now
= TimeTicks::Now();
230 if (current_percent_
< 0.0) {
231 // If we're setting the level for the first time, no need to animate.
232 current_percent_
= target_percent_
;
234 view_
->SetLevel(current_percent_
);
236 // Use the time since the last request as a hint for the duration of the
237 // animation. This makes us automatically adapt to the repeat rate if a key
238 // is being held down to change a setting (which prevents us from lagging
239 // behind when the key is finally released).
240 int64 duration_ms
= kInitialAnimationDurationMs
;
241 if (!last_target_update_time_
.is_null())
242 duration_ms
= min(kInitialAnimationDurationMs
,
243 (now
- last_target_update_time_
).InMilliseconds());
244 target_time_
= now
+ TimeDelta::FromMilliseconds(duration_ms
);
246 if (!is_animating_
) {
247 animation_timer_
.Start(FROM_HERE
,
248 TimeDelta::FromMilliseconds(kAnimationIntervalMs
),
250 &SettingLevelBubble::OnAnimationTimeout
);
251 is_animating_
= true;
252 last_animation_update_time_
= now
;
256 last_target_update_time_
= now
;
259 void SettingLevelBubble::StopAnimation() {
260 animation_timer_
.Stop();
261 is_animating_
= false;
264 } // namespace chromeos