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..13b88a8db343881c23af2ef06174d6afb04c3f1d 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,9 @@ 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 kFontSize = 15.0f; |
class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
public: |
explicit MenuDelegate(PermissionBubbleController* bubble) |
@@ -50,10 +54,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 +71,61 @@ 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 |
+ int index_; |
+ scoped_ptr<PermissionMenuModel> menuModel_; |
+ base::scoped_nsobject<MenuController> menuController_; |
+} |
+ |
+- (id)initWithIndex:(int)index |
+ forURL:(const GURL&)url |
+ allowed:(BOOL)allow |
+ withCallback:(void (^)(int, bool))callback; |
+@end |
+ |
+@implementation AllowBlockMenuButton |
+ |
+- (id)initWithIndex:(int)index |
+ forURL:(const GURL&)url |
+ allowed:(BOOL)allow |
+ withCallback:(void (^)(int, bool))callback { |
+ if (self = [super initWithFrame:NSMakeRect(0, 0, 1, 1) pullsDown:NO]) { |
+ index_ = index; |
+ ContentSetting setting = |
+ allow ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; |
+ [self setFont:[NSFont systemFontOfSize:kPermissionFontSize]]; |
+ [self setBordered:NO]; |
+ |
+ PermissionMenuModel::ChangeCallback changeCallback = |
+ base::BindBlock(^(const WebsiteSettingsUI::PermissionInfo& permission) { |
+ callback(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 cell] selectItemAtIndex:menuModel_->GetIndexOfCommandId(setting)]; |
groby-ooo-7-16
2014/04/18 18:18:18
I don't think you need the cell for selectItemAtIn
leng
2014/04/18 22:48:46
The first time I tried, it didn't work. Must've h
|
+ [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. |
+ [self setFrameSize:[PermissionSelectorButton sizeForTitle:[self title] |
groby-ooo-7-16
2014/04/18 18:18:18
I'm not happy with the tight coupling between the
leng
2014/04/18 22:48:46
As per our conversation offline, I'll make a follo
|
+ forButton:self]]; |
+ } |
+ return self; |
+} |
+ |
+// Getter for testing |
+- (int)index { |
+ return index_; |
+} |
+ |
+@end |
+ |
@interface PermissionBubbleController () |
// Returns an autoreleased NSView displaying the icon and label for |request|. |
@@ -79,10 +135,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 |
groby-ooo-7-16
2014/04/18 18:18:18
menuViewForRequest?
leng
2014/04/18 22:48:46
Would you like me to change the other names, as we
|
+ atIndex:(int)index |
+ allow:(BOOL)allow; |
// Returns an autoreleased NSView of a button with |title| and |action|. |
- (NSView*)buttonWithTitle:(NSString*)title |
@@ -102,6 +159,10 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
// 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; |
groby-ooo-7-16
2014/04/18 18:18:18
I'd probably do that as a standalone function, bec
leng
2014/04/18 22:48:46
Done.
|
+ |
// Called when the 'ok' button is pressed. |
- (void)ok:(id)sender; |
@@ -117,9 +178,6 @@ 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; |
- |
@end |
@implementation PermissionBubbleController |
@@ -176,25 +234,31 @@ 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. |
+ [self alignCenterOf:menu verticallyToCenterOf:permissionView]; |
+ [permissionMenus addObject:menu]; |
+ [contentView addSubview:menu]; |
+ } |
yOffset += NSHeight([permissionView frame]); |
} |
@@ -207,6 +271,17 @@ 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; |
+ for (NSView* view in permissionMenus.get()) { |
+ [view setFrameOrigin:NSMakePoint(xOffset, NSMinY([view frame]))]; |
+ } |
+ // And add the menu width to the bubble's width. |
+ NSView* menu = base::mac::ObjCCast<NSView>(permissionMenus[0]); |
groby-ooo-7-16
2014/04/18 18:18:18
Menu 0 is OK? We don't have varying sizes?
Also:
leng
2014/04/18 22:48:46
Changed to use a max width.
The NSUnionRect, above
|
+ bubbleFrame.size.width += NSWidth([menu frame]); |
+ } |
+ |
base::scoped_nsobject<NSView> titleView( |
[[self titleWithHostname:requests[0]->GetRequestingHostname().host()] |
retain]); |
@@ -290,6 +365,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 +402,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 +410,21 @@ 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_); |
+ PermissionBubbleView::Delegate* blockDelegate = delegate_; |
+ base::scoped_nsobject<AllowBlockMenuButton> button( |
+ [[AllowBlockMenuButton alloc] |
+ initWithIndex:index |
+ forURL:request->GetRequestingHostname() |
+ allowed:allow |
+ withCallback:^(int index, bool allow) { |
groby-ooo-7-16
2014/04/18 18:18:18
Why not just pass down the blockDelegate and save
leng
2014/04/18 22:48:46
Good idea. Done. I did the simplest version, whi
|
+ blockDelegate->ToggleAccept(index, allow); |
+ }]); |
+ return button.autorelease(); |
} |
- (NSView*)buttonWithTitle:(NSString*)title |
@@ -398,6 +475,14 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
return width; |
} |
+- (void)alignCenterOf:(NSView*)viewA verticallyToCenterOf:(NSView*)viewB { |
groby-ooo-7-16
2014/04/18 18:18:18
Grumble. I wish we had helper functions for this.
leng
2014/04/18 22:48:46
Me, too.
|
+ NSRect frameA = [viewA frame]; |
+ NSRect frameB = [viewB frame]; |
+ frameA.origin.y = |
+ NSMinY(frameB) + std::floor((NSHeight(frameB) - NSHeight(frameA)) / 2); |
+ [viewA setFrameOrigin:frameA.origin]; |
+} |
+ |
- (void)ok:(id)sender { |
DCHECK(delegate_); |
delegate_->Closing(); |
@@ -423,12 +508,6 @@ 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]; |