| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/system/holding_space/holding_space_progress_ring.h" |
| |
| #include <memory> |
| |
| #include "ash/public/cpp/holding_space/holding_space_controller.h" |
| #include "ash/public/cpp/holding_space/holding_space_item.h" |
| #include "ash/style/ash_color_provider.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/compositor/paint_recorder.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/geometry/insets.h" |
| #include "ui/gfx/scoped_canvas.h" |
| #include "ui/gfx/skia_util.h" |
| |
| namespace ash { |
| namespace { |
| |
| // Appearance. |
| constexpr float kStrokeWidth = 2.f; |
| constexpr float kTrackOpacity = 0.3f; |
| |
| // Helpers --------------------------------------------------------------------- |
| |
| // Returns the sweep angle to represent progress of the specified `item`. |
| // NOTE: This method may only be called if `item` has determinate progress. |
| float CalculateSweepAngle(const HoldingSpaceItem* item) { |
| DCHECK(item->IsInProgress()); |
| DCHECK(item->progress().has_value()); |
| return 360.f * item->progress().value(); |
| } |
| |
| } // namespace |
| |
| // HoldingSpaceProgressRing ---------------------------------------------------- |
| |
| HoldingSpaceProgressRing::HoldingSpaceProgressRing(const HoldingSpaceItem* item) |
| : ui::LayerOwner(std::make_unique<ui::Layer>(ui::LAYER_TEXTURED)), |
| item_(item) { |
| layer()->set_delegate(this); |
| layer()->SetFillsBoundsOpaquely(false); |
| model_observation_.Observe(HoldingSpaceController::Get()->model()); |
| } |
| |
| HoldingSpaceProgressRing::~HoldingSpaceProgressRing() = default; |
| |
| void HoldingSpaceProgressRing::InvalidateLayer() { |
| layer()->SchedulePaint(gfx::Rect(layer()->size())); |
| } |
| |
| void HoldingSpaceProgressRing::OnDeviceScaleFactorChanged(float old_scale, |
| float new_scale) { |
| InvalidateLayer(); |
| } |
| |
| // TODO(crbug.com/1184438): Handle indeterminate progress. |
| void HoldingSpaceProgressRing::OnPaintLayer(const ui::PaintContext& context) { |
| if (!item_ || !item_->IsInProgress() || !item_->progress().has_value()) |
| return; |
| |
| ui::PaintRecorder recorder(context, layer()->size()); |
| gfx::Canvas* canvas = recorder.canvas(); |
| |
| // The `canvas` should be flipped for RTL. |
| gfx::ScopedCanvas scoped_canvas(recorder.canvas()); |
| scoped_canvas.FlipIfRTL(layer()->size().width()); |
| |
| gfx::Rect bounds(layer()->size()); |
| bounds.Inset(gfx::Insets(std::ceil(kStrokeWidth / 2.f))); |
| |
| cc::PaintFlags flags; |
| flags.setAntiAlias(true); |
| flags.setStrokeCap(cc::PaintFlags::Cap::kRound_Cap); |
| flags.setStrokeWidth(kStrokeWidth); |
| flags.setStyle(cc::PaintFlags::Style::kStroke_Style); |
| |
| const SkColor color = AshColorProvider::Get()->GetControlsLayerColor( |
| AshColorProvider::ControlsLayerType::kFocusRingColor); |
| |
| // Track. |
| flags.setColor(SkColorSetA(color, 0xFF * kTrackOpacity)); |
| canvas->DrawCircle(gfx::PointF(bounds.CenterPoint()), |
| std::min(bounds.height(), bounds.width()) / 2.f, flags); |
| |
| // Ring. |
| flags.setColor(color); |
| canvas->DrawPath( |
| SkPath().arcTo(/*oval=*/gfx::RectToSkRect(bounds), /*start_angle=*/-90, |
| /*sweep_angle=*/CalculateSweepAngle(item_), |
| /*forceMoveTo=*/false), |
| flags); |
| } |
| |
| void HoldingSpaceProgressRing::OnHoldingSpaceItemUpdated( |
| const HoldingSpaceItem* item) { |
| if (item_ == item) |
| InvalidateLayer(); |
| } |
| |
| void HoldingSpaceProgressRing::OnHoldingSpaceItemsRemoved( |
| const std::vector<const HoldingSpaceItem*>& items) { |
| for (const HoldingSpaceItem* item : items) { |
| if (item_ == item) { |
| item_ = nullptr; |
| return; |
| } |
| } |
| } |
| |
| } // namespace ash |