blob: 87c28de7e4b6643c5b5068c2af77261799d332e2 [file] [log] [blame]
// 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