Tweaks to ToggleButton details.
- fix the color of the track and take more colors from the theme. The spec states 0.16a for the track whether off or on, but the color of the mock-up is actual 0.32a for the on version. I take this to be a bug in the text of the spec. - make the thumb the correct size. The actual white circle is supposed to be 15dip rather than 16dip (this is another thing confusing about the spec, as it states 16dp but where it measures from includes 1 dip of shadow). Note that to maintain crispness, we must align to pixel boundaries and must maintain a centered position, so there's a bit of rounding here depending on scale factor. BUG=626827 Review-Url: https://codereview.chromium.org/2440723003 Cr-Commit-Position: refs/heads/master@{#429682}
This commit is contained in:
ui
@ -5,6 +5,7 @@
|
||||
#include "ui/gfx/geometry/insets.h"
|
||||
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "ui/gfx/geometry/vector2d.h"
|
||||
|
||||
namespace gfx {
|
||||
|
||||
@ -13,4 +14,9 @@ std::string Insets::ToString() const {
|
||||
return base::StringPrintf("%d,%d,%d,%d", top(), left(), bottom(), right());
|
||||
}
|
||||
|
||||
Insets Insets::Offset(const gfx::Vector2d& vector) const {
|
||||
return gfx::Insets(top() + vector.y(), left() + vector.x(),
|
||||
bottom() - vector.y(), right() - vector.x());
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
|
@ -12,6 +12,8 @@
|
||||
|
||||
namespace gfx {
|
||||
|
||||
class Vector2d;
|
||||
|
||||
// Represents the widths of the four borders or margins of an unspecified
|
||||
// rectangle. An Insets stores the thickness of the top, left, bottom and right
|
||||
// edges, without storing the actual size and position of the rectangle itself.
|
||||
@ -93,6 +95,11 @@ class GFX_EXPORT Insets {
|
||||
static_cast<int>(right() * x_scale));
|
||||
}
|
||||
|
||||
// Adjusts the vertical and horizontal dimensions by the values described in
|
||||
// |vector|. Offsetting insets before applying to a rectangle would be
|
||||
// equivalent to offseting the rectangle then applying the insets.
|
||||
Insets Offset(const gfx::Vector2d& vector) const;
|
||||
|
||||
operator InsetsF() const {
|
||||
return InsetsF(static_cast<float>(top()), static_cast<float>(left()),
|
||||
static_cast<float>(bottom()), static_cast<float>(right()));
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "ui/gfx/geometry/insets_f.h"
|
||||
#include "ui/gfx/geometry/rect.h"
|
||||
#include "ui/gfx/geometry/vector2d.h"
|
||||
|
||||
TEST(InsetsTest, InsetsDefault) {
|
||||
gfx::Insets insets;
|
||||
@ -113,3 +115,25 @@ TEST(InsetsTest, ToString) {
|
||||
gfx::Insets insets(1, 2, 3, 4);
|
||||
EXPECT_EQ("1,2,3,4", insets.ToString());
|
||||
}
|
||||
|
||||
TEST(InsetsTest, Offset) {
|
||||
const gfx::Insets insets(1, 2, 3, 4);
|
||||
const gfx::Rect rect(5, 6, 7, 8);
|
||||
const gfx::Vector2d vector(9, 10);
|
||||
|
||||
// Whether you inset then offset the rect, offset then inset the rect, or
|
||||
// offset the insets then apply to the rect, the outcome should be the same.
|
||||
gfx::Rect inset_first = rect;
|
||||
inset_first.Inset(insets);
|
||||
inset_first.Offset(vector);
|
||||
|
||||
gfx::Rect offset_first = rect;
|
||||
offset_first.Offset(vector);
|
||||
offset_first.Inset(insets);
|
||||
|
||||
gfx::Rect inset_by_offset = rect;
|
||||
inset_by_offset.Inset(insets.Offset(vector));
|
||||
|
||||
EXPECT_EQ(inset_first, offset_first);
|
||||
EXPECT_EQ(inset_by_offset, inset_first);
|
||||
}
|
||||
|
@ -28,62 +28,66 @@ const int kThumbHorizontalMargin = 4;
|
||||
// Margin from top/bottom edge of thumb to top/bottom edge of view.
|
||||
const int kThumbVerticalMargin = 3;
|
||||
|
||||
// TODO(estade): get the base color (black) from the theme?
|
||||
const SkColor kTrackOffColor =
|
||||
SkColorSetA(SK_ColorBLACK, gfx::kDisabledControlAlpha);
|
||||
|
||||
} // namespace
|
||||
|
||||
// Class representing the thumb. When the thumb is clicked it is separated into
|
||||
// its own layer and the ink drop layer is made a child of the thumb layer
|
||||
// allowing the two to animate in sync.
|
||||
class ToggleButton::ThumbView : public views::View {
|
||||
// Class representing the thumb (the circle that slides horizontally).
|
||||
class ToggleButton::ThumbView : public InkDropHostView {
|
||||
public:
|
||||
ThumbView() : color_ratio_(0.) {}
|
||||
~ThumbView() override {}
|
||||
|
||||
void AddInkDropLayer(ui::Layer* ink_drop_layer) {
|
||||
SetPaintToLayer(true);
|
||||
layer()->SetFillsBoundsOpaquely(false);
|
||||
layer()->Add(ink_drop_layer);
|
||||
}
|
||||
|
||||
void RemoveInkDropLayer(ui::Layer* ink_drop_layer) {
|
||||
layer()->Remove(ink_drop_layer);
|
||||
SetPaintToLayer(false);
|
||||
}
|
||||
|
||||
void Update(const gfx::Rect& bounds, double color_ratio) {
|
||||
SetBoundsRect(bounds);
|
||||
color_ratio_ = color_ratio;
|
||||
SchedulePaint();
|
||||
}
|
||||
|
||||
// Returns the extra space needed to draw the shadows around the thumb. Since
|
||||
// the extra space is around the thumb, the insets will be negative.
|
||||
static gfx::Insets GetShadowOutsets() {
|
||||
return gfx::Insets(-kShadowBlur)
|
||||
.Offset(gfx::Vector2d(kShadowOffsetX, kShadowOffsetY));
|
||||
}
|
||||
|
||||
private:
|
||||
static const int kShadowOffsetX = 0;
|
||||
static const int kShadowOffsetY = 1;
|
||||
static const int kShadowBlur = 2;
|
||||
|
||||
// views::View:
|
||||
const char* GetClassName() const override {
|
||||
return "ToggleButton::ThumbView";
|
||||
}
|
||||
|
||||
void OnPaint(gfx::Canvas* canvas) override {
|
||||
const float dsf = canvas->UndoDeviceScaleFactor();
|
||||
std::vector<gfx::ShadowValue> shadows;
|
||||
shadows.emplace_back(gfx::Vector2d(0, 1), 4.f,
|
||||
SkColorSetA(SK_ColorBLACK, 0x99));
|
||||
gfx::ShadowValue shadow(
|
||||
gfx::Vector2d(kShadowOffsetX, kShadowOffsetY), 2 * kShadowBlur,
|
||||
SkColorSetA(GetNativeTheme()->GetSystemColor(
|
||||
ui::NativeTheme::kColorId_LabelEnabledColor),
|
||||
0x99));
|
||||
shadows.push_back(shadow.Scale(dsf));
|
||||
SkPaint thumb_paint;
|
||||
thumb_paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadows));
|
||||
thumb_paint.setStyle(SkPaint::kFill_Style);
|
||||
thumb_paint.setAntiAlias(true);
|
||||
const SkColor thumb_on_color = GetNativeTheme()->GetSystemColor(
|
||||
ui::NativeTheme::kColorId_ProminentButtonColor);
|
||||
// TODO(estade): get this color from the theme?
|
||||
const SkColor thumb_off_color = SK_ColorWHITE;
|
||||
const SkColor thumb_off_color = GetNativeTheme()->GetSystemColor(
|
||||
ui::NativeTheme::kColorId_DialogBackground);
|
||||
const SkAlpha blend = static_cast<SkAlpha>(SK_AlphaOPAQUE * color_ratio_);
|
||||
thumb_paint.setColor(
|
||||
color_utils::AlphaBlend(thumb_on_color, thumb_off_color, blend));
|
||||
gfx::Rect thumb_bounds = GetLocalBounds();
|
||||
thumb_bounds.Inset(gfx::Insets(kThumbVerticalMargin));
|
||||
canvas->DrawCircle(gfx::RectF(thumb_bounds).CenterPoint(),
|
||||
thumb_bounds.height() / 2.f, thumb_paint);
|
||||
|
||||
// We want the circle to have an integer pixel diameter and to be aligned
|
||||
// with pixel boundaries, so we scale dip bounds to pixel bounds and round.
|
||||
gfx::RectF thumb_bounds(GetLocalBounds());
|
||||
thumb_bounds.Inset(-GetShadowOutsets());
|
||||
thumb_bounds.Inset(gfx::InsetsF(0.5f));
|
||||
thumb_bounds.Scale(dsf);
|
||||
thumb_bounds = gfx::RectF(gfx::ToEnclosingRect(thumb_bounds));
|
||||
canvas->DrawCircle(thumb_bounds.CenterPoint(), thumb_bounds.height() / 2.f,
|
||||
thumb_paint);
|
||||
}
|
||||
|
||||
// Color ratio between 0 and 1 that controls the thumb color.
|
||||
@ -99,12 +103,12 @@ ToggleButton::ToggleButton(ButtonListener* listener)
|
||||
: CustomButton(listener),
|
||||
is_on_(false),
|
||||
slide_animation_(this),
|
||||
thumb_view_(new ToggleButton::ThumbView()) {
|
||||
thumb_view_(new ThumbView()) {
|
||||
slide_animation_.SetSlideDuration(80 /* ms */);
|
||||
slide_animation_.SetTweenType(gfx::Tween::LINEAR);
|
||||
SetBorder(Border::CreateEmptyBorder(
|
||||
gfx::Insets(kTrackVerticalMargin, kTrackHorizontalMargin)));
|
||||
AddChildView(thumb_view_.get());
|
||||
AddChildView(thumb_view_);
|
||||
SetInkDropMode(InkDropMode::ON);
|
||||
set_has_ink_drop_action_on_click(true);
|
||||
}
|
||||
@ -139,13 +143,22 @@ gfx::Rect ToggleButton::GetThumbBounds() const {
|
||||
// The thumb is a circle, so the width should match the height.
|
||||
thumb_bounds.set_width(thumb_bounds.height());
|
||||
thumb_bounds.set_x(GetMirroredXForRect(thumb_bounds));
|
||||
thumb_bounds.Inset(ThumbView::GetShadowOutsets());
|
||||
return thumb_bounds;
|
||||
}
|
||||
|
||||
void ToggleButton::UpdateThumb() {
|
||||
gfx::Rect thumb_bounds = GetThumbBounds();
|
||||
thumb_bounds.Inset(gfx::Insets(-kThumbVerticalMargin));
|
||||
thumb_view_->Update(thumb_bounds, slide_animation_.GetCurrentValue());
|
||||
thumb_view_->Update(GetThumbBounds(), slide_animation_.GetCurrentValue());
|
||||
}
|
||||
|
||||
SkColor ToggleButton::GetTrackColor(bool is_on) const {
|
||||
const SkAlpha kOffTrackAlpha = 0x29;
|
||||
const SkAlpha kOnTrackAlpha = kOffTrackAlpha * 2;
|
||||
ui::NativeTheme::ColorId color_id =
|
||||
is_on ? ui::NativeTheme::kColorId_ProminentButtonColor
|
||||
: ui::NativeTheme::kColorId_LabelEnabledColor;
|
||||
return SkColorSetA(GetNativeTheme()->GetSystemColor(color_id),
|
||||
is_on ? kOnTrackAlpha : kOffTrackAlpha);
|
||||
}
|
||||
|
||||
gfx::Size ToggleButton::GetPreferredSize() const {
|
||||
@ -160,17 +173,17 @@ const char* ToggleButton::GetClassName() const {
|
||||
}
|
||||
|
||||
void ToggleButton::OnPaint(gfx::Canvas* canvas) {
|
||||
// Paint the toggle track.
|
||||
// Paint the toggle track. To look sharp even at fractional scale factors,
|
||||
// round up to pixel boundaries.
|
||||
float dsf = canvas->UndoDeviceScaleFactor();
|
||||
gfx::RectF track_rect(GetContentsBounds());
|
||||
track_rect.Scale(dsf);
|
||||
track_rect = gfx::RectF(gfx::ToEnclosingRect(track_rect));
|
||||
SkPaint track_paint;
|
||||
track_paint.setAntiAlias(true);
|
||||
const SkColor track_on_color =
|
||||
SkColorSetA(GetNativeTheme()->GetSystemColor(
|
||||
ui::NativeTheme::kColorId_ProminentButtonColor),
|
||||
0xFF / 2);
|
||||
const double color_ratio = slide_animation_.GetCurrentValue();
|
||||
track_paint.setColor(color_utils::AlphaBlend(
|
||||
track_on_color, kTrackOffColor,
|
||||
GetTrackColor(true), GetTrackColor(false),
|
||||
static_cast<SkAlpha>(SK_AlphaOPAQUE * color_ratio)));
|
||||
canvas->DrawRoundRect(track_rect, track_rect.height() / 2, track_paint);
|
||||
}
|
||||
@ -190,25 +203,20 @@ void ToggleButton::OnNativeThemeChanged(const ui::NativeTheme* theme) {
|
||||
|
||||
void ToggleButton::AddInkDropLayer(ui::Layer* ink_drop_layer) {
|
||||
thumb_view_->AddInkDropLayer(ink_drop_layer);
|
||||
UpdateThumb();
|
||||
SchedulePaint();
|
||||
}
|
||||
|
||||
void ToggleButton::RemoveInkDropLayer(ui::Layer* ink_drop_layer) {
|
||||
thumb_view_->RemoveInkDropLayer(ink_drop_layer);
|
||||
SchedulePaint();
|
||||
}
|
||||
|
||||
std::unique_ptr<InkDropRipple> ToggleButton::CreateInkDropRipple() const {
|
||||
const int radius = (kTrackHeight + kTrackVerticalMargin * 2) / 2;
|
||||
return CreateDefaultInkDropRipple(gfx::Point(radius, radius));
|
||||
gfx::Rect rect = thumb_view_->GetLocalBounds();
|
||||
rect.Inset(-ThumbView::GetShadowOutsets());
|
||||
return CreateDefaultInkDropRipple(rect.CenterPoint());
|
||||
}
|
||||
|
||||
SkColor ToggleButton::GetInkDropBaseColor() const {
|
||||
return is_on()
|
||||
? GetNativeTheme()->GetSystemColor(
|
||||
ui::NativeTheme::kColorId_ProminentButtonColor)
|
||||
: kTrackOffColor;
|
||||
return GetTrackColor(is_on());
|
||||
}
|
||||
|
||||
bool ToggleButton::ShouldShowInkDropHighlight() const {
|
||||
|
@ -33,6 +33,8 @@ class VIEWS_EXPORT ToggleButton : public CustomButton {
|
||||
// Updates position and color of the thumb.
|
||||
void UpdateThumb();
|
||||
|
||||
SkColor GetTrackColor(bool is_on) const;
|
||||
|
||||
// CustomButton:
|
||||
gfx::Size GetPreferredSize() const override;
|
||||
const char* GetClassName() const override;
|
||||
@ -51,7 +53,7 @@ class VIEWS_EXPORT ToggleButton : public CustomButton {
|
||||
|
||||
bool is_on_;
|
||||
gfx::SlideAnimation slide_animation_;
|
||||
std::unique_ptr<ThumbView> thumb_view_;
|
||||
ThumbView* thumb_view_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ToggleButton);
|
||||
};
|
||||
|
Reference in New Issue
Block a user