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.
4
5 #include "chrome/browser/chromeos/setting_level_bubble.h"
6
7 #include <gdk/gdk.h>
8
9 #include "base/timer.h"
10 #include "chrome/browser/chromeos/login/background_view.h"
11 #include "chrome/browser/chromeos/login/login_utils.h"
12 #include "chrome/browser/chromeos/setting_level_bubble_view.h"
13 #include "chrome/browser/profiles/profile_manager.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_list.h"
16 #include "chrome/browser/ui/browser_window.h"
17 #include "chrome/browser/ui/views/bubble/bubble.h"
18 #include "views/widget/root_view.h"
19
20 namespace {
21
22 const int kBubbleShowTimeoutSec = 2;
23 const int kAnimationDurationMs = 200;
24
25 // Horizontal relative position: 0 - leftmost, 0.5 - center, 1 - rightmost.
26 const double kBubbleXRatio = 0.5;
27
28 // Vertical gap from the bottom of the screen in pixels.
29 const int kBubbleBottomGap = 30;
30
LimitPercent(int percent)31 int LimitPercent(int percent) {
32 if (percent < 0)
33 percent = 0;
34 else if (percent > 100)
35 percent = 100;
36 return percent;
37 }
38
39 } // namespace
40
41 namespace chromeos {
42
43 // Temporary helper routine. Tries to first return the widget from the
44 // most-recently-focused normal browser window, then from a login
45 // background, and finally NULL if both of those fail.
46 // TODO(glotov): remove this in favor of enabling Bubble class act
47 // without |parent| specified. crosbug.com/4025
GetToplevelWidget()48 static views::Widget* GetToplevelWidget() {
49 GtkWindow* window = NULL;
50
51 // We just use the default profile here -- this gets overridden as needed
52 // in Chrome OS depending on whether the user is logged in or not.
53 Browser* browser =
54 BrowserList::FindBrowserWithType(
55 ProfileManager::GetDefaultProfile(),
56 Browser::TYPE_NORMAL,
57 true); // match_incognito
58 if (browser) {
59 window = GTK_WINDOW(browser->window()->GetNativeHandle());
60 } else {
61 // Otherwise, see if there's a background window that we can use.
62 BackgroundView* background = LoginUtils::Get()->GetBackgroundView();
63 if (background)
64 window = GTK_WINDOW(background->GetNativeWindow());
65 }
66
67 if (!window)
68 return NULL;
69
70 views::NativeWidget* native_widget =
71 views::NativeWidget::GetNativeWidgetForNativeWindow(window);
72 return native_widget->GetWidget();
73 }
74
SettingLevelBubble(SkBitmap * increase_icon,SkBitmap * decrease_icon,SkBitmap * zero_icon)75 SettingLevelBubble::SettingLevelBubble(SkBitmap* increase_icon,
76 SkBitmap* decrease_icon,
77 SkBitmap* zero_icon)
78 : previous_percent_(-1),
79 current_percent_(-1),
80 increase_icon_(increase_icon),
81 decrease_icon_(decrease_icon),
82 zero_icon_(zero_icon),
83 bubble_(NULL),
84 view_(NULL),
85 animation_(this) {
86 animation_.SetSlideDuration(kAnimationDurationMs);
87 animation_.SetTweenType(ui::Tween::LINEAR);
88 }
89
ShowBubble(int percent)90 void SettingLevelBubble::ShowBubble(int percent) {
91 percent = LimitPercent(percent);
92 if (previous_percent_ == -1)
93 previous_percent_ = percent;
94 current_percent_ = percent;
95
96 SkBitmap* icon = increase_icon_;
97 if (current_percent_ == 0)
98 icon = zero_icon_;
99 else if (current_percent_ < previous_percent_)
100 icon = decrease_icon_;
101
102 if (!bubble_) {
103 views::Widget* widget = GetToplevelWidget();
104 if (widget == NULL)
105 return;
106 DCHECK(view_ == NULL);
107 view_ = new SettingLevelBubbleView;
108 view_->Init(icon, previous_percent_);
109 // Calculate position of the bubble.
110 gfx::Rect bounds = widget->GetClientAreaScreenBounds();
111 const gfx::Size view_size = view_->GetPreferredSize();
112 // Note that (x, y) is the point of the center of the bubble.
113 const int x = view_size.width() / 2 +
114 kBubbleXRatio * (bounds.width() - view_size.width());
115 const int y = bounds.height() - view_size.height() / 2 - kBubbleBottomGap;
116 bubble_ = Bubble::ShowFocusless(widget, // parent
117 gfx::Rect(x, y, 0, 20),
118 BubbleBorder::FLOAT,
119 view_, // contents
120 this, // delegate
121 true); // show while screen is locked
122 } else {
123 DCHECK(view_);
124 timeout_timer_.Stop();
125 view_->SetIcon(icon);
126 }
127 if (animation_.is_animating())
128 animation_.End();
129 animation_.Reset();
130 animation_.Show();
131 timeout_timer_.Start(base::TimeDelta::FromSeconds(kBubbleShowTimeoutSec),
132 this, &SettingLevelBubble::OnTimeout);
133 }
134
HideBubble()135 void SettingLevelBubble::HideBubble() {
136 if (bubble_)
137 bubble_->Close();
138 }
139
UpdateWithoutShowingBubble(int percent)140 void SettingLevelBubble::UpdateWithoutShowingBubble(int percent) {
141 percent = LimitPercent(percent);
142
143 previous_percent_ =
144 animation_.is_animating() ?
145 animation_.GetCurrentValue() :
146 current_percent_;
147 if (previous_percent_ < 0)
148 previous_percent_ = percent;
149 current_percent_ = percent;
150
151 if (animation_.is_animating())
152 animation_.End();
153 animation_.Reset();
154 animation_.Show();
155 }
156
OnTimeout()157 void SettingLevelBubble::OnTimeout() {
158 HideBubble();
159 }
160
BubbleClosing(Bubble * bubble,bool)161 void SettingLevelBubble::BubbleClosing(Bubble* bubble, bool) {
162 DCHECK(bubble == bubble_);
163 timeout_timer_.Stop();
164 animation_.Stop();
165 bubble_ = NULL;
166 view_ = NULL;
167 }
168
AnimationEnded(const ui::Animation * animation)169 void SettingLevelBubble::AnimationEnded(const ui::Animation* animation) {
170 previous_percent_ = current_percent_;
171 }
172
AnimationProgressed(const ui::Animation * animation)173 void SettingLevelBubble::AnimationProgressed(const ui::Animation* animation) {
174 if (view_) {
175 view_->Update(
176 ui::Tween::ValueBetween(animation->GetCurrentValue(),
177 previous_percent_,
178 current_percent_));
179 }
180 }
181
182 } // namespace chromeos
183