1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210

ash / system / progress_indicator / progress_indicator.h [blame]

// 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_