chromium/ash/app_list/views/app_list_bubble_apps_page.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_APP_LIST_VIEWS_APP_LIST_BUBBLE_APPS_PAGE_H_
#define ASH_APP_LIST_VIEWS_APP_LIST_BUBBLE_APPS_PAGE_H_

#include <memory>

#include "ash/app_list/app_list_model_provider.h"
#include "ash/app_list/app_list_view_provider.h"
#include "ash/app_list/views/app_list_nudge_controller.h"
#include "ash/app_list/views/app_list_toast_container_view.h"
#include "ash/ash_export.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/gfx/animation/tween.h"
#include "ui/views/view.h"
#include "ui/views/view_observer.h"

namespace base {
class TimeDelta;
}  // namespace base

namespace ui {
class Layer;
}  // namespace ui

namespace views {
class Label;
class Separator;
}  // namespace views

namespace ash {

class ApplicationDragAndDropHost;
class AppListA11yAnnouncer;
class AppListConfig;
class AppListFolderController;
class AppListKeyboardController;
class AppListNudgeController;
class AppListViewDelegate;
class ContinueSectionView;
class IconButton;
class RecentAppsView;
class RoundedScrollBar;
class ScrollableAppsGridView;
class ScrollViewGradientHelper;
class SearchBoxView;

// The default page for the app list bubble / clamshell launcher. Contains a
// scroll view with:
// - Continue section with recent tasks and recent apps
// - Grid of all apps
// Does not include the search box, which is owned by a parent view.
class ASH_EXPORT AppListBubbleAppsPage
    : public views::View,
      public views::ViewObserver,
      public AppListModelProvider::Observer,
      public AppListToastContainerView::Delegate,
      public AppListViewProvider {
  METADATA_HEADER(AppListBubbleAppsPage, views::View)

 public:
  AppListBubbleAppsPage(AppListViewDelegate* view_delegate,
                        ApplicationDragAndDropHost* drag_and_drop_host,
                        AppListConfig* app_list_config,
                        AppListA11yAnnouncer* a11y_announcer,
                        AppListFolderController* folder_controller,
                        SearchBoxView* search_box);
  AppListBubbleAppsPage(const AppListBubbleAppsPage&) = delete;
  AppListBubbleAppsPage& operator=(const AppListBubbleAppsPage&) = delete;
  ~AppListBubbleAppsPage() override;

  // Updates the continue section and recent apps.
  void UpdateSuggestions();

  // Starts the launcher show animation.
  void AnimateShowLauncher(bool is_side_shelf);

  // Prepares for launcher hide animation. None of the child views animate, but
  // this disables the scroll view gradient mask to improve performance.
  void PrepareForHideLauncher();

  // Starts the animation for showing the apps page, coming from another page.
  void AnimateShowPage();

  // Starts the animation for hiding the apps page, going to another page.
  void AnimateHidePage();

  // Resets the scroll position to the top.
  void ResetScrollPosition();

  // Aborts all layer animations, which invokes their cleanup callbacks.
  void AbortAllAnimations();

  // Disables all children so they cannot be focused, allowing the open folder
  // view to handle focus.
  void DisableFocusForShowingActiveFolder(bool disabled);

  // Handles `AppListController::UpdateAppListWithNewSortingOrder()` for the
  // bubble launcher apps page.
  void UpdateForNewSortingOrder(
      const std::optional<AppListSortOrder>& new_order,
      bool animate,
      base::OnceClosure update_position_closure,
      base::OnceClosure animation_done_closure);

  // Scrolls to fully show the toast if the toast is partially shown or hidden
  // from the scroll view's perspective. Returns true if scrolling is performed.
  bool MaybeScrollToShowToast();

  // views::View:
  void Layout(PassKey) override;
  void VisibilityChanged(views::View* starting_from, bool is_visible) override;
  void OnBoundsChanged(const gfx::Rect& old_bounds) override;

  // view::ViewObserver:
  void OnViewVisibilityChanged(views::View* observed_view,
                               views::View* starting_view) override;

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

  // AppListToastContainerView::Delegate:
  void OnNudgeRemoved() override;

  // AppListViewProvider:
  ContinueSectionView* GetContinueSectionView() override;
  RecentAppsView* GetRecentAppsView() override;
  AppsGridView* GetAppsGridView() override;
  AppListToastContainerView* GetToastContainerView() override;

  // Updates the visibility of the continue section based on user preference.
  void UpdateContinueSectionVisibility();

  // Invoked when the `scroll_view_` received an scrolling event.
  void OnPageScrolled();

  void RecordAboveTheFoldMetrics();

  views::ScrollView* scroll_view() { return scroll_view_; }
  IconButton* toggle_continue_section_button() {
    return toggle_continue_section_button_;
  }
  ScrollableAppsGridView* scrollable_apps_grid_view() {
    return scrollable_apps_grid_view_;
  }

  // Which layer animates is an implementation detail.
  ui::Layer* GetPageAnimationLayerForTest();

  views::View* continue_label_container_for_test() {
    return continue_label_container_;
  }
  RecentAppsView* recent_apps_for_test() { return recent_apps_; }
  views::Separator* separator_for_test() { return separator_; }
  AppListToastContainerView* toast_container_for_test() {
    return toast_container_;
  }

  AppListNudgeController* app_list_nudge_controller() {
    return app_list_nudge_controller_.get();
  }

  ScrollViewGradientHelper* gradient_helper_for_test() {
    return gradient_helper_.get();
  }

 private:
  friend class AppListTestHelper;

  // Creates the `continue_label_container_` view and its contents, the
  // continue label and the toggle continue section button.
  void InitContinueLabelContainer(views::View* scroll_contents);

  // Updates the continue label container and its child views, including the
  // container visibility and the toggle button state.
  void UpdateContinueLabelContainer();

  void UpdateSeparatorVisibility();

  // Destroys the layer for `view`. Not static so it can be used with weak
  // pointers.
  void DestroyLayerForView(views::View* view);

  // Callback for when the apps grid view animation ends.
  void OnAppsGridViewAnimationEnded();

  // Called after sort to handle focus.
  void HandleFocusAfterSort();

  // Called when the animation to fade out app list items is completed.
  // `aborted` indicates whether the fade out animation is aborted.
  void OnAppsGridViewFadeOutAnimationEnded(
      const std::optional<AppListSortOrder>& new_order,
      bool aborted);

  // Called when the animation to fade in app list items is completed.
  // `aborted` indicates whether the fade in animation is aborted.
  void OnAppsGridViewFadeInAnimationEnded(bool aborted);

  // Called at the end of the reorder animation. In detail, it is executed in
  // the following scenarios:
  // (1) At the end of the fade out animation when the fade out is aborted, or
  // (2) At the end of the fade in animation.
  void OnReorderAnimationEnded();

  // Animates `view` using a layer animation. Creates the layer if needed. The
  // layer is pushed down by `vertical_offset` at the start of the animation and
  // animates back to its original position with `duration`.
  void SlideViewIntoPosition(views::View* view,
                             int vertical_offset,
                             base::TimeDelta duration,
                             gfx::Tween::Type tween_type);

  // Animates `view` using a layer fade-in animation as part of the show
  // continue section animation. Takes `view` because the same animation is used
  // for continue tasks and for recent apps.
  void FadeInContinueSectionView(views::View* view);

  // Pressed callback for `toggle_continue_section_button_`.
  void OnToggleContinueSection();

  raw_ptr<AppListViewDelegate> view_delegate_ = nullptr;
  raw_ptr<views::ScrollView> scroll_view_ = nullptr;
  raw_ptr<RoundedScrollBar> scroll_bar_ = nullptr;

  // Wraps both the continue label and the toggle continue section button.
  raw_ptr<views::View> continue_label_container_ = nullptr;
  raw_ptr<views::Label> continue_label_ = nullptr;
  raw_ptr<IconButton> toggle_continue_section_button_ = nullptr;

  raw_ptr<ContinueSectionView> continue_section_ = nullptr;
  raw_ptr<RecentAppsView> recent_apps_ = nullptr;
  raw_ptr<views::Separator> separator_ = nullptr;
  raw_ptr<AppListToastContainerView> toast_container_ = nullptr;
  raw_ptr<ScrollableAppsGridView> scrollable_apps_grid_view_ = nullptr;

  // The search box owned by AppListBubbleView.
  raw_ptr<SearchBoxView, DanglingUntriaged> search_box_ = nullptr;

  std::unique_ptr<AppListKeyboardController> app_list_keyboard_controller_;
  std::unique_ptr<AppListNudgeController> app_list_nudge_controller_;

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

  // A closure to update item positions. It should run at the end of the fade
  // out animation when items are reordered.
  base::OnceClosure update_position_closure_;

  // A closure that runs at the end of the reorder animation.
  base::OnceClosure reorder_animation_done_closure_;

  // Subscription to notify of scrolling events.
  base::CallbackListSubscription on_contents_scrolled_subscription_;

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

}  // namespace ash

#endif  // ASH_APP_LIST_VIEWS_APP_LIST_BUBBLE_APPS_PAGE_H_