OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.h
" | 5 #import "chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.h
" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/mac/bind_objc_block.h" | 9 #include "base/mac/bind_objc_block.h" |
10 #include "base/strings/sys_string_conversions.h" | 10 #include "base/strings/sys_string_conversions.h" |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
50 class MenuDelegate : public ui::SimpleMenuModel::Delegate { | 50 class MenuDelegate : public ui::SimpleMenuModel::Delegate { |
51 public: | 51 public: |
52 explicit MenuDelegate(PermissionBubbleController* bubble) | 52 explicit MenuDelegate(PermissionBubbleController* bubble) |
53 : bubble_controller_(bubble) {} | 53 : bubble_controller_(bubble) {} |
54 bool IsCommandIdChecked(int command_id) const override { return false; } | 54 bool IsCommandIdChecked(int command_id) const override { return false; } |
55 bool IsCommandIdEnabled(int command_id) const override { return true; } | 55 bool IsCommandIdEnabled(int command_id) const override { return true; } |
56 bool GetAcceleratorForCommandId(int command_id, | 56 bool GetAcceleratorForCommandId(int command_id, |
57 ui::Accelerator* accelerator) override { | 57 ui::Accelerator* accelerator) override { |
58 return false; | 58 return false; |
59 } | 59 } |
60 void ExecuteCommand(int command_id, int event_flags) override { | |
61 [bubble_controller_ onMenuItemClicked:command_id]; | |
62 } | |
63 private: | 60 private: |
64 PermissionBubbleController* bubble_controller_; // Weak, owns us. | 61 PermissionBubbleController* bubble_controller_; // Weak, owns us. |
65 DISALLOW_COPY_AND_ASSIGN(MenuDelegate); | 62 DISALLOW_COPY_AND_ASSIGN(MenuDelegate); |
66 }; | 63 }; |
67 | 64 |
68 } // namespace | 65 } // namespace |
69 | 66 |
70 // NSPopUpButton with a menu containing two items: allow and block. | 67 // NSPopUpButton with a menu containing two items: allow and block. |
71 // One AllowBlockMenuButton is used for each requested permission, but only when | 68 // One AllowBlockMenuButton is used for each requested permission when there are |
72 // the permission bubble is in 'customize' mode. | 69 // multiple permissions in the bubble. |
73 @interface AllowBlockMenuButton : NSPopUpButton { | 70 @interface AllowBlockMenuButton : NSPopUpButton { |
74 @private | 71 @private |
75 scoped_ptr<PermissionMenuModel> menuModel_; | 72 scoped_ptr<PermissionMenuModel> menuModel_; |
76 base::scoped_nsobject<MenuController> menuController_; | 73 base::scoped_nsobject<MenuController> menuController_; |
77 } | 74 } |
78 | 75 |
79 - (id)initForURL:(const GURL&)url | 76 - (id)initForURL:(const GURL&)url |
80 allowed:(BOOL)allow | 77 allowed:(BOOL)allow |
81 index:(int)index | 78 index:(int)index |
82 delegate:(PermissionBubbleView::Delegate*)delegate; | 79 delegate:(PermissionBubbleView::Delegate*)delegate; |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
176 atIndex:(int)index | 173 atIndex:(int)index |
177 allow:(BOOL)allow; | 174 allow:(BOOL)allow; |
178 | 175 |
179 // Returns an autoreleased NSView of a button with |title| and |action|. | 176 // Returns an autoreleased NSView of a button with |title| and |action|. |
180 - (NSView*)buttonWithTitle:(NSString*)title | 177 - (NSView*)buttonWithTitle:(NSString*)title |
181 action:(SEL)action; | 178 action:(SEL)action; |
182 | 179 |
183 // Returns an autoreleased NSView displaying a block button. | 180 // Returns an autoreleased NSView displaying a block button. |
184 - (NSView*)blockButton; | 181 - (NSView*)blockButton; |
185 | 182 |
186 // Returns an autoreleased NSView with a block button and a drop-down menu | |
187 // with one item, which will change the UI to allow customizing the permissions. | |
188 - (NSView*)blockButtonWithCustomizeMenu; | |
189 | |
190 // Returns an autoreleased NSView displaying the close 'x' button. | 183 // Returns an autoreleased NSView displaying the close 'x' button. |
191 - (NSView*)closeButton; | 184 - (NSView*)closeButton; |
192 | 185 |
193 // Called when the 'ok' button is pressed. | 186 // Called when the 'ok' button is pressed. |
194 - (void)ok:(id)sender; | 187 - (void)ok:(id)sender; |
195 | 188 |
196 // Called when the 'allow' button is pressed. | 189 // Called when the 'allow' button is pressed. |
197 - (void)onAllow:(id)sender; | 190 - (void)onAllow:(id)sender; |
198 | 191 |
199 // Called when the 'block' button is pressed. | 192 // Called when the 'block' button is pressed. |
200 - (void)onBlock:(id)sender; | 193 - (void)onBlock:(id)sender; |
201 | 194 |
202 // Called when the 'close' button is pressed. | 195 // Called when the 'close' button is pressed. |
203 - (void)onClose:(id)sender; | 196 - (void)onClose:(id)sender; |
204 | 197 |
205 // Called when the 'customize' button is pressed. | |
206 - (void)onCustomize:(id)sender; | |
207 | |
208 // Sets the width of both |viewA| and |viewB| to be the larger of the | 198 // Sets the width of both |viewA| and |viewB| to be the larger of the |
209 // two views' widths. Does not change either view's origin or height. | 199 // two views' widths. Does not change either view's origin or height. |
210 + (CGFloat)matchWidthsOf:(NSView*)viewA andOf:(NSView*)viewB; | 200 + (CGFloat)matchWidthsOf:(NSView*)viewA andOf:(NSView*)viewB; |
211 | 201 |
212 // Sets the offset of |viewA| so that its vertical center is aligned with the | 202 // Sets the offset of |viewA| so that its vertical center is aligned with the |
213 // vertical center of |viewB|. | 203 // vertical center of |viewB|. |
214 + (void)alignCenterOf:(NSView*)viewA verticallyToCenterOf:(NSView*)viewB; | 204 + (void)alignCenterOf:(NSView*)viewA verticallyToCenterOf:(NSView*)viewB; |
215 | 205 |
216 @end | 206 @end |
217 | 207 |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
260 } | 250 } |
261 | 251 |
262 - (void)parentWindowDidMove:(NSNotification*)notification { | 252 - (void)parentWindowDidMove:(NSNotification*)notification { |
263 DCHECK(bridge_); | 253 DCHECK(bridge_); |
264 [self setAnchorPoint:bridge_->GetAnchorPoint()]; | 254 [self setAnchorPoint:bridge_->GetAnchorPoint()]; |
265 } | 255 } |
266 | 256 |
267 - (void)showAtAnchor:(NSPoint)anchorPoint | 257 - (void)showAtAnchor:(NSPoint)anchorPoint |
268 withDelegate:(PermissionBubbleView::Delegate*)delegate | 258 withDelegate:(PermissionBubbleView::Delegate*)delegate |
269 forRequests:(const std::vector<PermissionBubbleRequest*>&)requests | 259 forRequests:(const std::vector<PermissionBubbleRequest*>&)requests |
270 acceptStates:(const std::vector<bool>&)acceptStates | 260 acceptStates:(const std::vector<bool>&)acceptStates { |
271 customizationMode:(BOOL)customizationMode { | |
272 DCHECK(!requests.empty()); | 261 DCHECK(!requests.empty()); |
273 DCHECK(delegate); | 262 DCHECK(delegate); |
274 DCHECK(!customizationMode || (requests.size() == acceptStates.size())); | |
275 delegate_ = delegate; | 263 delegate_ = delegate; |
276 | 264 |
277 NSView* contentView = [[self window] contentView]; | 265 NSView* contentView = [[self window] contentView]; |
278 [contentView setSubviews:@[]]; | 266 [contentView setSubviews:@[]]; |
279 | 267 |
| 268 BOOL singlePermission = requests.size() == 1; |
| 269 |
280 // Create one button to use as a guide for the permissions' y-offsets. | 270 // Create one button to use as a guide for the permissions' y-offsets. |
281 base::scoped_nsobject<NSView> allowOrOkButton; | 271 base::scoped_nsobject<NSView> allowOrOkButton; |
282 if (customizationMode) { | 272 if (singlePermission) { |
| 273 NSString* allowTitle = l10n_util::GetNSString(IDS_PERMISSION_ALLOW); |
| 274 allowOrOkButton.reset([[self buttonWithTitle:allowTitle |
| 275 action:@selector(onAllow:)] retain]); |
| 276 } else { |
283 NSString* okTitle = l10n_util::GetNSString(IDS_OK); | 277 NSString* okTitle = l10n_util::GetNSString(IDS_OK); |
284 allowOrOkButton.reset([[self buttonWithTitle:okTitle | 278 allowOrOkButton.reset([[self buttonWithTitle:okTitle |
285 action:@selector(ok:)] retain]); | 279 action:@selector(ok:)] retain]); |
286 } else { | |
287 NSString* allowTitle = l10n_util::GetNSString(IDS_PERMISSION_ALLOW); | |
288 allowOrOkButton.reset([[self buttonWithTitle:allowTitle | |
289 action:@selector(onAllow:)] retain]); | |
290 } | 280 } |
291 CGFloat yOffset = 2 * kVerticalPadding + NSMaxY([allowOrOkButton frame]); | 281 CGFloat yOffset = 2 * kVerticalPadding + NSMaxY([allowOrOkButton frame]); |
292 BOOL singlePermission = requests.size() == 1; | |
293 | 282 |
294 base::scoped_nsobject<NSMutableArray> permissionMenus; | 283 base::scoped_nsobject<NSMutableArray> permissionMenus; |
295 if (customizationMode) | 284 if (!singlePermission) |
296 permissionMenus.reset([[NSMutableArray alloc] init]); | 285 permissionMenus.reset([[NSMutableArray alloc] init]); |
297 | 286 |
298 CGFloat maxPermissionLineWidth = 0; | 287 CGFloat maxPermissionLineWidth = 0; |
299 for (auto it = requests.begin(); it != requests.end(); it++) { | 288 for (auto it = requests.begin(); it != requests.end(); it++) { |
300 base::scoped_nsobject<NSView> permissionView( | 289 base::scoped_nsobject<NSView> permissionView( |
301 [[self labelForRequest:(*it)] retain]); | 290 [[self labelForRequest:(*it)] retain]); |
302 NSPoint origin = [permissionView frame].origin; | 291 NSPoint origin = [permissionView frame].origin; |
303 origin.x += kHorizontalPadding; | 292 origin.x += kHorizontalPadding; |
304 origin.y += yOffset; | 293 origin.y += yOffset; |
305 [permissionView setFrameOrigin:origin]; | 294 [permissionView setFrameOrigin:origin]; |
306 [contentView addSubview:permissionView]; | 295 [contentView addSubview:permissionView]; |
307 | 296 |
308 if (customizationMode) { | 297 if (!singlePermission) { |
309 int index = it - requests.begin(); | 298 int index = it - requests.begin(); |
310 base::scoped_nsobject<NSView> menu( | 299 base::scoped_nsobject<NSView> menu( |
311 [[self menuForRequest:(*it) | 300 [[self menuForRequest:(*it) |
312 atIndex:index | 301 atIndex:index |
313 allow:acceptStates[index] ? YES : NO] retain]); | 302 allow:acceptStates[index] ? YES : NO] retain]); |
314 // Align vertically. Horizontal alignment will be adjusted once the | 303 // Align vertically. Horizontal alignment will be adjusted once the |
315 // widest permission is know. | 304 // widest permission is know. |
316 [PermissionBubbleController alignCenterOf:menu | 305 [PermissionBubbleController alignCenterOf:menu |
317 verticallyToCenterOf:permissionView]; | 306 verticallyToCenterOf:permissionView]; |
318 [permissionMenus addObject:menu]; | 307 [permissionMenus addObject:menu]; |
(...skipping 16 matching lines...) Expand all Loading... |
335 | 324 |
336 // Determine the dimensions of the bubble. | 325 // Determine the dimensions of the bubble. |
337 // Once the height and width are set, the buttons and permission menus can | 326 // Once the height and width are set, the buttons and permission menus can |
338 // be laid out correctly. | 327 // be laid out correctly. |
339 NSRect bubbleFrame = NSMakeRect(0, 0, kBubbleMinWidth, 0); | 328 NSRect bubbleFrame = NSMakeRect(0, 0, kBubbleMinWidth, 0); |
340 | 329 |
341 // Fix the height of the bubble relative to the title. | 330 // Fix the height of the bubble relative to the title. |
342 bubbleFrame.size.height = NSMaxY([titleView frame]) + kVerticalPadding + | 331 bubbleFrame.size.height = NSMaxY([titleView frame]) + kVerticalPadding + |
343 info_bubble::kBubbleArrowHeight; | 332 info_bubble::kBubbleArrowHeight; |
344 | 333 |
345 if (customizationMode) { | 334 if (!singlePermission) { |
346 // Add the maximum menu width to the bubble width. | 335 // Add the maximum menu width to the bubble width. |
347 CGFloat maxMenuWidth = 0; | 336 CGFloat maxMenuWidth = 0; |
348 for (AllowBlockMenuButton* button in permissionMenus.get()) { | 337 for (AllowBlockMenuButton* button in permissionMenus.get()) { |
349 maxMenuWidth = std::max(maxMenuWidth, [button maximumTitleWidth]); | 338 maxMenuWidth = std::max(maxMenuWidth, [button maximumTitleWidth]); |
350 } | 339 } |
351 maxPermissionLineWidth += maxMenuWidth; | 340 maxPermissionLineWidth += maxMenuWidth; |
352 } | 341 } |
353 | 342 |
354 // The title and 'x' button row must fit within the bubble. | 343 // The title and 'x' button row must fit within the bubble. |
355 CGFloat titleRowWidth = NSMaxX([titleView frame]) + | 344 CGFloat titleRowWidth = NSMaxX([titleView frame]) + |
(...skipping 17 matching lines...) Expand all Loading... |
373 | 362 |
374 [closeButton setFrameOrigin:closeButtonOrigin]; | 363 [closeButton setFrameOrigin:closeButtonOrigin]; |
375 [contentView addSubview:closeButton]; | 364 [contentView addSubview:closeButton]; |
376 | 365 |
377 // Position the allow/ok button. | 366 // Position the allow/ok button. |
378 CGFloat xOrigin = NSWidth(bubbleFrame) - NSWidth([allowOrOkButton frame]) - | 367 CGFloat xOrigin = NSWidth(bubbleFrame) - NSWidth([allowOrOkButton frame]) - |
379 kButtonRightEdgePadding; | 368 kButtonRightEdgePadding; |
380 [allowOrOkButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)]; | 369 [allowOrOkButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)]; |
381 [contentView addSubview:allowOrOkButton]; | 370 [contentView addSubview:allowOrOkButton]; |
382 | 371 |
383 if (customizationMode) { | 372 if (singlePermission) { |
384 // Adjust the horizontal origin for each menu so that its right edge | |
385 // lines up with the right edge of the ok button. | |
386 CGFloat rightEdge = NSMaxX([allowOrOkButton frame]); | |
387 for (NSView* view in permissionMenus.get()) { | |
388 [view setFrameOrigin:NSMakePoint(rightEdge - NSWidth([view frame]), | |
389 NSMinY([view frame]))]; | |
390 } | |
391 } else { | |
392 base::scoped_nsobject<NSView> blockButton; | 373 base::scoped_nsobject<NSView> blockButton; |
393 if (singlePermission) | 374 blockButton.reset([[self blockButton] retain]); |
394 blockButton.reset([[self blockButton] retain]); | |
395 else | |
396 blockButton.reset([[self blockButtonWithCustomizeMenu] retain]); | |
397 CGFloat width = [PermissionBubbleController matchWidthsOf:blockButton | 375 CGFloat width = [PermissionBubbleController matchWidthsOf:blockButton |
398 andOf:allowOrOkButton]; | 376 andOf:allowOrOkButton]; |
399 // Ensure the allow/ok button is still in the correct position. | 377 // Ensure the allow/ok button is still in the correct position. |
400 xOrigin = NSWidth(bubbleFrame) - width - kHorizontalPadding; | 378 xOrigin = NSWidth(bubbleFrame) - width - kHorizontalPadding; |
401 [allowOrOkButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)]; | 379 [allowOrOkButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)]; |
402 // Line up the block button. | 380 // Line up the block button. |
403 xOrigin = NSMinX([allowOrOkButton frame]) - width - kBetweenButtonsPadding; | 381 xOrigin = NSMinX([allowOrOkButton frame]) - width - kBetweenButtonsPadding; |
404 [blockButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)]; | 382 [blockButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)]; |
405 [contentView addSubview:blockButton]; | 383 [contentView addSubview:blockButton]; |
| 384 } else { |
| 385 // Adjust the horizontal origin for each menu so that its right edge |
| 386 // lines up with the right edge of the ok button. |
| 387 CGFloat rightEdge = NSMaxX([allowOrOkButton frame]); |
| 388 for (NSView* view in permissionMenus.get()) { |
| 389 [view setFrameOrigin:NSMakePoint(rightEdge - NSWidth([view frame]), |
| 390 NSMinY([view frame]))]; |
| 391 } |
406 } | 392 } |
407 | 393 |
408 bubbleFrame = [[self window] frameRectForContentRect:bubbleFrame]; | 394 bubbleFrame = [[self window] frameRectForContentRect:bubbleFrame]; |
409 if ([[self window] isVisible]) { | 395 if ([[self window] isVisible]) { |
410 // Unfortunately, calling -setFrame followed by -setFrameOrigin (called | 396 // Unfortunately, calling -setFrame followed by -setFrameOrigin (called |
411 // within -setAnchorPoint) causes flickering. Avoid the flickering by | 397 // within -setAnchorPoint) causes flickering. Avoid the flickering by |
412 // manually adjusting the new frame's origin so that the top left stays the | 398 // manually adjusting the new frame's origin so that the top left stays the |
413 // same, and only calling -setFrame. | 399 // same, and only calling -setFrame. |
414 NSRect currentWindowFrame = [[self window] frame]; | 400 NSRect currentWindowFrame = [[self window] frame]; |
415 bubbleFrame.origin = currentWindowFrame.origin; | 401 bubbleFrame.origin = currentWindowFrame.origin; |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
511 [button sizeToFit]; | 497 [button sizeToFit]; |
512 return button.autorelease(); | 498 return button.autorelease(); |
513 } | 499 } |
514 | 500 |
515 - (NSView*)blockButton { | 501 - (NSView*)blockButton { |
516 NSString* blockTitle = l10n_util::GetNSString(IDS_PERMISSION_DENY); | 502 NSString* blockTitle = l10n_util::GetNSString(IDS_PERMISSION_DENY); |
517 return [self buttonWithTitle:blockTitle | 503 return [self buttonWithTitle:blockTitle |
518 action:@selector(onBlock:)]; | 504 action:@selector(onBlock:)]; |
519 } | 505 } |
520 | 506 |
521 - (NSView*)blockButtonWithCustomizeMenu { | |
522 menuDelegate_.reset(new MenuDelegate(self)); | |
523 base::scoped_nsobject<SplitBlockButton> blockButton([[SplitBlockButton alloc] | |
524 initWithMenuDelegate:menuDelegate_.get()]); | |
525 [blockButton sizeToFit]; | |
526 [blockButton setEnabled:YES]; | |
527 [blockButton setAction:@selector(onBlock:)]; | |
528 [blockButton setTarget:self]; | |
529 return blockButton.autorelease(); | |
530 } | |
531 | |
532 - (NSView*)closeButton { | 507 - (NSView*)closeButton { |
533 int dimension = chrome_style::GetCloseButtonSize(); | 508 int dimension = chrome_style::GetCloseButtonSize(); |
534 NSRect frame = NSMakeRect(0, 0, dimension, dimension); | 509 NSRect frame = NSMakeRect(0, 0, dimension, dimension); |
535 base::scoped_nsobject<NSButton> button( | 510 base::scoped_nsobject<NSButton> button( |
536 [[WebUIHoverCloseButton alloc] initWithFrame:frame]); | 511 [[WebUIHoverCloseButton alloc] initWithFrame:frame]); |
537 [button setAction:@selector(onClose:)]; | 512 [button setAction:@selector(onClose:)]; |
538 [button setTarget:self]; | 513 [button setTarget:self]; |
539 return button.autorelease(); | 514 return button.autorelease(); |
540 } | 515 } |
541 | 516 |
(...skipping 10 matching lines...) Expand all Loading... |
552 - (void)onBlock:(id)sender { | 527 - (void)onBlock:(id)sender { |
553 DCHECK(delegate_); | 528 DCHECK(delegate_); |
554 delegate_->Deny(); | 529 delegate_->Deny(); |
555 } | 530 } |
556 | 531 |
557 - (void)onClose:(id)sender { | 532 - (void)onClose:(id)sender { |
558 DCHECK(delegate_); | 533 DCHECK(delegate_); |
559 delegate_->Closing(); | 534 delegate_->Closing(); |
560 } | 535 } |
561 | 536 |
562 - (void)onCustomize:(id)sender { | |
563 DCHECK(delegate_); | |
564 delegate_->SetCustomizationMode(); | |
565 } | |
566 | |
567 - (void)onMenuItemClicked:(int)commandId { | |
568 DCHECK(commandId == 0); | |
569 [self onCustomize:nil]; | |
570 } | |
571 | |
572 - (void)activateTabWithContents:(content::WebContents*)newContents | 537 - (void)activateTabWithContents:(content::WebContents*)newContents |
573 previousContents:(content::WebContents*)oldContents | 538 previousContents:(content::WebContents*)oldContents |
574 atIndex:(NSInteger)index | 539 atIndex:(NSInteger)index |
575 reason:(int)reason { | 540 reason:(int)reason { |
576 // The show/hide of this bubble is handled by the PermissionBubbleManager. | 541 // The show/hide of this bubble is handled by the PermissionBubbleManager. |
577 // So bypass the base class, which would close the bubble here. | 542 // So bypass the base class, which would close the bubble here. |
578 } | 543 } |
579 | 544 |
580 + (CGFloat)matchWidthsOf:(NSView*)viewA andOf:(NSView*)viewB { | 545 + (CGFloat)matchWidthsOf:(NSView*)viewA andOf:(NSView*)viewB { |
581 NSRect frameA = [viewA frame]; | 546 NSRect frameA = [viewA frame]; |
582 NSRect frameB = [viewB frame]; | 547 NSRect frameB = [viewB frame]; |
583 CGFloat width = std::max(NSWidth(frameA), NSWidth(frameB)); | 548 CGFloat width = std::max(NSWidth(frameA), NSWidth(frameB)); |
584 [viewA setFrameSize:NSMakeSize(width, NSHeight(frameA))]; | 549 [viewA setFrameSize:NSMakeSize(width, NSHeight(frameA))]; |
585 [viewB setFrameSize:NSMakeSize(width, NSHeight(frameB))]; | 550 [viewB setFrameSize:NSMakeSize(width, NSHeight(frameB))]; |
586 return width; | 551 return width; |
587 } | 552 } |
588 | 553 |
589 + (void)alignCenterOf:(NSView*)viewA verticallyToCenterOf:(NSView*)viewB { | 554 + (void)alignCenterOf:(NSView*)viewA verticallyToCenterOf:(NSView*)viewB { |
590 NSRect frameA = [viewA frame]; | 555 NSRect frameA = [viewA frame]; |
591 NSRect frameB = [viewB frame]; | 556 NSRect frameB = [viewB frame]; |
592 frameA.origin.y = | 557 frameA.origin.y = |
593 NSMinY(frameB) + std::floor((NSHeight(frameB) - NSHeight(frameA)) / 2); | 558 NSMinY(frameB) + std::floor((NSHeight(frameB) - NSHeight(frameA)) / 2); |
594 [viewA setFrameOrigin:frameA.origin]; | 559 [viewA setFrameOrigin:frameA.origin]; |
595 } | 560 } |
596 | 561 |
597 @end // implementation PermissionBubbleController | 562 @end // implementation PermissionBubbleController |
OLD | NEW |