browser_actions_container.h revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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 21class BrowserActionsContainerObserver; 22class ExtensionPopup; 23 24namespace extensions { 25class ActiveTabPermissionGranter; 26class Command; 27class Extension; 28} 29 30namespace gfx { 31class SlideAnimation; 32} 33 34namespace views { 35class 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//////////////////////////////////////////////////////////////////////////////// 124class 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