chromium/ash/system/unified/unified_slider_view.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/system/unified/unified_slider_view.h"

#include <memory>
#include <utility>

#include "ash/constants/quick_settings_catalogs.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/quick_settings_metrics_util.h"
#include "ash/system/unified/quick_settings_slider.h"
#include "base/check_op.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/compositor/layer.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/border.h"
#include "ui/views/controls/slider.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/widget/widget.h"

namespace ash {

namespace {

constexpr auto kQsSliderRowPadding = gfx::Insets::TLBR(0, 12, 4, 16);
constexpr auto kQsSliderBorder = gfx::Insets::TLBR(4, 4, 4, 16);

std::unique_ptr<views::Slider> CreateSlider(
    UnifiedSliderListener* listener,
    bool read_only,
    QuickSettingsSlider::Style slider_style) {
  return read_only
             ? std::make_unique<ReadOnlySlider>(slider_style)
             : std::make_unique<QuickSettingsSlider>(listener, slider_style);
}

}  // namespace

void UnifiedSliderListener::TrackToggleUMA(bool target_toggle_state) {
  CHECK_NE(GetCatalogName(), QsSliderCatalogName::kUnknown);
  quick_settings_metrics_util::RecordQsSliderToggle(
      GetCatalogName(), /*enable=*/target_toggle_state);
}

void UnifiedSliderListener::TrackValueChangeUMA(bool going_up) {
  CHECK_NE(GetCatalogName(), QsSliderCatalogName::kUnknown);
  quick_settings_metrics_util::RecordQsSliderValueChange(GetCatalogName(),
                                                         /*going_up=*/going_up);
}

UnifiedSliderView::UnifiedSliderView(views::Button::PressedCallback callback,
                                     UnifiedSliderListener* listener,
                                     const gfx::VectorIcon& icon,
                                     int accessible_name_id,
                                     bool is_togglable,
                                     bool read_only,
                                     QuickSettingsSlider::Style slider_style)
    : icon_(&icon), is_togglable_(is_togglable) {
  slider_ = AddChildView(CreateSlider(listener, read_only, slider_style));
  slider_->SetBorder(views::CreateEmptyBorder(kQsSliderBorder));
  // Sets `slider_` to have a `BoxLayout` to align the child view
  // `slider_button_` to the left.
  auto* slider_layout =
      slider_->SetLayoutManager(std::make_unique<views::BoxLayout>(
          views::BoxLayout::Orientation::kHorizontal));
  slider_layout->set_main_axis_alignment(
      views::BoxLayout::MainAxisAlignment::kStart);

  const bool is_default_style =
      (slider_style == QuickSettingsSlider::Style::kDefault ||
       slider_style == QuickSettingsSlider::Style::kDefaultMuted);
  slider_button_ = slider_->AddChildView(std::make_unique<IconButton>(
      std::move(callback),
      is_default_style ? IconButton::Type::kMediumFloating
                       : IconButton::Type::kLargeFloating,
      &icon, accessible_name_id,
      /*is_togglable=*/true,
      /*has_border=*/true));
  slider_button_->SetIconColor(cros_tokens::kCrosSysSystemOnPrimaryContainer);
  // The `slider_button_` should be focusable by the ChromeVox.
  slider_button_->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
  // `slider_button_` should disable event processing if it's not togglable.
  if (!is_togglable_) {
    slider_button_->SetCanProcessEventsWithinSubtree(/*can_process=*/false);
  }

  // Prevent an accessibility event while initiallizing this view.
  // Typically the first update of the slider value is conducted by the
  // caller function to reflect the current value.
  slider_->SetEnableAccessibilityEvents(false);
  slider_->GetViewAccessibility().SetName(
      l10n_util::GetStringUTF16(accessible_name_id),
      ax::mojom::NameFrom::kAttribute);

  auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
      views::BoxLayout::Orientation::kHorizontal, kQsSliderRowPadding,
      kSliderChildrenViewSpacing));
  layout->SetFlexForView(slider_, /*flex=*/1);
  layout->set_cross_axis_alignment(
      views::BoxLayout::CrossAxisAlignment::kCenter);

  // Adds a layer to set it non-opaque. Otherwise the full part of the slider
  // will be drawn on top of the previous draw.
  SetPaintToLayer();
  layer()->SetFillsBoundsOpaquely(false);
}

void UnifiedSliderView::SetSliderValue(float value, bool by_user) {
  // SetValue() calls |listener|, so we should ignore the call when the widget
  // is closed, because controllers are already deleted.
  // It should allow the case GetWidget() returning null, so that initial
  // position can be properly set by controllers before the view is attached to
  // a widget.
  if (GetWidget() && GetWidget()->IsClosed()) {
    return;
  }

  slider_->SetValue(value);
  if (by_user) {
    slider_->SetEnableAccessibilityEvents(true);
  }
}

UnifiedSliderView::~UnifiedSliderView() = default;

void UnifiedSliderView::OnEvent(ui::Event* event) {
  // If `slider_button_` is not togglable, pressing the return key should not
  // trigger the clicking callback.
  if (!is_togglable_) {
    views::View::OnEvent(event);
    return;
  }

  if (!event->IsKeyEvent()) {
    views::View::OnEvent(event);
    return;
  }

  if (slider_ && !slider_->GetEnableAccessibilityEvents()) {
    slider_->SetEnableAccessibilityEvents(true);
  }

  auto* key_event = event->AsKeyEvent();
  auto key_code = key_event->key_code();

  // Only handles press event to avoid handling the event again when the key is
  // released.
  if (key_code == ui::VKEY_RETURN &&
      key_event->type() == ui::EventType::kKeyPressed) {
    slider_button_->NotifyClick(*event);
    return;
  }

  views::View::OnEvent(event);
}

BEGIN_METADATA(UnifiedSliderView)
END_METADATA

}  // namespace ash