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