// 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 "ui/views/highlight_border.h"
#include "ui/color/color_id.h"
#include "ui/color/color_provider.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/views/view.h"
namespace views {
// static
void HighlightBorder::PaintBorderToCanvas(
gfx::Canvas* canvas,
SkColor highlight_color,
SkColor border_color,
const gfx::Rect& bounds,
const gfx::RoundedCornersF& corner_radii,
Type type) {
cc::PaintFlags flags;
flags.setStrokeWidth(kHighlightBorderThickness);
flags.setColor(border_color);
flags.setStyle(cc::PaintFlags::kStroke_Style);
flags.setAntiAlias(true);
const float half_thickness = kHighlightBorderThickness / 2.0f;
// Scale bounds and corner radius with device scale factor to make sure
// border bounds match content bounds but keep border stroke width the same.
gfx::ScopedCanvas scoped_canvas(canvas);
const float dsf = canvas->UndoDeviceScaleFactor();
const gfx::RectF pixel_bounds = gfx::ConvertRectToPixels(bounds, dsf);
const SkScalar radii[8] = {
corner_radii.upper_left() * dsf, corner_radii.upper_left() * dsf,
corner_radii.upper_right() * dsf, corner_radii.upper_right() * dsf,
corner_radii.lower_right() * dsf, corner_radii.lower_right() * dsf,
corner_radii.lower_left() * dsf, corner_radii.lower_left() * dsf};
gfx::RectF outer_border_bounds(pixel_bounds);
outer_border_bounds.Inset(half_thickness);
SkPath outer_path;
outer_path.addRoundRect(gfx::RectFToSkRect(outer_border_bounds), radii);
canvas->DrawPath(outer_path, flags);
gfx::RectF inner_border_bounds(pixel_bounds);
inner_border_bounds.Inset(kHighlightBorderThickness);
inner_border_bounds.Inset(half_thickness);
flags.setColor(highlight_color);
SkPath inner_path;
inner_path.addRoundRect(gfx::RectFToSkRect(inner_border_bounds), radii);
canvas->DrawPath(inner_path, flags);
}
// static
void HighlightBorder::PaintBorderToCanvas(
gfx::Canvas* canvas,
const views::View& view,
const gfx::Rect& bounds,
const gfx::RoundedCornersF& corner_radii,
Type type) {
PaintBorderToCanvas(canvas, GetHighlightColor(view, type),
GetBorderColor(view, type), bounds, corner_radii, type);
}
// static
SkColor HighlightBorder::GetHighlightColor(const views::View& view,
HighlightBorder::Type type) {
ui::ColorId highlight_color_id;
switch (type) {
case HighlightBorder::Type::kHighlightBorderNoShadow:
case HighlightBorder::Type::kHighlightBorderOnShadow:
highlight_color_id = ui::kColorCrosSystemHighlight;
break;
case HighlightBorder::Type::kHighlightBorder1:
highlight_color_id = ui::kColorHighlightBorderHighlight1;
break;
case HighlightBorder::Type::kHighlightBorder2:
highlight_color_id = ui::kColorHighlightBorderHighlight2;
break;
case HighlightBorder::Type::kHighlightBorder3:
highlight_color_id = ui::kColorHighlightBorderHighlight3;
break;
}
// `view` should be embedded in a Widget to use color provider.
DCHECK(view.GetWidget());
return view.GetColorProvider()->GetColor(highlight_color_id);
}
// static
SkColor HighlightBorder::GetBorderColor(const views::View& view,
HighlightBorder::Type type) {
ui::ColorId border_color_id;
switch (type) {
case HighlightBorder::Type::kHighlightBorderNoShadow:
border_color_id = ui::kColorCrosSystemHighlightBorder;
break;
case HighlightBorder::Type::kHighlightBorderOnShadow:
border_color_id = ui::kColorCrosSystemHighlightBorder1;
break;
case HighlightBorder::Type::kHighlightBorder1:
border_color_id = ui::kColorHighlightBorderBorder1;
break;
case HighlightBorder::Type::kHighlightBorder2:
border_color_id = ui::kColorHighlightBorderBorder2;
break;
case HighlightBorder::Type::kHighlightBorder3:
border_color_id = ui::kColorHighlightBorderBorder3;
break;
}
// `view` should be embedded in a Widget to use color provider.
DCHECK(view.GetWidget());
return view.GetColorProvider()->GetColor(border_color_id);
}
HighlightBorder::HighlightBorder(int corner_radius,
Type type,
InsetsType insets_type)
: HighlightBorder(gfx::RoundedCornersF(corner_radius), type, insets_type) {}
HighlightBorder::HighlightBorder(const gfx::RoundedCornersF& rounded_corners,
Type type,
InsetsType insets_type)
: rounded_corners_(rounded_corners),
type_(type),
insets_type_(insets_type) {}
void HighlightBorder::Paint(const views::View& view, gfx::Canvas* canvas) {
PaintBorderToCanvas(canvas, view, view.GetLocalBounds(), rounded_corners_,
type_);
}
void HighlightBorder::OnViewThemeChanged(views::View* view) {
view->SchedulePaint();
}
gfx::Insets HighlightBorder::GetInsets() const {
switch (insets_type_) {
case InsetsType::kNoInsets:
return gfx::Insets();
case InsetsType::kHalfInsets:
return gfx::Insets(kHighlightBorderThickness);
case InsetsType::kFullInsets:
return gfx::Insets(2 * kHighlightBorderThickness);
}
}
gfx::Size HighlightBorder::GetMinimumSize() const {
return gfx::Size(kHighlightBorderThickness * 4,
kHighlightBorderThickness * 4);
}
} // namespace views