chromium/ash/app_list/views/app_list_folder_view.h

// Copyright 2013 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_APP_LIST_VIEWS_APP_LIST_FOLDER_VIEW_H_
#define ASH_APP_LIST_VIEWS_APP_LIST_FOLDER_VIEW_H_

#include <memory>
#include <string>
#include <vector>

#include "ash/app_list/app_list_model_provider.h"
#include "ash/app_list/model/app_list_item_list_observer.h"
#include "ash/app_list/model/app_list_model.h"
#include "ash/app_list/model/app_list_model_observer.h"
#include "ash/app_list/views/apps_grid_view.h"
#include "ash/app_list/views/apps_grid_view_folder_delegate.h"
#include "ash/app_list/views/folder_header_view.h"
#include "ash/app_list/views/folder_header_view_delegate.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/compositor/throughput_tracker.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/view.h"

namespace ash {

class AppListA11yAnnouncer;
class AppListFolderController;
class AppListFolderItem;
class AppListItemView;
class AppListModel;
class AppListViewDelegate;
class AppsContainerView;
class AppsGridView;
class FolderHeaderView;
class ScrollViewGradientHelper;
class SystemShadow;

// Displays folder contents via an AppsGridView. App items can be dragged out
// of the folder to the main apps grid.
class ASH_EXPORT AppListFolderView : public views::View,
                                     public FolderHeaderViewDelegate,
                                     public AppListModelProvider::Observer,
                                     public AppListModelObserver,
                                     public views::ViewObserver,
                                     public AppsGridViewFolderDelegate {
  METADATA_HEADER(AppListFolderView, views::View)

 public:
  // The maximum number of columns a folder can have.
  static constexpr int kMaxFolderColumns = 4;

  AppListFolderView(AppListFolderController* folder_controller,
                    AppsGridView* root_apps_grid_view,
                    AppListA11yAnnouncer* a11y_announcer,
                    AppListViewDelegate* view_delegate,
                    bool tablet_mode);
  AppListFolderView(const AppListFolderView&) = delete;
  AppListFolderView& operator=(const AppListFolderView&) = delete;
  ~AppListFolderView() override;

  // An interface for the folder opening and closing animations.
  class Animation {
   public:
    virtual ~Animation() = default;
    // `completion_callback` is an optional callback to be run when the
    // animation completes. Not run if the animation gets reset before
    // completion.
    virtual void ScheduleAnimation(base::OnceClosure completion_callback) = 0;
    virtual bool IsAnimationRunning() = 0;
  };

  // Sets the `AppListConfig` that should be used to configure app list item
  // size within the folder items grid.
  void UpdateAppListConfig(const AppListConfig* config);

  // Configures AppListFolderView to show the contents for the folder item
  // associated with `folder_item_view`. The folder view will be anchored at
  // `folder_item_view`. `hide_callback` gets called when the folder gets
  // hidden (after all hide animations complete).
  void ConfigureForFolderItemView(AppListItemView* folder_item_view,
                                  base::OnceClosure hide_callback);

  // Schedules an animation to show or hide the view.
  // If |show| is false, the view should be set to invisible after the
  // animation is done unless |hide_for_reparent| is true.
  void ScheduleShowHideAnimation(bool show, bool hide_for_reparent);

  // Hides the view immediately without animation.
  void HideViewImmediately();

  // Prepares folder item grid for closing the folder - it ends any in-progress
  // drag, and clears any selected view.
  void ResetItemsGridForClose();

  // Closes the folder page and goes back the top level page.
  void CloseFolderPage();

  // Focuses the name input text-field in the folder header.
  void FocusNameInput();

  // Focuses the first app item. Does not set the selection or perform a11y
  // announce if `silently` is true.
  void FocusFirstItem(bool silently);

  // views::View
  void AddedToWidget() override;
  void Layout(PassKey) override;
  void ChildPreferredSizeChanged(View* child) override;
  void OnGestureEvent(ui::GestureEvent* event) override;

  // AppListModelProvider::Observer:
  void OnActiveAppListModelsChanged(AppListModel* model,
                                    SearchModel* search_model) override;

  // views::ViewObserver:
  void OnViewIsDeleting(views::View* view) override;

  // AppListModelObserver
  void OnAppListItemWillBeDeleted(AppListItem* item) override;

  // Updates preferred bounds of this view based on the activated folder item
  // icon's bounds.
  void UpdatePreferredBounds();

  // Returns the Y-offset that would move a folder out from under a visible
  // Virtual keyboard
  int GetYOffsetForFolder();

  // Recalculates and updates the bounds of the folder `shadow_`  .
  void UpdateShadowBounds();

  // Called when the  `shadow_` layer gets recreated.
  void OnShadowLayerRecreated(ui::Layer* old_layer, ui::Layer* new_layer);

  // Returns true if this view's child views are in animation for opening or
  // closing the folder.
  bool IsAnimationRunning() const;

  // Sets the bounding box for the folder view bounds. The bounds are expected
  // to be in the parent view's coordinate system.
  void SetBoundingBox(const gfx::Rect& bounding_box);

  // Sets the callback that runs when the folder animation ends.
  void SetAnimationDoneTestCallback(base::OnceClosure animation_done_callback);

  AppsGridView* items_grid_view() { return items_grid_view_; }

  FolderHeaderView* folder_header_view() { return folder_header_view_; }

  views::View* animating_background() { return animating_background_; }

  views::View* contents_container() { return contents_container_; }

  const AppListFolderItem* folder_item() const { return folder_item_; }

  const gfx::Rect& folder_item_icon_bounds() const {
    return folder_item_icon_bounds_;
  }

  const gfx::Rect& preferred_bounds() const { return preferred_bounds_; }

  SystemShadow* shadow() { return shadow_.get(); }

  // Records the smoothness of folder show/hide animations mixed with the
  // BackgroundAnimation, FolderItemTitleAnimation, TopIconAnimation, and
  // ContentsContainerAnimation.
  void RecordAnimationSmoothness();

  // views::View:
  void OnScrollEvent(ui::ScrollEvent* event) override;
  void OnMouseEvent(ui::MouseEvent* event) override;

  // Overridden from FolderHeaderViewDelegate:
  void SetItemName(AppListFolderItem* item, const std::string& name) override;

  // Overridden from AppsGridViewFolderDelegate:
  void ReparentItem(AppsGridView::Pointer pointer,
                    AppListItemView* original_drag_view,
                    const gfx::Point& drag_point_in_folder_grid) override;
  void DispatchDragEventForReparent(
      AppsGridView::Pointer pointer,
      const gfx::Point& drag_point_in_folder_grid) override;
  void DispatchEndDragEventForReparent(
      bool events_forwarded_to_drag_drop_host,
      bool cancel_drag,
      std::unique_ptr<AppDragIconProxy> drag_icon_proxy) override;
  void Close() override;
  bool IsDragPointOutsideOfFolder(const gfx::Point& drag_point) override;
  bool IsOEMFolder() const override;
  void HandleKeyboardReparent(AppListItemView* reparented_view,
                              ui::KeyboardCode key_code) override;

  const AppListConfig* GetAppListConfig() const;

  AppListA11yAnnouncer* a11y_announcer_for_test() { return a11y_announcer_; }
  views::ScrollView* scroll_view_for_test() { return scroll_view_; }

 private:
  // Creates a vertically scrollable apps grid view.
  void CreateScrollableAppsGrid(bool tablet_mode);

  // Returns the compositor associated to the widget containing this view.
  // Returns nullptr if there isn't one associated with this widget.
  ui::Compositor* GetCompositor();

  // Called from the root apps grid view to cancel reparent drag from the root
  // apps grid.
  void CancelReparentDragFromRootGrid();

  // Resets the folder view state. Called when the folder view gets hidden (and
  // hide animations finish) to disassociate the folder view with the current
  // folder item (if any).
  // `restore_folder_item_view_state` - whether the folder item view state
  // should be restored to the default state (icon and title shown). Set to
  // false when resetting the folder state due to folder item view deletion.
  void ResetState(bool restore_folder_item_view_state);

  // Called when the animation to show the folder view is completed.
  void OnShowAnimationDone();

  // Called when the animation to hide the folder view is completed.
  // `hide_for_reparent` is true if an item in the folder is being reparented to
  // the root grid view.
  void OnHideAnimationDone(bool hide_for_reparent);

  void UpdateExpandedCollapsedAccessibleState() const;

  // Controller interface implemented by the container for this view.
  const raw_ptr<AppListFolderController> folder_controller_;

  // The root (non-folder) apps grid view.
  const raw_ptr<AppsGridView> root_apps_grid_view_;

  // Used to send accessibility alerts. Owned by the parent apps container.
  const raw_ptr<AppListA11yAnnouncer> a11y_announcer_;

  // The view is used to draw a background with corner radius.
  raw_ptr<views::View> background_view_;
  raw_ptr<views::View> animating_background_;

  // The view is used as a container for all following views.
  raw_ptr<views::View> contents_container_;  // Owned by views hierarchy.

  raw_ptr<FolderHeaderView> folder_header_view_;  // Owned by views hierarchy.
  raw_ptr<AppsGridView> items_grid_view_;         // Owned by views hierarchy.

  // Owned by views hierarchy.
  raw_ptr<views::ScrollView> scroll_view_ = nullptr;

  std::unique_ptr<SystemShadow> shadow_;

  // Adds fade in/out gradients to `scroll_view_`.
  std::unique_ptr<ScrollViewGradientHelper> gradient_helper_;

  const raw_ptr<AppListViewDelegate> view_delegate_;
  raw_ptr<AppListFolderItem> folder_item_ = nullptr;  // Not owned.

  // Whether the folder view is currently shown, or showing.
  bool shown_ = false;

  // If set, the callback that will be called when the folder hides (after hide
  // animations complete).
  base::OnceClosure hide_callback_;

  // The folder item in the root apps grid associated with this folder.
  raw_ptr<AppListItemView> folder_item_view_ = nullptr;

  // The bounds of the activated folder item icon relative to this view.
  gfx::Rect folder_item_icon_bounds_;

  // The preferred bounds of this view relative to AppsContainerView.
  gfx::Rect preferred_bounds_;

  // The bounds of the box within which the folder view can be shown. The bounds
  // are relative the the parent view's coordinate system.
  gfx::Rect bounding_box_;

  std::vector<std::unique_ptr<Animation>> folder_visibility_animations_;

  // Records smoothness of the folder show/hide animation.
  std::optional<ui::ThroughputTracker> show_hide_metrics_tracker_;

  base::ScopedObservation<AppListModel, AppListModelObserver>
      model_observation_{this};

  // Observes `folder_item_view_` deletion, so the folder state can be cleared
  // if the folder item view is destroyed (for example, the view may get deleted
  // during folder hide animation if the backing item gets deleted from the
  // model, and animations depend on the folder item view).
  base::ScopedObservation<views::View, views::ViewObserver>
      folder_item_view_observer_{this};

  // The callback that runs at the end of the folder animation.
  base::OnceClosure animation_done_test_callback_;

  base::WeakPtrFactory<AppListFolderView> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // ASH_APP_LIST_VIEWS_APP_LIST_FOLDER_VIEW_H_