browser_actions_container.h revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2011 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_VIEWS_BROWSER_ACTIONS_CONTAINER_H_
6#define CHROME_BROWSER_UI_VIEWS_BROWSER_ACTIONS_CONTAINER_H_
7#pragma once
8
9#include <set>
10#include <string>
11#include <vector>
12
13#include "base/task.h"
14#include "chrome/browser/extensions/extension_context_menu_model.h"
15#include "chrome/browser/extensions/extension_toolbar_model.h"
16#include "chrome/browser/extensions/image_loading_tracker.h"
17#include "chrome/browser/ui/views/browser_bubble.h"
18#include "chrome/browser/ui/views/extensions/browser_action_overflow_menu_controller.h"
19#include "chrome/browser/ui/views/extensions/extension_popup.h"
20#include "chrome/common/notification_observer.h"
21#include "chrome/common/notification_registrar.h"
22#include "ui/base/animation/animation_delegate.h"
23#include "ui/base/animation/tween.h"
24#include "views/controls/button/menu_button.h"
25#include "views/controls/menu/view_menu_delegate.h"
26#include "views/controls/resize_area.h"
27#include "views/view.h"
28
29class Browser;
30class BrowserActionsContainer;
31class BrowserActionOverflowMenuController;
32class BrowserActionsContainer;
33class Extension;
34class ExtensionAction;
35class ExtensionPopup;
36class PrefService;
37class Profile;
38
39namespace gfx {
40class CanvasSkia;
41}
42
43namespace ui {
44class SlideAnimation;
45}
46
47namespace views {
48class Menu2;
49}
50
51////////////////////////////////////////////////////////////////////////////////
52// BrowserActionButton
53
54// The BrowserActionButton is a specialization of the MenuButton class.
55// It acts on a ExtensionAction, in this case a BrowserAction and handles
56// loading the image for the button asynchronously on the file thread.
57class BrowserActionButton : public views::MenuButton,
58                            public views::ButtonListener,
59                            public ImageLoadingTracker::Observer,
60                            public NotificationObserver {
61 public:
62  BrowserActionButton(const Extension* extension,
63                      BrowserActionsContainer* panel);
64
65  // Call this instead of delete.
66  void Destroy();
67
68  ExtensionAction* browser_action() const { return browser_action_; }
69  const Extension* extension() { return extension_; }
70
71  // Called to update the display to match the browser action's state.
72  void UpdateState();
73
74  // Returns the default icon, if any.
75  const SkBitmap& default_icon() const { return default_icon_; }
76
77  // Overridden from views::View:
78  virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child);
79
80  // Overridden from views::ButtonListener:
81  virtual void ButtonPressed(views::Button* sender, const views::Event& event);
82
83  // Overridden from ImageLoadingTracker.
84  virtual void OnImageLoaded(
85      SkBitmap* image, ExtensionResource resource, int index);
86
87  // Overridden from NotificationObserver:
88  virtual void Observe(NotificationType type,
89                       const NotificationSource& source,
90                       const NotificationDetails& details);
91
92  // MenuButton behavior overrides.  These methods all default to TextButton
93  // behavior unless this button is a popup.  In that case, it uses MenuButton
94  // behavior.  MenuButton has the notion of a child popup being shown where the
95  // button will stay in the pushed state until the "menu" (a popup in this
96  // case) is dismissed.
97  virtual bool Activate();
98  virtual bool OnMousePressed(const views::MouseEvent& e);
99  virtual void OnMouseReleased(const views::MouseEvent& e, bool canceled);
100  virtual bool OnKeyReleased(const views::KeyEvent& e);
101  virtual void OnMouseExited(const views::MouseEvent& event);
102  virtual void ShowContextMenu(const gfx::Point& p, bool is_mouse_gesture);
103
104  // Does this button's action have a popup?
105  virtual bool IsPopup();
106  virtual GURL GetPopupUrl();
107
108  // Notifications when to set button state to pushed/not pushed (for when the
109  // popup/context menu is hidden or shown by the container).
110  void SetButtonPushed();
111  void SetButtonNotPushed();
112
113 private:
114  virtual ~BrowserActionButton();
115
116  // The browser action this view represents. The ExtensionAction is not owned
117  // by this class.
118  ExtensionAction* browser_action_;
119
120  // The extension associated with the browser action we're displaying.
121  const Extension* extension_;
122
123  // The object that is waiting for the image loading to complete
124  // asynchronously.
125  ImageLoadingTracker tracker_;
126
127  // Whether we are currently showing/just finished showing a context menu.
128  bool showing_context_menu_;
129
130  // The default icon for our browser action. This might be non-empty if the
131  // browser action had a value for default_icon in the manifest.
132  SkBitmap default_icon_;
133
134  // The browser action shelf.
135  BrowserActionsContainer* panel_;
136
137  scoped_refptr<ExtensionContextMenuModel> context_menu_contents_;
138  scoped_ptr<views::Menu2> context_menu_menu_;
139
140  NotificationRegistrar registrar_;
141
142  friend class DeleteTask<BrowserActionButton>;
143
144  DISALLOW_COPY_AND_ASSIGN(BrowserActionButton);
145};
146
147
148////////////////////////////////////////////////////////////////////////////////
149// BrowserActionView
150// A single section in the browser action container. This contains the actual
151// BrowserActionButton, as well as the logic to paint the badge.
152
153class BrowserActionView : public views::View {
154 public:
155  BrowserActionView(const Extension* extension, BrowserActionsContainer* panel);
156  virtual ~BrowserActionView();
157
158  BrowserActionButton* button() { return button_; }
159
160  // Allocates a canvas object on the heap and draws into it the icon for the
161  // view as well as the badge (if any). Caller is responsible for deleting the
162  // returned object.
163  gfx::Canvas* GetIconWithBadge();
164
165  // Accessibility accessors, overridden from View.
166  virtual AccessibilityTypes::Role GetAccessibleRole();
167
168 private:
169  virtual void Layout();
170
171  // Override PaintChildren so that we can paint the badge on top of children.
172  virtual void PaintChildren(gfx::Canvas* canvas);
173
174  // The container for this view.
175  BrowserActionsContainer* panel_;
176
177  // The button this view contains.
178  BrowserActionButton* button_;
179
180  DISALLOW_COPY_AND_ASSIGN(BrowserActionView);
181};
182
183////////////////////////////////////////////////////////////////////////////////
184//
185// The BrowserActionsContainer is a container view, responsible for drawing the
186// browser action icons (extensions that add icons to the toolbar).
187//
188// The container is placed flush against the omnibox and wrench menu, and its
189// layout looks like:
190//   rI_I_IcCs
191// Where the letters are as follows:
192//   r: An invisible resize area.  This is ToolbarView::kStandardSpacing pixels
193//      wide and directly adjacent to the omnibox.
194//   I: An icon.  This is as wide as the IDR_BROWSER_ACTION image.
195//   _: kItemSpacing pixels of empty space.
196//   c: kChevronSpacing pixels of empty space.  Only present if C is present.
197//   C: An optional chevron, visible for overflow.  As wide as the
198//      IDR_BROWSER_ACTIONS_OVERFLOW image.
199//   s: ToolbarView::kStandardSpacing pixels of empty space (before the wrench
200//      menu).
201// The reason the container contains the trailing space "s", rather than having
202// it be handled by the parent view, is so that when the chevron is invisible
203// and the user starts dragging an icon around, we have the space to draw the
204// ultimate drop indicator.  (Otherwise, we'd be trying to draw it into the
205// padding beyond our right edge, and it wouldn't appear.)
206//
207// The BrowserActionsContainer follows a few rules, in terms of user experience:
208//
209// 1) The container can never grow beyond the space needed to show all icons
210// (hereby referred to as the max width).
211// 2) The container can never shrink below the space needed to show just the
212// initial padding and the chevron (ignoring the case where there are no icons
213// to show, in which case the container won't be visible anyway).
214// 3) The container snaps into place (to the pixel count that fits the visible
215// icons) to make sure there is no wasted space at the edges of the container.
216// 4) If the user adds or removes icons (read: installs/uninstalls browser
217// actions) we grow and shrink the container as needed - but ONLY if the
218// container was at max width to begin with.
219// 5) If the container is NOT at max width (has an overflow menu), we respect
220// that size when adding and removing icons and DON'T grow/shrink the container.
221// This means that new icons (which always appear at the far right) will show up
222// in the overflow menu. The install bubble for extensions points to the chevron
223// menu in this case.
224//
225// Resizing the BrowserActionsContainer:
226//
227// The ResizeArea view sends OnResize messages to the BrowserActionsContainer
228// class as the user drags it. This modifies the value for |resize_amount_|.
229// That indicates to the container that a resize is in progress and is used to
230// calculate the size in GetPreferredSize(), though that function never exceeds
231// the defined minimum and maximum size of the container.
232//
233// When the user releases the mouse (ends the resize), we calculate a target
234// size for the container (animation_target_size_), clamp that value to the
235// containers min and max and then animate from the *current* position (that the
236// user has dragged the view to) to the target size.
237//
238// Animating the BrowserActionsContainer:
239//
240// Animations are used when snapping the container to a value that fits all
241// visible icons. This can be triggered when the user finishes resizing the
242// container or when Browser Actions are added/removed.
243//
244// We always animate from the current width (container_width_) to the target
245// size (animation_target_size_), using |resize_amount| to keep track of the
246// animation progress.
247//
248// NOTE: When adding Browser Actions to a maximum width container (no overflow)
249// we make sure to suppress the chevron menu if it wasn't visible. This is
250// because we won't have enough space to show the new Browser Action until the
251// animation ends and we don't want the chevron to flash into view while we are
252// growing the container.
253//
254////////////////////////////////////////////////////////////////////////////////
255class BrowserActionsContainer
256    : public views::View,
257      public views::ViewMenuDelegate,
258      public views::DragController,
259      public views::ResizeArea::ResizeAreaDelegate,
260      public ui::AnimationDelegate,
261      public ExtensionToolbarModel::Observer,
262      public BrowserActionOverflowMenuController::Observer,
263      public ExtensionContextMenuModel::PopupDelegate,
264      public ExtensionPopup::Observer {
265 public:
266  BrowserActionsContainer(Browser* browser, views::View* owner_view);
267  virtual ~BrowserActionsContainer();
268
269  static void RegisterUserPrefs(PrefService* prefs);
270
271  void Init();
272
273  // Get the number of browser actions being displayed.
274  int num_browser_actions() const { return browser_action_views_.size(); }
275
276  // Whether we are performing resize animation on the container.
277  bool animating() const { return animation_target_size_ > 0; }
278
279  // Returns the chevron, if any.
280  const views::View* chevron() const { return chevron_; }
281
282  // Returns the profile this container is associated with.
283  Profile* profile() const { return profile_; }
284
285  // Returns the browser this container is associated with.
286  Browser* browser() const { return browser_; }
287
288  // Returns the current tab's ID, or -1 if there is no current tab.
289  int GetCurrentTabId() const;
290
291  // Get a particular browser action view.
292  BrowserActionView* GetBrowserActionViewAt(int index) {
293    return browser_action_views_[index];
294  }
295
296  // Retrieve the BrowserActionView for |extension|.
297  BrowserActionView* GetBrowserActionView(ExtensionAction* action);
298
299  // Update the views to reflect the state of the browser action icons.
300  void RefreshBrowserActionViews();
301
302  // Sets up the browser action view vector.
303  void CreateBrowserActionViews();
304
305  // Delete all browser action views.
306  void DeleteBrowserActionViews();
307
308  // Called when a browser action becomes visible/hidden.
309  void OnBrowserActionVisibilityChanged();
310
311  // Returns how many browser actions are visible.
312  size_t VisibleBrowserActions() const;
313
314  // Called when the user clicks on the browser action icon.
315  void OnBrowserActionExecuted(BrowserActionButton* button,
316                               bool inspect_with_devtools);
317
318  // Overridden from views::View:
319  virtual gfx::Size GetPreferredSize();
320  virtual void Layout();
321  virtual void Paint(gfx::Canvas* canvas);
322  virtual void ViewHierarchyChanged(bool is_add,
323                                    views::View* parent,
324                                    views::View* child);
325  virtual bool GetDropFormats(
326      int* formats, std::set<ui::OSExchangeData::CustomFormat>* custom_formats);
327  virtual bool AreDropTypesRequired();
328  virtual bool CanDrop(const ui::OSExchangeData& data);
329  virtual void OnDragEntered(const views::DropTargetEvent& event);
330  virtual int OnDragUpdated(const views::DropTargetEvent& event);
331  virtual void OnDragExited();
332  virtual int OnPerformDrop(const views::DropTargetEvent& event);
333  virtual void OnThemeChanged();
334  virtual AccessibilityTypes::Role GetAccessibleRole();
335
336  // Overridden from views::ViewMenuDelegate:
337  virtual void RunMenu(View* source, const gfx::Point& pt);
338
339  // Overridden from views::DragController:
340  virtual void WriteDragData(View* sender,
341                             const gfx::Point& press_pt,
342                             ui::OSExchangeData* data);
343  virtual int GetDragOperations(View* sender, const gfx::Point& p);
344  virtual bool CanStartDrag(View* sender,
345                            const gfx::Point& press_pt,
346                            const gfx::Point& p);
347
348  // Overridden from ResizeArea::ResizeAreaDelegate:
349  virtual void OnResize(int resize_amount, bool done_resizing);
350
351  // Overridden from ui::AnimationDelegate:
352  virtual void AnimationProgressed(const ui::Animation* animation);
353  virtual void AnimationEnded(const ui::Animation* animation);
354
355  // Overridden from BrowserActionOverflowMenuController::Observer:
356  virtual void NotifyMenuDeleted(
357      BrowserActionOverflowMenuController* controller);
358
359  // Overridden from ExtensionContextMenuModel::PopupDelegate
360  virtual void InspectPopup(ExtensionAction* action);
361
362  // Overriden from ExtensionPopup::Delegate
363  virtual void ExtensionPopupIsClosing(ExtensionPopup* popup);
364
365  // Moves a browser action with |id| to |new_index|.
366  void MoveBrowserAction(const std::string& extension_id, size_t new_index);
367
368  // Hide the current popup.
369  void HidePopup();
370
371  // Simulate a click on a browser action button.  This should only be
372  // used by unit tests.
373  void TestExecuteBrowserAction(int index);
374
375  // Retrieve the current popup.  This should only be used by unit tests.
376  ExtensionPopup* TestGetPopup() { return popup_; }
377
378  // Set how many icons the container should show. This should only be used by
379  // unit tests.
380  void TestSetIconVisibilityCount(size_t icons);
381
382  // During testing we can disable animations by setting this flag to true,
383  // so that the bar resizes instantly, instead of having to poll it while it
384  // animates to open/closed status.
385  static bool disable_animations_during_testing_;
386
387 private:
388  friend class BrowserActionView;  // So it can access IconHeight().
389  friend class ShowFolderMenuTask;
390
391  typedef std::vector<BrowserActionView*> BrowserActionViews;
392
393  // Returns the width of an icon, optionally with its padding.
394  static int IconWidth(bool include_padding);
395
396  // Returns the height of an icon.
397  static int IconHeight();
398
399  // ExtensionToolbarModel::Observer implementation.
400  virtual void BrowserActionAdded(const Extension* extension, int index);
401  virtual void BrowserActionRemoved(const Extension* extension);
402  virtual void BrowserActionMoved(const Extension* extension, int index);
403  virtual void ModelLoaded();
404
405  void LoadImages();
406
407  // Sets the initial container width.
408  void SetContainerWidth();
409
410  // Closes the overflow menu if open.
411  void CloseOverflowMenu();
412
413  // Cancels the timer for showing the drop down menu.
414  void StopShowFolderDropMenuTimer();
415
416  // Show the drop down folder after a slight delay.
417  void StartShowFolderDropMenuTimer();
418
419  // Show the overflow menu.
420  void ShowDropFolder();
421
422  // Sets the drop indicator position (and schedules paint if the position has
423  // changed).
424  void SetDropIndicator(int x_pos);
425
426  // Given a number of |icons| and whether to |display_chevron|, returns the
427  // amount of pixels needed to draw the entire container.  For convenience,
428  // callers can set |icons| to -1 to mean "all icons".
429  int IconCountToWidth(int icons, bool display_chevron) const;
430
431  // Given a pixel width, returns the number of icons that fit.  (This
432  // automatically determines whether a chevron will be needed and includes it
433  // in the calculation.)
434  size_t WidthToIconCount(int pixels) const;
435
436  // Returns the absolute minimum size you can shrink the container down to and
437  // still show it.  This assumes a visible chevron because the only way we
438  // would not have a chevron when shrinking down this far is if there were no
439  // icons, in which case the container wouldn't be shown at all.
440  int ContainerMinSize() const;
441
442  // Animate to the target size (unless testing, in which case we go straight to
443  // the target size).  This also saves the target number of visible icons in
444  // the pref if we're not off the record.
445  void SaveDesiredSizeAndAnimate(ui::Tween::Type type,
446                                 size_t num_visible_icons);
447
448  // Returns true if this extension should be shown in this toolbar. This can
449  // return false if we are in an incognito window and the extension is disabled
450  // for incognito.
451  bool ShouldDisplayBrowserAction(const Extension* extension);
452
453  // The vector of browser actions (icons/image buttons for each action). Note
454  // that not every BrowserAction in the ToolbarModel will necessarily be in
455  // this collection. Some extensions may be disabled in incognito windows.
456  BrowserActionViews browser_action_views_;
457
458  Profile* profile_;
459
460  // The Browser object the container is associated with.
461  Browser* browser_;
462
463  // The view that owns us.
464  views::View* owner_view_;
465
466  // The current popup and the button it came from.  NULL if no popup.
467  ExtensionPopup* popup_;
468
469  // The button that triggered the current popup (just a reference to a button
470  // from browser_action_views_).
471  BrowserActionButton* popup_button_;
472
473  // The model that tracks the order of the toolbar icons.
474  ExtensionToolbarModel* model_;
475
476  // The current width of the container.
477  int container_width_;
478
479  // The resize area for the container.
480  views::ResizeArea* resize_area_;
481
482  // The chevron for accessing the overflow items.
483  views::MenuButton* chevron_;
484
485  // The menu to show for the overflow button (chevron). This class manages its
486  // own lifetime so that it can stay alive during drag and drop operations.
487  BrowserActionOverflowMenuController* overflow_menu_;
488
489  // The animation that happens when the container snaps to place.
490  scoped_ptr<ui::SlideAnimation> resize_animation_;
491
492  // Don't show the chevron while animating.
493  bool suppress_chevron_;
494
495  // This is used while the user is resizing (and when the animations are in
496  // progress) to know how wide the delta is between the current state and what
497  // we should draw.
498  int resize_amount_;
499
500  // Keeps track of the absolute pixel width the container should have when we
501  // are done animating.
502  int animation_target_size_;
503
504  // The x position for where to draw the drop indicator. -1 if no indicator.
505  int drop_indicator_position_;
506
507  ScopedRunnableMethodFactory<BrowserActionsContainer> task_factory_;
508
509  // Handles delayed showing of the overflow menu when hovering.
510  ScopedRunnableMethodFactory<BrowserActionsContainer> show_menu_task_factory_;
511
512  DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainer);
513};
514
515#endif  // CHROME_BROWSER_UI_VIEWS_BROWSER_ACTIONS_CONTAINER_H_
516