1 // Copyright 2013 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_TOOLBAR_BROWSER_ACTIONS_CONTAINER_H_ 6 #define CHROME_BROWSER_UI_VIEWS_TOOLBAR_BROWSER_ACTIONS_CONTAINER_H_ 7 8 #include "base/observer_list.h" 9 #include "chrome/browser/extensions/extension_keybinding_registry.h" 10 #include "chrome/browser/extensions/extension_toolbar_model.h" 11 #include "chrome/browser/ui/views/extensions/browser_action_overflow_menu_controller.h" 12 #include "chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h" 13 #include "chrome/browser/ui/views/toolbar/browser_action_view.h" 14 #include "ui/gfx/animation/animation_delegate.h" 15 #include "ui/gfx/animation/tween.h" 16 #include "ui/views/controls/button/menu_button_listener.h" 17 #include "ui/views/controls/resize_area_delegate.h" 18 #include "ui/views/drag_controller.h" 19 #include "ui/views/view.h" 20 21 class BrowserActionsContainerObserver; 22 class ExtensionPopup; 23 24 namespace extensions { 25 class ActiveTabPermissionGranter; 26 class Command; 27 class Extension; 28 } 29 30 namespace gfx { 31 class SlideAnimation; 32 } 33 34 namespace views { 35 class ResizeArea; 36 } 37 38 // The BrowserActionsContainer is a container view, responsible for drawing the 39 // browser action icons (extensions that add icons to the toolbar). It comes in 40 // two flavors, a main container (when residing on the toolbar) and an overflow 41 // container (that resides in the main application menu, aka the Chrome menu). 42 // 43 // When in 'main' mode, the container supports the full functionality of a 44 // BrowserActionContainer, but in 'overflow' mode the container is effectively 45 // just an overflow for the 'main' toolbar (shows only the icons that the main 46 // toolbar does not) and as such does not have an overflow itself. The overflow 47 // container also does not support resizing. Since the main container only shows 48 // icons in the Chrome toolbar, it is limited to a single row of icons. The 49 // overflow container, however, is allowed to display icons in multiple rows. 50 // 51 // The main container is placed flush against the omnibox and hot dog menu, 52 // whereas the overflow container is placed within the hot dog menu. The 53 // layout is similar to this: 54 // rI_I_IcCs 55 // Where the letters are as follows: 56 // r: An invisible resize area. This is ToolbarView::kStandardSpacing pixels 57 // wide and directly adjacent to the omnibox. Only shown for the main 58 // container. 59 // I: An icon. This is as wide as the IDR_BROWSER_ACTION image. 60 // _: kItemSpacing pixels of empty space. 61 // c: kChevronSpacing pixels of empty space. Only present if C is present. 62 // C: An optional chevron, as wide as the IDR_BROWSER_ACTIONS_OVERFLOW image, 63 // and visible only when both of the following statements are true: 64 // - The container is set to a width smaller than needed to show all icons. 65 // - There is no other container in 'overflow' mode to handle the 66 // non-visible icons for this container. 67 // s: ToolbarView::kStandardSpacing pixels of empty space (before the wrench 68 // menu). 69 // The reason the container contains the trailing space "s", rather than having 70 // it be handled by the parent view, is so that when the chevron is invisible 71 // and the user starts dragging an icon around, we have the space to draw the 72 // ultimate drop indicator. (Otherwise, we'd be trying to draw it into the 73 // padding beyond our right edge, and it wouldn't appear.) 74 // 75 // The BrowserActionsContainer in 'main' mode follows a few rules, in terms of 76 // user experience: 77 // 78 // 1) The container can never grow beyond the space needed to show all icons 79 // (hereby referred to as the max width). 80 // 2) The container can never shrink below the space needed to show just the 81 // initial padding and the chevron (ignoring the case where there are no icons 82 // to show, in which case the container won't be visible anyway). 83 // 3) The container snaps into place (to the pixel count that fits the visible 84 // icons) to make sure there is no wasted space at the edges of the container. 85 // 4) If the user adds or removes icons (read: installs/uninstalls browser 86 // actions) we grow and shrink the container as needed - but ONLY if the 87 // container was at max width to begin with. 88 // 5) If the container is NOT at max width (has an overflow menu), we respect 89 // that size when adding and removing icons and DON'T grow/shrink the container. 90 // This means that new icons (which always appear at the far right) will show up 91 // in the overflow. The install bubble for extensions points to the chevron 92 // menu in this case. 93 // 94 // Resizing the BrowserActionsContainer: 95 // 96 // The ResizeArea view sends OnResize messages to the BrowserActionsContainer 97 // class as the user drags it. This modifies the value for |resize_amount_|. 98 // That indicates to the container that a resize is in progress and is used to 99 // calculate the size in GetPreferredSize(), though that function never exceeds 100 // the defined minimum and maximum size of the container. 101 // 102 // When the user releases the mouse (ends the resize), we calculate a target 103 // size for the container (animation_target_size_), clamp that value to the 104 // containers min and max and then animate from the *current* position (that the 105 // user has dragged the view to) to the target size. 106 // 107 // Animating the BrowserActionsContainer: 108 // 109 // Animations are used when snapping the container to a value that fits all 110 // visible icons. This can be triggered when the user finishes resizing the 111 // container or when Browser Actions are added/removed. 112 // 113 // We always animate from the current width (container_width_) to the target 114 // size (animation_target_size_), using |resize_amount| to keep track of the 115 // animation progress. 116 // 117 // NOTE: When adding Browser Actions to a maximum width container (no overflow) 118 // we make sure to suppress the chevron menu if it wasn't visible. This is 119 // because we won't have enough space to show the new Browser Action until the 120 // animation ends and we don't want the chevron to flash into view while we are 121 // growing the container. 122 // 123 //////////////////////////////////////////////////////////////////////////////// 124 class BrowserActionsContainer 125 : public views::View, 126 public views::MenuButtonListener, 127 public views::ResizeAreaDelegate, 128 public gfx::AnimationDelegate, 129 public extensions::ExtensionToolbarModel::Observer, 130 public BrowserActionOverflowMenuController::Observer, 131 public BrowserActionView::Delegate, 132 public extensions::ExtensionKeybindingRegistry::Delegate { 133 public: 134 // Horizontal spacing between most items in the container, as well as after 135 // the last item or chevron (if visible). 136 static const int kItemSpacing; 137 138 // Constructs a BrowserActionContainer for a particular |browser| object, and 139 // specifies which view is the |owner_view|. For documentation of 140 // |main_container|, see class comments. 141 BrowserActionsContainer(Browser* browser, 142 views::View* owner_view, 143 BrowserActionsContainer* main_container); 144 virtual ~BrowserActionsContainer(); 145 146 void Init(); 147 148 // Get the number of browser actions being displayed. 149 size_t num_browser_actions() const { return browser_action_views_.size(); } 150 151 // Whether we are performing resize animation on the container. 152 bool animating() const { return animation_target_size_ > 0; } 153 154 // Returns the chevron, if any. 155 views::View* chevron() { return chevron_; } 156 const views::View* chevron() const { return chevron_; } 157 158 // Returns the profile this container is associated with. 159 Profile* profile() const { return profile_; } 160 161 // The class that registers for keyboard shortcuts for extension commands. 162 extensions::ExtensionKeybindingRegistry* extension_keybinding_registry() { 163 return extension_keybinding_registry_.get(); 164 } 165 166 // Get a particular browser action view. 167 BrowserActionView* GetBrowserActionViewAt(int index) { 168 return browser_action_views_[index]; 169 } 170 171 // Returns the BrowserActionView* associated with the given |extension|, or 172 // NULL if none exists. 173 BrowserActionView* GetViewForExtension( 174 const extensions::Extension* extension); 175 176 // Update the views to reflect the state of the browser action icons. 177 void RefreshBrowserActionViews(); 178 179 // Sets up the browser action view vector. 180 void CreateBrowserActionViews(); 181 182 // Delete all browser action views. 183 void DeleteBrowserActionViews(); 184 185 // Returns how many browser actions are currently visible. If the intent is 186 // to find how many are visible once the container finishes animation, see 187 // VisibleBrowserActionsAfterAnimation() below. 188 size_t VisibleBrowserActions() const; 189 190 // Returns how many browser actions will be visible once the container 191 // finishes animating to a new size, or (if not animating) the currently 192 // visible icons. 193 size_t VisibleBrowserActionsAfterAnimation() const; 194 195 // Executes |command| registered by |extension|. 196 void ExecuteExtensionCommand(const extensions::Extension* extension, 197 const extensions::Command& command); 198 199 // Notify the browser action container that an extension has been moved to 200 // the overflow container. 201 void NotifyActionMovedToOverflow(); 202 203 // Add or remove an observer. 204 void AddObserver(BrowserActionsContainerObserver* observer); 205 void RemoveObserver(BrowserActionsContainerObserver* observer); 206 207 // Overridden from views::View: 208 virtual gfx::Size GetPreferredSize() const OVERRIDE; 209 virtual int GetHeightForWidth(int width) const OVERRIDE; 210 virtual gfx::Size GetMinimumSize() const OVERRIDE; 211 virtual void Layout() OVERRIDE; 212 virtual bool GetDropFormats(int* formats, 213 std::set<ui::OSExchangeData::CustomFormat>* custom_formats) OVERRIDE; 214 virtual bool AreDropTypesRequired() OVERRIDE; 215 virtual bool CanDrop(const ui::OSExchangeData& data) OVERRIDE; 216 virtual int OnDragUpdated(const ui::DropTargetEvent& event) OVERRIDE; 217 virtual void OnDragExited() OVERRIDE; 218 virtual int OnPerformDrop(const ui::DropTargetEvent& event) OVERRIDE; 219 virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; 220 221 // Overridden from views::MenuButtonListener: 222 virtual void OnMenuButtonClicked(views::View* source, 223 const gfx::Point& point) OVERRIDE; 224 225 // Overridden from views::DragController: 226 virtual void WriteDragDataForView(View* sender, 227 const gfx::Point& press_pt, 228 ui::OSExchangeData* data) OVERRIDE; 229 virtual int GetDragOperationsForView(View* sender, 230 const gfx::Point& p) OVERRIDE; 231 virtual bool CanStartDragForView(View* sender, 232 const gfx::Point& press_pt, 233 const gfx::Point& p) OVERRIDE; 234 235 // Overridden from views::ResizeAreaDelegate: 236 virtual void OnResize(int resize_amount, bool done_resizing) OVERRIDE; 237 238 // Overridden from gfx::AnimationDelegate: 239 virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE; 240 virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE; 241 242 // Overridden from BrowserActionOverflowMenuController::Observer: 243 virtual void NotifyMenuDeleted( 244 BrowserActionOverflowMenuController* controller) OVERRIDE; 245 246 // Overridden from BrowserActionView::Delegate: 247 virtual content::WebContents* GetCurrentWebContents() OVERRIDE; 248 virtual bool ShownInsideMenu() const OVERRIDE; 249 virtual void OnBrowserActionViewDragDone() OVERRIDE; 250 virtual views::MenuButton* GetOverflowReferenceView() OVERRIDE; 251 virtual void SetPopupOwner(BrowserActionView* popup_owner) OVERRIDE; 252 virtual void HideActivePopup() OVERRIDE; 253 virtual BrowserActionView* GetMainViewForExtension( 254 const extensions::Extension* extension) OVERRIDE; 255 256 // Overridden from extension::ExtensionKeybindingRegistry::Delegate: 257 virtual extensions::ActiveTabPermissionGranter* 258 GetActiveTabPermissionGranter() OVERRIDE; 259 260 // Retrieve the current popup. This should only be used by unit tests. 261 ExtensionPopup* TestGetPopup(); 262 263 // Set how many icons the container should show. This should only be used by 264 // unit tests. 265 void TestSetIconVisibilityCount(size_t icons); 266 267 // Returns the width of an icon, optionally with its padding. 268 static int IconWidth(bool include_padding); 269 270 // Returns the height of an icon. 271 static int IconHeight(); 272 273 // During testing we can disable animations by setting this flag to true, 274 // so that the bar resizes instantly, instead of having to poll it while it 275 // animates to open/closed status. 276 static bool disable_animations_during_testing_; 277 278 protected: 279 // Overridden from views::View: 280 virtual void ViewHierarchyChanged( 281 const ViewHierarchyChangedDetails& details) OVERRIDE; 282 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; 283 virtual void OnThemeChanged() OVERRIDE; 284 285 private: 286 // A struct representing the position at which an action will be dropped. 287 struct DropPosition; 288 289 typedef std::vector<BrowserActionView*> BrowserActionViews; 290 291 // extensions::ExtensionToolbarModel::Observer implementation. 292 virtual void ToolbarExtensionAdded(const extensions::Extension* extension, 293 int index) OVERRIDE; 294 virtual void ToolbarExtensionRemoved( 295 const extensions::Extension* extension) OVERRIDE; 296 virtual void ToolbarExtensionMoved(const extensions::Extension* extension, 297 int index) OVERRIDE; 298 virtual void ToolbarExtensionUpdated( 299 const extensions::Extension* extension) OVERRIDE; 300 virtual bool ShowExtensionActionPopup( 301 const extensions::Extension* extension, 302 bool grant_active_tab) OVERRIDE; 303 virtual void ToolbarVisibleCountChanged() OVERRIDE; 304 virtual void ToolbarHighlightModeChanged(bool is_highlighting) OVERRIDE; 305 virtual Browser* GetBrowser() OVERRIDE; 306 307 void LoadImages(); 308 309 // Called when a browser action's visibility may have changed. 310 void OnBrowserActionVisibilityChanged(); 311 312 // Returns the preferred width of the container in order to show all icons 313 // that should be visible and, optionally, the chevron. 314 int GetPreferredWidth(); 315 316 // Sets the chevron to be visible or not based on whether all browser actions 317 // are displayed. 318 void SetChevronVisibility(); 319 320 // Closes the overflow menu if open. 321 void CloseOverflowMenu(); 322 323 // Cancels the timer for showing the drop down menu. 324 void StopShowFolderDropMenuTimer(); 325 326 // Show the drop down folder after a slight delay. 327 void StartShowFolderDropMenuTimer(); 328 329 // Show the overflow menu. 330 void ShowDropFolder(); 331 332 // Given a number of |icons| and whether to |display_chevron|, returns the 333 // amount of pixels needed to draw the entire container. For convenience, 334 // callers can set |icons| to -1 to mean "all icons". 335 int IconCountToWidth(int icons, bool display_chevron) const; 336 337 // Given a pixel width, returns the number of icons that fit. (This 338 // automatically determines whether a chevron will be needed and includes it 339 // in the calculation.) 340 size_t WidthToIconCount(int pixels) const; 341 342 // Returns the absolute minimum size you can shrink the container down to and 343 // still show it. This assumes a visible chevron because the only way we 344 // would not have a chevron when shrinking down this far is if there were no 345 // icons, in which case the container wouldn't be shown at all. 346 int MinimumNonemptyWidth() const; 347 348 // Animate to the target size (unless testing, in which case we go straight to 349 // the target size). 350 void Animate(gfx::Tween::Type type, size_t num_visible_icons); 351 352 // Returns true if this extension should be shown in this toolbar. This can 353 // return false if we are in an incognito window and the extension is disabled 354 // for incognito. 355 bool ShouldDisplayBrowserAction(const extensions::Extension* extension) const; 356 357 // Returns the number of icons that this container should draw. This differs 358 // from the model's GetVisibleIconCount if this container is for the overflow. 359 size_t GetIconCount() const; 360 361 // Whether this container is in overflow mode (as opposed to in 'main' 362 // mode). See class comments for details on the difference. 363 bool in_overflow_mode() const { return main_container_ != NULL; } 364 365 // Whether or not the container has been initialized. 366 bool initialized_; 367 368 // The vector of browser actions (icons/image buttons for each action). Note 369 // that not every BrowserAction in the ToolbarModel will necessarily be in 370 // this collection. Some extensions may be disabled in incognito windows. 371 BrowserActionViews browser_action_views_; 372 373 Profile* profile_; 374 375 // The Browser object the container is associated with. 376 Browser* browser_; 377 378 // The view that owns us. 379 views::View* owner_view_; 380 381 // The main container we are serving as overflow for, or NULL if this 382 // class is the the main container. See class comments for details on 383 // the difference between main and overflow. 384 BrowserActionsContainer* main_container_; 385 386 // The view that triggered the current popup (just a reference to a view 387 // from browser_action_views_). 388 BrowserActionView* popup_owner_; 389 390 // The model that tracks the order of the toolbar icons. 391 extensions::ExtensionToolbarModel* model_; 392 393 // The current width of the container. 394 int container_width_; 395 396 // The resize area for the container. 397 views::ResizeArea* resize_area_; 398 399 // The chevron for accessing the overflow items. Can be NULL when in overflow 400 // mode or if the toolbar is permanently suppressing the chevron menu. 401 views::MenuButton* chevron_; 402 403 // The painter used when we are highlighting a subset of extensions. 404 scoped_ptr<views::Painter> highlight_painter_; 405 406 // The menu to show for the overflow button (chevron). This class manages its 407 // own lifetime so that it can stay alive during drag and drop operations. 408 BrowserActionOverflowMenuController* overflow_menu_; 409 410 // The animation that happens when the container snaps to place. 411 scoped_ptr<gfx::SlideAnimation> resize_animation_; 412 413 // Don't show the chevron while animating. 414 bool suppress_chevron_; 415 416 // This is used while the user is resizing (and when the animations are in 417 // progress) to know how wide the delta is between the current state and what 418 // we should draw. 419 int resize_amount_; 420 421 // Keeps track of the absolute pixel width the container should have when we 422 // are done animating. 423 int animation_target_size_; 424 425 // The DropPosition for the current drag-and-drop operation, or NULL if there 426 // is none. 427 scoped_ptr<DropPosition> drop_position_; 428 429 // The class that registers for keyboard shortcuts for extension commands. 430 scoped_ptr<ExtensionKeybindingRegistryViews> extension_keybinding_registry_; 431 432 ObserverList<BrowserActionsContainerObserver> observers_; 433 434 // The maximum number of icons to show per row when in overflow mode (showing 435 // icons in the application menu). 436 static int icons_per_overflow_menu_row_; 437 438 // Handles delayed showing of the overflow menu when hovering. 439 base::WeakPtrFactory<BrowserActionsContainer> show_menu_task_factory_; 440 441 DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainer); 442 }; 443 444 #endif // CHROME_BROWSER_UI_VIEWS_TOOLBAR_BROWSER_ACTIONS_CONTAINER_H_ 445