chromium/ash/app_list/views/app_list_bubble_view.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_VIEW_H_
#define ASH_APP_LIST_VIEWS_APP_LIST_BUBBLE_VIEW_H_

#include <memory>
#include <set>

#include "ash/app_list/app_list_view_provider.h"
#include "ash/app_list/views/app_list_folder_controller.h"
#include "ash/app_list/views/search_box_view_delegate.h"
#include "ash/ash_export.h"
#include "ash/public/cpp/app_list/app_list_types.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/views/view.h"

namespace views {
class ViewShadow;
}  // namespace views

namespace ash {

class ApplicationDragAndDropHost;
class AppListA11yAnnouncer;
class AppListBubbleAppsPage;
class AppListBubbleAppsCollectionsPage;
class AppListBubbleAssistantPage;
class AppListBubbleSearchPage;
class AppListFolderItem;
class AppListFolderView;
class AppListViewDelegate;
class ButtonFocusSkipper;
class FolderBackgroundView;
class SearchBoxView;
class SearchResultPageDialogController;

// Contains the views for the bubble version of the launcher. It looks like a
// system tray bubble. It does not derive from TrayBubbleView because it takes
// focus by default, uses a different EventHandler for closing, and isn't tied
// to the system tray area.
class ASH_EXPORT AppListBubbleView : public views::View,
                                     public SearchBoxViewDelegate,
                                     public AppListFolderController {
  METADATA_HEADER(AppListBubbleView, views::View)

 public:
  AppListBubbleView(AppListViewDelegate* view_delegate,
                    ApplicationDragAndDropHost* drag_and_drop_host);
  AppListBubbleView(const AppListBubbleView&) = delete;
  AppListBubbleView& operator=(const AppListBubbleView&) = delete;
  ~AppListBubbleView() override;

  // If |drag_and_drop_host| is not nullptr it will be called upon drag and drop
  // operations outside the app list (e.g. to the shelf).
  void SetDragAndDropHostOfCurrentAppList(
      ApplicationDragAndDropHost* drag_and_drop_host);

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

  // Starts the bubble show animation. Pass `is_side_shelf` true for left or
  // right aligned shelf.
  void StartShowAnimation(bool is_side_shelf);

  // Starts the bubble hide animation. Pass `is_side_shelf` true for left or
  // right aligned shelf. `on_hide_animation_ended` is called on end or abort.
  void StartHideAnimation(bool is_side_shelf,
                          base::OnceClosure on_hide_animation_ended);

  // Aborts all layer animations started by StartShowAnimation() or
  // StartHideAnimation(). This invokes their cleanup callbacks.
  void AbortAllAnimations();

  // Handles back action if it we have a use for it besides dismissing.
  bool Back();

  // Shows a sub-page.
  void ShowPage(AppListBubblePage page);

  // Returns true if the assistant page is showing.
  bool IsShowingEmbeddedAssistantUI() const;

  // Shows the assistant page.
  void ShowEmbeddedAssistantUI();

  // Returns the required height for this view in DIPs to show all apps in the
  // apps grid. Used for computing the bubble height on large screens.
  int GetHeightToFitAllApps() const;

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

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

  // views::View:
  bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
  void Layout(PassKey) override;
  bool GetDropFormats(int* formats,
                      std::set<ui::ClipboardFormatType>* format_types) override;
  bool CanDrop(const OSExchangeData& data) override;
  int OnDragUpdated(const ui::DropTargetEvent& event) override;
  void OnDragEntered(const ui::DropTargetEvent& event) override;
  void OnDragExited() override;
  DropCallback GetDropCallback(const ui::DropTargetEvent& event) override;

  // SearchBoxViewDelegate:
  void QueryChanged(const std::u16string& trimmed_query,
                    bool initiated_by_user) override;
  void AssistantButtonPressed() override;
  void CloseButtonPressed() override;
  void ActiveChanged(SearchBoxViewBase* sender) override {}
  void OnSearchBoxKeyEvent(ui::KeyEvent* event) override;
  bool CanSelectSearchResults() override;

  // AppListFolderController:
  void ShowFolderForItemView(AppListItemView* folder_item_view,
                             bool focus_name_input,
                             base::OnceClosure hide_callback) override;
  void ShowApps(AppListItemView* folder_item_view, bool select_folder) override;
  void ReparentFolderItemTransit(AppListFolderItem* folder_item) override;
  void ReparentDragEnded() override;

  // Initialize Assistant UIs for bubble view. Assistant UIs
  // (AppListAssistantMainStage, SuggestionContainerView) expect that their
  // OnUiVisibilityChanged methods get called via value update in
  // AssistantUiModel.
  //
  // But it does not happen for bubble view as AppListBubblePresenter have an
  // async call for OnZeroStateSearchDone. AppListBubbleView is instantiated
  // after the async call and those UIs will miss the event.
  //
  // This is a helper method to manually trigger the UI initialization.
  //
  // This method is designed to be explicitly called from AppListBubblePresenter
  // (i.e. instead of doing this in the constructor of AppListBubbleView) to
  // make the intention clear.
  //
  // TODO(b/239754561): Clean up: refactor Assistant UI initialization
  void InitializeUIForBubbleView();

  AppListBubblePage current_page_for_test() { return current_page_; }
  views::ViewShadow* view_shadow_for_test() { return view_shadow_.get(); }
  SearchBoxView* search_box_view_for_test() { return search_box_view_; }
  views::View* separator_for_test() { return separator_; }
  bool showing_folder_for_test() { return showing_folder_; }
  AppListBubbleAppsPage* apps_page_for_test() { return apps_page_; }
  AppListBubbleSearchPage* search_page() { return search_page_; }
  AppListFolderView* folder_view_for_test() { return folder_view_; }

 private:
  friend class AppListTestHelper;
  friend class AssistantTestApiImpl;

  // Initializes the main contents (search box, apps page, and search page).
  void InitContentsView(ApplicationDragAndDropHost* drag_and_drop_host);

  // Initializes the folder view, which appears on top of all other views.
  void InitFolderView(ApplicationDragAndDropHost* drag_and_drop_host);

  // Makes the root apps grid view and other top-level views unfocusable if
  // `disabled` is true, such that focus is contained in the folder view.
  void DisableFocusForShowingActiveFolder(bool disabled);

  // Called when the show animation ends or aborts.
  void OnShowAnimationEnded(const gfx::Rect& layer_bounds);

  // Called when the hide animation ends or aborts.v
  void OnHideAnimationEnded(const gfx::Rect& layer_bounds);

  // Hides the folder view if it's currently shown. It can be called if the
  // folder is not currently shown.
  // `animate` - Whether the folder view should be hidden using an animation.
  // `hide_for_reparent` - Whether the folder view is being hidden to initiate
  // item reparent user action (e.g. when dragging folder item out of the folder
  // view bounds).
  void HideFolderView(bool animate, bool hide_for_reparent);

  // Called when the reorder animation completes.
  void OnAppListReorderAnimationDone();

  // Focuses the search box if the view is not hiding.
  void MaybeFocusAndActivateSearchBox();

  const raw_ptr<AppListViewDelegate> view_delegate_;

  std::unique_ptr<AppListA11yAnnouncer> a11y_announcer_;

  // Controller for showing a modal dialog in search results page.
  std::unique_ptr<SearchResultPageDialogController>
      search_page_dialog_controller_;

  // Explicitly store the current page because multiple pages can be visible
  // during animations.
  AppListBubblePage current_page_ = AppListBubblePage::kNone;

  std::unique_ptr<views::ViewShadow> view_shadow_;

  // The individual views are implementation details and are intentionally not
  // exposed via getters (except for tests).
  raw_ptr<SearchBoxView> search_box_view_ = nullptr;
  raw_ptr<views::View> separator_ = nullptr;
  raw_ptr<AppListBubbleAppsPage> apps_page_ = nullptr;
  raw_ptr<AppListBubbleSearchPage> search_page_ = nullptr;
  raw_ptr<AppListBubbleAssistantPage> assistant_page_ = nullptr;
  raw_ptr<AppListBubbleAppsCollectionsPage> apps_collections_page_ = nullptr;

  // Lives in this class because it can overlap the search box.
  raw_ptr<AppListFolderView, DanglingUntriaged> folder_view_ = nullptr;

  // Used to close an open folder view.
  raw_ptr<FolderBackgroundView> folder_background_view_ = nullptr;

  // Whether we're showing the folder view. This is different from
  // folder_view_->GetVisible() because the view is "visible" but hidden when
  // dragging an item out of a folder.
  bool showing_folder_ = false;

  // Whether the view is animating hidden.
  bool is_hiding_ = false;

  // Called after the hide animation ends or aborts.
  base::OnceClosure on_hide_animation_ended_;

  // See class comment in .cc file.
  std::unique_ptr<ButtonFocusSkipper> button_focus_skipper_;

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

}  // namespace ash

#endif  // ASH_APP_LIST_VIEWS_APP_LIST_BUBBLE_VIEW_H_