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

ash / clipboard / clipboard_history_menu_model_adapter.h [blame]

// 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_CLIPBOARD_CLIPBOARD_HISTORY_MENU_MODEL_ADAPTER_H_
#define ASH_CLIPBOARD_CLIPBOARD_HISTORY_MENU_MODEL_ADAPTER_H_

#include <memory>
#include <optional>

#include "ash/ash_export.h"
#include "ash/public/cpp/clipboard_history_controller.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "chromeos/crosapi/mojom/clipboard_history.mojom.h"
#include "ui/base/mojom/menu_source_type.mojom-forward.h"
#include "ui/menus/simple_menu_model.h"
#include "ui/views/controls/menu/menu_model_adapter.h"

namespace gfx {
class Rect;
}  // namespace gfx

namespace views {
class MenuItemView;
class MenuRunner;
}  // namespace views

namespace ash {

namespace clipboard_history_util {
enum class Action;
}  // namespace clipboard_history_util

class ClipboardHistory;
class ClipboardHistoryItem;
class ClipboardHistoryItemView;

// Used to show the clipboard history menu, which holds the last few things
// copied.
class ASH_EXPORT ClipboardHistoryMenuModelAdapter
    : public views::MenuModelAdapter {
 public:
  static std::unique_ptr<ClipboardHistoryMenuModelAdapter> Create(
      ui::SimpleMenuModel::Delegate* delegate,
      ClipboardHistoryController::OnMenuClosingCallback
          on_menu_closing_callback,
      base::RepeatingClosure menu_closed_callback,
      const ClipboardHistory* clipboard_history);

  ClipboardHistoryMenuModelAdapter(const ClipboardHistoryMenuModelAdapter&) =
      delete;
  ClipboardHistoryMenuModelAdapter& operator=(
      const ClipboardHistoryMenuModelAdapter&) = delete;
  ~ClipboardHistoryMenuModelAdapter() override;

  // Shows the menu anchored at `anchor_rect`. `source_type` and `show_source`
  // indicate how the menu was triggered. `menu_last_time_shown` and
  // `nudge_last_time_shown` indicate when the menu or any nudge was last shown.
  void Run(const gfx::Rect& anchor_rect,
           ui::mojom::MenuSourceType source_type,
           crosapi::mojom::ClipboardHistoryControllerShowSource show_source,
           const std::optional<base::Time>& menu_last_time_shown,
           const std::optional<base::Time>& nudge_last_time_shown);

  // Returns if the menu is currently running.
  bool IsRunning() const;

  // Hides and cancels the menu. `will_paste_item` indicates whether a clipboard
  // history item will be pasted after the menu is closed.
  void Cancel(bool will_paste_item);

  // Returns the command of the menu's first clipboard history item. This
  // differs from `clipboard_history_util::kFirstItemCommandId` when the menu's
  // first item has been removed. If the menu is empty, the result is absent.
  std::optional<int> GetFirstMenuItemCommand();

  // Returns the command of the currently selected menu item. If no menu item is
  // currently selected, returns |std::nullopt|.
  std::optional<int> GetSelectedMenuItemCommand() const;

  // Returns the item mapped by `command_id` in `item_snapshots_`.
  const ClipboardHistoryItem& GetItemFromCommandId(int command_id) const;

  // Returns the count of menu items.
  size_t GetMenuItemsCount() const;

  // Selects the menu item specified by `command_id`.
  void SelectMenuItemWithCommandId(int command_id);

  // Selects the menu item hovered by mouse.
  void SelectMenuItemHoveredByMouse();

  // Removes the menu item specified by `command_id`.
  void RemoveMenuItemWithCommandId(int command_id);

  // Advances the pseudo focus (backward if `reverse` is true).
  void AdvancePseudoFocus(bool reverse);

  // Returns the action to take on the menu item specified by `command_id`.
  clipboard_history_util::Action GetActionForCommandId(int command_id) const;

  // Returns menu bounds in screen coordinates.
  gfx::Rect GetMenuBoundsInScreenForTest() const;

  const views::MenuItemView* GetMenuItemViewAtForTest(size_t index) const;
  views::MenuItemView* GetMenuItemViewAtForTest(size_t index);

  const ui::SimpleMenuModel* GetModelForTest() const;

 private:
  class MenuModelWithWillCloseCallback;
  class ScopedA11yIgnore;

  using ItemViewsByCommandId =
      std::map<int, raw_ptr<ClipboardHistoryItemView, CtnExperimental>>;

  ClipboardHistoryMenuModelAdapter(
      std::unique_ptr<MenuModelWithWillCloseCallback> model,
      base::RepeatingClosure menu_closed_callback,
      const ClipboardHistory* clipboard_history);

  // Advances the pseduo focus from the selected history item view (backward if
  // `reverse` is true).
  void AdvancePseudoFocusFromSelectedItem(bool reverse);

  // Returns the command id of the menu item to be selected after the
  // menu item specified by `command_id` is deleted.
  int CalculateSelectedCommandIdAfterDeletion(int command_id) const;

  // Removes the item view specified by `command_id` from the root menu.
  void RemoveItemView(int command_id);

  // views::MenuModelAdapter:
  views::MenuItemView* AppendMenuItem(views::MenuItemView* menu,
                                      ui::MenuModel* model,
                                      size_t index) override;
  void OnMenuClosed(views::MenuItemView* menu) override;

  // The model which holds the contents of the menu.
  std::unique_ptr<MenuModelWithWillCloseCallback> const model_;

  // Responsible for showing `root_view_`.
  std::unique_ptr<views::MenuRunner> menu_runner_;

  // The root MenuItemView which contains all child MenuItemViews. Owned by
  // `menu_runner_`.
  raw_ptr<views::MenuItemView> root_view_ = nullptr;

  // The timestamp taken when the menu is opened. Used in metrics.
  base::TimeTicks menu_open_time_;

  // The source which opened the menu, absent until the menu is `Run()`.
  std::optional<crosapi::mojom::ClipboardHistoryControllerShowSource>
      menu_show_source_;

  // The mapping between the command ids and items that are copied from
  // `clipboard_history_` when the menu is created. It is used to solve the
  // possible inconsistency between the menu model data and the clipboard
  // history data. For example, a new item is added to `clipboard_history_`
  // while the menu is showing.
  // It updates synchronously when a item is removed.
  std::map<int, ClipboardHistoryItem> item_snapshots_;

  // Stores mappings between command ids and history item view pointers.
  // It updates synchronously when a item is removed.
  ItemViewsByCommandId item_views_by_command_id_;

  const raw_ptr<const ClipboardHistory> clipboard_history_;

  // Indicates the number of item deletion operations in progress. Note that
  // a `ClipboardHistoryItemView` instance is deleted asynchronously.
  int item_deletion_in_progress_count_ = 0;

  // The index of the clipboard history menu header, if it exists.
  std::optional<size_t> header_index_;

  // The index of the clipboard history menu footer, if it exists.
  std::optional<size_t> footer_index_;

  std::unique_ptr<ScopedA11yIgnore> scoped_ignore_;

  // Indicates whether `Run()` has been called before.
  bool run_before_ = false;

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

}  // namespace ash

#endif  // ASH_CLIPBOARD_CLIPBOARD_HISTORY_MENU_MODEL_ADAPTER_H_