| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "chrome/browser/ui/cocoa/extensions/browser_action_button.h" | 5 #import "chrome/browser/ui/cocoa/extensions/browser_action_button.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <cmath> | 8 #include <cmath> |
| 9 | 9 |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/strings/sys_string_conversions.h" | 11 #include "base/strings/sys_string_conversions.h" |
| 12 #include "chrome/browser/extensions/extension_action.h" | |
| 13 #include "chrome/browser/extensions/extension_action_icon_factory.h" | |
| 14 #include "chrome/browser/extensions/extension_action_manager.h" | |
| 15 #include "chrome/browser/profiles/profile.h" | 12 #include "chrome/browser/profiles/profile.h" |
| 16 #include "chrome/browser/ui/browser.h" | 13 #include "chrome/browser/ui/browser.h" |
| 17 #include "chrome/browser/ui/cocoa/extensions/extension_action_context_menu_contr
oller.h" | 14 #import "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h" |
| 18 #include "extensions/common/extension.h" | 15 #import "chrome/browser/ui/cocoa/extensions/extension_action_context_menu_contro
ller.h" |
| 16 #import "chrome/browser/ui/cocoa/toolbar/toolbar_action_view_delegate_cocoa.h" |
| 17 #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h" |
| 19 #include "grit/theme_resources.h" | 18 #include "grit/theme_resources.h" |
| 20 #include "skia/ext/skia_utils_mac.h" | 19 #include "skia/ext/skia_utils_mac.h" |
| 21 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSAnimation+Duration.h
" | 20 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSAnimation+Duration.h
" |
| 22 #include "ui/gfx/canvas_skia_paint.h" | 21 #include "ui/gfx/canvas_skia_paint.h" |
| 23 #include "ui/gfx/image/image.h" | 22 #include "ui/gfx/image/image.h" |
| 24 #include "ui/gfx/rect.h" | 23 #include "ui/gfx/rect.h" |
| 25 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" | 24 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" |
| 26 #include "ui/gfx/size.h" | |
| 27 | |
| 28 using extensions::Extension; | |
| 29 | 25 |
| 30 NSString* const kBrowserActionButtonDraggingNotification = | 26 NSString* const kBrowserActionButtonDraggingNotification = |
| 31 @"BrowserActionButtonDraggingNotification"; | 27 @"BrowserActionButtonDraggingNotification"; |
| 32 NSString* const kBrowserActionButtonDragEndNotification = | 28 NSString* const kBrowserActionButtonDragEndNotification = |
| 33 @"BrowserActionButtonDragEndNotification"; | 29 @"BrowserActionButtonDragEndNotification"; |
| 34 | 30 |
| 35 static const CGFloat kBrowserActionBadgeOriginYOffset = 5; | 31 static const CGFloat kBrowserActionBadgeOriginYOffset = 5; |
| 36 static const CGFloat kAnimationDuration = 0.2; | 32 static const CGFloat kAnimationDuration = 0.2; |
| 37 static const CGFloat kMinimumDragDistance = 5; | 33 static const CGFloat kMinimumDragDistance = 5; |
| 38 | 34 |
| 39 // A helper class to bridge the asynchronous Skia bitmap loading mechanism to | 35 // A class to bridge the ToolbarActionViewController and the |
| 40 // the extension's button. | 36 // BrowserActionButton. |
| 41 class ExtensionActionIconFactoryBridge | 37 class ToolbarActionViewDelegateBridge : public ToolbarActionViewDelegateCocoa { |
| 42 : public ExtensionActionIconFactory::Observer { | |
| 43 public: | 38 public: |
| 44 ExtensionActionIconFactoryBridge(BrowserActionButton* owner, | 39 ToolbarActionViewDelegateBridge(BrowserActionButton* owner, |
| 45 Profile* profile, | 40 BrowserActionsController* controller); |
| 46 const Extension* extension) | 41 ~ToolbarActionViewDelegateBridge(); |
| 47 : owner_(owner), | |
| 48 browser_action_([[owner cell] extensionAction]), | |
| 49 icon_factory_(profile, extension, browser_action_, this) { | |
| 50 } | |
| 51 | |
| 52 ~ExtensionActionIconFactoryBridge() override {} | |
| 53 | |
| 54 // ExtensionActionIconFactory::Observer implementation. | |
| 55 void OnIconUpdated() override { [owner_ updateState]; } | |
| 56 | |
| 57 gfx::Image GetIcon(int tabId) { | |
| 58 return icon_factory_.GetIcon(tabId); | |
| 59 } | |
| 60 | 42 |
| 61 private: | 43 private: |
| 62 // Weak. Owns us. | 44 // ToolbarActionViewDelegateCocoa: |
| 45 ToolbarActionViewController* GetPreferredPopupViewController() override; |
| 46 content::WebContents* GetCurrentWebContents() const override; |
| 47 void UpdateState() override; |
| 48 NSPoint GetPopupPoint() override; |
| 49 |
| 50 // The owning button. Weak. |
| 63 BrowserActionButton* owner_; | 51 BrowserActionButton* owner_; |
| 64 | 52 |
| 65 // The browser action whose images we're loading. | 53 // The BrowserActionsController that owns the button. Weak. |
| 66 ExtensionAction* const browser_action_; | 54 BrowserActionsController* controller_; |
| 67 | 55 |
| 68 // The object that will be used to get the browser action icon for us. | 56 DISALLOW_COPY_AND_ASSIGN(ToolbarActionViewDelegateBridge); |
| 69 // It may load the icon asynchronously (in which case the initial icon | 57 }; |
| 70 // returned by the factory will be transparent), so we have to observe it for | |
| 71 // updates to the icon. | |
| 72 ExtensionActionIconFactory icon_factory_; | |
| 73 | 58 |
| 74 DISALLOW_COPY_AND_ASSIGN(ExtensionActionIconFactoryBridge); | 59 ToolbarActionViewDelegateBridge::ToolbarActionViewDelegateBridge( |
| 75 }; | 60 BrowserActionButton* owner, |
| 61 BrowserActionsController* controller) |
| 62 : owner_(owner), |
| 63 controller_(controller) { |
| 64 } |
| 65 |
| 66 ToolbarActionViewDelegateBridge::~ToolbarActionViewDelegateBridge() { |
| 67 } |
| 68 |
| 69 ToolbarActionViewController* |
| 70 ToolbarActionViewDelegateBridge::GetPreferredPopupViewController() { |
| 71 return [owner_ viewController]; |
| 72 } |
| 73 |
| 74 content::WebContents* ToolbarActionViewDelegateBridge::GetCurrentWebContents() |
| 75 const { |
| 76 return [controller_ currentWebContents]; |
| 77 } |
| 78 |
| 79 void ToolbarActionViewDelegateBridge::UpdateState() { |
| 80 [owner_ updateState]; |
| 81 } |
| 82 |
| 83 NSPoint ToolbarActionViewDelegateBridge::GetPopupPoint() { |
| 84 return [controller_ popupPointForId:[owner_ viewController]->GetId()]; |
| 85 } |
| 76 | 86 |
| 77 @interface BrowserActionCell (Internals) | 87 @interface BrowserActionCell (Internals) |
| 78 - (void)drawBadgeWithinFrame:(NSRect)frame; | 88 - (void)drawBadgeWithinFrame:(NSRect)frame; |
| 79 @end | 89 @end |
| 80 | 90 |
| 81 @interface BrowserActionButton (Private) | 91 @interface BrowserActionButton (Private) |
| 82 - (void)endDrag; | 92 - (void)endDrag; |
| 83 @end | 93 @end |
| 84 | 94 |
| 85 @implementation BrowserActionButton | 95 @implementation BrowserActionButton |
| 86 | 96 |
| 87 @synthesize isBeingDragged = isBeingDragged_; | 97 @synthesize isBeingDragged = isBeingDragged_; |
| 88 @synthesize extension = extension_; | |
| 89 @synthesize tabId = tabId_; | |
| 90 | 98 |
| 91 + (Class)cellClass { | 99 + (Class)cellClass { |
| 92 return [BrowserActionCell class]; | 100 return [BrowserActionCell class]; |
| 93 } | 101 } |
| 94 | 102 |
| 95 - (id)initWithFrame:(NSRect)frame | 103 - (id)initWithFrame:(NSRect)frame |
| 96 extension:(const Extension*)extension | 104 viewController:(scoped_ptr<ToolbarActionViewController>)viewController |
| 97 browser:(Browser*)browser | 105 controller:(BrowserActionsController*)controller |
| 98 tabId:(int)tabId { | 106 menuController:(ExtensionActionContextMenuController*)menuController { |
| 99 if ((self = [super initWithFrame:frame])) { | 107 if ((self = [super initWithFrame:frame])) { |
| 100 BrowserActionCell* cell = [[[BrowserActionCell alloc] init] autorelease]; | 108 BrowserActionCell* cell = [[[BrowserActionCell alloc] init] autorelease]; |
| 101 // [NSButton setCell:] warns to NOT use setCell: other than in the | 109 // [NSButton setCell:] warns to NOT use setCell: other than in the |
| 102 // initializer of a control. However, we are using a basic | 110 // initializer of a control. However, we are using a basic |
| 103 // NSButton whose initializer does not take an NSCell as an | 111 // NSButton whose initializer does not take an NSCell as an |
| 104 // object. To honor the assumed semantics, we do nothing with | 112 // object. To honor the assumed semantics, we do nothing with |
| 105 // NSButton between alloc/init and setCell:. | 113 // NSButton between alloc/init and setCell:. |
| 106 [self setCell:cell]; | 114 [self setCell:cell]; |
| 107 [cell setTabId:tabId]; | 115 |
| 108 ExtensionAction* browser_action = | 116 browserActionsController_ = controller; |
| 109 extensions::ExtensionActionManager::Get(browser->profile())-> | 117 viewControllerDelegate_.reset( |
| 110 GetExtensionAction(*extension); | 118 new ToolbarActionViewDelegateBridge(self, controller)); |
| 111 CHECK(browser_action) | 119 viewController_ = viewController.Pass(); |
| 112 << "Don't create a BrowserActionButton if there is no browser action."; | 120 viewController_->SetDelegate(viewControllerDelegate_.get()); |
| 113 [cell setExtensionAction:browser_action]; | 121 |
| 122 [cell setBrowserActionsController:controller]; |
| 123 [cell setViewController:viewController_.get()]; |
| 114 [cell | 124 [cell |
| 115 accessibilitySetOverrideValue:base::SysUTF8ToNSString(extension->name()) | 125 accessibilitySetOverrideValue:base::SysUTF16ToNSString( |
| 126 viewController_->GetAccessibleName([controller currentWebContents])) |
| 116 forAttribute:NSAccessibilityDescriptionAttribute]; | 127 forAttribute:NSAccessibilityDescriptionAttribute]; |
| 117 [cell setImageID:IDR_BROWSER_ACTION | 128 [cell setImageID:IDR_BROWSER_ACTION |
| 118 forButtonState:image_button_cell::kDefaultState]; | 129 forButtonState:image_button_cell::kDefaultState]; |
| 119 [cell setImageID:IDR_BROWSER_ACTION_H | 130 [cell setImageID:IDR_BROWSER_ACTION_H |
| 120 forButtonState:image_button_cell::kHoverState]; | 131 forButtonState:image_button_cell::kHoverState]; |
| 121 [cell setImageID:IDR_BROWSER_ACTION_P | 132 [cell setImageID:IDR_BROWSER_ACTION_P |
| 122 forButtonState:image_button_cell::kPressedState]; | 133 forButtonState:image_button_cell::kPressedState]; |
| 123 [cell setImageID:IDR_BROWSER_ACTION | 134 [cell setImageID:IDR_BROWSER_ACTION |
| 124 forButtonState:image_button_cell::kDisabledState]; | 135 forButtonState:image_button_cell::kDisabledState]; |
| 125 | 136 |
| 126 [self setTitle:@""]; | 137 [self setTitle:@""]; |
| 127 [self setButtonType:NSMomentaryChangeButton]; | 138 [self setButtonType:NSMomentaryChangeButton]; |
| 128 [self setShowsBorderOnlyWhileMouseInside:YES]; | 139 [self setShowsBorderOnlyWhileMouseInside:YES]; |
| 129 | 140 |
| 130 contextMenuController_.reset([[ExtensionActionContextMenuController alloc] | 141 contextMenuController_.reset(menuController); |
| 131 initWithExtension:extension | 142 |
| 132 browser:browser | |
| 133 extensionAction:browser_action]); | |
| 134 base::scoped_nsobject<NSMenu> contextMenu( | 143 base::scoped_nsobject<NSMenu> contextMenu( |
| 135 [[NSMenu alloc] initWithTitle:@""]); | 144 [[NSMenu alloc] initWithTitle:@""]); |
| 136 [contextMenu setDelegate:self]; | 145 [contextMenu setDelegate:self]; |
| 137 [self setMenu:contextMenu]; | 146 [self setMenu:contextMenu]; |
| 138 | 147 |
| 139 tabId_ = tabId; | |
| 140 extension_ = extension; | |
| 141 iconFactoryBridge_.reset(new ExtensionActionIconFactoryBridge( | |
| 142 self, browser->profile(), extension)); | |
| 143 | |
| 144 moveAnimation_.reset([[NSViewAnimation alloc] init]); | 148 moveAnimation_.reset([[NSViewAnimation alloc] init]); |
| 145 [moveAnimation_ gtm_setDuration:kAnimationDuration | 149 [moveAnimation_ gtm_setDuration:kAnimationDuration |
| 146 eventMask:NSLeftMouseUpMask]; | 150 eventMask:NSLeftMouseUpMask]; |
| 147 [moveAnimation_ setAnimationBlockingMode:NSAnimationNonblocking]; | 151 [moveAnimation_ setAnimationBlockingMode:NSAnimationNonblocking]; |
| 148 | 152 |
| 149 [self updateState]; | 153 [self updateState]; |
| 150 } | 154 } |
| 151 | 155 |
| 152 return self; | 156 return self; |
| 153 } | 157 } |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 233 [NSValue valueWithRect:[self frame]], NSViewAnimationStartFrameKey, | 237 [NSValue valueWithRect:[self frame]], NSViewAnimationStartFrameKey, |
| 234 [NSValue valueWithRect:frameRect], NSViewAnimationEndFrameKey, | 238 [NSValue valueWithRect:frameRect], NSViewAnimationEndFrameKey, |
| 235 nil]; | 239 nil]; |
| 236 [moveAnimation_ setViewAnimations: | 240 [moveAnimation_ setViewAnimations: |
| 237 [NSArray arrayWithObject:animationDictionary]]; | 241 [NSArray arrayWithObject:animationDictionary]]; |
| 238 [moveAnimation_ startAnimation]; | 242 [moveAnimation_ startAnimation]; |
| 239 } | 243 } |
| 240 } | 244 } |
| 241 | 245 |
| 242 - (void)updateState { | 246 - (void)updateState { |
| 243 if (tabId_ < 0) | 247 content::WebContents* webContents = |
| 248 [browserActionsController_ currentWebContents]; |
| 249 if (!webContents) |
| 244 return; | 250 return; |
| 245 | 251 |
| 246 std::string tooltip = [[self cell] extensionAction]->GetTitle(tabId_); | 252 base::string16 tooltip = viewController_->GetTooltip(webContents); |
| 247 if (tooltip.empty()) { | 253 [self setToolTip:(tooltip.empty() ? nil : base::SysUTF16ToNSString(tooltip))]; |
| 248 [self setToolTip:nil]; | |
| 249 } else { | |
| 250 [self setToolTip:base::SysUTF8ToNSString(tooltip)]; | |
| 251 } | |
| 252 | 254 |
| 253 gfx::Image image = iconFactoryBridge_->GetIcon(tabId_); | 255 gfx::Image image = viewController_->GetIcon(webContents); |
| 254 | 256 |
| 255 if (!image.IsEmpty()) | 257 if (!image.IsEmpty()) |
| 256 [self setImage:image.ToNSImage()]; | 258 [self setImage:image.ToNSImage()]; |
| 257 | 259 |
| 258 [[self cell] setTabId:tabId_]; | 260 [self setEnabled:viewController_->IsEnabled(webContents)]; |
| 259 | |
| 260 bool enabled = [[self cell] extensionAction]->GetIsVisible(tabId_); | |
| 261 [self setEnabled:enabled]; | |
| 262 | 261 |
| 263 [self setNeedsDisplay:YES]; | 262 [self setNeedsDisplay:YES]; |
| 264 } | 263 } |
| 265 | 264 |
| 266 - (BOOL)isAnimating { | 265 - (BOOL)isAnimating { |
| 267 return [moveAnimation_ isAnimating]; | 266 return [moveAnimation_ isAnimating]; |
| 268 } | 267 } |
| 269 | 268 |
| 269 - (ToolbarActionViewController*)viewController { |
| 270 return viewController_.get(); |
| 271 } |
| 272 |
| 270 - (NSImage*)compositedImage { | 273 - (NSImage*)compositedImage { |
| 271 NSRect bounds = [self bounds]; | 274 NSRect bounds = [self bounds]; |
| 272 NSImage* image = [[[NSImage alloc] initWithSize:bounds.size] autorelease]; | 275 NSImage* image = [[[NSImage alloc] initWithSize:bounds.size] autorelease]; |
| 273 [image lockFocus]; | 276 [image lockFocus]; |
| 274 | 277 |
| 275 [[NSColor clearColor] set]; | 278 [[NSColor clearColor] set]; |
| 276 NSRectFill(bounds); | 279 NSRectFill(bounds); |
| 277 | 280 |
| 278 NSImage* actionImage = [self image]; | 281 NSImage* actionImage = [self image]; |
| 279 const NSSize imageSize = [actionImage size]; | 282 const NSSize imageSize = [actionImage size]; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 297 | 300 |
| 298 - (void)menuNeedsUpdate:(NSMenu*)menu { | 301 - (void)menuNeedsUpdate:(NSMenu*)menu { |
| 299 [menu removeAllItems]; | 302 [menu removeAllItems]; |
| 300 [contextMenuController_ populateMenu:menu]; | 303 [contextMenuController_ populateMenu:menu]; |
| 301 } | 304 } |
| 302 | 305 |
| 303 @end | 306 @end |
| 304 | 307 |
| 305 @implementation BrowserActionCell | 308 @implementation BrowserActionCell |
| 306 | 309 |
| 307 @synthesize tabId = tabId_; | 310 @synthesize browserActionsController = browserActionsController_; |
| 308 @synthesize extensionAction = extensionAction_; | 311 @synthesize viewController = viewController_; |
| 309 | 312 |
| 310 - (void)drawBadgeWithinFrame:(NSRect)frame { | 313 - (void)drawBadgeWithinFrame:(NSRect)frame |
| 314 webContents:(content::WebContents*)webContents { |
| 311 gfx::CanvasSkiaPaint canvas(frame, false); | 315 gfx::CanvasSkiaPaint canvas(frame, false); |
| 312 canvas.set_composite_alpha(true); | 316 canvas.set_composite_alpha(true); |
| 313 gfx::Rect boundingRect(NSRectToCGRect(frame)); | 317 gfx::Rect boundingRect(NSRectToCGRect(frame)); |
| 314 extensionAction_->PaintBadge(&canvas, boundingRect, tabId_); | 318 viewController_->PaintExtra(&canvas, boundingRect, webContents); |
| 315 } | 319 } |
| 316 | 320 |
| 317 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { | 321 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { |
| 318 gfx::ScopedNSGraphicsContextSaveGState scopedGState; | 322 gfx::ScopedNSGraphicsContextSaveGState scopedGState; |
| 319 [super drawWithFrame:cellFrame inView:controlView]; | 323 [super drawWithFrame:cellFrame inView:controlView]; |
| 320 CHECK(extensionAction_); | 324 DCHECK(viewController_); |
| 321 bool enabled = extensionAction_->GetIsVisible(tabId_); | 325 content::WebContents* webContents = |
| 326 [browserActionsController_ currentWebContents]; |
| 327 bool enabled = viewController_->IsEnabled(webContents); |
| 322 const NSSize imageSize = self.image.size; | 328 const NSSize imageSize = self.image.size; |
| 323 const NSRect imageRect = | 329 const NSRect imageRect = |
| 324 NSMakeRect(std::floor((NSWidth(cellFrame) - imageSize.width) / 2.0), | 330 NSMakeRect(std::floor((NSWidth(cellFrame) - imageSize.width) / 2.0), |
| 325 std::floor((NSHeight(cellFrame) - imageSize.height) / 2.0), | 331 std::floor((NSHeight(cellFrame) - imageSize.height) / 2.0), |
| 326 imageSize.width, imageSize.height); | 332 imageSize.width, imageSize.height); |
| 327 [self.image drawInRect:imageRect | 333 [self.image drawInRect:imageRect |
| 328 fromRect:NSZeroRect | 334 fromRect:NSZeroRect |
| 329 operation:NSCompositeSourceOver | 335 operation:NSCompositeSourceOver |
| 330 fraction:enabled ? 1.0 : 0.4 | 336 fraction:enabled ? 1.0 : 0.4 |
| 331 respectFlipped:YES | 337 respectFlipped:YES |
| 332 hints:nil]; | 338 hints:nil]; |
| 333 | 339 |
| 334 cellFrame.origin.y += kBrowserActionBadgeOriginYOffset; | 340 cellFrame.origin.y += kBrowserActionBadgeOriginYOffset; |
| 335 [self drawBadgeWithinFrame:cellFrame]; | 341 [self drawBadgeWithinFrame:cellFrame |
| 342 webContents:webContents]; |
| 336 } | 343 } |
| 337 | 344 |
| 338 @end | 345 @end |
| OLD | NEW |