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

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: added a few comments 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/mac_util.h"
10 #include "base/strings/sys_string_conversions.h"
11 #include "base/strings/utf_string_conversions.h"
groby-ooo-7-16 2014/01/31 22:31:51 I don't think you're using these
leng 2014/02/03 22:38:04 Only the sys_strings one. Done.
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 kCheckboxYAdjustment = 2;
33
34 const CGFloat kButtonWidth = 80.0f;
35 const CGFloat kButtonHeight = 30.0f;
36
37 const base::char16 kBulletPoint = 0x2022;
38 const int kButtonBackgroundColor = 239;
groby-ooo-7-16 2014/01/31 22:31:51 Is that shared across platforms? Would it be worth
leng 2014/02/03 22:38:04 I don't know. I took the color from the mocks, so
groby-ooo-7-16 2014/02/04 02:39:29 "theme" file is fancy speak for a shared header wi
leng 2014/02/04 21:48:22 I see. Thanks. :) This is no longer relevant, th
39
40 } // namespace
41
42 @interface PermissionBubbleController (PrivateAPI)
groby-ooo-7-16 2014/01/31 22:31:51 just @interface PermissionBubbleController ()
leng 2014/02/03 22:38:04 Done.
43
44 // Called when the 'ok' button is pressed.
45 - (void)ok;
groby-ooo-7-16 2014/01/31 22:31:51 NSButton actions always take a sender parameter, i
leng 2014/02/03 22:38:04 Done.
46
47 // Called when the 'allow' button is pressed.
48 - (void)allow;
groby-ooo-7-16 2014/01/31 22:31:51 nit: Button callbacks except ok/cancel are usually
leng 2014/02/03 22:38:04 Done.
49
50 // Called when the 'block' button is pressed.
51 - (void)block;
52
53 // Called when the 'customize' button is pressed.
54 - (void)customize;
55
56 // Called when a checkbox changes from checked to unchecked, or vice versa.
57 - (void)checkboxChanged;
58
59 @end
60
61 @implementation PermissionBubbleController
62
63 - (id)initWithParentWindow:(NSWindow*)parentWindow
64 bridge:(PermissionBubbleCocoa*)bridge {
65 DCHECK(parentWindow);
66 DCHECK(bridge);
67 base::scoped_nsobject<InfoBubbleWindow> window(
68 [[InfoBubbleWindow alloc] initWithContentRect:NSMakeRect(0, 0, 240, 150)
69 styleMask:NSBorderlessWindowMask
70 backing:NSBackingStoreBuffered
71 defer:NO]);
72 [window setAllowedAnimations:info_bubble::kAnimateNone];
73 if ((self = [super initWithWindow:window
74 parentWindow:parentWindow
75 anchoredAt:NSZeroPoint])) {
76 [self setShouldCloseOnResignKey:NO];
77 [[self bubble] setArrowLocation:info_bubble::kTopLeft];
78 bridge_ = bridge;
79 }
80 return self;
81 }
82
83 - (void)windowWillClose:(NSNotification*)notification {
84 bridge_->OnBubbleClosing();
85 [super windowWillClose:notification];
86 }
87
88 - (void)showAtAnchor:(NSPoint)anchorPoint
89 withDelegate:(PermissionBubbleView::Delegate*)delegate
90 forRequests:(const std::vector<PermissionBubbleDelegate*>&)requests
91 acceptStates:(const std::vector<bool>&)acceptStates
92 customizationMode:(BOOL)customizationMode {
93 DCHECK(!customizationMode || (requests.size() == acceptStates.size()));
94 delegate_ = delegate;
95
96 base::scoped_nsobject<NSMutableArray> subViews([[NSMutableArray alloc] init]);
groby-ooo-7-16 2014/01/31 22:31:51 Curious: Why a separate NSMutableArray, instead of
leng 2014/02/03 22:38:04 I was copying the behavior from another class. Si
97 BOOL singlePermission = requests.size() == 1;
98 NSRect windowFrame = [[self window] frame];
99 CGFloat yOffset = 2 * kVerticalPadding + kButtonHeight;
100 CGFloat maxContentsWidth = 0.0f;
101
102 for (auto it = requests.begin(); it != requests.end(); it++) {
103 base::scoped_nsobject<NSView> permissionView;
104 if (customizationMode) {
105 int index = it - requests.begin();
106 permissionView.reset(
107 [[self checkboxForRequest:(*it)
108 checked:acceptStates[index] ? YES : NO] retain]);
109 checkboxes_.push_back(permissionView);
110 } else {
111 permissionView.reset([[self labelForRequest:(*it)
112 isSingleRequest:singlePermission] retain]);
113 }
114 NSPoint origin = [permissionView frame].origin;
115 origin.x += kHorizontalPadding;
116 origin.y += yOffset;
117 [permissionView setFrameOrigin:origin];
118 [subViews addObject:permissionView];
119
120 yOffset += [permissionView frame].size.height;
groby-ooo-7-16 2014/01/31 22:31:51 NSHeight([permissionView frame])
leng 2014/02/03 22:38:04 Done.
121 maxContentsWidth = fmax(maxContentsWidth,
groby-ooo-7-16 2014/01/31 22:31:51 Please use std::max Alternatively, you can simply
leng 2014/02/03 22:38:04 I like it - thanks! Done.
122 [permissionView frame].size.width);
123 }
124
125 if (!singlePermission && !customizationMode) {
126 base::scoped_nsobject<NSView> customizeButton(
127 [[self customizationButton] retain]);
128 // The Y center should match the Y centers of the buttons.
129 CGFloat customizeButtonYOffset = kVerticalPadding +
130 (kButtonHeight - [customizeButton frame].size.height) * 0.5f;
groby-ooo-7-16 2014/01/31 22:31:51 You might want to ceil/floor this so the button is
leng 2014/02/03 22:38:04 Done.
131 [customizeButton setFrameOrigin:NSMakePoint(kHorizontalPadding,
132 customizeButtonYOffset)];
133 [subViews addObject:customizeButton];
134 }
135
136 CGFloat windowWidth = fmax(windowFrame.size.width,
groby-ooo-7-16 2014/01/31 22:31:51 NSWidth(windowFrame) - we like to pretend we don't
leng 2014/02/03 22:38:04 I didn't know that the structure of NSRect was so
137 maxContentsWidth + 2 * kHorizontalPadding);
138 base::scoped_nsobject<NSView> allowOrOkButton;
139 if (customizationMode) {
140 allowOrOkButton.reset([[self buttonWithTitle:@"OK"
141 action:@selector(ok)] retain]);
142 } else {
143 allowOrOkButton.reset([[self buttonWithTitle:@"Allow"
144 action:@selector(allow)] retain]);
145 }
146 CGFloat xOrigin = windowWidth - kButtonWidth - kHorizontalPadding;
147 [allowOrOkButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)];
148 [subViews addObject:allowOrOkButton];
149
150 if (!customizationMode) {
151 base::scoped_nsobject<NSView> blockButton(
152 [[self buttonWithTitle:@"Block" action:@selector(block)] retain]);
153 xOrigin = NSMinX([allowOrOkButton frame]) - [blockButton frame].size.width -
154 kHorizontalPadding * 0.5f;
groby-ooo-7-16 2014/01/31 22:31:51 I assume kHorizontalPadding/2 is the gap you want
leng 2014/02/03 22:38:04 Done.
155 [blockButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)];
156 [subViews addObject:blockButton];
157 }
158
159 if (!singlePermission) {
160 base::scoped_nsobject<NSView> titleView(
161 [[self titleForMultipleRequests] retain]);
162 [subViews addObject:titleView];
163 [titleView setFrameOrigin:NSMakePoint(kHorizontalPadding,
164 kVerticalPadding + yOffset)];
165 yOffset += [titleView frame].size.height + kVerticalPadding;
166 }
167
168 [[[self window] contentView] setSubviews:subViews];
169 windowFrame.size.width = windowWidth;
170 windowFrame.size.height = yOffset + kVerticalPadding;
groby-ooo-7-16 2014/01/31 22:31:51 Please use -frameRectForContentRect to convert the
leng 2014/02/03 22:38:04 Good catch. I've changed the name of the variable
171 [[self window] setFrame:windowFrame display:NO];
172
173 [self setAnchorPoint:anchorPoint];
174 [self showWindow:nil];
175 }
176
177 - (NSView*)labelForRequest:(PermissionBubbleDelegate*)request
178 isSingleRequest:(BOOL)singleRequest {
179 DCHECK(request);
180 base::scoped_nsobject<NSTextField> permissionLabel(
181 [[NSTextField alloc] initWithFrame:NSZeroRect]);
182 base::string16 label;
183 if (!singleRequest) {
184 label.push_back(kBulletPoint);
185 label.push_back(' ');
186 }
187 // TODO(leng): Make the appropriate call when it's working.
groby-ooo-7-16 2014/01/31 22:31:51 When what is working?
leng 2014/02/03 22:38:04 When the correct text-query function is working.
groby-ooo-7-16 2014/02/04 02:39:29 Much - thank you!
groby-ooo-7-16 2014/02/04 02:39:29 Much - thank you!
188 if (singleRequest) {
189 label += request->GetMessageTextFragment();
190 } else {
191 label += request->GetMessageTextFragment();
groby-ooo-7-16 2014/01/31 22:31:51 Can that if/else collapse, or is that related to t
leng 2014/02/03 22:38:04 Related to the TODO.
192 }
193 [permissionLabel setDrawsBackground:NO];
194 [permissionLabel setBezeled:NO];
195 [permissionLabel setEditable:NO];
196 [permissionLabel setSelectable:NO];
197 [permissionLabel setStringValue:base::SysUTF16ToNSString(label)];
198 [permissionLabel sizeToFit];
199 return permissionLabel.autorelease();
200 }
201
202 - (NSView*)titleForMultipleRequests {
203 base::scoped_nsobject<NSTextField> titleView(
204 [[NSTextField alloc] initWithFrame:NSZeroRect]);
205 [titleView setDrawsBackground:NO];
206 [titleView setBezeled:NO];
207 [titleView setEditable:NO];
208 [titleView setSelectable:NO];
209 [titleView setStringValue:@"This site would like to:"];
210 [titleView setFont:[NSFont systemFontOfSize:15.0]];
groby-ooo-7-16 2014/01/31 22:31:51 Might want to call this out as constant
leng 2014/02/03 22:38:04 Done.
211 [titleView sizeToFit];
212 return titleView.autorelease();
213 }
214
215 - (NSView*)checkboxForRequest:(PermissionBubbleDelegate*)request
216 checked:(BOOL)checked {
217 DCHECK(request);
218 base::scoped_nsobject<NSButton> checkbox(
219 [[NSButton alloc] initWithFrame:NSZeroRect]);
220 [checkbox setButtonType:NSSwitchButton];
221 base::string16 permission = request->GetMessageTextFragment();
222 [checkbox setTitle:base::SysUTF16ToNSString(permission)];
223 [checkbox setState:checked ? NSOnState : NSOffState];
groby-ooo-7-16 2014/01/31 22:31:51 nit: Parens around ternary
leng 2014/02/03 22:38:04 Done.
224 [checkbox sizeToFit];
225 [checkbox setFrameOrigin:NSMakePoint(0, kCheckboxYAdjustment)];
226 return checkbox.autorelease();
227 }
228
229 - (NSView*)customizationButton {
230 NSColor* linkColor =
231 gfx::SkColorToCalibratedNSColor(chrome_style::GetLinkColor());
232 base::scoped_nsobject<NSButton> customizeButton(
233 [[NSButton alloc] initWithFrame:NSZeroRect]);
234 [customizeButton setButtonType:NSMomentaryChangeButton];
235 [customizeButton setAttributedTitle:[[NSAttributedString alloc]
236 initWithString:@"Customize"
237 attributes:@{ NSForegroundColorAttributeName : linkColor }]];
groby-ooo-7-16 2014/01/31 22:31:51 Is the customizeButton supposed to be an actual li
leng 2014/02/03 22:38:04 I tried that, but not only is it significantly mor
groby-ooo-7-16 2014/02/04 02:39:29 Odd - it should be very simple: linkView([[HyperL
leng 2014/02/04 21:48:22 So I think I ignored, or glossed over, the questio
238 [customizeButton setTarget:self];
239 [customizeButton setAction:@selector(customize:)];
240 [customizeButton sizeToFit];
241 return customizeButton.autorelease();
242 }
243
244 - (NSView*)buttonWithTitle:(NSString*)title
245 action:(SEL)action {
246 NSRect frame = { {0, 0}, {kButtonWidth, kButtonHeight} };
groby-ooo-7-16 2014/01/31 22:31:51 NSRect frame = NSMakeRect(0, 0, kButtonWidth, kBut
leng 2014/02/03 22:38:04 Done.
247 NSColor* buttonBackgroundColor =
248 gfx::SkColorToCalibratedNSColor(SkColorSetRGB(kButtonBackgroundColor,
249 kButtonBackgroundColor,
250 kButtonBackgroundColor));
251
252 base::scoped_nsobject<NSButton> button(
253 [[NSButton alloc] initWithFrame:frame]);
254 [button setButtonType:NSMomentaryPushInButton];
255 [button setTitle:title];
256 [button setTarget:self];
257 [button setAction:action];
258 [button setBordered:NO];
259 [[button cell] setBackgroundColor:buttonBackgroundColor];
260 return button.autorelease();
261 }
262
263 - (void)ok {
264 if (delegate_)
groby-ooo-7-16 2014/01/31 22:31:51 I'd prefer a DCHECK delegate_ - I don't think this
leng 2014/02/03 22:38:04 It was when I was first testing - I have an outsta
265 delegate_->Closing();
266 }
267
268 - (void)allow {
269 if (delegate_)
270 delegate_->Accept();
271 }
272
273 - (void)block {
274 if (delegate_)
275 delegate_->Deny();
276 }
277
278 - (void)customize {
279 if (delegate_)
280 delegate_->SetCustomizationMode();
281 }
282
283 - (void)checkboxChanged:(id)sender {
284 if (!delegate_)
285 return;
286 auto iter = std::find(
287 checkboxes_.begin(), checkboxes_.end(), (NSView*)sender);
288 if (iter != checkboxes_.end()) {
groby-ooo-7-16 2014/01/31 22:31:51 Again, probably DCHECK. Can never happen
leng 2014/02/03 22:38:04 Done.
289 NSButton *checkbox = (NSButton*)(*iter);
groby-ooo-7-16 2014/01/31 22:31:51 base::ObjCCast or base::ObjCCastStrict when castin
leng 2014/02/03 22:38:04 I had no idea about ObjCCast and ObjCCastStrict.
290 delegate_->ToggleAccept(iter - checkboxes_.begin(),
291 [checkbox state] == NSOnState);
292 }
293 }
294
295 @end // implementation BookmarkBubbleController(ExposedForUnitTesting)
groby-ooo-7-16 2014/01/31 22:31:51 ExposedForUnitTesting?
leng 2014/02/03 22:38:04 BookmarkBubbleController? Now you know how I start
groby-ooo-7-16 2014/02/04 02:39:29 Heh - I didn't even notice that part. Bad reviewer
groby-ooo-7-16 2014/02/04 02:39:29 Heh - I didn't even notice that part. Bad reviewer
leng 2014/02/04 21:48:22 Am I supposed to pay you with cookies? Are you al
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698