1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_ACTIONS_BAR_H_
6 #define CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_ACTIONS_BAR_H_
7 
8 #include <stddef.h>
9 
10 #include <memory>
11 #include <vector>
12 
13 #include "base/callback.h"
14 #include "base/macros.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/observer_list.h"
17 #include "base/optional.h"
18 #include "base/scoped_observer.h"
19 #include "chrome/browser/ui/extensions/extensions_container.h"
20 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
21 #include "chrome/browser/ui/toolbar/toolbar_actions_bar_bubble_delegate.h"
22 #include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
23 #include "ui/gfx/animation/tween.h"
24 #include "ui/gfx/geometry/size.h"
25 
26 namespace user_prefs {
27 class PrefRegistrySyncable;
28 }
29 
30 class BrowserWindow;
31 class ToolbarActionsBarDelegate;
32 class ToolbarActionsBarObserver;
33 class ToolbarActionViewController;
34 
35 // A platform-independent version of the container for toolbar actions,
36 // including extension actions and component actions.
37 //
38 // This is a per-window instance, unlike the ToolbarActionsModel, which is
39 // per-profile. In most cases, ordering and visible count will be identical
40 // between the base model and the window; however, there are exceptions in the
41 // case of very small windows (which may be too narrow to display all the
42 // icons), or windows in which an action is "popped out", resulting in a
43 // re-ordering.
44 //
45 // This can come in two flavors, main and "overflow". The main bar is visible
46 // next to the omnibox, and the overflow bar is visible inside the chrome
47 // app menu. The main bar can have only a single row of icons with flexible
48 // width, whereas the overflow bar has multiple rows of icons with a fixed
49 // width (the width of the menu).
50 class ToolbarActionsBar : public ExtensionsContainer,
51                           public ToolbarActionsModel::Observer,
52                           public TabStripModelObserver {
53  public:
54   using ToolbarActions =
55       std::vector<std::unique_ptr<ToolbarActionViewController>>;
56 
57   // A struct to contain the platform settings.
58   struct PlatformSettings {
59     PlatformSettings();
60 
61     // The spacing between each of the icons, between the start of the
62     // container and the first item, and between the last item and end of
63     // the container.
64     int item_spacing;
65     // The number of icons per row in the overflow menu.
66     int icons_per_overflow_menu_row;
67   };
68 
69   // The type of drag that occurred in a drag-and-drop operation.
70   enum DragType {
71     // The icon was dragged to the same container it started in.
72     DRAG_TO_SAME,
73     // The icon was dragged from the main container to the overflow.
74     DRAG_TO_OVERFLOW,
75     // The icon was dragged from the overflow container to the main.
76     DRAG_TO_MAIN,
77   };
78 
79   enum HighlightType {
80     HIGHLIGHT_NONE,
81     HIGHLIGHT_WARNING,
82   };
83 
84   ToolbarActionsBar(ToolbarActionsBarDelegate* delegate,
85                     Browser* browser,
86                     ToolbarActionsBar* main_bar);
87   ~ToolbarActionsBar() override;
88 
89   // Gets the ToolbarActionsBar from the given BrowserWindow. This method is
90   // essentially deprecated. Use BrowserWindow::GetExtensionsContainer instead.
91   static ToolbarActionsBar* FromBrowserWindow(BrowserWindow* window);
92 
93   // Registers profile preferences.
94   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
95 
96   // Returns the size of the area where the action icon resides.
97   static gfx::Size GetIconAreaSize();
98 
99   // Returns the size of ToolbarActionView.
100   gfx::Size GetViewSize() const;
101 
102   // Returns the default/full size for the toolbar; this does *not* reflect any
103   // animations that may be running.
104   gfx::Size GetFullSize() const;
105 
106   // Returns the [minimum|maximum] possible width for the toolbar.
107   virtual int GetMinimumWidth() const;
108   int GetMaximumWidth() const;
109 
110   // Returns the width for the given number of icons.
111   int IconCountToWidth(size_t icons) const;
112 
113   // Returns the number of icons that can fit within the given width.
114   size_t WidthToIconCount(int width) const;
115 
116   // Returns the number of icons that should be displayed if space allows. Can
117   // be overridden by children to impose a smaller limit on the number of icons.
118   virtual size_t GetIconCount() const;
119 
120   // Returns the starting index (inclusive) for displayable icons.
121   size_t GetStartIndexInBounds() const;
122 
123   // Returns the ending index (exclusive) for displayable icons.
124   size_t GetEndIndexInBounds() const;
125 
126   // Returns true if an overflow container is necessary to display any other
127   // icons for this particular window. This is different than
128   // ToolbarActionsModel::all_icons_visible() because the ToolbarActionsBar
129   // is limited to a single window, whereas the model is the underlying model
130   // of *all* windows, independent of size. As such, the model is identical
131   // between a very wide window and a very narrow window, and the user's stored
132   // preference may be to have all icons visible. But if the very narrow window
133   // doesn't have the width to display all those actions, some will need to be
134   // implicitly pushed to the overflow, even though the user's global preference
135   // has not changed.
136   bool NeedsOverflow() const;
137 
138   // Returns the frame (bounds) that the specified index should have, taking
139   // into account if this is the main or overflow bar. If this is the overflow
140   // bar and the index should not be displayed (i.e., it is shown on the main
141   // bar), returns an empty rect.
142   gfx::Rect GetFrameForIndex(size_t index) const;
143 
144   // Returns the actions in the proper order; this may differ from the
145   // underlying order in the case of actions being popped out to show a popup.
146   std::vector<ToolbarActionViewController*> GetActions() const;
147 
148   // Creates the toolbar actions.
149   void CreateActions();
150 
151   // Deletes all toolbar actions.
152   void DeleteActions();
153 
154   // Updates all the toolbar actions.
155   void Update();
156 
157   // Sets the width for the overflow menu rows.
158   void SetOverflowRowWidth(int width);
159 
160   // Notifies the ToolbarActionsBar that a user completed a resize gesture, and
161   // the new width is |width|.
162   void OnResizeComplete(int width);
163 
164   // Notifies the ToolbarActionsBar that the user has started dragging the
165   // action at index |index_of_dragged_item|.
166   void OnDragStarted(size_t index_of_dragged_item);
167 
168   // Notifies the ToolbarActionsBar that a drag-and-drop sequence ended. This
169   // may not coincide with OnDragDrop(), since the view may be dropped somewhere
170   // else.
171   void OnDragEnded();
172 
173   // Notifies the ToolbarActionsBar that a user completed a drag and drop event,
174   // and dragged the view from |dragged_index| to |dropped_index|.
175   // |drag_type| indicates whether or not the icon was dragged between the
176   // overflow and main containers.
177   // The main container should handle all drag/drop notifications.
178   void OnDragDrop(int dragged_index,
179                   int dropped_index,
180                   DragType drag_type);
181 
182   // The index of the action currently being dragged, or |base::nullopt| if
183   // no drag is in progress. Should only be called on the main bar.
184   const base::Optional<size_t> IndexOfDraggedItem() const;
185 
186   // Notifies the ToolbarActionsBar that the delegate finished animating.
187   void OnAnimationEnded();
188 
189   // Called when the active bubble is closed.
190   void OnBubbleClosed();
191 
192   // Add or remove an observer.
193   void AddObserver(ToolbarActionsBarObserver* observer);
194   void RemoveObserver(ToolbarActionsBarObserver* observer);
195 
196   // Returns the underlying toolbar actions, but does not order them. Primarily
197   // for use in testing.
toolbar_actions_unordered()198   const ToolbarActions& toolbar_actions_unordered() const {
199     return toolbar_actions_;
200   }
enabled()201   bool enabled() const { return model_ != nullptr; }
suppress_layout()202   bool suppress_layout() const { return suppress_layout_; }
suppress_animation()203   bool suppress_animation() const {
204     return suppress_animation_ || disable_animations_for_testing_;
205   }
is_highlighting()206   bool is_highlighting() const { return model_ && model_->is_highlighting(); }
highlight_type()207   ToolbarActionsModel::HighlightType highlight_type() const {
208     return model_ ? model_->highlight_type()
209                   : ToolbarActionsModel::HIGHLIGHT_NONE;
210   }
platform_settings()211   const PlatformSettings& platform_settings() const {
212     return platform_settings_;
213   }
popup_owner()214   ToolbarActionViewController* popup_owner() { return popup_owner_; }
in_overflow_mode()215   bool in_overflow_mode() const { return main_bar_ != nullptr; }
is_showing_bubble()216   bool is_showing_bubble() const { return is_showing_bubble_; }
217 
is_drag_in_progress()218   bool is_drag_in_progress() const {
219     return index_of_dragged_item_ != base::nullopt;
220   }
221 
delegate_for_test()222   ToolbarActionsBarDelegate* delegate_for_test() { return delegate_; }
223 
224   // During testing we can disable animations by setting this flag to true,
225   // so that the bar resizes instantly, instead of having to poll it while it
226   // animates to open/closed status.
227   static bool disable_animations_for_testing_;
228   static void set_extension_bubble_appearance_wait_time_for_testing(
229       int time_in_seconds);
230 
231   // ExtensionsContainer:
232   ToolbarActionViewController* GetActionForId(
233       const std::string& action_id) override;
234   ToolbarActionViewController* GetPoppedOutAction() const override;
235   bool IsActionVisibleOnToolbar(
236       const ToolbarActionViewController* action) const override;
237   extensions::ExtensionContextMenuModel::ButtonVisibility GetActionVisibility(
238       const ToolbarActionViewController* action) const override;
239   void UndoPopOut() override;
240   void SetPopupOwner(ToolbarActionViewController* popup_owner) override;
241   void HideActivePopup() override;
242   bool CloseOverflowMenuIfOpen() override;
243   void PopOutAction(ToolbarActionViewController* action,
244                     bool is_sticky,
245                     const base::Closure& closure) override;
246   bool ShowToolbarActionPopupForAPICall(const std::string& id) override;
247   void ShowToolbarActionBubble(
248       std::unique_ptr<ToolbarActionsBarBubbleDelegate> bubble) override;
249   void ShowToolbarActionBubbleAsync(
250       std::unique_ptr<ToolbarActionsBarBubbleDelegate> bubble) override;
251 
252  private:
253   // Returns the insets by which the icon area bounds (See GetIconAreaRect())
254   // are insetted. This defines the amount of paddings around the icon area.
255   virtual gfx::Insets GetIconAreaInsets() const;
256 
257   // Returns the number of icons that can fit within the given width.
258   size_t WidthToIconCountUnclamped(int width) const;
259 
260   // ToolbarActionsModel::Observer:
261   void OnToolbarActionAdded(const ToolbarActionsModel::ActionId& action_id,
262                             int index) override;
263   void OnToolbarActionRemoved(
264       const ToolbarActionsModel::ActionId& action_id) override;
265   void OnToolbarActionMoved(const ToolbarActionsModel::ActionId& action_id,
266                             int index) override;
267   void OnToolbarActionLoadFailed() override;
268   void OnToolbarActionUpdated(
269       const ToolbarActionsModel::ActionId& action_id) override;
270   void OnToolbarVisibleCountChanged() override;
271   void OnToolbarHighlightModeChanged(bool is_highlighting) override;
272   void OnToolbarModelInitialized() override;
273   void OnToolbarPinnedActionsChanged() override;
274 
275   // TabStripModelObserver:
276   void OnTabStripModelChanged(
277       TabStripModel* tab_strip_model,
278       const TabStripModelChange& change,
279       const TabStripSelectionChange& selection) override;
280 
281   // Resizes the delegate (if necessary) to the preferred size using the given
282   // |tween_type|.
283   void ResizeDelegate(gfx::Tween::Type tween_type);
284 
285   // Returns the current web contents.
286   content::WebContents* GetCurrentWebContents();
287 
288   // Reorders the toolbar actions to reflect the model and, optionally, to
289   // "pop out" any overflowed actions that want to run (depending on the
290   // value of |pop_out_actions_to_run|.
291   void ReorderActions();
292 
293   // Shows an extension message bubble, if any should be shown.
294   void MaybeShowExtensionBubble();
295 
296   // Returns the main bar, which is |main_bar_| if this is in overflow mode, and
297   // |this| otherwise.
298   ToolbarActionsBar* GetMainBar();
299 
300   // The delegate for this object (in a real build, this is the view).
301   ToolbarActionsBarDelegate* delegate_;
302 
303   // The associated browser.
304   Browser* const browser_;
305 
306   // The observed toolbar model.
307   ToolbarActionsModel* model_;
308 
309   // The controller for the main toolbar actions bar. This will be null if this
310   // is the main bar.
311   ToolbarActionsBar* main_bar_;
312 
313   // Platform-specific settings for dimensions.
314   PlatformSettings platform_settings_;
315 
316   // The toolbar actions.
317   ToolbarActions toolbar_actions_;
318 
319   // The action that triggered the current popup (just a reference to an action
320   // from toolbar_actions_).
321   ToolbarActionViewController* popup_owner_;
322 
323   ScopedObserver<ToolbarActionsModel, ToolbarActionsModel::Observer>
324       model_observer_;
325 
326   // True if we should suppress layout, such as when we are creating or
327   // adjusting a lot of actions at once.
328   bool suppress_layout_;
329 
330   // True if we should suppress animation; we do this when first creating the
331   // toolbar, and also when switching tabs changes the state of the icons.
332   bool suppress_animation_;
333 
334   // If this is true, actions that want to run (e.g., an extension's page
335   // action) will pop out of overflow to draw more attention.
336   // See also TabOrderHelper in the .cc file.
337   static bool pop_out_actions_to_run_;
338 
339   // True if we should check to see if there is an extension bubble that should
340   // be displayed, and, if there is, started the process for showing that
341   // bubble. This is only ever true for the main bar.
342   bool should_check_extension_bubble_;
343 
344   // The action, if any, which is currently "popped out" of the overflow in
345   // order to show a popup.
346   ToolbarActionViewController* popped_out_action_;
347 
348   // True if the popped out action is "sticky", meaning it will stay popped
349   // out even if another menu is opened.
350   bool is_popped_out_sticky_;
351 
352   // The task to alert the |popped_out_action_| that animation has finished, and
353   // it is fully popped out.
354   base::Closure popped_out_closure_;
355 
356   // The controller for the toolbar action bubble to show once animation
357   // finishes, if any.
358   std::unique_ptr<ToolbarActionsBarBubbleDelegate> pending_bubble_controller_;
359 
360   // True if a bubble is currently being shown.
361   bool is_showing_bubble_;
362 
363   // The index of the action currently being dragged, or |base::nullopt| if
364   // no drag is in progress.
365   base::Optional<size_t> index_of_dragged_item_;
366 
367   base::ObserverList<ToolbarActionsBarObserver>::Unchecked observers_;
368 
369   base::WeakPtrFactory<ToolbarActionsBar> weak_ptr_factory_{this};
370 
371   DISALLOW_COPY_AND_ASSIGN(ToolbarActionsBar);
372 };
373 
374 #endif  // CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_ACTIONS_BAR_H_
375