OLD | NEW |
---|---|
(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/website_settings/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/constrained_window/constrained_window_button.h" | |
17 #import "chrome/browser/ui/cocoa/hyperlink_text_view.h" | |
18 #import "chrome/browser/ui/cocoa/info_bubble_view.h" | |
19 #import "chrome/browser/ui/cocoa/info_bubble_window.h" | |
20 #include "chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h" | |
21 #include "chrome/browser/ui/website_settings/permission_bubble_delegate.h" | |
22 #include "chrome/browser/ui/website_settings/permission_bubble_view.h" | |
23 #include "content/public/browser/user_metrics.h" | |
24 #include "grit/generated_resources.h" | |
25 #include "skia/ext/skia_utils_mac.h" | |
26 #include "ui/base/l10n/l10n_util_mac.h" | |
27 | |
28 using base::UserMetricsAction; | |
29 | |
30 namespace { | |
31 const CGFloat kHorizontalPadding = 20.0f; | |
32 const CGFloat kVerticalPadding = 20.0f; | |
33 const CGFloat kButtonPadding = 10.0f; | |
34 const CGFloat kCheckboxYAdjustment = 2.0f; | |
35 | |
36 const CGFloat kButtonWidth = 80.0f; | |
37 const CGFloat kButtonHeight = 30.0f; | |
38 | |
39 const CGFloat kFontSize = 15.0f; | |
40 const base::char16 kBulletPoint = 0x2022; | |
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) | |
groby-ooo-7-16
2014/02/11 20:10:06
nit: since showAtAnchor computes a new window size
leng
2014/02/11 22:45:48
Done.
| |
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]; | |
groby-ooo-7-16
2014/02/11 20:10:06
[[self window] contentRectForFrameRect:[[self wind
leng
2014/02/11 22:45:48
I used NSZeroRect and added padding for it with th
| |
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++) { | |
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 [base::mac::ObjCCastStrict<NSButton>(permissionView) | |
115 setTag:(it - requests.begin())]; | |
groby-ooo-7-16
2014/02/11 20:10:06
setTag:index
leng
2014/02/11 22:45:48
Done.
| |
116 [checkboxes_ addObject:permissionView]; | |
117 } else { | |
118 permissionView.reset([[self labelForRequest:(*it) | |
119 isSingleRequest:singlePermission] retain]); | |
120 } | |
121 NSPoint origin = [permissionView frame].origin; | |
122 origin.x += kHorizontalPadding; | |
123 origin.y += yOffset; | |
124 [permissionView setFrameOrigin:origin]; | |
125 [contentView addSubview:permissionView]; | |
126 | |
127 yOffset += NSHeight([permissionView frame]); | |
128 } | |
129 | |
130 if (!singlePermission && !customizationMode) { | |
131 base::scoped_nsobject<NSView> customizeButton( | |
132 [[self customizationButton] retain]); | |
133 // The Y center should match the Y centers of the buttons. | |
134 CGFloat customizeButtonYOffset = kVerticalPadding + | |
135 (kButtonHeight - std::ceil(NSHeight([customizeButton frame])) * 0.5f); | |
groby-ooo-7-16
2014/02/11 20:10:06
I think the braces for ceil are off - you want to
leng
2014/02/11 22:45:48
Good catch! The ceil() was off, actually. Fixed.
| |
136 [customizeButton setFrameOrigin:NSMakePoint(kHorizontalPadding, | |
137 customizeButtonYOffset)]; | |
138 [contentView addSubview:customizeButton]; | |
139 } | |
140 | |
141 // The maximum width of the above permissions will dictate the width of the | |
142 // bubble. It is calculated here so that it can be used for the positioning | |
143 // of the buttons. | |
144 for (NSView* view in [contentView subviews]) | |
145 bubbleFrame = NSUnionRect(bubbleFrame, [view frame]); | |
146 | |
147 base::scoped_nsobject<NSView> allowOrOkButton; | |
148 if (customizationMode) { | |
149 allowOrOkButton.reset([[self buttonWithTitle:@"OK" | |
groby-ooo-7-16
2014/02/11 20:10:06
You should at some point i18n the text constants -
leng
2014/02/11 22:45:48
Yup, will do asap in a follow-up CL.
| |
150 action:@selector(ok:)] retain]); | |
151 } else { | |
152 allowOrOkButton.reset([[self buttonWithTitle:@"Allow" | |
153 action:@selector(onAllow:)] retain]); | |
154 } | |
155 CGFloat xOrigin = NSWidth(bubbleFrame) - kButtonWidth - kHorizontalPadding; | |
156 [allowOrOkButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)]; | |
157 [contentView addSubview:allowOrOkButton]; | |
158 | |
159 if (!customizationMode) { | |
160 base::scoped_nsobject<NSView> blockButton( | |
161 [[self buttonWithTitle:@"Block" action:@selector(onBlock:)] retain]); | |
162 xOrigin = NSMinX([allowOrOkButton frame]) - NSWidth([blockButton frame]) - | |
163 kButtonPadding; | |
164 [blockButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)]; | |
165 [contentView addSubview:blockButton]; | |
166 } | |
167 | |
168 if (!singlePermission) { | |
169 base::scoped_nsobject<NSView> titleView( | |
170 [[self titleForMultipleRequests] retain]); | |
171 [contentView addSubview:titleView]; | |
172 [titleView setFrameOrigin:NSMakePoint(kHorizontalPadding, | |
173 kVerticalPadding + yOffset)]; | |
174 yOffset += NSHeight([titleView frame]) + kVerticalPadding; | |
175 } | |
176 | |
177 bubbleFrame.size.height = yOffset + kVerticalPadding; | |
178 bubbleFrame = [[self window] frameRectForContentRect:bubbleFrame]; | |
179 [[self window] setFrame:bubbleFrame display:NO]; | |
180 | |
181 [self setAnchorPoint:anchorPoint]; | |
182 [self showWindow:nil]; | |
183 } | |
184 | |
185 - (NSView*)labelForRequest:(PermissionBubbleDelegate*)request | |
186 isSingleRequest:(BOOL)singleRequest { | |
187 DCHECK(request); | |
188 base::scoped_nsobject<NSTextField> permissionLabel( | |
189 [[NSTextField alloc] initWithFrame:NSZeroRect]); | |
190 base::string16 label; | |
191 if (!singleRequest) { | |
192 label.push_back(kBulletPoint); | |
193 label.push_back(' '); | |
194 } | |
195 if (singleRequest) { | |
196 // TODO(leng): Make the appropriate call when it's working. It should call | |
197 // GetMessageText(), but it's not returning the correct string yet. | |
198 label += request->GetMessageTextFragment(); | |
199 } else { | |
200 label += request->GetMessageTextFragment(); | |
201 } | |
202 [permissionLabel setDrawsBackground:NO]; | |
203 [permissionLabel setBezeled:NO]; | |
204 [permissionLabel setEditable:NO]; | |
205 [permissionLabel setSelectable:NO]; | |
206 [permissionLabel setStringValue:base::SysUTF16ToNSString(label)]; | |
207 [permissionLabel sizeToFit]; | |
208 return permissionLabel.autorelease(); | |
209 } | |
210 | |
211 - (NSView*)titleForMultipleRequests { | |
212 base::scoped_nsobject<NSTextField> titleView( | |
213 [[NSTextField alloc] initWithFrame:NSZeroRect]); | |
214 [titleView setDrawsBackground:NO]; | |
215 [titleView setBezeled:NO]; | |
216 [titleView setEditable:NO]; | |
217 [titleView setSelectable:NO]; | |
218 [titleView setStringValue:@"This site would like to:"]; | |
219 [titleView setFont:[NSFont systemFontOfSize:kFontSize]]; | |
220 [titleView sizeToFit]; | |
221 return titleView.autorelease(); | |
222 } | |
223 | |
224 - (NSView*)checkboxForRequest:(PermissionBubbleDelegate*)request | |
225 checked:(BOOL)checked { | |
226 DCHECK(request); | |
227 base::scoped_nsobject<NSButton> checkbox( | |
228 [[NSButton alloc] initWithFrame:NSZeroRect]); | |
229 [checkbox setButtonType:NSSwitchButton]; | |
230 base::string16 permission = request->GetMessageTextFragment(); | |
231 [checkbox setTitle:base::SysUTF16ToNSString(permission)]; | |
232 [checkbox setState:(checked ? NSOnState : NSOffState)]; | |
233 [checkbox setTarget:self]; | |
234 [checkbox setAction:@selector(onCheckboxChanged:)]; | |
235 [checkbox sizeToFit]; | |
236 [checkbox setFrameOrigin:NSMakePoint(0, kCheckboxYAdjustment)]; | |
groby-ooo-7-16
2014/02/11 20:10:06
since you're setting the permissionView's origin,
leng
2014/02/11 22:45:48
Yes - the calling function just adds to the existi
| |
237 return checkbox.autorelease(); | |
238 } | |
239 | |
240 - (NSView*)customizationButton { | |
241 NSColor* linkColor = | |
242 gfx::SkColorToCalibratedNSColor(chrome_style::GetLinkColor()); | |
243 base::scoped_nsobject<NSButton> customizeButton( | |
244 [[NSButton alloc] initWithFrame:NSZeroRect]); | |
245 [customizeButton setButtonType:NSMomentaryChangeButton]; | |
246 [customizeButton setAttributedTitle:[[NSAttributedString alloc] | |
247 initWithString:@"Customize" | |
248 attributes:@{ NSForegroundColorAttributeName : linkColor }]]; | |
249 [customizeButton setTarget:self]; | |
250 [customizeButton setAction:@selector(onCustomize:)]; | |
251 [customizeButton setBordered:NO]; | |
252 [customizeButton sizeToFit]; | |
253 return customizeButton.autorelease(); | |
254 } | |
255 | |
256 - (NSView*)buttonWithTitle:(NSString*)title | |
257 action:(SEL)action { | |
258 NSRect frame = NSMakeRect(0, 0, kButtonWidth, kButtonHeight); | |
groby-ooo-7-16
2014/02/11 20:10:06
N.B.: This will break for internationalized string
leng
2014/02/11 22:45:48
Good catch. Done.
| |
259 base::scoped_nsobject<NSButton> button( | |
260 [[ConstrainedWindowButton alloc] initWithFrame:frame]); | |
261 [button setButtonType:NSMomentaryPushInButton]; | |
262 [button setTitle:title]; | |
263 [button setTarget:self]; | |
264 [button setAction:action]; | |
265 return button.autorelease(); | |
266 } | |
267 | |
268 - (void)ok:(id)sender { | |
269 DCHECK(delegate_); | |
270 delegate_->Closing(); | |
271 } | |
272 | |
273 - (void)onAllow:(id)sender { | |
274 DCHECK(delegate_); | |
275 delegate_->Accept(); | |
276 } | |
277 | |
278 - (void)onBlock:(id)sender { | |
279 DCHECK(delegate_); | |
280 delegate_->Deny(); | |
281 } | |
282 | |
283 - (void)onCustomize:(id)sender { | |
284 DCHECK(delegate_); | |
285 delegate_->SetCustomizationMode(); | |
286 } | |
287 | |
288 - (void)onCheckboxChanged:(id)sender { | |
289 DCHECK(delegate_); | |
290 NSButton* checkbox = base::mac::ObjCCastStrict<NSButton>(sender); | |
291 delegate_->ToggleAccept([checkbox tag], [checkbox state] == NSOnState); | |
292 } | |
293 | |
294 @end // implementation PermissionBubbleController | |
OLD | NEW |