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