Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 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/chooser_bubble_ui_cocoa.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <cmath> | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "base/mac/scoped_nsobject.h" | |
| 12 #include "base/strings/sys_string_conversions.h" | |
| 13 #include "base/strings/utf_string_conversions.h" | |
| 14 #include "chrome/browser/ui/browser.h" | |
| 15 #include "chrome/browser/ui/browser_finder.h" | |
| 16 #include "chrome/browser/ui/browser_window.h" | |
| 17 #import "chrome/browser/ui/chrome_style.h" | |
| 18 #import "chrome/browser/ui/cocoa/base_bubble_controller.h" | |
| 19 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | |
| 20 #import "chrome/browser/ui/cocoa/browser_window_utils.h" | |
| 21 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h" | |
| 22 #import "chrome/browser/ui/cocoa/hover_close_button.h" | |
| 23 #import "chrome/browser/ui/cocoa/info_bubble_view.h" | |
| 24 #import "chrome/browser/ui/cocoa/info_bubble_window.h" | |
| 25 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" | |
| 26 #include "chrome/browser/ui/website_settings/chooser_bubble_delegate.h" | |
| 27 #include "chrome/grit/generated_resources.h" | |
| 28 #include "content/public/browser/native_web_keyboard_event.h" | |
| 29 #include "ui/base/cocoa/window_size_constants.h" | |
| 30 #include "ui/base/l10n/l10n_util_mac.h" | |
| 31 | |
| 32 namespace { | |
| 33 | |
| 34 // Chooser bubble width. | |
| 35 const CGFloat kChooserBubbleWidth = 320.0f; | |
| 36 | |
| 37 // Chooser bubble height. | |
| 38 const CGFloat kChooserBubbleHeight = 220.0f; | |
| 39 | |
| 40 // Distance between the bubble border and the view that is closest to the | |
| 41 // border. | |
| 42 const CGFloat kMarginX = 20.0f; | |
| 43 const CGFloat kMarginY = 20.0f; | |
| 44 | |
| 45 // Distance between two views inside the bubble. | |
| 46 const CGFloat kHorizontalPadding = 10.0f; | |
| 47 const CGFloat kVerticalPadding = 10.0f; | |
| 48 | |
| 49 const CGFloat kTitlePaddingX = 50.0f; | |
| 50 } | |
| 51 | |
| 52 @interface ChooserBubbleUiController | |
| 53 : BaseBubbleController<NSTableViewDataSource, NSTableViewDelegate> { | |
| 54 @private | |
| 55 // Bridge to the C++ class that created this object. | |
| 56 ChooserBubbleUiCocoa* bridge_; | |
| 57 | |
| 58 base::scoped_nsobject<NSTextField> titleView_; | |
| 59 base::scoped_nsobject<NSButton> closeButton_; | |
| 60 base::scoped_nsobject<NSScrollView> scrollView_; | |
| 61 base::scoped_nsobject<NSTableColumn> tableColumn_; | |
| 62 base::scoped_nsobject<NSTableView> tableView_; | |
| 63 base::scoped_nsobject<NSButton> connectButton_; | |
| 64 base::scoped_nsobject<NSButton> cancelButton_; | |
| 65 | |
| 66 Browser* browser_; // Weak. | |
| 67 ChooserOptions* chooser_options_; // Weak. | |
| 68 ChooserBubbleDelegate* chooser_bubble_delegate_; // Weak. | |
| 69 } | |
| 70 | |
| 71 // Designated initializer. |browser| and |bridge| must both be non-nil. | |
| 72 - (id)initWithBrowser:(Browser*)browser | |
| 73 initWithChooserOptions:(ChooserOptions*)chooser_options | |
| 74 initWithChooserBubbleDelegate: | |
| 75 (ChooserBubbleDelegate*)chooser_bubble_delegate | |
| 76 bridge:(ChooserBubbleUiCocoa*)bridge; | |
| 77 | |
| 78 // Makes the bubble visible. | |
| 79 - (void)show; | |
| 80 | |
| 81 // Will reposition the bubble based in case the anchor or parent should change. | |
| 82 - (void)updateAnchorPosition; | |
| 83 | |
| 84 // Will calculate the expected anchor point for this bubble. | |
| 85 // Should only be used outside this class for tests. | |
| 86 - (NSPoint)getExpectedAnchorPoint; | |
| 87 | |
| 88 // Returns true of the browser has support for the location bar. | |
| 89 // Should only be used outside this class for tests. | |
| 90 - (bool)hasLocationBar; | |
| 91 | |
| 92 // Update table view when chooser options were initialized. | |
| 93 - (void)onOptionsInitialized; | |
| 94 | |
| 95 // Update table view when chooser option was added. | |
| 96 - (void)onOptionAdded:(NSInteger)index; | |
| 97 | |
| 98 // Update table view when chooser option was removed. | |
| 99 - (void)onOptionRemoved:(NSInteger)index; | |
| 100 | |
| 101 // Update table view when chooser options changed. | |
| 102 - (void)updateTableView; | |
| 103 | |
| 104 // Determines if the bubble has an anchor in a corner or no anchor at all. | |
| 105 - (info_bubble::BubbleArrowLocation)getExpectedArrowLocation; | |
| 106 | |
| 107 // Returns the expected parent for this bubble. | |
| 108 - (NSWindow*)getExpectedParentWindow; | |
| 109 | |
| 110 // Returns an autoreleased NSView displaying the title for the bubble | |
| 111 // requesting settings for |host|. | |
| 112 - (NSTextField*)bubbleTitle; | |
| 113 | |
| 114 // Returns an autoreleased NSView displaying the close 'x' button. | |
| 115 - (NSButton*)closeButton; | |
| 116 | |
| 117 // Returns an autoreleased NSView of a button with |title| and |action|. | |
| 118 - (NSButton*)buttonWithTitle:(NSString*)title action:(SEL)action; | |
| 119 | |
| 120 // Returns an autoreleased NSView displaying a connect button. | |
| 121 - (NSButton*)connectButton; | |
| 122 | |
| 123 // Returns an autoreleased NSView displaying a cancel button. | |
| 124 - (NSButton*)cancelButton; | |
| 125 | |
| 126 // Called when the 'Connect' button is pressed. | |
| 127 - (void)onConnect:(id)sender; | |
| 128 | |
| 129 // Called when the 'Cancel' button is pressed. | |
| 130 - (void)onCancel:(id)sender; | |
| 131 | |
| 132 // Called when the 'close' button is pressed. | |
| 133 - (void)onClose:(id)sender; | |
| 134 | |
| 135 @end | |
| 136 | |
| 137 @implementation ChooserBubbleUiController | |
| 138 | |
| 139 - (id)initWithBrowser:(Browser*)browser | |
| 140 initWithChooserOptions:(ChooserOptions*)chooser_options | |
| 141 initWithChooserBubbleDelegate: | |
| 142 (ChooserBubbleDelegate*)chooser_bubble_delegate | |
| 143 bridge:(ChooserBubbleUiCocoa*)bridge { | |
| 144 DCHECK(browser); | |
| 145 DCHECK(chooser_options); | |
| 146 DCHECK(chooser_bubble_delegate); | |
| 147 DCHECK(bridge); | |
| 148 browser_ = browser; | |
| 149 chooser_options_ = chooser_options; | |
| 150 chooser_bubble_delegate_ = chooser_bubble_delegate; | |
| 151 | |
| 152 base::scoped_nsobject<InfoBubbleWindow> window([[InfoBubbleWindow alloc] | |
| 153 initWithContentRect:ui::kWindowSizeDeterminedLater | |
| 154 styleMask:NSBorderlessWindowMask | |
| 155 backing:NSBackingStoreBuffered | |
| 156 defer:NO]); | |
| 157 [window setAllowedAnimations:info_bubble::kAnimateNone]; | |
| 158 [window setReleasedWhenClosed:NO]; | |
| 159 if ((self = [super initWithWindow:window | |
| 160 parentWindow:[self getExpectedParentWindow] | |
| 161 anchoredAt:NSZeroPoint])) { | |
| 162 [self setShouldCloseOnResignKey:NO]; | |
| 163 [self setShouldOpenAsKeyWindow:YES]; | |
| 164 [[self bubble] setArrowLocation:[self getExpectedArrowLocation]]; | |
| 165 bridge_ = bridge; | |
| 166 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; | |
| 167 [center addObserver:self | |
| 168 selector:@selector(parentWindowDidMove:) | |
| 169 name:NSWindowDidMoveNotification | |
| 170 object:[self getExpectedParentWindow]]; | |
| 171 } | |
| 172 return self; | |
| 173 } | |
| 174 | |
| 175 - (void)windowWillClose:(NSNotification*)notification { | |
| 176 bridge_->OnBubbleClosing(); | |
| 177 [super windowWillClose:notification]; | |
| 178 } | |
| 179 | |
| 180 - (void)parentWindowWillToggleFullScreen:(NSNotification*)notification { | |
| 181 // Override the base class implementation, which would have closed the bubble. | |
| 182 } | |
| 183 | |
| 184 - (void)parentWindowDidResize:(NSNotification*)notification { | |
| 185 DCHECK(bridge_); | |
| 186 [self setAnchorPoint:[self getExpectedAnchorPoint]]; | |
| 187 } | |
| 188 | |
| 189 - (void)parentWindowDidMove:(NSNotification*)notification { | |
| 190 DCHECK(bridge_); | |
| 191 [self setAnchorPoint:[self getExpectedAnchorPoint]]; | |
| 192 } | |
| 193 | |
| 194 - (void)show { | |
| 195 NSView* view = [[self window] contentView]; | |
| 196 [view setSubviews:@[]]; | |
| 197 | |
| 198 // ------------------------------------ | |
| 199 // | Chooser bubble title x | | |
| 200 // | -------------------------------- | | |
| 201 // | | option 0 | | | |
| 202 // | | option 1 | | | |
| 203 // | | option 2 | | | |
| 204 // | | | | | |
| 205 // | | | | | |
| 206 // | | | | | |
| 207 // | -------------------------------- | | |
| 208 // | [ Connect] [ Cancel ] | | |
| 209 // ------------------------------------ | |
| 210 | |
| 211 // Determine the dimensions of the bubble. | |
| 212 // Once the height and width are set, the buttons and permission menus can | |
| 213 // be laid out correctly. | |
| 214 NSRect bubbleFrame = | |
| 215 NSMakeRect(0, 0, kChooserBubbleWidth, kChooserBubbleHeight); | |
| 216 | |
| 217 // Create the views. | |
| 218 // Title. | |
| 219 titleView_.reset([[self bubbleTitle] retain]); | |
| 220 CGFloat titleOriginX = kMarginX; | |
| 221 CGFloat titleHeight = NSHeight([titleView_ frame]); | |
| 222 CGFloat titleOriginY = kChooserBubbleHeight - kMarginY - titleHeight; | |
| 223 [titleView_ setFrameOrigin:NSMakePoint(titleOriginX, titleOriginY)]; | |
| 224 [view addSubview:titleView_]; | |
| 225 | |
| 226 // Close button. | |
| 227 // 'x' button in the upper-right-hand corner. | |
| 228 closeButton_.reset([[self closeButton] retain]); | |
| 229 CGFloat closeButtonOriginX = | |
| 230 kChooserBubbleWidth - kMarginX - NSWidth([closeButton_ frame]); | |
| 231 CGFloat closeButtonOriginY = | |
| 232 kChooserBubbleHeight - kMarginY - NSHeight([closeButton_ frame]); | |
| 233 [closeButton_ | |
| 234 setFrameOrigin:NSMakePoint(closeButtonOriginX, closeButtonOriginY)]; | |
| 235 [view addSubview:closeButton_]; | |
| 236 | |
| 237 // Connect button. | |
| 238 connectButton_.reset([[self connectButton] retain]); | |
| 239 // Cancel button. | |
| 240 cancelButton_.reset([[self cancelButton] retain]); | |
| 241 CGFloat connectButtonWidth = NSWidth([connectButton_ frame]); | |
| 242 CGFloat connectButtonHeight = NSHeight([connectButton_ frame]); | |
| 243 CGFloat cancelButtonWidth = NSWidth([cancelButton_ frame]); | |
| 244 | |
| 245 // ScollView embedding with TableView. | |
| 246 CGFloat scrollViewWidth = kChooserBubbleWidth - 2 * kMarginX; | |
| 247 CGFloat scrollViewHeight = kChooserBubbleHeight - 2 * kMarginY - | |
| 248 2 * kVerticalPadding - titleHeight - | |
| 249 connectButtonHeight; | |
| 250 NSRect scrollFrame = | |
| 251 NSMakeRect(kMarginX, kMarginY + connectButtonHeight + kVerticalPadding, | |
| 252 scrollViewWidth, scrollViewHeight); | |
| 253 scrollView_.reset([[[NSScrollView alloc] initWithFrame:scrollFrame] retain]); | |
| 254 [scrollView_ setBorderType:NSBezelBorder]; | |
| 255 [scrollView_ setHasVerticalScroller:YES]; | |
| 256 [scrollView_ setHasHorizontalScroller:YES]; | |
| 257 [scrollView_ setAutohidesScrollers:YES]; | |
| 258 | |
| 259 // TableView. | |
| 260 tableView_.reset([[[NSTableView alloc] initWithFrame:NSZeroRect] retain]); | |
| 261 tableColumn_.reset([[[NSTableColumn alloc] initWithIdentifier:@""] retain]); | |
| 262 [tableColumn_ setWidth:scrollViewWidth]; | |
| 263 [tableView_ addTableColumn:tableColumn_]; | |
| 264 [tableView_ setDelegate:self]; | |
| 265 [tableView_ setDataSource:self]; | |
| 266 // Make the column title invisible. | |
| 267 [tableView_ setHeaderView:nil]; | |
| 268 [tableView_ setFocusRingType:NSFocusRingTypeNone]; | |
| 269 | |
| 270 [scrollView_ setDocumentView:tableView_]; | |
| 271 [view addSubview:scrollView_]; | |
| 272 | |
| 273 // Set connect button and cancel button to the right place. | |
| 274 CGFloat connectButtonOriginX = kChooserBubbleWidth - kMarginX - | |
| 275 kHorizontalPadding - connectButtonWidth - | |
| 276 cancelButtonWidth; | |
| 277 CGFloat connectButtonOriginY = kMarginY; | |
| 278 [connectButton_ | |
| 279 setFrameOrigin:NSMakePoint(connectButtonOriginX, connectButtonOriginY)]; | |
| 280 [connectButton_ setEnabled:NO]; | |
| 281 [view addSubview:connectButton_]; | |
| 282 | |
| 283 CGFloat cancelButtonOriginX = | |
| 284 kChooserBubbleWidth - kMarginX - cancelButtonWidth; | |
| 285 CGFloat cancelButtonOriginY = kMarginY; | |
| 286 [cancelButton_ | |
| 287 setFrameOrigin:NSMakePoint(cancelButtonOriginX, cancelButtonOriginY)]; | |
| 288 [view addSubview:cancelButton_]; | |
| 289 | |
| 290 bubbleFrame = [[self window] frameRectForContentRect:bubbleFrame]; | |
| 291 if ([[self window] isVisible]) { | |
| 292 // Unfortunately, calling -setFrame followed by -setFrameOrigin (called | |
| 293 // within -setAnchorPoint) causes flickering. Avoid the flickering by | |
| 294 // manually adjusting the new frame's origin so that the top left stays the | |
| 295 // same, and only calling -setFrame. | |
| 296 NSRect currentWindowFrame = [[self window] frame]; | |
| 297 bubbleFrame.origin = currentWindowFrame.origin; | |
| 298 bubbleFrame.origin.y = bubbleFrame.origin.y + | |
| 299 currentWindowFrame.size.height - | |
| 300 bubbleFrame.size.height; | |
| 301 [[self window] setFrame:bubbleFrame display:YES]; | |
| 302 } else { | |
| 303 [[self window] setFrame:bubbleFrame display:NO]; | |
| 304 [self setAnchorPoint:[self getExpectedAnchorPoint]]; | |
| 305 [self showWindow:nil]; | |
| 306 [[self window] makeFirstResponder:nil]; | |
| 307 [[self window] setInitialFirstResponder:connectButton_.get()]; | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 - (NSInteger)numberOfRowsInTableView:(NSTableView*)tableView { | |
| 312 const std::vector<base::string16>& device_names = | |
| 313 chooser_options_->GetOptions(); | |
| 314 if (device_names.empty()) { | |
| 315 return 1; | |
| 316 } else { | |
| 317 return static_cast<NSInteger>(device_names.size()); | |
| 318 } | |
| 319 } | |
| 320 | |
| 321 - (id)tableView:(NSTableView*)tableView | |
| 322 objectValueForTableColumn:(NSTableColumn*)tableColumn | |
| 323 row:(NSInteger)rowIndex { | |
| 324 const std::vector<base::string16>& device_names = | |
| 325 chooser_options_->GetOptions(); | |
| 326 if (device_names.empty()) { | |
| 327 DCHECK(rowIndex == 0); | |
| 328 return l10n_util::GetNSString(IDS_CHOOSER_BUBBLE_NO_DEVICES_FOUND_PROMPT); | |
| 329 } else { | |
| 330 if (rowIndex >= 0 && rowIndex < static_cast<int>(device_names.size())) { | |
| 331 return base::SysUTF16ToNSString(device_names[rowIndex]); | |
| 332 } else { | |
| 333 return @""; | |
| 334 } | |
| 335 } | |
| 336 } | |
| 337 | |
| 338 - (BOOL)tableView:(NSTableView*)aTableView | |
| 339 shouldEditTableColumn:(NSTableColumn*)aTableColumn | |
| 340 row:(NSInteger)rowIndex { | |
| 341 return NO; | |
| 342 } | |
| 343 | |
| 344 - (void)onOptionsInitialized { | |
| 345 [self updateTableView]; | |
| 346 } | |
| 347 | |
| 348 - (void)onOptionAdded:(NSInteger)index { | |
| 349 [self updateTableView]; | |
| 350 } | |
| 351 | |
| 352 - (void)onOptionRemoved:(NSInteger)index { | |
| 353 // Table view will automatically selects the next item if the current | |
| 354 // item is removed, so here it tracks if the removed item is the item | |
| 355 // that was previously selected, if so, disselect it. | |
|
Reilly Grant (use Gerrit)
2015/12/02 19:25:53
s/disselect/deselect/
juncai
2015/12/03 05:19:08
Done.
| |
| 356 if ([tableView_ selectedRow] == index) | |
| 357 [tableView_ deselectRow:index]; | |
| 358 | |
| 359 [self updateTableView]; | |
| 360 } | |
| 361 | |
| 362 - (void)updateTableView { | |
| 363 const std::vector<base::string16>& device_names = | |
| 364 chooser_options_->GetOptions(); | |
| 365 if (device_names.empty()) { | |
| 366 [tableView_ setEnabled:NO]; | |
| 367 } else { | |
| 368 [tableView_ setEnabled:YES]; | |
| 369 } | |
|
Reilly Grant (use Gerrit)
2015/12/02 19:25:53
There might be some obscure bool -> BOOL conversio
juncai
2015/12/03 05:19:08
Done.
| |
| 370 | |
| 371 [tableView_ reloadData]; | |
| 372 } | |
| 373 | |
| 374 - (void)tableViewSelectionDidChange:(NSNotification*)aNotification { | |
| 375 if ([tableView_ numberOfSelectedRows] == 0) | |
| 376 [connectButton_ setEnabled:NO]; | |
| 377 else | |
| 378 [connectButton_ setEnabled:YES]; | |
|
Reilly Grant (use Gerrit)
2015/12/02 19:25:53
This one will definitely work:
[connectButton_ se
juncai
2015/12/03 05:19:08
Done.
| |
| 379 } | |
| 380 | |
| 381 - (void)updateAnchorPosition { | |
| 382 [self setParentWindow:[self getExpectedParentWindow]]; | |
| 383 [self setAnchorPoint:[self getExpectedAnchorPoint]]; | |
| 384 } | |
| 385 | |
| 386 - (NSPoint)getExpectedAnchorPoint { | |
| 387 NSPoint anchor; | |
| 388 if ([self hasLocationBar]) { | |
| 389 LocationBarViewMac* location_bar = | |
| 390 [[[self getExpectedParentWindow] windowController] locationBarBridge]; | |
| 391 anchor = location_bar->GetPageInfoBubblePoint(); | |
| 392 } else { | |
| 393 // Center the bubble if there's no location bar. | |
| 394 NSRect contentFrame = [[[self getExpectedParentWindow] contentView] frame]; | |
| 395 anchor = NSMakePoint(NSMidX(contentFrame), NSMaxY(contentFrame)); | |
| 396 } | |
| 397 | |
| 398 return [[self getExpectedParentWindow] convertBaseToScreen:anchor]; | |
| 399 } | |
| 400 | |
| 401 - (bool)hasLocationBar { | |
| 402 return browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR); | |
| 403 } | |
| 404 | |
| 405 - (info_bubble::BubbleArrowLocation)getExpectedArrowLocation { | |
| 406 return [self hasLocationBar] ? info_bubble::kTopLeft : info_bubble::kNoArrow; | |
| 407 } | |
| 408 | |
| 409 - (NSWindow*)getExpectedParentWindow { | |
| 410 DCHECK(browser_->window()); | |
| 411 return browser_->window()->GetNativeWindow(); | |
| 412 } | |
| 413 | |
| 414 - (NSTextField*)bubbleTitle { | |
| 415 NSTextField* titleView = | |
| 416 [[[NSTextField alloc] initWithFrame:NSZeroRect] autorelease]; | |
| 417 [titleView setDrawsBackground:NO]; | |
| 418 [titleView setBezeled:NO]; | |
| 419 [titleView setEditable:NO]; | |
| 420 [titleView setSelectable:NO]; | |
| 421 [titleView setStringValue:l10n_util::GetNSString(IDS_CHOOSER_BUBBLE_PROMPT)]; | |
| 422 [titleView setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]]; | |
| 423 [titleView sizeToFit]; | |
| 424 NSRect titleFrame = [titleView frame]; | |
| 425 [titleView setFrameSize:NSMakeSize(NSWidth(titleFrame) + kTitlePaddingX, | |
| 426 NSHeight(titleFrame))]; | |
| 427 return titleView; | |
| 428 } | |
| 429 | |
| 430 - (NSButton*)closeButton { | |
| 431 int dimension = chrome_style::GetCloseButtonSize(); | |
| 432 NSRect frame = NSMakeRect(0, 0, dimension, dimension); | |
| 433 NSButton* button = | |
| 434 [[[WebUIHoverCloseButton alloc] initWithFrame:frame] autorelease]; | |
| 435 [button setAction:@selector(onClose:)]; | |
| 436 [button setTarget:self]; | |
| 437 return button; | |
| 438 } | |
| 439 | |
| 440 - (NSButton*)buttonWithTitle:(NSString*)title action:(SEL)action { | |
| 441 NSButton* button = | |
| 442 [[[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect] autorelease]; | |
| 443 [button setButtonType:NSMomentaryPushInButton]; | |
| 444 [button setTitle:title]; | |
| 445 [button setTarget:self]; | |
| 446 [button setAction:action]; | |
| 447 [button sizeToFit]; | |
| 448 return button; | |
| 449 } | |
| 450 | |
| 451 - (NSButton*)connectButton { | |
| 452 NSString* connectTitle = | |
| 453 l10n_util::GetNSString(IDS_CHOOSER_BUBBLE_CONNECT_BUTTON_TEXT); | |
| 454 return [self buttonWithTitle:connectTitle action:@selector(onConnect:)]; | |
| 455 } | |
| 456 | |
| 457 - (NSButton*)cancelButton { | |
| 458 NSString* cancelTitle = | |
| 459 l10n_util::GetNSString(IDS_CHOOSER_BUBBLE_CANCEL_BUTTON_TEXT); | |
| 460 return [self buttonWithTitle:cancelTitle action:@selector(onCancel:)]; | |
| 461 } | |
| 462 | |
| 463 + (CGFloat)matchWidthsOf:(NSView*)viewA andOf:(NSView*)viewB { | |
| 464 NSRect frameA = [viewA frame]; | |
| 465 NSRect frameB = [viewB frame]; | |
| 466 CGFloat width = std::max(NSWidth(frameA), NSWidth(frameB)); | |
| 467 [viewA setFrameSize:NSMakeSize(width, NSHeight(frameA))]; | |
| 468 [viewB setFrameSize:NSMakeSize(width, NSHeight(frameB))]; | |
| 469 return width; | |
| 470 } | |
| 471 | |
| 472 + (void)alignCenterOf:(NSView*)viewA verticallyToCenterOf:(NSView*)viewB { | |
| 473 NSRect frameA = [viewA frame]; | |
| 474 NSRect frameB = [viewB frame]; | |
| 475 frameA.origin.y = | |
| 476 NSMinY(frameB) + std::floor((NSHeight(frameB) - NSHeight(frameA)) / 2); | |
| 477 [viewA setFrameOrigin:frameA.origin]; | |
| 478 } | |
| 479 | |
| 480 - (void)onConnect:(id)sender { | |
| 481 NSInteger row = [tableView_ selectedRow]; | |
| 482 chooser_bubble_delegate_->Select(row); | |
| 483 [self close]; | |
| 484 } | |
| 485 | |
| 486 - (void)onCancel:(id)sender { | |
| 487 [self close]; | |
| 488 } | |
| 489 | |
| 490 - (void)onClose:(id)sender { | |
| 491 [self close]; | |
| 492 } | |
| 493 | |
| 494 @end | |
| 495 | |
| 496 ChooserBubbleUiCocoa::ChooserBubbleUiCocoa( | |
| 497 Browser* browser, | |
| 498 ChooserOptions* chooser_options, | |
| 499 ChooserBubbleDelegate* chooser_bubble_delegate) | |
| 500 : browser_(browser), | |
| 501 chooser_options_(chooser_options), | |
| 502 chooser_bubble_delegate_(chooser_bubble_delegate), | |
| 503 chooser_bubble_ui_controller_(nil) { | |
| 504 DCHECK(browser); | |
| 505 DCHECK(chooser_options); | |
| 506 DCHECK(chooser_bubble_delegate); | |
| 507 chooser_options_->set_observer(this); | |
| 508 } | |
| 509 | |
| 510 ChooserBubbleUiCocoa::~ChooserBubbleUiCocoa() { | |
| 511 chooser_options_->set_observer(nullptr); | |
| 512 if (chooser_bubble_ui_controller_) { | |
| 513 [chooser_bubble_ui_controller_ close]; | |
| 514 chooser_bubble_ui_controller_ = nil; | |
| 515 } | |
| 516 } | |
| 517 | |
| 518 void ChooserBubbleUiCocoa::Show(BubbleReference bubble_reference) { | |
| 519 if (!chooser_bubble_ui_controller_) { | |
| 520 chooser_bubble_ui_controller_ = [[ChooserBubbleUiController alloc] | |
| 521 initWithBrowser:browser_ | |
| 522 initWithChooserOptions:chooser_options_ | |
| 523 initWithChooserBubbleDelegate:chooser_bubble_delegate_ | |
| 524 bridge:this]; | |
| 525 } | |
| 526 | |
| 527 [chooser_bubble_ui_controller_ show]; | |
| 528 } | |
| 529 | |
| 530 void ChooserBubbleUiCocoa::Close() { | |
| 531 if (chooser_bubble_ui_controller_) { | |
| 532 [chooser_bubble_ui_controller_ close]; | |
| 533 chooser_bubble_ui_controller_ = nil; | |
| 534 } | |
| 535 } | |
| 536 | |
| 537 void ChooserBubbleUiCocoa::UpdateAnchorPosition() { | |
| 538 [chooser_bubble_ui_controller_ updateAnchorPosition]; | |
| 539 } | |
| 540 | |
| 541 void ChooserBubbleUiCocoa::OnOptionsInitialized() { | |
| 542 [chooser_bubble_ui_controller_ onOptionsInitialized]; | |
| 543 } | |
| 544 | |
| 545 void ChooserBubbleUiCocoa::OnOptionAdded(int index) { | |
| 546 [chooser_bubble_ui_controller_ onOptionAdded:index]; | |
| 547 } | |
| 548 | |
| 549 void ChooserBubbleUiCocoa::OnOptionRemoved(int index) { | |
| 550 [chooser_bubble_ui_controller_ onOptionRemoved:index]; | |
| 551 } | |
| 552 | |
| 553 void ChooserBubbleUiCocoa::OnBubbleClosing() { | |
| 554 chooser_bubble_ui_controller_ = nil; | |
| 555 } | |
| OLD | NEW |