Skip to content

Commit

Permalink
Added the new class QsSlider to create the new slider for QsRevamp
Browse files Browse the repository at this point in the history
In this CL, we added a new class QsSlider to create the new slider for
the QsRevamp project. Also, a ReadOnlySlider class is added to create
the read-only slider for keyboard brightness toast. These two classes
will replace the old base slider SystemSlider and ReadOnlySlider defined
in UnifiedSliderView. The colors for some parts of the slider are going
to be updated once the spec is ready. For a11y, the tooltip is to be
added and the focus ring may be updated too.

Light mode QuickSettingsSlider v.s. base Slider on main page and audio
subpage:
https://screenshot.googleplex.com/8LuRYiv8viaREQj
Dark mode:
https://screenshot.googleplex.com/6TjUZVkiwhuuesC

Note that the implementation will be in a separate CL, so only the
slider itself is changed in the above screenshots. The icon button will
be modified in the implementation.

Bug: b/251723605
Change-Id: I6e3228ac44ae6b6279b18ffdedf07e91b0261c8e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3994174
Reviewed-by: Alex Newcomer <newcomer@chromium.org>
Commit-Queue: Sylvie Liu <sylvieliu@chromium.org>
Reviewed-by: Jiaming Cheng <jiamingc@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1067670}
  • Loading branch information
