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