Index: chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.mm |
diff --git a/chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.mm b/chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.mm |
index 0b6f6f03f8b6c240922da6e39e02ff2e961e8f46..71832b1b701c59e09566ec1610d90aba7c25ec46 100644 |
--- a/chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.mm |
+++ b/chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.mm |
@@ -6,6 +6,7 @@ |
#include <algorithm> |
+#include "base/mac/bind_objc_block.h" |
#include "base/mac/foundation_util.h" |
#include "base/mac/mac_util.h" |
#include "base/strings/sys_string_conversions.h" |
@@ -20,12 +21,15 @@ |
#import "chrome/browser/ui/cocoa/info_bubble_view.h" |
#import "chrome/browser/ui/cocoa/info_bubble_window.h" |
#include "chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h" |
+#include "chrome/browser/ui/cocoa/website_settings/permission_selector_button.h" |
#include "chrome/browser/ui/cocoa/website_settings/split_block_button.h" |
#include "chrome/browser/ui/website_settings/permission_bubble_request.h" |
#include "chrome/browser/ui/website_settings/permission_bubble_view.h" |
+#include "chrome/browser/ui/website_settings/permission_menu_model.h" |
#include "content/public/browser/user_metrics.h" |
#include "grit/generated_resources.h" |
#include "skia/ext/skia_utils_mac.h" |
+#import "ui/base/cocoa/menu_controller.h" |
#include "ui/base/cocoa/window_size_constants.h" |
#import "ui/base/cocoa/menu_controller.h" |
#include "ui/base/l10n/l10n_util_mac.h" |
@@ -39,9 +43,10 @@ const CGFloat kHorizontalPadding = 20.0f; |
const CGFloat kVerticalPadding = 20.0f; |
const CGFloat kButtonPadding = 10.0f; |
const CGFloat kTitlePaddingX = 50.0f; |
-const CGFloat kCheckboxYAdjustment = 2.0f; |
+const CGFloat kTitleFontSize = 15.0f; |
+const CGFloat kPermissionFontSize = 12.0f; |
+const CGFloat kPermissionButtonTitleRightPadding = 4.0f; |
-const CGFloat kFontSize = 15.0f; |
class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
public: |
explicit MenuDelegate(PermissionBubbleController* bubble) |
@@ -50,10 +55,7 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
return false; |
} |
virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE { |
- // TODO(leng): Change this to true once setting the bubble to be |
- // customizable works properly. Ideally, the bubble will alter its |
- // contents, rather than reshowing completely. |
- return false; |
+ return true; |
} |
virtual bool GetAcceleratorForCommandId( |
int command_id, |
@@ -70,6 +72,63 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
} // namespace |
+// NSPopUpButton with a menu containing two items: allow and block. |
+// One AllowBlockMenuButton is used for each requested permission, but only when |
+// the permission bubble is in 'customize' mode. |
+@interface AllowBlockMenuButton : NSPopUpButton { |
+ @private |
+ scoped_ptr<PermissionMenuModel> menuModel_; |
+ base::scoped_nsobject<MenuController> menuController_; |
+} |
+ |
+- (id)initForURL:(const GURL&)url |
+ allowed:(BOOL)allow |
+ index:(int)index |
+ delegate:(PermissionBubbleView::Delegate*)delegate; |
+@end |
+ |
+@implementation AllowBlockMenuButton |
+ |
+- (id)initForURL:(const GURL&)url |
+ allowed:(BOOL)allow |
+ index:(int)index |
+ delegate:(PermissionBubbleView::Delegate*)delegate { |
+ if (self = [super initWithFrame:NSMakeRect(0, 0, 1, 1) pullsDown:NO]) { |
groby-ooo-7-16
2014/04/25 19:25:28
Why not NSZeroRect?
leng
2014/04/25 22:18:56
Done.
|
+ ContentSetting setting = |
+ allow ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; |
+ [self setFont:[NSFont systemFontOfSize:kPermissionFontSize]]; |
+ [self setBordered:NO]; |
+ |
+ __block PermissionBubbleView::Delegate* blockDelegate = delegate; |
+ PermissionMenuModel::ChangeCallback changeCallback = |
+ base::BindBlock(^(const WebsiteSettingsUI::PermissionInfo& permission) { |
groby-ooo-7-16
2014/04/25 19:25:28
I just realized we created an ObjC block for the O
leng
2014/04/25 22:18:56
On 2014/04/25 19:25:28, groby wrote:
> I just real
|
+ blockDelegate->ToggleAccept( |
+ index, permission.setting == CONTENT_SETTING_ALLOW); |
+ }); |
+ |
+ menuModel_.reset(new PermissionMenuModel(url, setting, changeCallback)); |
+ menuController_.reset([[MenuController alloc] initWithModel:menuModel_.get() |
+ useWithPopUpButtonCell:NO]); |
+ [self setMenu:[menuController_ menu]]; |
+ [self selectItemAtIndex:menuModel_->GetIndexOfCommandId(setting)]; |
+ [self sizeToFit]; |
+ // Adjust the size to fit the current title. Using only -sizeToFit leaves |
+ // an ugly amount of whitespace between the title and the arrows. |
groby-ooo-7-16
2014/04/25 19:25:28
Ah. That's because NSPopUpButton's sizeToFit fits
leng
2014/04/25 22:18:56
Done.
|
+ // TODO(leng): This was copied from PermissionSelectorButton. Move to a |
+ // shared location, so that the code is not duplicated. |
+ NSDictionary* textAttributes = @{NSFontAttributeName : [self font]}; |
groby-ooo-7-16
2014/04/25 19:25:28
You could call
[[self attributedTitle] attribut
leng
2014/04/25 22:18:56
Done.
|
+ NSSize titleSize = [[self title] sizeWithAttributes:textAttributes]; |
+ NSRect frame = [self frame]; |
+ NSRect titleRect = [[self cell] titleRectForBounds:frame]; |
+ CGFloat width = titleSize.width + NSWidth(frame) - NSWidth(titleRect); |
+ [self setFrameSize:NSMakeSize(width + kPermissionButtonTitleRightPadding, |
+ NSHeight(frame))]; |
+ } |
+ return self; |
+} |
+ |
+@end |
+ |
@interface PermissionBubbleController () |
// Returns an autoreleased NSView displaying the icon and label for |request|. |
@@ -79,10 +138,11 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
// requesting settings for |host|. |
- (NSView*)titleWithHostname:(const std::string&)host; |
-// Returns an autoreleased NSView displaying a checkbox for |request|. The |
-// checkbox will be initialized as checked if |checked| is YES. |
-- (NSView*)checkboxForRequest:(PermissionBubbleRequest*)request |
- checked:(BOOL)checked; |
+// Returns an autoreleased NSView displaying a menu for |request|. The |
+// menu will be initialized as 'allow' if |allow| is YES. |
+- (NSView*)menuForRequest:(PermissionBubbleRequest*)request |
+ atIndex:(int)index |
+ allow:(BOOL)allow; |
// Returns an autoreleased NSView of a button with |title| and |action|. |
- (NSView*)buttonWithTitle:(NSString*)title |
@@ -98,10 +158,6 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
// Returns an autoreleased NSView displaying the close 'x' button. |
- (NSView*)closeButton; |
-// Sets the width of both |viewA| and |viewB| to be the larger of the |
-// two views' widths. Does not change either view's origin or height. |
-- (CGFloat)matchWidthsOf:(NSView*)viewA andOf:(NSView*)viewB; |
- |
// Called when the 'ok' button is pressed. |
- (void)ok:(id)sender; |
@@ -117,8 +173,13 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
// Called when the 'customize' button is pressed. |
- (void)onCustomize:(id)sender; |
-// Called when a checkbox changes from checked to unchecked, or vice versa. |
-- (void)onCheckboxChanged:(id)sender; |
+// Sets the width of both |viewA| and |viewB| to be the larger of the |
+// two views' widths. Does not change either view's origin or height. |
++ (CGFloat)matchWidthsOf:(NSView*)viewA andOf:(NSView*)viewB; |
+ |
+// Sets the offset of |viewA| so that its vertical center is aligned with the |
+// vertical center of |viewB|. |
++ (void)alignCenterOf:(NSView*)viewA verticallyToCenterOf:(NSView*)viewB; |
@end |
@@ -176,25 +237,32 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
CGFloat yOffset = 2 * kVerticalPadding + NSMaxY([allowOrOkButton frame]); |
BOOL singlePermission = requests.size() == 1; |
- checkboxes_.reset(customizationMode ? [[NSMutableArray alloc] init] : nil); |
+ base::scoped_nsobject<NSMutableArray> permissionMenus; |
+ if (customizationMode) |
+ permissionMenus.reset([[NSMutableArray alloc] init]); |
+ |
for (auto it = requests.begin(); it != requests.end(); it++) { |
- base::scoped_nsobject<NSView> permissionView; |
- if (customizationMode) { |
- int index = it - requests.begin(); |
- permissionView.reset( |
- [[self checkboxForRequest:(*it) |
- checked:acceptStates[index] ? YES : NO] retain]); |
- [base::mac::ObjCCastStrict<NSButton>(permissionView) setTag:index]; |
- [checkboxes_ addObject:permissionView]; |
- } else { |
- permissionView.reset([[self labelForRequest:(*it)] retain]); |
- } |
+ base::scoped_nsobject<NSView> permissionView( |
+ [[self labelForRequest:(*it)] retain]); |
NSPoint origin = [permissionView frame].origin; |
origin.x += kHorizontalPadding; |
origin.y += yOffset; |
[permissionView setFrameOrigin:origin]; |
[contentView addSubview:permissionView]; |
+ if (customizationMode) { |
+ int index = it - requests.begin(); |
+ base::scoped_nsobject<NSView> menu( |
+ [[self menuForRequest:(*it) |
+ atIndex:index |
+ allow:acceptStates[index] ? YES : NO] retain]); |
+ // Align vertically. Horizontal alignment will be adjusted once the |
+ // widest permission is know. |
+ [PermissionBubbleController alignCenterOf:menu |
+ verticallyToCenterOf:permissionView]; |
+ [permissionMenus addObject:menu]; |
+ [contentView addSubview:menu]; |
+ } |
yOffset += NSHeight([permissionView frame]); |
} |
@@ -207,6 +275,18 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
bubbleFrame, NSInsetRect([view frame], -kHorizontalPadding, 0)); |
} |
+ if (customizationMode) { |
+ // Adjust the horizontal origin for each menu. |
+ CGFloat xOffset = NSWidth(bubbleFrame) - kHorizontalPadding; |
+ CGFloat maxMenuWidth = 0; |
+ for (NSView* view in permissionMenus.get()) { |
+ [view setFrameOrigin:NSMakePoint(xOffset, NSMinY([view frame]))]; |
+ maxMenuWidth = std::max(maxMenuWidth, NSWidth([view frame])); |
+ } |
+ // And add the menu width to the bubble's width. |
+ bubbleFrame.size.width += maxMenuWidth; |
+ } |
+ |
base::scoped_nsobject<NSView> titleView( |
[[self titleWithHostname:requests[0]->GetRequestingHostname().host()] |
retain]); |
@@ -242,7 +322,8 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
blockButton.reset([[self blockButton] retain]); |
else |
blockButton.reset([[self blockButtonWithCustomizeMenu] retain]); |
- CGFloat width = [self matchWidthsOf:blockButton andOf:allowOrOkButton]; |
+ CGFloat width = [PermissionBubbleController matchWidthsOf:blockButton |
+ andOf:allowOrOkButton]; |
// Ensure the allow/ok button is still in the correct position. |
xOrigin = NSWidth(bubbleFrame) - width - kHorizontalPadding; |
[allowOrOkButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)]; |
@@ -290,6 +371,7 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
[permissionLabel setBezeled:NO]; |
[permissionLabel setEditable:NO]; |
[permissionLabel setSelectable:NO]; |
+ [permissionLabel setFont:[NSFont systemFontOfSize:kPermissionFontSize]]; |
[permissionLabel setStringValue:base::SysUTF16ToNSString(label)]; |
[permissionLabel sizeToFit]; |
[permissionLabel setFrameOrigin: |
@@ -326,7 +408,7 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
[titleView setStringValue: |
l10n_util::GetNSStringF(IDS_PERMISSIONS_BUBBLE_PROMPT, |
base::UTF8ToUTF16(host))]; |
- [titleView setFont:[NSFont systemFontOfSize:kFontSize]]; |
+ [titleView setFont:[NSFont systemFontOfSize:kTitleFontSize]]; |
[titleView sizeToFit]; |
NSRect titleFrame = [titleView frame]; |
[titleView setFrameSize:NSMakeSize(NSWidth(titleFrame) + kTitlePaddingX, |
@@ -334,20 +416,17 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
return titleView.autorelease(); |
} |
-- (NSView*)checkboxForRequest:(PermissionBubbleRequest*)request |
- checked:(BOOL)checked { |
+- (NSView*)menuForRequest:(PermissionBubbleRequest*)request |
+ atIndex:(int)index |
+ allow:(BOOL)allow { |
DCHECK(request); |
- base::scoped_nsobject<NSButton> checkbox( |
- [[NSButton alloc] initWithFrame:NSZeroRect]); |
- [checkbox setButtonType:NSSwitchButton]; |
- base::string16 permission = request->GetMessageTextFragment(); |
- [checkbox setTitle:base::SysUTF16ToNSString(permission)]; |
- [checkbox setState:(checked ? NSOnState : NSOffState)]; |
- [checkbox setTarget:self]; |
- [checkbox setAction:@selector(onCheckboxChanged:)]; |
- [checkbox sizeToFit]; |
- [checkbox setFrameOrigin:NSMakePoint(0, kCheckboxYAdjustment)]; |
- return checkbox.autorelease(); |
+ DCHECK(delegate_); |
+ base::scoped_nsobject<AllowBlockMenuButton> button( |
+ [[AllowBlockMenuButton alloc] initForURL:request->GetRequestingHostname() |
+ allowed:allow |
+ index:index |
+ delegate:delegate_]); |
+ return button.autorelease(); |
} |
- (NSView*)buttonWithTitle:(NSString*)title |
@@ -389,15 +468,6 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
return button.autorelease(); |
} |
-- (CGFloat)matchWidthsOf:(NSView*)viewA andOf:(NSView*)viewB { |
- NSRect frameA = [viewA frame]; |
- NSRect frameB = [viewB frame]; |
- CGFloat width = std::max(NSWidth(frameA), NSWidth(frameB)); |
- [viewA setFrameSize:NSMakeSize(width, NSHeight(frameA))]; |
- [viewB setFrameSize:NSMakeSize(width, NSHeight(frameB))]; |
- return width; |
-} |
- |
- (void)ok:(id)sender { |
DCHECK(delegate_); |
delegate_->Closing(); |
@@ -423,15 +493,26 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
delegate_->SetCustomizationMode(); |
} |
-- (void)onCheckboxChanged:(id)sender { |
- DCHECK(delegate_); |
- NSButton* checkbox = base::mac::ObjCCastStrict<NSButton>(sender); |
- delegate_->ToggleAccept([checkbox tag], [checkbox state] == NSOnState); |
-} |
- |
- (void)onMenuItemClicked:(int)commandId { |
DCHECK(commandId == 0); |
[self onCustomize:nil]; |
} |
++ (CGFloat)matchWidthsOf:(NSView*)viewA andOf:(NSView*)viewB { |
+ NSRect frameA = [viewA frame]; |
+ NSRect frameB = [viewB frame]; |
+ CGFloat width = std::max(NSWidth(frameA), NSWidth(frameB)); |
+ [viewA setFrameSize:NSMakeSize(width, NSHeight(frameA))]; |
+ [viewB setFrameSize:NSMakeSize(width, NSHeight(frameB))]; |
+ return width; |
+} |
+ |
++ (void)alignCenterOf:(NSView*)viewA verticallyToCenterOf:(NSView*)viewB { |
+ NSRect frameA = [viewA frame]; |
+ NSRect frameB = [viewB frame]; |
+ frameA.origin.y = |
+ NSMinY(frameB) + std::floor((NSHeight(frameB) - NSHeight(frameA)) / 2); |
+ [viewA setFrameOrigin:frameA.origin]; |
+} |
+ |
@end // implementation PermissionBubbleController |