chromium/ash/system/holding_space/holding_space_item_views_section.h

// Copyright 2020 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_HOLDING_SPACE_HOLDING_SPACE_ITEM_VIEWS_SECTION_H_
#define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ITEM_VIEWS_SECTION_H_

#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>

#include "ash/ash_export.h"
#include "ash/public/cpp/holding_space/holding_space_item.h"
#include "ash/public/cpp/holding_space/holding_space_section.h"
#include "base/memory/raw_ptr.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/view.h"

namespace ui {
class CallbackLayerAnimationObserver;
class LayerAnimationObserver;
}  // namespace ui

namespace views {
class ScrollView;
}  // namespace views

namespace ash {

class HoldingSpaceItemView;
class HoldingSpaceViewDelegate;

// A section of holding space item views in a `HoldingSpaceTrayChildBubble`.
class ASH_EXPORT HoldingSpaceItemViewsSection : public views::View {
  METADATA_HEADER(HoldingSpaceItemViewsSection, views::View)

 public:
  HoldingSpaceItemViewsSection(HoldingSpaceViewDelegate* delegate,
                               HoldingSpaceSectionId section_id);
  HoldingSpaceItemViewsSection(const HoldingSpaceItemViewsSection& other) =
      delete;
  HoldingSpaceItemViewsSection& operator=(
      const HoldingSpaceItemViewsSection& other) = delete;
  ~HoldingSpaceItemViewsSection() override;

  // Initializes the section.
  void Init();

  // Resets the section. Called when the tray bubble starts closing to ensure
  // that no new items are created while the bubble widget is being
  // asynchronously closed.
  void Reset();

  // Returns all holding space item views in the section. Views are returned in
  // top-to-bottom, left-to-right order (or mirrored for RTL).
  std::vector<HoldingSpaceItemView*> GetHoldingSpaceItemViews();

  // views::View:
  void ChildPreferredSizeChanged(views::View* child) override;
  void ChildVisibilityChanged(views::View* child) override;
  void PreferredSizeChanged() override;
  void ViewHierarchyChanged(const views::ViewHierarchyChangedDetails&) override;

  // `HoldingSpaceModelObserver` events forwarded from the parent
  // `HoldingSpaceTrayChildBubble`. Note that events may be withheld from this
  // view if, for example, its parent is animating out.
  void OnHoldingSpaceItemsAdded(const std::vector<const HoldingSpaceItem*>&);
  void OnHoldingSpaceItemsRemoved(const std::vector<const HoldingSpaceItem*>&);
  void OnHoldingSpaceItemInitialized(const HoldingSpaceItem* item);

  // Removes all holding space item views from this section. This method is
  // expected to only be called:
  // * from the parent `HoldingSpaceTrayChildBubble` when this view is hidden.
  // * internally after having animated out the `container_` just prior to
  //   swapping in new contents.
  void RemoveAllHoldingSpaceItemViews();

  // Returns whether this section has a placeholder to show in lieu of item
  // views when the model contains no initialized items of supported types.
  bool has_placeholder() const { return !!placeholder_; }

  // Returns the types of holding space items supported by this section.
  const std::set<HoldingSpaceItem::Type>& supported_types() const {
    return section_->supported_types;
  }

 protected:
  // Invoked to create the `header_` for this section.
  virtual std::unique_ptr<views::View> CreateHeader() = 0;

  // Invoked to create the `container_` for this section which parents its
  // holding space item views.
  virtual std::unique_ptr<views::View> CreateContainer() = 0;

  // Invoked to create the view for the specified holding space `item`. Note
  // that the created view will be parented by `container_`.
  virtual std::unique_ptr<HoldingSpaceItemView> CreateView(
      const HoldingSpaceItem* item) = 0;

  // Invoked to create the `placeholder_` for this section which shows when
  // `container_` is empty. The `placeholder_` can be destroyed via call to
  // `DestroyPlaceholder()` if it is no longer needed to exist.
  virtual std::unique_ptr<views::View> CreatePlaceholder();

  // Invoked to destroy `placeholder_`.
  void DestroyPlaceholder();

  // Whether to display this section's contents: either its `container_` or its
  // `placeholder_` as applicable. Sections that have no concept of expanded
  // state are always treated as expanded.
  virtual bool IsExpanded();

  // Updates the section's views based on changes to the expanded state.
  void OnExpandedChanged();

  HoldingSpaceViewDelegate* delegate() { return delegate_; }

 private:
  enum AnimationState : uint32_t {
    kNotAnimating = 0,
    kAnimatingIn = 1 << 1,
    kAnimatingOut = 1 << 2,
  };

  // Invoke to start animating in the contents of this section. No-ops if
  // animate in is already in progress.
  void MaybeAnimateIn();

  // Invoke to start animating out the contents of this section. No-ops if
  // animate out is already in progress.
  void MaybeAnimateOut();

  // Invoked to animate in the contents of this section. Any created animation
  // sequences must be observed by `observer`.
  void AnimateIn(ui::LayerAnimationObserver* observer);

  // Invoked to animate out the contents of this section. Any created animation
  // sequences must be observed by `observer`.
  void AnimateOut(ui::LayerAnimationObserver* observer);

  // Invoked when an animate in/out of the contents of this section has been
  // completed. Note that the provided observer will be deleted after returning.
  void OnAnimateInCompleted(const ui::CallbackLayerAnimationObserver&);
  void OnAnimateOutCompleted(const ui::CallbackLayerAnimationObserver&);

  const raw_ptr<HoldingSpaceViewDelegate, DanglingUntriaged> delegate_;
  const raw_ptr<const HoldingSpaceSection> section_;

  // Owned by view hierarchy.
  raw_ptr<views::View> header_ = nullptr;
  raw_ptr<views::View> container_ = nullptr;
  raw_ptr<views::View, DanglingUntriaged> placeholder_ = nullptr;
  raw_ptr<views::ScrollView> scroll_view_ = nullptr;
  std::map<std::string, HoldingSpaceItemView*> views_by_item_id_;

  // Bit flag representation of current `AnimationState`. Note that it is
  // briefly possible to be both `kAnimatingIn` and `kAnimatingOut` when one
  // animation is preempting another.
  uint32_t animation_state_ = AnimationState::kNotAnimating;

  // Whether or not animations are disabled. Animations are only disabled during
  // initialization as holding space child bubbles are animated in instead.
  bool disable_animations_ = false;

  // Whether or not `PreferredSizeChanged()` is allowed to propagate up the
  // view hierarchy. This is disabled during batch child additions, removals,
  // and visibility change operations to reduce the number of layout events.
  bool disable_preferred_size_changed_ = false;

  base::WeakPtrFactory<HoldingSpaceItemViewsSection> weak_factory_{this};
};

}  // namespace ash

#endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ITEM_VIEWS_SECTION_H_