OLD | NEW |
| (Empty) |
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 "ash/system/audio/tray_volume.h" | |
6 | |
7 #include <cmath> | |
8 | |
9 #include "ash/ash_constants.h" | |
10 #include "ash/shell.h" | |
11 #include "ash/system/tray/system_tray_delegate.h" | |
12 #include "ash/system/tray/system_tray_notifier.h" | |
13 #include "ash/system/tray/tray_bar_button_with_title.h" | |
14 #include "ash/system/tray/tray_constants.h" | |
15 #include "ash/volume_control_delegate.h" | |
16 #include "base/utf_string_conversions.h" | |
17 #include "grit/ash_resources.h" | |
18 #include "grit/ash_strings.h" | |
19 #include "third_party/skia/include/core/SkCanvas.h" | |
20 #include "third_party/skia/include/core/SkPaint.h" | |
21 #include "third_party/skia/include/core/SkRect.h" | |
22 #include "third_party/skia/include/effects/SkGradientShader.h" | |
23 #include "ui/base/resource/resource_bundle.h" | |
24 #include "ui/gfx/canvas.h" | |
25 #include "ui/gfx/image/image.h" | |
26 #include "ui/gfx/image/image_skia_operations.h" | |
27 #include "ui/views/controls/button/image_button.h" | |
28 #include "ui/views/controls/image_view.h" | |
29 #include "ui/views/controls/label.h" | |
30 #include "ui/views/controls/slider.h" | |
31 #include "ui/views/layout/box_layout.h" | |
32 #include "ui/views/view.h" | |
33 | |
34 namespace ash { | |
35 namespace internal { | |
36 | |
37 namespace { | |
38 const int kVolumeImageWidth = 25; | |
39 const int kVolumeImageHeight = 25; | |
40 | |
41 // IDR_AURA_UBER_TRAY_VOLUME_LEVELS contains 5 images, | |
42 // The one for mute is at the 0 index and the other | |
43 // four are used for ascending volume levels. | |
44 const int kVolumeLevels = 4; | |
45 | |
46 bool IsAudioMuted() { | |
47 return Shell::GetInstance()->system_tray_delegate()-> | |
48 GetVolumeControlDelegate()->IsAudioMuted(); | |
49 } | |
50 | |
51 float GetVolumeLevel() { | |
52 return Shell::GetInstance()->system_tray_delegate()-> | |
53 GetVolumeControlDelegate()->GetVolumeLevel(); | |
54 } | |
55 | |
56 } // namespace | |
57 | |
58 namespace tray { | |
59 | |
60 class VolumeButton : public views::ToggleImageButton { | |
61 public: | |
62 explicit VolumeButton(views::ButtonListener* listener) | |
63 : views::ToggleImageButton(listener), | |
64 image_index_(-1) { | |
65 SetImageAlignment(ALIGN_CENTER, ALIGN_MIDDLE); | |
66 image_ = ui::ResourceBundle::GetSharedInstance().GetImageNamed( | |
67 IDR_AURA_UBER_TRAY_VOLUME_LEVELS); | |
68 SetPreferredSize(gfx::Size(kTrayPopupItemHeight, kTrayPopupItemHeight)); | |
69 Update(); | |
70 } | |
71 | |
72 virtual ~VolumeButton() {} | |
73 | |
74 void Update() { | |
75 float level = GetVolumeLevel(); | |
76 int image_index = IsAudioMuted() ? | |
77 0 : (level == 1.0 ? | |
78 kVolumeLevels : | |
79 std::max(1, int(std::ceil(level * (kVolumeLevels - 1))))); | |
80 if (image_index != image_index_) { | |
81 gfx::Rect region(0, image_index * kVolumeImageHeight, | |
82 kVolumeImageWidth, kVolumeImageHeight); | |
83 gfx::ImageSkia image_skia = gfx::ImageSkiaOperations::ExtractSubset( | |
84 *(image_.ToImageSkia()), region); | |
85 SetImage(views::CustomButton::STATE_NORMAL, &image_skia); | |
86 image_index_ = image_index; | |
87 } | |
88 SchedulePaint(); | |
89 } | |
90 | |
91 private: | |
92 // Overridden from views::View. | |
93 virtual gfx::Size GetPreferredSize() OVERRIDE { | |
94 gfx::Size size = views::ToggleImageButton::GetPreferredSize(); | |
95 size.set_height(kTrayPopupItemHeight); | |
96 return size; | |
97 } | |
98 | |
99 gfx::Image image_; | |
100 int image_index_; | |
101 | |
102 DISALLOW_COPY_AND_ASSIGN(VolumeButton); | |
103 }; | |
104 | |
105 class MuteButton : public TrayBarButtonWithTitle { | |
106 public: | |
107 explicit MuteButton(views::ButtonListener* listener) | |
108 : TrayBarButtonWithTitle(listener, | |
109 -1, // no title under mute button | |
110 kTrayBarButtonWidth) { | |
111 Update(); | |
112 } | |
113 virtual ~MuteButton() {} | |
114 | |
115 void Update() { | |
116 UpdateButton(IsAudioMuted()); | |
117 SchedulePaint(); | |
118 } | |
119 | |
120 private: | |
121 DISALLOW_COPY_AND_ASSIGN(MuteButton); | |
122 }; | |
123 | |
124 class VolumeSlider : public views::Slider { | |
125 public: | |
126 explicit VolumeSlider(views::SliderListener* listener) | |
127 : views::Slider(listener, views::Slider::HORIZONTAL) { | |
128 set_focus_border_color(kFocusBorderColor); | |
129 SetValue(GetVolumeLevel()); | |
130 SetAccessibleName( | |
131 ui::ResourceBundle::GetSharedInstance().GetLocalizedString( | |
132 IDS_ASH_STATUS_TRAY_VOLUME)); | |
133 Update(); | |
134 } | |
135 virtual ~VolumeSlider() {} | |
136 | |
137 void Update() { | |
138 UpdateState(!IsAudioMuted()); | |
139 } | |
140 | |
141 DISALLOW_COPY_AND_ASSIGN(VolumeSlider); | |
142 }; | |
143 | |
144 class VolumeView : public views::View, | |
145 public views::ButtonListener, | |
146 public views::SliderListener { | |
147 public: | |
148 VolumeView() { | |
149 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, | |
150 kTrayPopupPaddingHorizontal, 0, kTrayPopupPaddingBetweenItems)); | |
151 | |
152 icon_ = new VolumeButton(this); | |
153 AddChildView(icon_); | |
154 | |
155 mute_ = new MuteButton(this); | |
156 AddChildView(mute_); | |
157 | |
158 slider_ = new VolumeSlider(this); | |
159 AddChildView(slider_); | |
160 } | |
161 | |
162 virtual ~VolumeView() {} | |
163 | |
164 void Update() { | |
165 icon_->Update(); | |
166 mute_->Update(); | |
167 slider_->Update(); | |
168 } | |
169 | |
170 void SetVolumeLevel(float percent) { | |
171 // The change in volume will be reflected via accessibility system events, | |
172 // so we prevent the UI event from being sent here. | |
173 slider_->set_enable_accessibility_events(false); | |
174 slider_->SetValue(percent); | |
175 // It is possible that the volume was (un)muted, but the actual volume level | |
176 // did not change. In that case, setting the value of the slider won't | |
177 // trigger an update. So explicitly trigger an update. | |
178 Update(); | |
179 slider_->set_enable_accessibility_events(true); | |
180 } | |
181 | |
182 private: | |
183 // Overridden from views::View. | |
184 virtual void OnBoundsChanged(const gfx::Rect& old_bounds) OVERRIDE { | |
185 int w = width() - slider_->x(); | |
186 slider_->SetSize(gfx::Size(w, slider_->height())); | |
187 } | |
188 | |
189 // Overridden from views::ButtonListener. | |
190 virtual void ButtonPressed(views::Button* sender, | |
191 const ui::Event& event) OVERRIDE { | |
192 CHECK(sender == icon_ || sender == mute_); | |
193 ash::Shell::GetInstance()->system_tray_delegate()-> | |
194 GetVolumeControlDelegate()->SetAudioMuted(!IsAudioMuted()); | |
195 } | |
196 | |
197 // Overridden from views:SliderListener. | |
198 virtual void SliderValueChanged(views::Slider* sender, | |
199 float value, | |
200 float old_value, | |
201 views::SliderChangeReason reason) OVERRIDE { | |
202 if (reason == views::VALUE_CHANGED_BY_USER) { | |
203 ash::Shell::GetInstance()->system_tray_delegate()-> | |
204 GetVolumeControlDelegate()->SetVolumeLevel(value); | |
205 } | |
206 icon_->Update(); | |
207 } | |
208 | |
209 VolumeButton* icon_; | |
210 MuteButton* mute_; | |
211 VolumeSlider* slider_; | |
212 | |
213 DISALLOW_COPY_AND_ASSIGN(VolumeView); | |
214 }; | |
215 | |
216 } // namespace tray | |
217 | |
218 TrayVolume::TrayVolume(SystemTray* system_tray) | |
219 : TrayImageItem(system_tray, IDR_AURA_UBER_TRAY_VOLUME_MUTE), | |
220 volume_view_(NULL), | |
221 is_default_view_(false) { | |
222 Shell::GetInstance()->system_tray_notifier()->AddAudioObserver(this); | |
223 } | |
224 | |
225 TrayVolume::~TrayVolume() { | |
226 Shell::GetInstance()->system_tray_notifier()->RemoveAudioObserver(this); | |
227 } | |
228 | |
229 bool TrayVolume::GetInitialVisibility() { | |
230 return IsAudioMuted(); | |
231 } | |
232 | |
233 views::View* TrayVolume::CreateDefaultView(user::LoginStatus status) { | |
234 volume_view_ = new tray::VolumeView; | |
235 is_default_view_ = true; | |
236 return volume_view_; | |
237 } | |
238 | |
239 views::View* TrayVolume::CreateDetailedView(user::LoginStatus status) { | |
240 volume_view_ = new tray::VolumeView; | |
241 is_default_view_ = false; | |
242 return volume_view_; | |
243 } | |
244 | |
245 void TrayVolume::DestroyDefaultView() { | |
246 if (is_default_view_) | |
247 volume_view_ = NULL; | |
248 } | |
249 | |
250 void TrayVolume::DestroyDetailedView() { | |
251 if (!is_default_view_) | |
252 volume_view_ = NULL; | |
253 } | |
254 | |
255 bool TrayVolume::ShouldHideArrow() const { | |
256 return true; | |
257 } | |
258 | |
259 bool TrayVolume::ShouldShowLauncher() const { | |
260 return false; | |
261 } | |
262 | |
263 void TrayVolume::OnVolumeChanged(float percent) { | |
264 if (tray_view()) | |
265 tray_view()->SetVisible(GetInitialVisibility()); | |
266 | |
267 if (volume_view_) { | |
268 if (IsAudioMuted()) | |
269 percent = 0.0; | |
270 volume_view_->SetVolumeLevel(percent); | |
271 SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds); | |
272 return; | |
273 } | |
274 PopupDetailedView(kTrayPopupAutoCloseDelayInSeconds, false); | |
275 } | |
276 | |
277 void TrayVolume::OnMuteToggled() { | |
278 if (tray_view()) | |
279 tray_view()->SetVisible(GetInitialVisibility()); | |
280 | |
281 if (volume_view_) | |
282 volume_view_->Update(); | |
283 else | |
284 PopupDetailedView(kTrayPopupAutoCloseDelayInSeconds, false); | |
285 } | |
286 | |
287 } // namespace internal | |
288 } // namespace ash | |
OLD | NEW |