| // Copyright 2012 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/border.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/check.h" |
| #include "base/memory/ptr_util.h" |
| #include "cc/paint/paint_flags.h" |
| #include "ui/color/color_provider.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/geometry/dip_util.h" |
| #include "ui/gfx/geometry/insets_conversions.h" |
| #include "ui/gfx/geometry/rect_f.h" |
| #include "ui/gfx/scoped_canvas.h" |
| #include "ui/views/painter.h" |
| #include "ui/views/view.h" |
| |
| namespace views { |
| |
| namespace { |
| |
| // A simple border with different thicknesses on each side and single color. |
| class SolidSidedBorder : public Border { |
| public: |
| SolidSidedBorder(const gfx::Insets& insets, SkColor color); |
| |
| SolidSidedBorder(const SolidSidedBorder&) = delete; |
| SolidSidedBorder& operator=(const SolidSidedBorder&) = delete; |
| |
| // Overridden from Border: |
| void Paint(const View& view, gfx::Canvas* canvas) override; |
| gfx::Insets GetInsets() const override; |
| gfx::Size GetMinimumSize() const override; |
| |
| private: |
| const gfx::Insets insets_; |
| }; |
| |
| SolidSidedBorder::SolidSidedBorder(const gfx::Insets& insets, SkColor color) |
| : Border(color), insets_(insets) {} |
| |
| void SolidSidedBorder::Paint(const View& view, gfx::Canvas* canvas) { |
| // Undo DSF so that we can be sure to draw an integral number of pixels for |
| // the border. Integral scale factors should be unaffected by this, but for |
| // fractional scale factors this ensures sharp lines. |
| gfx::ScopedCanvas scoped(canvas); |
| float dsf = canvas->UndoDeviceScaleFactor(); |
| |
| gfx::RectF scaled_bounds; |
| if (view.layer()) { |
| scaled_bounds = gfx::ConvertRectToPixels( |
| view.GetLocalBounds(), view.layer()->device_scale_factor()); |
| } else { |
| scaled_bounds = gfx::RectF(view.GetLocalBounds()); |
| scaled_bounds.Scale(dsf); |
| } |
| |
| gfx::InsetsF insets_in_pixels( |
| gfx::ToFlooredInsets(gfx::ConvertInsetsToPixels(insets_, dsf))); |
| scaled_bounds.Inset(insets_in_pixels); |
| canvas->sk_canvas()->clipRect(gfx::RectFToSkRect(scaled_bounds), |
| SkClipOp::kDifference, true); |
| canvas->DrawColor(color()); |
| } |
| |
| gfx::Insets SolidSidedBorder::GetInsets() const { |
| return insets_; |
| } |
| |
| gfx::Size SolidSidedBorder::GetMinimumSize() const { |
| return gfx::Size(insets_.width(), insets_.height()); |
| } |
| |
| class ThemedSolidSidedBorder : public SolidSidedBorder { |
| public: |
| ThemedSolidSidedBorder(const gfx::Insets& insets, ui::ColorId color_id); |
| |
| // SolidSidedBorder: |
| void Paint(const View& view, gfx::Canvas* canvas) override; |
| void OnViewThemeChanged(View* view) override; |
| |
| private: |
| ui::ColorId color_id_; |
| }; |
| |
| ThemedSolidSidedBorder::ThemedSolidSidedBorder(const gfx::Insets& insets, |
| ui::ColorId color_id) |
| : SolidSidedBorder(insets, gfx::kPlaceholderColor), color_id_(color_id) {} |
| |
| void ThemedSolidSidedBorder::Paint(const View& view, gfx::Canvas* canvas) { |
| set_color(view.GetColorProvider()->GetColor(color_id_)); |
| SolidSidedBorder::Paint(view, canvas); |
| } |
| |
| void ThemedSolidSidedBorder::OnViewThemeChanged(View* view) { |
| view->SchedulePaint(); |
| } |
| |
| // A border with a rounded rectangle and single color. |
| class RoundedRectBorder : public Border { |
| public: |
| RoundedRectBorder(int thickness, |
| float corner_radius, |
| const gfx::Insets& paint_insets, |
| SkColor color); |
| |
| RoundedRectBorder(const RoundedRectBorder&) = delete; |
| RoundedRectBorder& operator=(const RoundedRectBorder&) = delete; |
| |
| // Overridden from Border: |
| void Paint(const View& view, gfx::Canvas* canvas) override; |
| gfx::Insets GetInsets() const override; |
| gfx::Size GetMinimumSize() const override; |
| |
| private: |
| const int thickness_; |
| const float corner_radius_; |
| const gfx::Insets paint_insets_; |
| }; |
| |
| RoundedRectBorder::RoundedRectBorder(int thickness, |
| float corner_radius, |
| const gfx::Insets& paint_insets, |
| SkColor color) |
| : Border(color), |
| thickness_(thickness), |
| corner_radius_(corner_radius), |
| paint_insets_(paint_insets) {} |
| |
| void RoundedRectBorder::Paint(const View& view, gfx::Canvas* canvas) { |
| cc::PaintFlags flags; |
| flags.setStrokeWidth(thickness_); |
| flags.setColor(color()); |
| flags.setStyle(cc::PaintFlags::kStroke_Style); |
| flags.setAntiAlias(true); |
| |
| const float half_thickness = thickness_ / 2.0f; |
| gfx::RectF bounds(view.GetLocalBounds()); |
| bounds.Inset(gfx::InsetsF(paint_insets_)); |
| bounds.Inset(half_thickness); |
| canvas->DrawRoundRect(bounds, corner_radius_ - half_thickness, flags); |
| } |
| |
| gfx::Insets RoundedRectBorder::GetInsets() const { |
| return gfx::Insets(thickness_) + paint_insets_; |
| } |
| |
| gfx::Size RoundedRectBorder::GetMinimumSize() const { |
| return gfx::Size(thickness_ * 2, thickness_ * 2); |
| } |
| |
| class EmptyBorder : public Border { |
| public: |
| explicit EmptyBorder(const gfx::Insets& insets); |
| |
| EmptyBorder(const EmptyBorder&) = delete; |
| EmptyBorder& operator=(const EmptyBorder&) = delete; |
| |
| // Overridden from Border: |
| void Paint(const View& view, gfx::Canvas* canvas) override; |
| gfx::Insets GetInsets() const override; |
| gfx::Size GetMinimumSize() const override; |
| |
| private: |
| const gfx::Insets insets_; |
| }; |
| |
| EmptyBorder::EmptyBorder(const gfx::Insets& insets) : insets_(insets) {} |
| |
| void EmptyBorder::Paint(const View& view, gfx::Canvas* canvas) {} |
| |
| gfx::Insets EmptyBorder::GetInsets() const { |
| return insets_; |
| } |
| |
| gfx::Size EmptyBorder::GetMinimumSize() const { |
| return gfx::Size(); |
| } |
| |
| class ExtraInsetsBorder : public Border { |
| public: |
| ExtraInsetsBorder(std::unique_ptr<Border> border, const gfx::Insets& insets); |
| |
| ExtraInsetsBorder(const ExtraInsetsBorder&) = delete; |
| ExtraInsetsBorder& operator=(const ExtraInsetsBorder&) = delete; |
| |
| // Overridden from Border: |
| void Paint(const View& view, gfx::Canvas* canvas) override; |
| gfx::Insets GetInsets() const override; |
| gfx::Size GetMinimumSize() const override; |
| |
| private: |
| std::unique_ptr<Border> border_; |
| const gfx::Insets extra_insets_; |
| }; |
| |
| ExtraInsetsBorder::ExtraInsetsBorder(std::unique_ptr<Border> border, |
| const gfx::Insets& insets) |
| : Border(border->color()), |
| border_(std::move(border)), |
| extra_insets_(insets) {} |
| |
| void ExtraInsetsBorder::Paint(const View& view, gfx::Canvas* canvas) { |
| border_->Paint(view, canvas); |
| } |
| |
| gfx::Insets ExtraInsetsBorder::GetInsets() const { |
| return border_->GetInsets() + extra_insets_; |
| } |
| |
| gfx::Size ExtraInsetsBorder::GetMinimumSize() const { |
| gfx::Size size = border_->GetMinimumSize(); |
| size.Enlarge(extra_insets_.width(), extra_insets_.height()); |
| return size; |
| } |
| |
| class BorderPainter : public Border { |
| public: |
| BorderPainter(std::unique_ptr<Painter> painter, const gfx::Insets& insets); |
| |
| BorderPainter(const BorderPainter&) = delete; |
| BorderPainter& operator=(const BorderPainter&) = delete; |
| |
| // Overridden from Border: |
| void Paint(const View& view, gfx::Canvas* canvas) override; |
| gfx::Insets GetInsets() const override; |
| gfx::Size GetMinimumSize() const override; |
| |
| private: |
| std::unique_ptr<Painter> painter_; |
| const gfx::Insets insets_; |
| }; |
| |
| BorderPainter::BorderPainter(std::unique_ptr<Painter> painter, |
| const gfx::Insets& insets) |
| : painter_(std::move(painter)), insets_(insets) { |
| DCHECK(painter_); |
| } |
| |
| void BorderPainter::Paint(const View& view, gfx::Canvas* canvas) { |
| Painter::PaintPainterAt(canvas, painter_.get(), view.GetLocalBounds()); |
| } |
| |
| gfx::Insets BorderPainter::GetInsets() const { |
| return insets_; |
| } |
| |
| gfx::Size BorderPainter::GetMinimumSize() const { |
| return painter_->GetMinimumSize(); |
| } |
| |
| class ThemedRoundedRectBorder : public RoundedRectBorder { |
| public: |
| ThemedRoundedRectBorder(int thickness, |
| float corner_radius, |
| const gfx::Insets& paint_insets, |
| ui::ColorId color_id) |
| : RoundedRectBorder(thickness, |
| corner_radius, |
| paint_insets, |
| gfx::kPlaceholderColor), |
| color_id_(color_id) {} |
| |
| ThemedRoundedRectBorder(const ThemedRoundedRectBorder&) = delete; |
| ThemedRoundedRectBorder& operator=(const ThemedRoundedRectBorder&) = delete; |
| |
| void Paint(const View& view, gfx::Canvas* canvas) override { |
| set_color(view.GetColorProvider()->GetColor(color_id_)); |
| RoundedRectBorder::Paint(view, canvas); |
| } |
| |
| void OnViewThemeChanged(View* view) override { view->SchedulePaint(); } |
| |
| private: |
| const ui::ColorId color_id_; |
| }; |
| |
| } // namespace |
| |
| Border::Border() = default; |
| |
| Border::Border(SkColor color) : color_(color) {} |
| |
| Border::~Border() = default; |
| |
| void Border::OnViewThemeChanged(View* view) {} |
| |
| std::unique_ptr<Border> NullBorder() { |
| return nullptr; |
| } |
| |
| std::unique_ptr<Border> CreateSolidBorder(int thickness, SkColor color) { |
| return std::make_unique<SolidSidedBorder>(gfx::Insets(thickness), color); |
| } |
| |
| std::unique_ptr<Border> CreateThemedSolidBorder(int thickness, |
| ui::ColorId color) { |
| return std::make_unique<ThemedSolidSidedBorder>(gfx::Insets(thickness), |
| color); |
| } |
| |
| std::unique_ptr<Border> CreateEmptyBorder(const gfx::Insets& insets) { |
| return std::make_unique<EmptyBorder>(insets); |
| } |
| |
| std::unique_ptr<Border> CreateEmptyBorder(int thickness) { |
| return CreateEmptyBorder(gfx::Insets(thickness)); |
| } |
| |
| std::unique_ptr<Border> CreateRoundedRectBorder(int thickness, |
| float corner_radius, |
| SkColor color) { |
| return CreateRoundedRectBorder(thickness, corner_radius, gfx::Insets(), |
| color); |
| } |
| |
| std::unique_ptr<Border> CreateRoundedRectBorder(int thickness, |
| float corner_radius, |
| const gfx::Insets& paint_insets, |
| SkColor color) { |
| return std::make_unique<RoundedRectBorder>(thickness, corner_radius, |
| paint_insets, color); |
| } |
| |
| std::unique_ptr<Border> CreateThemedRoundedRectBorder(int thickness, |
| float corner_radius, |
| ui::ColorId color_id) { |
| return CreateThemedRoundedRectBorder(thickness, corner_radius, gfx::Insets(), |
| color_id); |
| } |
| |
| std::unique_ptr<Border> CreateThemedRoundedRectBorder( |
| int thickness, |
| float corner_radius, |
| const gfx::Insets& paint_insets, |
| ui::ColorId color_id) { |
| return std::make_unique<ThemedRoundedRectBorder>(thickness, corner_radius, |
| paint_insets, color_id); |
| } |
| |
| std::unique_ptr<Border> CreateSolidSidedBorder(const gfx::Insets& insets, |
| SkColor color) { |
| return std::make_unique<SolidSidedBorder>(insets, color); |
| } |
| |
| std::unique_ptr<Border> CreateThemedSolidSidedBorder(const gfx::Insets& insets, |
| ui::ColorId color_id) { |
| return std::make_unique<ThemedSolidSidedBorder>(insets, color_id); |
| } |
| |
| std::unique_ptr<Border> CreatePaddedBorder(std::unique_ptr<Border> border, |
| const gfx::Insets& insets) { |
| return std::make_unique<ExtraInsetsBorder>(std::move(border), insets); |
| } |
| |
| std::unique_ptr<Border> CreateBorderPainter(std::unique_ptr<Painter> painter, |
| const gfx::Insets& insets) { |
| return base::WrapUnique(new BorderPainter(std::move(painter), insets)); |
| } |
| |
| } // namespace views |