Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(114)

Side by Side Diff: chrome/browser/ui/cocoa/permission_bubble_controller.mm

Issue 151593005: Implement permission bubble view for Cocoa. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: more feedback addressed Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2014 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 #import "chrome/browser/ui/cocoa/permission_bubble_controller.h"
6
7 #include <algorithm>
8
9 #include "base/mac/foundation_util.h"
10 #include "base/mac/mac_util.h"
11 #include "base/strings/sys_string_conversions.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_finder.h"
14 #import "chrome/browser/ui/chrome_style.h"
15 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
16 #import "chrome/browser/ui/cocoa/hyperlink_text_view.h"
17 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
18 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
19 #include "chrome/browser/ui/cocoa/permission_bubble_cocoa.h"
20 #include "chrome/browser/ui/website_settings/permission_bubble_delegate.h"
21 #include "chrome/browser/ui/website_settings/permission_bubble_view.h"
22 #include "content/public/browser/user_metrics.h"
23 #include "grit/generated_resources.h"
24 #include "skia/ext/skia_utils_mac.h"
25 #include "ui/base/l10n/l10n_util_mac.h"
26
27 using base::UserMetricsAction;
28
29 namespace {
30 const CGFloat kHorizontalPadding = 20.0f;
31 const CGFloat kVerticalPadding = 20.0f;
32 const CGFloat kButtonPadding = 10.0f;
33 const CGFloat kCheckboxYAdjustment = 2.0f;
34
35 const CGFloat kButtonWidth = 80.0f;
36 const CGFloat kButtonHeight = 30.0f;
37
38 const CGFloat kFontSize = 15.0f;
39 const base::char16 kBulletPoint = 0x2022;
40 const int kButtonBackgroundColor = 239;
41
42 } // namespace
43
44 @interface PermissionBubbleController ()
45
46 // Called when the 'ok' button is pressed.
47 - (void)ok:(id)sender;
48
49 // Called when the 'allow' button is pressed.
50 - (void)onAllow:(id)sender;
51
52 // Called when the 'block' button is pressed.
53 - (void)onBlock:(id)sender;
54
55 // Called when the 'customize' button is pressed.
56 - (void)onCustomize:(id)sender;
57
58 // Called when a checkbox changes from checked to unchecked, or vice versa.
59 - (void)onCheckboxChanged:(id)sender;
60
61 @end
62
63 @implementation PermissionBubbleController
64
65 - (id)initWithParentWindow:(NSWindow*)parentWindow
66 bridge:(PermissionBubbleCocoa*)bridge {
67 DCHECK(parentWindow);
68 DCHECK(bridge);
69 base::scoped_nsobject<InfoBubbleWindow> window(
70 [[InfoBubbleWindow alloc] initWithContentRect:NSMakeRect(0, 0, 240, 150)
71 styleMask:NSBorderlessWindowMask
72 backing:NSBackingStoreBuffered
73 defer:NO]);
74 [window setAllowedAnimations:info_bubble::kAnimateNone];
75 if ((self = [super initWithWindow:window
76 parentWindow:parentWindow
77 anchoredAt:NSZeroPoint])) {
78 [self setShouldCloseOnResignKey:NO];
79 [[self bubble] setArrowLocation:info_bubble::kTopLeft];
80 bridge_ = bridge;
81 }
82 return self;
83 }
84
85 - (void)windowWillClose:(NSNotification*)notification {
86 bridge_->OnBubbleClosing();
87 [super windowWillClose:notification];
88 }
89
90 - (void)showAtAnchor:(NSPoint)anchorPoint
91 withDelegate:(PermissionBubbleView::Delegate*)delegate
92 forRequests:(const std::vector<PermissionBubbleDelegate*>&)requests
93 acceptStates:(const std::vector<bool>&)acceptStates
94 customizationMode:(BOOL)customizationMode {
95 DCHECK(delegate);
96 DCHECK(!customizationMode || (requests.size() == acceptStates.size()));
97 delegate_ = delegate;
98
99 NSView* contentView = [[self window] contentView];
100 DCHECK([[contentView subviews] count] == 0);
101
102 BOOL singlePermission = requests.size() == 1;
103 NSRect bubbleFrame = [[self window] frame];
104 CGFloat yOffset = 2 * kVerticalPadding + kButtonHeight;
105
106 checkboxes_.reset(customizationMode ? [[NSMutableArray alloc] init] : nil);
107 for (auto it = requests.begin(); it != requests.end(); it++) {
groby-ooo-7-16 2014/02/04 02:39:30 I envy our Cocoa code the ability to use auto... W
108 base::scoped_nsobject<NSView> permissionView;
109 if (customizationMode) {
110 int index = it - requests.begin();
111 permissionView.reset(
112 [[self checkboxForRequest:(*it)
113 checked:acceptStates[index] ? YES : NO] retain]);
114 [checkboxes_ addObject:permissionView];
115 } else {
116 permissionView.reset([[self labelForRequest:(*it)
117 isSingleRequest:singlePermission] retain]);
118 }
119 NSPoint origin = [permissionView frame].origin;
120 origin.x += kHorizontalPadding;
121 origin.y += yOffset;
122 [permissionView setFrameOrigin:origin];
123 [contentView addSubview:permissionView];
124
125 yOffset += NSHeight([permissionView frame]);
126 }
127
128 if (!singlePermission && !customizationMode) {
129 base::scoped_nsobject<NSView> customizeButton(
130 [[self customizationButton] retain]);
131 // The Y center should match the Y centers of the buttons.
132 CGFloat customizeButtonYOffset = kVerticalPadding +
133 (kButtonHeight - std::ceil(NSHeight([customizeButton frame])) * 0.5f);
134 [customizeButton setFrameOrigin:NSMakePoint(kHorizontalPadding,
135 customizeButtonYOffset)];
136 [contentView addSubview:customizeButton];
137 }
138
139 // The maximum width of the above permissions will dictate the width of the
140 // bubble. It is calculated here so that it can be used for the positioning
141 // of the buttons.
142 for (NSView* view in [contentView subviews])
143 bubbleFrame = NSUnionRect(bubbleFrame, [view frame]);
144 bubbleFrame.size.width += 250;
groby-ooo-7-16 2014/02/04 02:39:30 Please call out as constant - I'm not sure why 250
leng 2014/02/04 21:48:22 That was a testing leftover... thanks for catchin
145
146 base::scoped_nsobject<NSView> allowOrOkButton;
147 if (customizationMode) {
148 allowOrOkButton.reset([[self buttonWithTitle:@"OK"
149 action:@selector(ok:)] retain]);
150 } else {
151 allowOrOkButton.reset([[self buttonWithTitle:@"Allow"
152 action:@selector(onAllow:)] retain]);
153 }
154 CGFloat xOrigin = NSWidth(bubbleFrame) - kButtonWidth - kHorizontalPadding;
155 [allowOrOkButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)];
156 [contentView addSubview:allowOrOkButton];
157
158 if (!customizationMode) {
159 base::scoped_nsobject<NSView> blockButton(
160 [[self buttonWithTitle:@"Block" action:@selector(onBlock:)] retain]);
161 xOrigin = NSMinX([allowOrOkButton frame]) - NSWidth([blockButton frame]) -
162 kButtonPadding;
163 [blockButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)];
164 [contentView addSubview:blockButton];
165 }
166
167 if (!singlePermission) {
168 base::scoped_nsobject<NSView> titleView(
169 [[self titleForMultipleRequests] retain]);
170 [contentView addSubview:titleView];
171 [titleView setFrameOrigin:NSMakePoint(kHorizontalPadding,
172 kVerticalPadding + yOffset)];
173 yOffset += NSHeight([titleView frame]) + kVerticalPadding;
174 }
175
176 bubbleFrame.size.height = yOffset + kVerticalPadding;
177 bubbleFrame = [[self window] frameRectForContentRect:bubbleFrame];
178 [[self window] setFrame:bubbleFrame display:NO];
179
180 [self setAnchorPoint:anchorPoint];
181 [self showWindow:nil];
182 }
183
184 - (NSView*)labelForRequest:(PermissionBubbleDelegate*)request
185 isSingleRequest:(BOOL)singleRequest {
186 DCHECK(request);
187 base::scoped_nsobject<NSTextField> permissionLabel(
188 [[NSTextField alloc] initWithFrame:NSZeroRect]);
189 base::string16 label;
190 if (!singleRequest) {
191 label.push_back(kBulletPoint);
192 label.push_back(' ');
193 }
194 if (singleRequest) {
195 // TODO(leng): Make the appropriate call when it's working. It should call
196 // GetMessageText(), but it's not returning the correct string yet.
197 label += request->GetMessageTextFragment();
198 } else {
199 label += request->GetMessageTextFragment();
200 }
201 [permissionLabel setDrawsBackground:NO];
202 [permissionLabel setBezeled:NO];
203 [permissionLabel setEditable:NO];
204 [permissionLabel setSelectable:NO];
205 [permissionLabel setStringValue:base::SysUTF16ToNSString(label)];
206 [permissionLabel sizeToFit];
207 return permissionLabel.autorelease();
208 }
209
210 - (NSView*)titleForMultipleRequests {
211 base::scoped_nsobject<NSTextField> titleView(
212 [[NSTextField alloc] initWithFrame:NSZeroRect]);
213 [titleView setDrawsBackground:NO];
214 [titleView setBezeled:NO];
215 [titleView setEditable:NO];
216 [titleView setSelectable:NO];
217 [titleView setStringValue:@"This site would like to:"];
218 [titleView setFont:[NSFont systemFontOfSize:kFontSize]];
219 [titleView sizeToFit];
220 return titleView.autorelease();
221 }
222
223 - (NSView*)checkboxForRequest:(PermissionBubbleDelegate*)request
224 checked:(BOOL)checked {
225 DCHECK(request);
226 base::scoped_nsobject<NSButton> checkbox(
227 [[NSButton alloc] initWithFrame:NSZeroRect]);
228 [checkbox setButtonType:NSSwitchButton];
229 base::string16 permission = request->GetMessageTextFragment();
230 [checkbox setTitle:base::SysUTF16ToNSString(permission)];
231 [checkbox setState:(checked ? NSOnState : NSOffState)];
232 [checkbox setTarget:self];
233 [checkbox setAction:@selector(onCheckboxChanged:)];
234 [checkbox sizeToFit];
235 [checkbox setFrameOrigin:NSMakePoint(0, kCheckboxYAdjustment)];
236 return checkbox.autorelease();
237 }
238
239 - (NSView*)customizationButton {
240 NSColor* linkColor =
241 gfx::SkColorToCalibratedNSColor(chrome_style::GetLinkColor());
242 base::scoped_nsobject<NSButton> customizeButton(
243 [[NSButton alloc] initWithFrame:NSZeroRect]);
244 [customizeButton setButtonType:NSMomentaryChangeButton];
245 [customizeButton setAttributedTitle:[[NSAttributedString alloc]
246 initWithString:@"Customize"
247 attributes:@{ NSForegroundColorAttributeName : linkColor }]];
248 [customizeButton setTarget:self];
249 [customizeButton setAction:@selector(onCustomize:)];
250 [customizeButton sizeToFit];
251 return customizeButton.autorelease();
252 }
253
254 - (NSView*)buttonWithTitle:(NSString*)title
255 action:(SEL)action {
256 NSRect frame = NSMakeRect(0, 0, kButtonWidth, kButtonHeight);
257 NSColor* buttonBackgroundColor =
258 gfx::SkColorToCalibratedNSColor(SkColorSetRGB(kButtonBackgroundColor,
259 kButtonBackgroundColor,
260 kButtonBackgroundColor));
261
262 base::scoped_nsobject<NSButton> button(
263 [[NSButton alloc] initWithFrame:frame]);
264 [button setButtonType:NSMomentaryPushInButton];
265 [button setTitle:title];
266 [button setTarget:self];
267 [button setAction:action];
268 [button setBordered:NO];
269 [[button cell] setBackgroundColor:buttonBackgroundColor];
groby-ooo-7-16 2014/02/04 02:39:30 I'm wondering about the backgroundcolor thing. Loo
leng 2014/02/04 21:48:22 I did - and we're using the version with the borde
270 return button.autorelease();
271 }
272
273 - (void)ok:(id)sender {
274 DCHECK(delegate_);
275 delegate_->Closing();
276 }
277
278 - (void)onAllow:(id)sender {
279 DCHECK(delegate_);
280 delegate_->Accept();
281 }
282
283 - (void)onBlock:(id)sender {
284 DCHECK(delegate_);
285 delegate_->Deny();
286 }
287
288 - (void)onCustomize:(id)sender {
289 DCHECK(delegate_);
290 delegate_->SetCustomizationMode();
291 }
292
293 - (void)onCheckboxChanged:(id)sender {
294 DCHECK(delegate_);
295 NSButton* checkbox = base::mac::ObjCCastStrict<NSButton>(sender);
296 NSUInteger index = [checkboxes_ indexOfObject:checkbox];
groby-ooo-7-16 2014/02/04 02:39:30 If you want to avoid indexOfObject:, you can inste
leng 2014/02/04 21:48:22 Done.
297 DCHECK(index != NSNotFound);
298 delegate_->ToggleAccept(index, [checkbox state] == NSOnState);
299 }
300
301 @end // implementation PermissionBubbleController
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698