Sylvie Liu authored and Chromium LUCI CQ committed Nov 4, 2022
1 parent 6802fd2 commit 2a93223
Show file tree
Hide file tree
Showing 5 changed files with 335 additions and 68 deletions.
2 changes: 2 additions & 0 deletions ash/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1837,6 +1837,8 @@ component("ash") {
"system/unified/quick_settings_header.h",
"system/unified/quick_settings_metrics_util.cc",
"system/unified/quick_settings_metrics_util.h",
"system/unified/quick_settings_slider.cc",
"system/unified/quick_settings_slider.h",
"system/unified/quick_settings_view.cc",
"system/unified/quick_settings_view.h",
"system/unified/quiet_mode_feature_pod_controller.cc",
Expand Down
214 changes: 214 additions & 0 deletions ash/system/unified/quick_settings_slider.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// Copyright 2022 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/quick_settings_slider.h"

#include "ash/constants/ash_features.h"
#include "ash/style/ash_color_provider.h"
#include "ash/style/color_util.h"
#include "base/notreached.h"
#include "cc/paint/paint_flags.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/color/color_id.h"
#include "ui/color/color_provider.h"
#include "ui/events/event.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/views/controls/slider.h"

namespace ash {

namespace {

// The thickness of the empty slider.
constexpr int kEmptySliderThickness = 4;

// The thickness of the full slider.
constexpr int kFullSliderThickness = 32;

// The radius used to draw the rounded empty slider ends.
constexpr float kEmptySliderRoundedRadius = 2.f;
constexpr float kEmptySliderWidth = 2 * kEmptySliderRoundedRadius;

// The radius used to draw the rounded full slider ends.
constexpr float kFullSliderRoundedRadius = 16.f;
constexpr float kFullSliderWidth = 2 * kFullSliderRoundedRadius;

// The radius used to draw the rounded corner for active/inactive slider on the
// audio subpage.
constexpr float kActiveRadioSliderRoundedRadius = 16.f;
// TODO(b/256705775): Replace the actual radius value once the spec is updated.
constexpr float kInactiveRadioSliderRoundedRadius = 8.f;

// TODO(b/256705775): Replace the value once the spec is updated.
// The thickness of the focus ring border.
constexpr int kLineThickness = 2;
// The gap between the focus ring and the slider.
constexpr int kFocusOffset = 2;

float GetSliderRoundedCornerRadius(QuickSettingsSlider::Style slider_style) {
switch (slider_style) {
case QuickSettingsSlider::Style::kDefault:
return kFullSliderRoundedRadius;
case QuickSettingsSlider::Style::kRadioActive:
return kActiveRadioSliderRoundedRadius;
case QuickSettingsSlider::Style::kRadioInactive:
return kInactiveRadioSliderRoundedRadius;
default:
NOTREACHED();
}
}

} // namespace

QuickSettingsSlider::QuickSettingsSlider(views::SliderListener* listener,
Style slider_style)
: views::Slider(listener), slider_style_(slider_style) {
if (!features::IsQsRevampEnabled())
return;
SetValueIndicatorRadius(kFullSliderRoundedRadius);
SetFocusBehavior(FocusBehavior::ALWAYS);
}

QuickSettingsSlider::~QuickSettingsSlider() = default;

void QuickSettingsSlider::SetSliderStyle(Style style) {
if (slider_style_ == style)
return;

slider_style_ = style;

if (slider_style_ == Style::kRadioInactive)
SetFocusBehavior(FocusBehavior::NEVER);

SchedulePaint();
}

SkColor QuickSettingsSlider::GetThumbColor() const {
// TODO(b/256705775): Updates the color when QsRevamp is disabled but Jelly is
// enabled.
if (!features::IsQsRevampEnabled()) {
using Type = AshColorProvider::ContentLayerType;
return AshColorProvider::Get()->GetContentLayerColor(
(style() == RenderingStyle::kMinimalStyle) ? Type::kSliderColorInactive
: Type::kSliderColorActive);
}

switch (slider_style_) {
case Style::kDefault:
case Style::kRadioActive:
return GetColorProvider()->GetColor(
static_cast<ui::ColorId>(cros_tokens::kCrosSysSysPrimaryContainer));
case Style::kRadioInactive:
return GetColorProvider()->GetColor(
static_cast<ui::ColorId>(cros_tokens::kCrosSysDisabled));
default:
NOTREACHED();
}
}

SkColor QuickSettingsSlider::GetTroughColor() const {
// TODO(b/256705775): Updates the color when QsRevamp is disabled but Jelly is
// enabled.
if (!features::IsQsRevampEnabled())
return ColorUtil::GetSecondToneColor(GetThumbColor());

switch (slider_style_) {
case Style::kDefault:
return GetColorProvider()->GetColor(
static_cast<ui::ColorId>(cros_tokens::kCrosSysSysOnBase));
case Style::kRadioActive:
return GetColorProvider()->GetColor(
static_cast<ui::ColorId>(cros_tokens::kCrosSysHighlightShape));
case Style::kRadioInactive:
return GetColorProvider()->GetColor(
static_cast<ui::ColorId>(cros_tokens::kCrosSysDisabled));
default:
NOTREACHED();
}
}

void QuickSettingsSlider::OnPaint(gfx::Canvas* canvas) {
// Paints the `QuickSettingsSlider`. If the feature is not enabled, use
// `Slider::OnPaint()`.
if (!ash::features::IsQsRevampEnabled()) {
views::Slider::OnPaint(canvas);
return;
}

const gfx::Rect content = GetContentsBounds();
const int width = content.width() - kFullSliderWidth;
const int full_width = GetAnimatingValue() * width + kFullSliderWidth;
const int x = content.x();
const int y = content.height() / 2 - kFullSliderThickness / 2;

gfx::Rect empty_slider_rect;
float empty_slider_radius;
switch (slider_style_) {
case Style::kDefault: {
const int empty_width =
width + kFullSliderRoundedRadius - full_width + kEmptySliderWidth;
const int x_empty = x + full_width - kEmptySliderRoundedRadius;
const int y_empty = content.height() / 2 - kEmptySliderThickness / 2;

empty_slider_rect =
gfx::Rect(x_empty, y_empty, empty_width, kEmptySliderThickness);
empty_slider_radius = kEmptySliderRoundedRadius;
break;
}
case Style::kRadioActive:
case Style::kRadioInactive: {
empty_slider_rect =
gfx::Rect(x, y, content.width(), kFullSliderThickness);
empty_slider_radius = GetSliderRoundedCornerRadius(slider_style_);
break;
}
default:
NOTREACHED();
}

cc::PaintFlags slider_flags;
slider_flags.setAntiAlias(true);

slider_flags.setColor(GetTroughColor());
canvas->DrawRoundRect(empty_slider_rect, empty_slider_radius, slider_flags);

slider_flags.setColor(GetThumbColor());
canvas->DrawRoundRect(gfx::Rect(x, y, full_width, kFullSliderThickness),
GetSliderRoundedCornerRadius(slider_style_),
slider_flags);

// Paints the focusing ring for the slider. It should be painted last to be
// on the top.
if (HasFocus()) {
cc::PaintFlags highlight_border;
highlight_border.setColor(GetColorProvider()->GetColor(
static_cast<ui::ColorId>(cros_tokens::kCrosSysPrimary)));
highlight_border.setAntiAlias(true);
highlight_border.setStyle(cc::PaintFlags::kStroke_Style);
highlight_border.setStrokeWidth(kLineThickness);
canvas->DrawRoundRect(gfx::Rect(x - kFocusOffset, y - kFocusOffset,
full_width + 2 * kFocusOffset,
kFullSliderThickness + 2 * kFocusOffset),
kFullSliderRoundedRadius, highlight_border);
}
}

ReadOnlySlider::ReadOnlySlider(Style slider_style)
: QuickSettingsSlider(/*listener=*/nullptr, slider_style) {}

ReadOnlySlider::~ReadOnlySlider() = default;

bool ReadOnlySlider::CanAcceptEvent(const ui::Event& event) {
return false;
}

BEGIN_METADATA(QuickSettingsSlider, views::View)
END_METADATA

BEGIN_METADATA(ReadOnlySlider, views::View)
END_METADATA

} // namespace ash
94 changes: 94 additions & 0 deletions ash/system/unified/quick_settings_slider.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_SYSTEM_UNIFIED_QUICK_SETTINGS_SLIDER_H_
#define ASH_SYSTEM_UNIFIED_QUICK_SETTINGS_SLIDER_H_

#include "ash/ash_export.h"
#include "ui/views/controls/slider.h"

namespace gfx {
class Canvas;
} // namespace gfx

namespace views {
class View;
} // namespace views

namespace ui {
class Event;
} // namespace ui

namespace ash {

// This slider view is used in quick settings in the status area. It will be
// used in the `QuickSettingsView` and `TrayBubbleView`. This slider view
// supports different styles. `kDefault` slider is used in `QuickSettingsView`
// and in `TrayBubbleView`. `kRadioActive` slider will be used for the active
// input/output device in `AudioDetailedView`. `kRadioInactive` slider will be
// used for the inactive device in `AudioDetailedView`.
class ASH_EXPORT QuickSettingsSlider : public views::Slider {
public:
METADATA_HEADER(QuickSettingsSlider);

// Represents the style of the slider.
enum class Style {
// Represents the slider where the full part is a rounded corner rectangle
// with a height of `kFullSliderThickness`, and the empty part is a rounded
// corner rectangle with a height of `kEmptySliderThickness`. These two
// parts are center-aligned horizontally. The ends of both parts have fully
// rounded corners.
kDefault,
// Represents the style where both the full part and the empty part of the
// slider have a height of `kFullSliderThickness`. The ends are fully
// rounded.
kRadioActive,
// Represents the style where the full part and the empty part also have the
// same height of `kFullSliderThickness`, except that the ends are not fully
// rounded but have a radius of `kInactiveRadioSliderRoundedRadius`.
kRadioInactive
};

QuickSettingsSlider(views::SliderListener* listener, Style slider_style);
QuickSettingsSlider(const QuickSettingsSlider&) = delete;
QuickSettingsSlider& operator=(const QuickSettingsSlider&) = delete;
~QuickSettingsSlider() override;

// Setter and Getter of the slider style. Schedules paint after setting the
// style since styles and colors may change for the radio sliders because of
// the active status change. If the slider is the `kRadioInactive`, also
// disables the focus behavior for it.
void SetSliderStyle(Style style);
Style slider_style() const { return slider_style_; }

private:
// views::Slider:
SkColor GetThumbColor() const override;
SkColor GetTroughColor() const override;

// views::View:
void OnPaint(gfx::Canvas* canvas) override;

Style slider_style_;
};

// A slider that ignores inputs. This will be used in the
// `UnifiedKeyboardBrightnessView` and `UnifiedKeyboardBacklightToggleView`.
class ASH_EXPORT ReadOnlySlider : public QuickSettingsSlider {
public:
METADATA_HEADER(ReadOnlySlider);

explicit ReadOnlySlider(Style slider_style);
ReadOnlySlider(const ReadOnlySlider&) = delete;
ReadOnlySlider& operator=(const ReadOnlySlider&) = delete;
~ReadOnlySlider() override;

private:
// views::View:
bool CanAcceptEvent(const ui::Event& event) override;
};

} // namespace ash

#endif // ASH_SYSTEM_UNIFIED_QUICK_SETTINGS_SLIDER_H_

0 comments on commit 2a93223

Please sign in to comment.