| // 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/public/cpp/network_icon_image_source.h" |
| |
| #include <array> |
| |
| #include "third_party/skia/include/core/SkPath.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/geometry/insets.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/rect_f.h" |
| #include "ui/gfx/geometry/skia_conversions.h" |
| #include "ui/gfx/paint_vector_icon.h" |
| #include "ui/gfx/scoped_canvas.h" |
| #include "ui/gfx/vector_icon_types.h" |
| #include "ui/gfx/vector_icon_utils.h" |
| |
| namespace ash { |
| namespace network_icon { |
| |
| namespace { |
| |
| constexpr int kIconStrokeWidth = 2; |
| constexpr int kCellularIconOffset = 1; |
| |
| SkPath CreateArcPath(gfx::RectF oval, float start_angle, float sweep_angle) { |
| SkPath path; |
| path.setIsVolatile(true); |
| path.setFillType(SkPathFillType::kWinding); |
| path.moveTo(oval.CenterPoint().x(), oval.CenterPoint().y()); |
| path.arcTo(gfx::RectFToSkRect(oval), start_angle, sweep_angle, false); |
| path.close(); |
| return path; |
| } |
| |
| } // namespace |
| |
| //------------------------------------------------------------------------------ |
| // Badges |
| |
| Badges::Badges() = default; |
| Badges::~Badges() = default; |
| Badges::Badges(const Badges&) = default; |
| Badges& Badges::operator=(const Badges&) = default; |
| |
| //------------------------------------------------------------------------------ |
| // NetworkIconImageSource |
| |
| NetworkIconImageSource::NetworkIconImageSource(const gfx::Size& size, |
| const gfx::ImageSkia& icon, |
| const Badges& badges) |
| : CanvasImageSource(size), icon_(icon), badges_(badges) {} |
| |
| NetworkIconImageSource::~NetworkIconImageSource() = default; |
| |
| void NetworkIconImageSource::Draw(gfx::Canvas* canvas) { |
| const int width = size().width(); |
| const int height = size().height(); |
| |
| // The base icon is centered in both dimensions. |
| const int icon_x = (width - icon_.width()) / 2; |
| const int icon_y = (height - icon_.height()) / 2; |
| canvas->DrawImageInt(icon_, icon_x, icon_y); |
| |
| auto paint_badge = [&canvas](const Badge& badge, int x, int y, |
| int badge_size = 0) { |
| gfx::ScopedCanvas scoped(canvas); |
| canvas->Translate(gfx::Vector2d(x, y)); |
| if (badge_size) |
| gfx::PaintVectorIcon(canvas, *badge.icon, badge_size, badge.color); |
| else |
| gfx::PaintVectorIcon(canvas, *badge.icon, badge.color); |
| }; |
| |
| // The center badge is scaled and centered over the icon. |
| if (badges_.center.icon) |
| paint_badge(badges_.center, icon_x, icon_y, icon_.width()); |
| |
| if (badges_.top_left.icon) |
| paint_badge(badges_.top_left, 0, icon_y); |
| |
| if (badges_.bottom_left.icon) { |
| paint_badge( |
| badges_.bottom_left, 0, |
| height - gfx::GetDefaultSizeOfVectorIcon(*badges_.bottom_left.icon)); |
| } |
| if (badges_.bottom_right.icon) { |
| const int badge_offset = |
| gfx::GetDefaultSizeOfVectorIcon(*badges_.bottom_right.icon) - 1; |
| paint_badge(badges_.bottom_right, width - badge_offset, |
| height - badge_offset); |
| } |
| } |
| |
| bool NetworkIconImageSource::HasRepresentationAtAllScales() const { |
| return true; |
| } |
| |
| //------------------------------------------------------------------------------ |
| // SignalStrengthImageSource |
| |
| SignalStrengthImageSource::SignalStrengthImageSource(ImageType image_type, |
| SkColor color, |
| const gfx::Size& size, |
| int signal_strength, |
| int padding) |
| : CanvasImageSource(size), |
| image_type_(image_type), |
| color_(color), |
| signal_strength_(signal_strength), |
| padding_(padding) { |
| if (image_type_ == NONE) |
| image_type_ = ARCS; |
| |
| DCHECK_GE(signal_strength, 0); |
| DCHECK_LT(signal_strength, kNumNetworkImages); |
| } |
| |
| SignalStrengthImageSource::~SignalStrengthImageSource() = default; |
| |
| // gfx::CanvasImageSource: |
| void SignalStrengthImageSource::Draw(gfx::Canvas* canvas) { |
| if (image_type_ == ARCS) |
| DrawArcs(canvas); |
| else |
| DrawBars(canvas); |
| } |
| |
| bool SignalStrengthImageSource::HasRepresentationAtAllScales() const { |
| return true; |
| } |
| |
| void SignalStrengthImageSource::DrawArcs(gfx::Canvas* canvas) { |
| gfx::RectF oval_bounds((gfx::Rect(size()))); |
| oval_bounds.Inset(padding_); |
| // Double the width and height. The new midpoint should be the former |
| // bottom center. |
| oval_bounds.Inset(gfx::InsetsF::TLBR(0, -oval_bounds.width() / 2, |
| -oval_bounds.height(), |
| -oval_bounds.width() / 2)); |
| |
| constexpr SkScalar kAngleAboveHorizontal = 51.f; |
| constexpr SkScalar kStartAngle = 180.f + kAngleAboveHorizontal; |
| constexpr SkScalar kSweepAngle = 180.f - 2 * kAngleAboveHorizontal; |
| |
| cc::PaintFlags flags; |
| flags.setAntiAlias(true); |
| flags.setStyle(cc::PaintFlags::kStroke_Style); |
| flags.setStrokeWidth(kIconStrokeWidth); |
| flags.setColor(color_); |
| |
| // Background (outline) |
| canvas->sk_canvas()->drawPath( |
| CreateArcPath(oval_bounds, kStartAngle, kSweepAngle), flags); |
| |
| // Foreground (signal strength). |
| if (signal_strength_ != 0) { |
| flags.setStyle(cc::PaintFlags::kFill_Style); |
| // Percent of the height of the background wedge that we draw the |
| // foreground wedge, indexed by signal strength. |
| static constexpr std::array<float, 5> kWedgeHeightPercentages = { |
| 0.f, 0.375f, 0.5833f, 0.75f, 1.f}; |
| const float wedge_percent = kWedgeHeightPercentages[signal_strength_]; |
| oval_bounds.Inset( |
| gfx::InsetsF((oval_bounds.height() / 2) * (1.f - wedge_percent))); |
| canvas->sk_canvas()->drawPath( |
| CreateArcPath(oval_bounds, kStartAngle, kSweepAngle), flags); |
| } |
| } |
| |
| void SignalStrengthImageSource::DrawBars(gfx::Canvas* canvas) { |
| // Undo the canvas's device scaling and round values to the nearest whole |
| // number so we can draw on exact pixel boundaries. |
| const float dsf = canvas->UndoDeviceScaleFactor(); |
| auto scale = [dsf](SkScalar dimension) { |
| return std::round(dimension * dsf); |
| }; |
| |
| // Length of short side of an isosceles right triangle, in dip. |
| const SkScalar kFullTriangleSide = |
| SkIntToScalar(size().width()) - padding_ * 2; |
| |
| auto make_triangle = [scale, kFullTriangleSide, this](SkScalar side) { |
| SkPath triangle; |
| triangle.moveTo(scale(padding_ + kCellularIconOffset), |
| scale(padding_ + kFullTriangleSide + kCellularIconOffset)); |
| triangle.rLineTo(scale(side), 0); |
| triangle.rLineTo(0, -scale(side)); |
| triangle.close(); |
| return triangle; |
| }; |
| |
| cc::PaintFlags flags; |
| flags.setAntiAlias(true); |
| flags.setColor(color_); |
| |
| // Background. |
| flags.setStyle(cc::PaintFlags::kStroke_Style); |
| flags.setStrokeWidth(kIconStrokeWidth); |
| |
| canvas->DrawPath(make_triangle(kFullTriangleSide), flags); |
| |
| // Foreground (signal strength). |
| if (signal_strength_ != 0) { |
| flags.setStyle(cc::PaintFlags::kFill_Style); |
| // As a percentage of the bg triangle, the length of one of the short |
| // sides of the fg triangle, indexed by signal strength. |
| static constexpr std::array<float, 5> kTriangleSidePercents = { |
| 0.f, 0.375f, 0.5833f, 0.75f, 1.f}; |
| canvas->DrawPath(make_triangle(kTriangleSidePercents[signal_strength_] * |
| kFullTriangleSide), |
| flags); |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| gfx::ImageSkia GetImageForWifiNetwork(SkColor color, gfx::Size size) { |
| return gfx::CanvasImageSource::MakeImageSkia<SignalStrengthImageSource>( |
| ARCS, color, size, kNumNetworkImages - 1); |
| } |
| |
| } // namespace network_icon |
| } // namespace ash |