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

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

Issue 151593005: Implement permission bubble view for Cocoa. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Ensure only one delegate owns the view at a time Created 6 years, 9 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/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_request.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/cocoa/window_size_constants.h"
27 #include "ui/base/l10n/l10n_util_mac.h"
28
29 using base::UserMetricsAction;
30
31 namespace {
32 const CGFloat kHorizontalPadding = 20.0f;
33 const CGFloat kVerticalPadding = 20.0f;
34 const CGFloat kButtonPadding = 10.0f;
35 const CGFloat kCheckboxYAdjustment = 2.0f;
36
37 const CGFloat kFontSize = 15.0f;
38 const base::char16 kBulletPoint = 0x2022;
39
40 } // namespace
41
42 @interface PermissionBubbleController ()
43
44 // Called when the 'ok' button is pressed.
45 - (void)ok:(id)sender;
46
47 // Called when the 'allow' button is pressed.
48 - (void)onAllow:(id)sender;
49
50 // Called when the 'block' button is pressed.
51 - (void)onBlock:(id)sender;
52
53 // Called when the 'customize' button is pressed.
54 - (void)onCustomize:(id)sender;
55
56 // Called when a checkbox changes from checked to unchecked, or vice versa.
57 - (void)onCheckboxChanged:(id)sender;
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([[InfoBubbleWindow alloc]
68 initWithContentRect:ui::kWindowSizeDeterminedLater
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<PermissionBubbleRequest*>&)requests
91 acceptStates:(const std::vector<bool>&)acceptStates
92 customizationMode:(BOOL)customizationMode {
93 DCHECK(delegate);
94 DCHECK(!customizationMode || (requests.size() == acceptStates.size()));
95 delegate_ = delegate;
96
97 NSView* contentView = [[self window] contentView];
98 DCHECK([[contentView subviews] count] == 0);
99
100 // Create one button to use as a guide for the permissions' y-offsets.
101 base::scoped_nsobject<NSView> allowOrOkButton;
102 if (customizationMode) {
103 allowOrOkButton.reset([[self buttonWithTitle:@"OK"
104 action:@selector(ok:)
105 pairedWith:nil] retain]);
106 } else {
107 allowOrOkButton.reset([[self buttonWithTitle:@"Allow"
108 action:@selector(onAllow:)
109 pairedWith:nil] retain]);
110 }
111 CGFloat yOffset = 2 * kVerticalPadding + NSMaxY([allowOrOkButton frame]);
112 BOOL singlePermission = requests.size() == 1;
113
114 checkboxes_.reset(customizationMode ? [[NSMutableArray alloc] init] : nil);
115 for (auto it = requests.begin(); it != requests.end(); it++) {
116 base::scoped_nsobject<NSView> permissionView;
117 if (customizationMode) {
118 int index = it - requests.begin();
119 permissionView.reset(
120 [[self checkboxForRequest:(*it)
121 checked:acceptStates[index] ? YES : NO] retain]);
122 [base::mac::ObjCCastStrict<NSButton>(permissionView) setTag:index];
123 [checkboxes_ addObject:permissionView];
124 } else {
125 permissionView.reset([[self labelForRequest:(*it)
126 isSingleRequest:singlePermission] retain]);
127 }
128 NSPoint origin = [permissionView frame].origin;
129 origin.x += kHorizontalPadding;
130 origin.y += yOffset;
131 [permissionView setFrameOrigin:origin];
132 [contentView addSubview:permissionView];
133
134 yOffset += NSHeight([permissionView frame]);
135 }
136
137 // The maximum width of the above permissions will dictate the width of the
138 // bubble. It is calculated here so that it can be used for the positioning
139 // of the buttons.
140 NSRect bubbleFrame = NSZeroRect;
141 for (NSView* view in [contentView subviews]) {
142 bubbleFrame = NSUnionRect(
143 bubbleFrame, NSInsetRect([view frame], -kHorizontalPadding, 0));
144 }
145
146 // Position the allow/ok button.
147 CGFloat xOrigin = NSWidth(bubbleFrame) - NSWidth([allowOrOkButton frame]) -
148 kHorizontalPadding;
149 [allowOrOkButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)];
150 [contentView addSubview:allowOrOkButton];
151
152 if (!customizationMode) {
153 base::scoped_nsobject<NSView> blockButton(
154 [[self buttonWithTitle:@"Block"
155 action:@selector(onBlock:)
156 pairedWith:allowOrOkButton] retain]);
157 xOrigin = NSMinX([allowOrOkButton frame]) - NSWidth([blockButton frame]) -
158 kButtonPadding;
159 [blockButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)];
160 [contentView addSubview:blockButton];
161 }
162
163 if (!singlePermission && !customizationMode) {
164 base::scoped_nsobject<NSView> customizeButton(
165 [[self customizationButton] retain]);
166 // The Y center should match the Y centers of the buttons.
167 CGFloat customizeButtonYOffset = kVerticalPadding +
168 std::ceil(0.5f * (NSHeight([allowOrOkButton frame]) -
169 NSHeight([customizeButton frame])));
170 [customizeButton setFrameOrigin:NSMakePoint(kHorizontalPadding,
171 customizeButtonYOffset)];
172 [contentView addSubview:customizeButton];
173 }
174
175 if (!singlePermission) {
176 base::scoped_nsobject<NSView> titleView(
177 [[self titleForMultipleRequests] retain]);
178 [contentView addSubview:titleView];
179 [titleView setFrameOrigin:NSMakePoint(kHorizontalPadding,
180 kVerticalPadding + yOffset)];
181 yOffset += NSHeight([titleView frame]) + kVerticalPadding;
182 }
183
184 bubbleFrame.size.height = yOffset + kVerticalPadding;
185 bubbleFrame = [[self window] frameRectForContentRect:bubbleFrame];
186 [[self window] setFrame:bubbleFrame display:NO];
187
188 [self setAnchorPoint:anchorPoint];
189 [self showWindow:nil];
190 }
191
192 - (NSView*)labelForRequest:(PermissionBubbleRequest*)request
193 isSingleRequest:(BOOL)singleRequest {
194 DCHECK(request);
195 base::scoped_nsobject<NSTextField> permissionLabel(
196 [[NSTextField alloc] initWithFrame:NSZeroRect]);
197 base::string16 label;
198 if (!singleRequest) {
199 label.push_back(kBulletPoint);
200 label.push_back(' ');
201 }
202 if (singleRequest) {
203 // TODO(leng): Make the appropriate call when it's working. It should call
204 // GetMessageText(), but it's not returning the correct string yet.
205 label += request->GetMessageTextFragment();
206 } else {
207 label += request->GetMessageTextFragment();
208 }
209 [permissionLabel setDrawsBackground:NO];
210 [permissionLabel setBezeled:NO];
211 [permissionLabel setEditable:NO];
212 [permissionLabel setSelectable:NO];
213 [permissionLabel setStringValue:base::SysUTF16ToNSString(label)];
214 [permissionLabel sizeToFit];
215 return permissionLabel.autorelease();
216 }
217
218 - (NSView*)titleForMultipleRequests {
219 base::scoped_nsobject<NSTextField> titleView(
220 [[NSTextField alloc] initWithFrame:NSZeroRect]);
221 [titleView setDrawsBackground:NO];
222 [titleView setBezeled:NO];
223 [titleView setEditable:NO];
224 [titleView setSelectable:NO];
225 [titleView setStringValue:@"This site would like to:"];
226 [titleView setFont:[NSFont systemFontOfSize:kFontSize]];
227 [titleView sizeToFit];
228 return titleView.autorelease();
229 }
230
231 - (NSView*)checkboxForRequest:(PermissionBubbleRequest*)request
232 checked:(BOOL)checked {
233 DCHECK(request);
234 base::scoped_nsobject<NSButton> checkbox(
235 [[NSButton alloc] initWithFrame:NSZeroRect]);
236 [checkbox setButtonType:NSSwitchButton];
237 base::string16 permission = request->GetMessageTextFragment();
238 [checkbox setTitle:base::SysUTF16ToNSString(permission)];
239 [checkbox setState:(checked ? NSOnState : NSOffState)];
240 [checkbox setTarget:self];
241 [checkbox setAction:@selector(onCheckboxChanged:)];
242 [checkbox sizeToFit];
243 [checkbox setFrameOrigin:NSMakePoint(0, kCheckboxYAdjustment)];
244 return checkbox.autorelease();
245 }
246
247 - (NSView*)customizationButton {
248 NSColor* linkColor =
249 gfx::SkColorToCalibratedNSColor(chrome_style::GetLinkColor());
250 base::scoped_nsobject<NSButton> customizeButton(
251 [[NSButton alloc] initWithFrame:NSZeroRect]);
252 [customizeButton setButtonType:NSMomentaryChangeButton];
253 [customizeButton setAttributedTitle:[[NSAttributedString alloc]
254 initWithString:@"Customize"
255 attributes:@{ NSForegroundColorAttributeName : linkColor }]];
256 [customizeButton setTarget:self];
257 [customizeButton setAction:@selector(onCustomize:)];
258 [customizeButton setBordered:NO];
259 [customizeButton sizeToFit];
260 return customizeButton.autorelease();
261 }
262
263 - (NSView*)buttonWithTitle:(NSString*)title
264 action:(SEL)action
265 pairedWith:(NSView*)pairedButton {
266 base::scoped_nsobject<NSButton> button(
267 [[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect]);
268 [button setButtonType:NSMomentaryPushInButton];
269 [button setTitle:title];
270 [button setTarget:self];
271 [button setAction:action];
272 [button sizeToFit];
273 if (pairedButton) {
274 NSRect buttonFrame = [button frame];
275 NSRect pairedFrame = [pairedButton frame];
276 CGFloat width = std::max(NSWidth(buttonFrame), NSWidth(pairedFrame));
277 [button setFrameSize:NSMakeSize(width, buttonFrame.size.height)];
278 [pairedButton setFrameSize:NSMakeSize(width, pairedFrame.size.height)];
279 }
280 return button.autorelease();
281 }
282
283 - (void)ok:(id)sender {
284 DCHECK(delegate_);
285 delegate_->Closing();
286 }
287
288 - (void)onAllow:(id)sender {
289 DCHECK(delegate_);
290 delegate_->Accept();
291 }
292
293 - (void)onBlock:(id)sender {
294 DCHECK(delegate_);
295 delegate_->Deny();
296 }
297
298 - (void)onCustomize:(id)sender {
299 DCHECK(delegate_);
300 delegate_->SetCustomizationMode();
301 }
302
303 - (void)onCheckboxChanged:(id)sender {
304 DCHECK(delegate_);
305 NSButton* checkbox = base::mac::ObjCCastStrict<NSButton>(sender);
306 delegate_->ToggleAccept([checkbox tag], [checkbox state] == NSOnState);
307 }
308
309 @end // implementation PermissionBubbleController
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698