chromium/ash/system/progress_indicator/progress_indicator.h

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_INDICATOR_H_
#define ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_INDICATOR_H_

#include <memory>
#include <optional>
#include <vector>

#include "ash/ash_export.h"
#include "ash/system/progress_indicator/progress_indicator_animation_registry.h"
#include "base/callback_list.h"
#include "base/memory/raw_ptr.h"
#include "ui/color/color_provider.h"
#include "ui/compositor/layer_delegate.h"
#include "ui/compositor/layer_owner.h"

namespace ash {

class ProgressIconAnimation;
class ProgressRingAnimation;

// A class owning a `ui::Layer` which paints indication of progress.
// NOTE: The owned `layer()` is not painted if progress == `1.f`, but we can
// paint the `layer()` by setting the progress back to `kForcedShow`.
class ASH_EXPORT ProgressIndicator : public ui::LayerOwner,
                                     public ui::LayerDelegate {
 public:
  static constexpr char kClassName[] = "ProgressIndicator";
  static constexpr float kProgressComplete = 1.f;
  static constexpr float kForcedShow = 0.999999f;

  ProgressIndicator(const ProgressIndicator&) = delete;
  ProgressIndicator& operator=(const ProgressIndicator&) = delete;
  ~ProgressIndicator() override;

  // Returns an instance which paints indication of progress returned by the
  // specified `progress_callback`. NOTE: This instance comes pre-wired with an
  // `animation_registry_` that will manage progress animations as needed.
  static std::unique_ptr<ProgressIndicator> CreateDefaultInstance(
      base::RepeatingCallback<std::optional<float>()> progress_callback);

  // Adds the specified `callback` to be notified of `progress_` changes. The
  // `callback` will continue to receive events so long as both `this` and the
  // returned subscription exist.
  base::CallbackListSubscription AddProgressChangedCallback(
      base::RepeatingClosureList::CallbackType callback);

  // Creates and returns the `layer()` which is owned by this progress
  // indicator. Note that this may only be called if `layer()` does not exist.
  using ColorResolver = base::RepeatingCallback<SkColor(ui::ColorId)>;
  ui::Layer* CreateLayer(ColorResolver color_resolver);

  // Destroys the `layer()` which is owned by this progress indicator. Note that
  // this will no-op if `layer()` does not exist.
  void DestroyLayer();

  // Invoke to schedule repaint of the entire `layer()`.
  void InvalidateLayer();

  // Sets the `color_id` to use in lieu of the default when painting progress
  // indication. If `color_id` is absent, default colors are used.
  void SetColorId(const std::optional<ui::ColorId>& color_id);

  // Sets the visibility for this progress indicator's inner icon. Note that
  // the inner icon will only be painted while `progress_` is incomplete,
  // regardless of the value of `visible` provided.
  void SetInnerIconVisible(bool visible);
  bool inner_icon_visible() const { return inner_icon_visible_; }

  // Sets the visibility for this progress indicator's inner ring. Note that
  // the inner ring will only be painted while `progress_` is incomplete,
  // regardless of the value of `visible` provided.
  void SetInnerRingVisible(bool visible);

  // Sets the visibility of the progress indicator's outer ring track. Note that
  // the track will only be painted while `progress_` is incomplete, regardless
  // of the value of `visible` provided.
  void SetOuterRingTrackVisible(bool visible);

  // Sets the width for this progress indicator's outer ring stroke.
  void SetOuterRingStrokeWidth(float width);

  // Returns the underlying `animation_registry_` in which to look up animations
  // for the associated `animation_key_`. NOTE: This may return `nullptr`.
  ProgressIndicatorAnimationRegistry* animation_registry() {
    return animation_registry_;
  }

  // Returns the `animation_key_` for which to look up animations in the
  // underlying `animation_registry_`. NOTE: This may return `nullptr`.
  ProgressIndicatorAnimationRegistry::AnimationKey animation_key() const {
    return animation_key_;
  }

  // Returns the underlying `progress_` for which to paint indication.
  // NOTE: If absent, progress is indeterminate.
  // NOTE: If present, progress must be >= `0.f` and <= `1.f`.
  const std::optional<float>& progress() const { return progress_; }

 protected:
  // Each progress indicator is associated with an `animation_key_` which is
  // used to look up animations in the provided `animation_registry`. When an
  // animation exists, it will be painted in lieu of the determinate progress
  // indication that would otherwise be painted for the cached `progress_`.
  // NOTE: `animation_registry` may be `nullptr` if animations are not needed.
  ProgressIndicator(
      ProgressIndicatorAnimationRegistry* animation_registry,
      ProgressIndicatorAnimationRegistry::AnimationKey animation_key);

  // Returns the calculated progress to paint to the owned `layer()`. This is
  // invoked during `UpdateVisualState()` just prior to painting.
  // NOTE: If absent, progress is indeterminate.
  // NOTE: If present, progress must be >= `0.f` and <= `1.f`.
  // NOTE: If progress == `1.f`, progress is complete and will not be painted.
  virtual std::optional<float> CalculateProgress() const = 0;

 private:
  // ui::LayerDelegate:
  void OnDeviceScaleFactorChanged(float old_scale, float new_scale) override;
  void OnPaintLayer(const ui::PaintContext& context) override;
  void UpdateVisualState() override;

  // Invoked when the icon `animation` associated with this progress indicator's
  // `animation_key_` has changed in the `animation_registry_`.
  // NOTE: The specified `animation` may be `nullptr`.
  void OnProgressIconAnimationChanged(ProgressIconAnimation* animation);

  // Invoked when the ring `animation` associated with this progress indicator's
  // `animation_key_` has changed in the `animation_registry_`.
  // NOTE: The specified `animation` may be `nullptr`.
  void OnProgressRingAnimationChanged(ProgressRingAnimation* animation);

  // The animation registry in which to look up animations for the associated
  // `animation_key_`. When an animation exists, it will be painted in lieu of
  // the determinate progress indication that would otherwise be painted for the
  // cached `progress_`.
  const raw_ptr<ProgressIndicatorAnimationRegistry, DanglingUntriaged>
      animation_registry_;

  // The key for which to look up animations in the `animation_registry_`.
  // When an animation exists, it will be painted in lieu of the determinate
  // progress indication that would otherwise be painted for the cached
  // `progress_`.
  const ProgressIndicatorAnimationRegistry::AnimationKey animation_key_;

  // A subscription to receive events when the icon animation associated with
  // this progress indicator's `animation_key_` has changed in the
  // `animation_registry_`.
  base::CallbackListSubscription icon_animation_changed_subscription_;

  // A subscription to receive events on updates to the icon animation owned by
  // the `animation_registry_` which is associated with this progress
  // indicator's `animation_key_`. On icon animation update, the progress
  // indicator will `InvalidateLayer()` to trigger paint of the next animation
  // frame.
  base::CallbackListSubscription icon_animation_updated_subscription_;

  // A subscription to receive events when the ring animation associated with
  // this progress indicator's `animation_key_` has changed in the
  // `animation_registry_`.
  base::CallbackListSubscription ring_animation_changed_subscription_;

  // A subscription to receive events on updates to the ring animation owned by
  // the `animation_registry_` which is associated with this progress
  // indicator's `animation_key_`. On ring animation update, the progress
  // indicator will `InvalidateLayer()` to trigger paint of the next animation
  // frame.
  base::CallbackListSubscription ring_animation_updated_subscription_;

  // Used to resolve the color to use to paint progress indication. Non-null if
  // and only if the `layer()` which is owned by this progress indicator exists.
  ColorResolver color_resolver_;

  // The color ID to use in lieu of the default when painting progress
  // indication. If absent, default colors are used.
  std::optional<ui::ColorId> color_id_;

  // Cached progress returned from `CalculateProgress()` just prior to painting.
  // NOTE: If absent, progress is indeterminate.
  // NOTE: If present, progress must be >= `0.f` and <= `1.f`.
  std::optional<float> progress_ = kProgressComplete;

  // The list of callbacks for which to notify `progress_` changes.
  base::RepeatingClosureList progress_changed_callback_list_;

  // Whether this progress indicator's inner icon is visible. Note that the
  // inner icon will only be painted while `progress_` is incomplete, regardless
  // of this value.
  bool inner_icon_visible_ = true;

  // Whether this progress indicator's inner ring is visible. Note that the
  // inner ring will only be painted while `progress_` is incomplete, regardless
  // of this value.
  bool inner_ring_visible_ = true;

  // Whether this progress indicator's outer ring track is visible. Note that
  // the track will only be painted while `progress_` is incomplete, regardless
  // of this value.
  bool outer_ring_track_visible_ = false;

  // The width for the outer ring stroke.
  std::optional<float> outer_ring_stroke_width_;
};

}  // namespace ash

#endif  // ASH_SYSTEM_PROGRESS_INDICATOR_PROGRESS_INDICATOR_H_