File: toolbar_controller.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (317 lines) | stat: -rw-r--r-- 12,568 bytes parent folder | download | duplicates (3)
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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_UI_VIEWS_TOOLBAR_TOOLBAR_CONTROLLER_H_
#define CHROME_BROWSER_UI_VIEWS_TOOLBAR_TOOLBAR_CONTROLLER_H_

#include <variant>
#include <vector>

#include "base/callback_list.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/toolbar/pinned_toolbar/pinned_toolbar_actions_model.h"
#include "chrome/browser/ui/views/toolbar/overflow_button.h"
#include "chrome/browser/ui/views/toolbar/toolbar_button.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "ui/actions/action_id.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/layout/flex_layout_types.h"
#include "ui/views/layout/proposed_layout.h"
#include "ui/views/view.h"

class Browser;

// Manages toolbar elements' visibility using flex rules. This also owns the
// overflow menu and the logic to generate the menu model. It also listens to
// action item changes and updates the menu as required.
class ToolbarController : public views::MenuDelegate,
                          public ui::SimpleMenuModel::Delegate,
                          public PinnedToolbarActionsModel::Observer {
 public:
  // Manages action-based pinned toolbar elements.
  class PinnedActionsDelegate {
   public:
    virtual actions::ActionItem* GetActionItemFor(actions::ActionId id) = 0;

    // Returns true if the corresponding element is hidden.
    virtual bool IsOverflowed(actions::ActionId id) = 0;

    virtual views::View* GetContainerView() = 0;

    // Return true if any buttons should overflow given available size.
    virtual bool ShouldAnyButtonsOverflow(gfx::Size available_size) const = 0;

    // Returns the ordered list of pinned ActionIds.
    virtual const std::vector<actions::ActionId>& PinnedActionIds() const = 0;

   protected:
    virtual ~PinnedActionsDelegate() = default;
  };

  // Data structure to store information specifically used to support
  // ui::ElementIdentifier as element reference.
  struct ElementIdInfo {
    explicit ElementIdInfo(ui::ElementIdentifier overflow_identifier,
                           int menu_text_id,
                           raw_ptr<const gfx::VectorIcon> menu_icon,
                           ui::ElementIdentifier activate_identifier,
                           std::optional<ui::ElementIdentifier>
                               observed_identifier = std::nullopt);

    // The identifier of toolbar element that potentially overflows.
    ui::ElementIdentifier overflow_identifier;

    // Menu text when the element is overflow to the overflow menu. For
    // ActionId-based elements this value is supplied when constructing action
    // items.
    int menu_text_id;

    // Menu item icon. nullptr if this menu item has no icon.
    raw_ptr<const gfx::VectorIcon> menu_icon = nullptr;

    // The toolbar button to be activated with menu text pressed. This is not
    // necessarily the same as the element that overflows. E.g. when the
    // overflowed element is kToolbarExtensionsContainerElementId the
    // `activate_identifier` should be kExtensionsMenuButtonElementId.
    ui::ElementIdentifier activate_identifier;

    // Pop out button when `observed_identifier` is shown. End pop out when it's
    // hidden.
    std::optional<ui::ElementIdentifier> observed_identifier;
  };

  // Data structure to store information of responsive elements. Supports both
  // ui::ElementIdentifier and ActionId as element reference.
  struct ResponsiveElementInfo {
    // Overflow menu structure:
    // -------------------
    // | Forward         |
    // |-----------------|
    // | Home            | -> section end
    // |=================| -> potential separator
    // | Reading list    |
    // |-----------------|
    // | Bookmarks       | -> section end
    // |=================| -> potential separator
    // | Labs            |
    // |-----------------|
    // | Cast            |
    // |-----------------|
    // | Media controls  |
    // |-----------------|
    // | Downloads       | -> section end
    // |=================| -> potential separator
    // | Profile         |
    // |-----------------|
    explicit ResponsiveElementInfo(
        std::variant<ElementIdInfo, actions::ActionId> overflow_id,
        bool is_section_end = false);
    ResponsiveElementInfo(const ResponsiveElementInfo&);
    ~ResponsiveElementInfo();

    // The toolbar element that potentially overflows.
    std::variant<ElementIdInfo, actions::ActionId> overflow_id;

    // True if current element is a section end in overflow menu structure.
    bool is_section_end = false;
  };

  ToolbarController(
      const std::vector<ResponsiveElementInfo>& responsive_elements,
      const std::vector<ui::ElementIdentifier>& elements_in_overflow_order,
      int element_flex_order_start,
      views::View* toolbar_container_view,
      OverflowButton* overflow_button,
      PinnedActionsDelegate* PinnedActionsDelegate,
      PinnedToolbarActionsModel* pinned_toolbar_actions_model);
  ToolbarController(const ToolbarController&) = delete;
  ToolbarController& operator=(const ToolbarController&) = delete;
  ~ToolbarController() override;

  // Handler to pop out `identifier` when `observed_identier` is shown and end
  // the pop out when it's hidden. For example, a toolbar button needs to pop
  // out when a bubble is anchored to it.
  class PopOutHandler {
   public:
    PopOutHandler(ToolbarController* controller,
                  ui::ElementContext context,
                  ui::ElementIdentifier identifier,
                  ui::ElementIdentifier observed_identifier);
    PopOutHandler(const PopOutHandler&) = delete;
    PopOutHandler& operator=(const PopOutHandler&) = delete;
    virtual ~PopOutHandler();

   private:
    // Called when element with `observed_identifier` is shown.
    void OnElementShown(ui::TrackedElement* element);

    // Called when element with `observed_identifier` is hidden.
    void OnElementHidden(ui::TrackedElement* element);

    const raw_ptr<ToolbarController> controller_;
    const ui::ElementIdentifier identifier_;
    const ui::ElementIdentifier observed_identifier_;
    base::CallbackListSubscription shown_subscription_;
    base::CallbackListSubscription hidden_subscription_;
  };

  // Data structure to store the state of the responsive element. It's used for
  // pop out/end pop out.
  struct PopOutState {
    PopOutState();
    PopOutState(const PopOutState&) = delete;
    PopOutState& operator=(const PopOutState&) = delete;
    ~PopOutState();

    // The original FlexSpecification.
    std::optional<views::FlexSpecification> original_spec;

    // The responsive FlexSpecification modified by ToolbarController.
    views::FlexSpecification responsive_spec;

    // Whether the element is current popped out.
    bool is_popped_out = false;

    std::unique_ptr<PopOutHandler> handler;
  };

  // PinnedToolbarActionsModel::Observer
  void OnActionsChanged() override;

  // Return the default responsive elements list in the toolbar.
  static std::vector<ResponsiveElementInfo> GetDefaultResponsiveElements(
      Browser* browser);

  // Return the element list in desired overflow order. The list should contain
  // only the immediate children of toolbar i.e. those managed by
  // `toolbar_container_view_` layout manager. For those inside a child
  // container (e.g. PinnedToolbarActionsContainer) of `toolbar_container_view_`
  // they should have their own overflow order.
  static std::vector<ui::ElementIdentifier> GetDefaultOverflowOrder();

  // Return the action name from element identifier. Return empty if not found.
  static std::string GetActionNameFromElementIdentifier(
      std::variant<ui::ElementIdentifier, actions::ActionId> identifier);

  // Force the UI element with the identifier to show. Return whether the action
  // is successful.
  virtual bool PopOut(ui::ElementIdentifier identifier);

  // End forcing the UI element with the identifier to show. Return whether the
  // action is successful.
  virtual bool EndPopOut(ui::ElementIdentifier identifier);

  // Returns true if any overflow-able elements are hidden when
  // `toolbar_container_view_` is set to `size`. This excludes the overflow
  // button itself from the calculation, providing a much more accurate idea of
  // whether overflow would happen. Because of this, however, it must fully
  // recalculate the layout which could be expensive; call this method as little
  // as possible.
  bool ShouldShowOverflowButton(gfx::Size size);

  // Return true if any buttons overflow.
  bool InOverflowMode() const;

  OverflowButton* overflow_button() { return overflow_button_; }

  const base::flat_map<ui::ElementIdentifier, std::unique_ptr<PopOutState>>&
  pop_out_state_for_testing() const {
    return pop_out_state_;
  }

  // Create the overflow menu model for hidden buttons.
  std::unique_ptr<ui::SimpleMenuModel> CreateOverflowMenuModel();

  // Generate menu text from the responsive element.
  virtual std::u16string GetMenuText(
      const ResponsiveElementInfo& element_info) const;

  // Get menu icon from the responsive element.
  std::optional<ui::ImageModel> GetMenuIcon(
      const ResponsiveElementInfo& element_info) const;

  // Utility that recursively searches for a view with `id` from `view`.
  static views::View* FindToolbarElementWithId(views::View* view,
                                               ui::ElementIdentifier id);

  // Shows the overflow menu that is anchored to the `overflow_button_`.
  void ShowMenu();

  bool IsMenuRunning() const;

  const views::MenuItemView* root_menu_item() const {
    return root_menu_item_.get();
  }

  const ui::SimpleMenuModel* menu_model_for_testing() const {
    return menu_model_.get();
  }

 private:
  friend class ToolbarControllerUiTest;
  friend class ToolbarControllerUnitTest;

  // Returns currently hidden elements.
  std::vector<const ResponsiveElementInfo*> GetOverflowedElements();

  // Check if element has overflowed. Check the visibility in proposed_layout if
  // provided.
  bool IsOverflowed(
      const ResponsiveElementInfo& element,
      const views::ProposedLayout* proposed_layout = nullptr) const;

  void PopulateMenu(views::MenuItemView* parent);
  void CloseMenu();

  // Adds the status indicator to all the menu items and makes it visible if
  // needed.
  void ShowStatusIndicator();

  // Listens to changes in `action_item` and updates the visibility of the
  // status indicator.
  void ActionItemChanged(actions::ActionItem* action_item);

  // ui::SimpleMenuModel::Delegate:
  void ExecuteCommand(int command_id, int event_flags) override;
  bool IsCommandIdEnabled(int command_id) const override;

  // The toolbar elements managed by this controller.
  // Actions are kept in order by observing the PinnedToolbarActionsModel.
  // To facilitate menu creation elements order should match overflow
  // menu top to bottom.
  std::vector<ResponsiveElementInfo> responsive_elements_;
  // Returns responsive_elements_ but with the Actions in the correct order,
  // as defined by the pinned_actions_delegate_
  std::vector<ResponsiveElementInfo> GetResponsiveElementsWithOrderedActions()
      const;

  std::vector<base::CallbackListSubscription> action_changed_subscription_;

  // The starting flex order assigned to the last overflowed element in
  // `responsive_elements_`.
  const int element_flex_order_start_;

  // Reference to ToolbarView::container_view_. Must outlive `this`.
  const raw_ptr<views::View> toolbar_container_view_;

  // The button with a chevron icon that indicates at least one element in
  // `responsive_elements_` overflows. Owned by `toolbar_container_view_`.
  raw_ptr<OverflowButton> overflow_button_;

  std::unique_ptr<views::MenuRunner> menu_runner_;
  std::unique_ptr<ui::SimpleMenuModel> menu_model_;
  raw_ptr<views::MenuItemView> root_menu_item_ = nullptr;

  const raw_ptr<PinnedActionsDelegate> pinned_actions_delegate_;
  const raw_ptr<PinnedToolbarActionsModel> pinned_actions_model_;

  // A map to save the original and modified FlexSpecification of responsive
  // elements that need to pop out. Set when ToolbarController is initialized.
  base::flat_map<ui::ElementIdentifier, std::unique_ptr<PopOutState>>
      pop_out_state_;
};

#endif  // CHROME_BROWSER_UI_VIEWS_TOOLBAR_TOOLBAR_CONTROLLER_H_