unified_volume_view.cc 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // Copyright 2018 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. #include "ash/system/audio/unified_volume_view.h"
  5. #include <memory>
  6. #include <utility>
  7. #include "ash/resources/vector_icons/vector_icons.h"
  8. #include "ash/strings/grit/ash_strings.h"
  9. #include "ash/style/ash_color_provider.h"
  10. #include "ash/style/style_util.h"
  11. #include "ash/system/tray/tray_constants.h"
  12. #include "base/bind.h"
  13. #include "base/i18n/rtl.h"
  14. #include "components/vector_icons/vector_icons.h"
  15. #include "ui/base/l10n/l10n_util.h"
  16. #include "ui/color/color_id.h"
  17. #include "ui/gfx/canvas.h"
  18. #include "ui/gfx/image/image_skia_operations.h"
  19. #include "ui/gfx/paint_vector_icon.h"
  20. #include "ui/gfx/vector_icon_utils.h"
  21. #include "ui/views/background.h"
  22. #include "ui/views/border.h"
  23. #include "ui/views/controls/focus_ring.h"
  24. #include "ui/views/controls/highlight_path_generator.h"
  25. #include "ui/views/controls/image_view.h"
  26. #include "ui/views/layout/box_layout.h"
  27. namespace ash {
  28. namespace {
  29. // References to the icons that correspond to different volume levels.
  30. const gfx::VectorIcon* const kVolumeLevelIcons[] = {
  31. &kUnifiedMenuVolumeLowIcon, // Low volume.
  32. &kUnifiedMenuVolumeMediumIcon, // Medium volume.
  33. &kUnifiedMenuVolumeHighIcon, // High volume.
  34. &kUnifiedMenuVolumeHighIcon, // Full volume.
  35. };
  36. // The maximum index of kVolumeLevelIcons.
  37. constexpr int kVolumeLevels = std::size(kVolumeLevelIcons) - 1;
  38. // Get vector icon reference that corresponds to the given volume level. |level|
  39. // is between 0.0 to 1.0.
  40. const gfx::VectorIcon& GetVolumeIconForLevel(float level) {
  41. int index = static_cast<int>(std::ceil(level * kVolumeLevels));
  42. if (index < 0)
  43. index = 0;
  44. else if (index > kVolumeLevels)
  45. index = kVolumeLevels;
  46. return *kVolumeLevelIcons[index];
  47. }
  48. // A template class for the UnifiedVolumeView buttons, used by the More button.
  49. // |T| must be a subtype of |views::Button|.
  50. template <typename T>
  51. class UnifiedVolumeViewButton : public T {
  52. public:
  53. static_assert(std::is_base_of<views::Button, T>::value,
  54. "T must be a subtype of views::Button");
  55. // A constructor that forwards |args| to |T|'s constructor, so |args| are the
  56. // exact same as required by |T|'s constructor. It sets up the ink drop on the
  57. // view.
  58. template <typename... Args>
  59. explicit UnifiedVolumeViewButton(Args... args)
  60. : T(std::forward<Args>(args)...) {
  61. StyleUtil::SetUpInkDropForButton(this);
  62. views::InstallRoundRectHighlightPathGenerator(this, gfx::Insets(),
  63. kTrayItemCornerRadius);
  64. T::SetBackground(views::CreateRoundedRectBackground(GetBackgroundColor(),
  65. kTrayItemCornerRadius));
  66. }
  67. ~UnifiedVolumeViewButton() override = default;
  68. void OnThemeChanged() override {
  69. T::OnThemeChanged();
  70. views::FocusRing::Get(this)->SetColorId(ui::kColorAshFocusRing);
  71. T::background()->SetNativeControlColor(GetBackgroundColor());
  72. }
  73. SkColor GetIconColor() {
  74. return AshColorProvider::Get()->GetContentLayerColor(
  75. AshColorProvider::ContentLayerType::kButtonIconColor);
  76. }
  77. SkColor GetBackgroundColor() {
  78. return AshColorProvider::Get()->GetControlsLayerColor(
  79. AshColorProvider::ControlsLayerType::kControlBackgroundColorInactive);
  80. }
  81. };
  82. class MoreButton : public UnifiedVolumeViewButton<views::Button> {
  83. public:
  84. explicit MoreButton(PressedCallback callback)
  85. : UnifiedVolumeViewButton(std::move(callback)) {
  86. SetLayoutManager(std::make_unique<views::BoxLayout>(
  87. views::BoxLayout::Orientation::kHorizontal,
  88. gfx::Insets((kTrayItemSize -
  89. GetDefaultSizeOfVectorIcon(kUnifiedMenuExpandIcon)) /
  90. 2),
  91. 2));
  92. more_image_ = AddChildView(std::make_unique<views::ImageView>());
  93. more_image_->SetCanProcessEventsWithinSubtree(false);
  94. SetTooltipText(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO));
  95. }
  96. MoreButton(const MoreButton&) = delete;
  97. MoreButton& operator=(const MoreButton&) = delete;
  98. ~MoreButton() override = default;
  99. const char* GetClassName() const override { return "MoreButton"; }
  100. void OnThemeChanged() override {
  101. UnifiedVolumeViewButton::OnThemeChanged();
  102. const SkColor icon_color = GetIconColor();
  103. DCHECK(more_image_);
  104. auto icon_rotation = base::i18n::IsRTL()
  105. ? SkBitmapOperations::ROTATION_270_CW
  106. : SkBitmapOperations::ROTATION_90_CW;
  107. more_image_->SetImage(gfx::ImageSkiaOperations::CreateRotatedImage(
  108. CreateVectorIcon(kUnifiedMenuExpandIcon, icon_color), icon_rotation));
  109. }
  110. private:
  111. views::ImageView* more_image_ = nullptr;
  112. };
  113. } // namespace
  114. UnifiedVolumeView::UnifiedVolumeView(
  115. UnifiedVolumeSliderController* controller,
  116. UnifiedVolumeSliderController::Delegate* delegate)
  117. : UnifiedSliderView(base::BindRepeating(
  118. &UnifiedVolumeSliderController::SliderButtonPressed,
  119. base::Unretained(controller)),
  120. controller,
  121. kSystemMenuVolumeHighIcon,
  122. IDS_ASH_STATUS_TRAY_VOLUME_SLIDER_LABEL),
  123. more_button_(new MoreButton(
  124. base::BindRepeating(&UnifiedVolumeSliderController::Delegate::
  125. OnAudioSettingsButtonClicked,
  126. delegate->weak_ptr_factory_.GetWeakPtr()))) {
  127. CrasAudioHandler::Get()->AddAudioObserver(this);
  128. AddChildView(more_button_);
  129. Update(false /* by_user */);
  130. }
  131. UnifiedVolumeView::~UnifiedVolumeView() {
  132. CrasAudioHandler::Get()->RemoveAudioObserver(this);
  133. }
  134. const char* UnifiedVolumeView::GetClassName() const {
  135. return "UnifiedVolumeView";
  136. }
  137. void UnifiedVolumeView::Update(bool by_user) {
  138. bool is_muted = CrasAudioHandler::Get()->IsOutputMuted();
  139. float level = CrasAudioHandler::Get()->GetOutputVolumePercent() / 100.f;
  140. // To indicate that the volume is muted, set the volume slider to the minimal
  141. // visual style.
  142. slider()->SetRenderingStyle(
  143. is_muted ? views::Slider::RenderingStyle::kMinimalStyle
  144. : views::Slider::RenderingStyle::kDefaultStyle);
  145. slider()->SetEnabled(!CrasAudioHandler::Get()->IsOutputMutedByPolicy());
  146. // The button should be gray when muted and colored otherwise.
  147. button()->SetToggled(!is_muted);
  148. button()->SetVectorIcon(is_muted ? kUnifiedMenuVolumeMuteIcon
  149. : GetVolumeIconForLevel(level));
  150. std::u16string state_tooltip_text = l10n_util::GetStringUTF16(
  151. is_muted ? IDS_ASH_STATUS_TRAY_VOLUME_STATE_MUTED
  152. : IDS_ASH_STATUS_TRAY_VOLUME_STATE_ON);
  153. button()->SetTooltipText(l10n_util::GetStringFUTF16(
  154. IDS_ASH_STATUS_TRAY_VOLUME, state_tooltip_text));
  155. // Slider's value is in finer granularity than audio volume level(0.01),
  156. // there will be a small discrepancy between slider's value and volume level
  157. // on audio side. To avoid the jittering in slider UI, use the slider's
  158. // current value.
  159. if (level != 1.0 && std::abs(level - slider()->GetValue()) <
  160. kAudioSliderIgnoreUpdateThreshold) {
  161. level = slider()->GetValue();
  162. }
  163. // Note: even if the value does not change, we still need to call this
  164. // function to enable accessibility events (crbug.com/1013251).
  165. SetSliderValue(level, by_user);
  166. }
  167. void UnifiedVolumeView::OnOutputNodeVolumeChanged(uint64_t node_id,
  168. int volume) {
  169. Update(true /* by_user */);
  170. }
  171. void UnifiedVolumeView::OnOutputMuteChanged(bool mute_on) {
  172. Update(true /* by_user */);
  173. }
  174. void UnifiedVolumeView::OnAudioNodesChanged() {
  175. Update(true /* by_user */);
  176. }
  177. void UnifiedVolumeView::OnActiveOutputNodeChanged() {
  178. Update(true /* by_user */);
  179. }
  180. void UnifiedVolumeView::OnActiveInputNodeChanged() {
  181. Update(true /* by_user */);
  182. }
  183. void UnifiedVolumeView::ChildVisibilityChanged(views::View* child) {
  184. Layout();
  185. }
  186. } // namespace ash