Index: chrome/browser/ui/cocoa/autofill/generated_credit_card_bubble_cocoa.mm |
diff --git a/chrome/browser/ui/cocoa/autofill/generated_credit_card_bubble_cocoa.mm b/chrome/browser/ui/cocoa/autofill/generated_credit_card_bubble_cocoa.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a55981059926aa99d9be475c8986070867a05a0c |
--- /dev/null |
+++ b/chrome/browser/ui/cocoa/autofill/generated_credit_card_bubble_cocoa.mm |
@@ -0,0 +1,267 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/ui/cocoa/autofill/generated_credit_card_bubble_cocoa.h" |
+ |
+#include "base/mac/foundation_util.h" |
+#include "base/mac/scoped_block.h" |
+#include "base/mac/scoped_nsobject.h" |
+#include "base/strings/sys_string_conversions.h" |
+#include "chrome/browser/ui/autofill/generated_credit_card_bubble_view.h" |
+#include "chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h" |
Scott Hess - ex-Googler
2013/10/31 20:23:19
order c, v.
groby-ooo-7-16
2013/10/31 22:55:47
Done. (Odd that presubmit didn't catch it, thanks
|
+#import "chrome/browser/ui/cocoa/base_bubble_controller.h" |
+#import "chrome/browser/ui/cocoa/hyperlink_text_view.h" |
+#import "chrome/browser/ui/cocoa/info_bubble_view.h" |
+#import "chrome/browser/ui/cocoa/info_bubble_window.h" |
+#include "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" |
+#import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" |
+#include "content/public/browser/web_contents_view.h" |
+#include "skia/ext/skia_utils_mac.h" |
+#include "ui/native_theme/native_theme.h" |
+ |
+typedef void(^CloseObserver)(GeneratedCreditCardBubbleControllerCocoa*); |
+typedef void(^ClickObserver)(int); |
Scott Hess - ex-Googler
2013/10/31 20:23:19
AFAICT, neither of these actually references their
groby-ooo-7-16
2013/10/31 22:55:47
Getting rid of blocks or base::Bind, instead havin
|
+ |
+namespace { |
+ |
+const CGFloat kTitlePadding = 20.0; |
+const CGFloat kInset = 20.0; |
+ |
+} // namespace |
+ |
+// The Cocoa side of the bubble controller. |
+@interface GeneratedCreditCardBubbleControllerCocoa : |
+ BaseBubbleController<NSTextViewDelegate> { |
+ // A block that is run when the bubble is being closed. That allows wiping |
+ // out weak references. (BaseBubbleController self-destroys on close). |
+ base::mac::ScopedBlock<CloseObserver> closeObserver_; |
+ |
+ // A block that is run when the user clicks on a link. |
+ base::mac::ScopedBlock<ClickObserver> clickObserver_; |
+} |
+ |
+// Designate initializer. Runs |closeObserver| when -windowWillClose: runs. |
+- (id)initWithParentWindow:(NSWindow*)parentWindow |
+ controller: |
+ (autofill::GeneratedCreditCardBubbleController*)controller |
+ clickObserver:(ClickObserver)clickObserver |
+ closeObserver:(CloseObserver)closeObserver; |
Scott Hess - ex-Googler
2013/10/31 20:23:19
The nice thing about a weak ptr to the bridge obje
groby-ooo-7-16
2013/10/31 22:55:47
No weak ptr needed - the bridge must live on until
|
+ |
+// Show the bubble at the given |anchor| point. Coordinates are in screen space. |
+- (void)showAtAnchor:(NSPoint)anchor; |
+ |
+// Return true if the bubble is in the process of hiding. |
+- (BOOL)isHiding; |
+ |
+// Build the window contents and lay them out. |
+- (void)performLayoutWithController: |
+ (autofill::GeneratedCreditCardBubbleController*)controller; |
+ |
+@end |
+ |
+ |
+@implementation GeneratedCreditCardBubbleControllerCocoa |
+ |
+- (id)initWithParentWindow:(NSWindow*)parentWindow |
+ controller: |
+ (autofill::GeneratedCreditCardBubbleController*)controller |
+ clickObserver:(ClickObserver)clickObserver |
+ closeObserver:(CloseObserver)closeObserver { |
+ base::scoped_nsobject<InfoBubbleWindow> window( |
+ [[InfoBubbleWindow alloc] initWithContentRect:NSMakeRect(0, 0, 200, 100) |
+ styleMask:NSBorderlessWindowMask |
+ backing:NSBackingStoreBuffered |
+ defer:NO]); |
+ if ((self = [super initWithWindow:window |
+ parentWindow:parentWindow |
+ anchoredAt:NSZeroPoint])) { |
+ [window setCanBecomeKeyWindow:NO]; |
+ closeObserver_.reset(Block_copy(closeObserver)); |
+ clickObserver_.reset(Block_copy(clickObserver)); |
+ |
+ ui::NativeTheme* nativeTheme = ui::NativeTheme::instance(); |
+ [[self bubble] setAlignment:info_bubble::kAlignArrowToAnchor]; |
+ [[self bubble] setArrowLocation:info_bubble::kTopRight]; |
+ [[self bubble] setBackgroundColor: |
+ gfx::SkColorToCalibratedNSColor(nativeTheme->GetSystemColor( |
+ ui::NativeTheme::kColorId_DialogBackground))]; |
+ [self performLayoutWithController:controller]; |
+ } |
+ return self; |
+} |
+ |
+- (void)windowWillClose:(NSNotification*)notification { |
+ closeObserver_.get()(self); |
+ [super windowWillClose:notification]; |
+} |
+ |
+// Called when embedded links are clicked. |
+- (BOOL)textView:(NSTextView*)textView |
+ clickedOnLink:(id)link |
+ atIndex:(NSUInteger)charIndex { |
Scott Hess - ex-Googler
2013/10/31 20:23:19
Align the : chars.
groby-ooo-7-16
2013/10/31 22:55:47
Done.
|
+ int index = [base::mac::ObjCCastStrict<NSNumber>(link) intValue]; |
+ clickObserver_.get()(index); |
+ return YES; |
+} |
+ |
+- (void)showAtAnchor:(NSPoint)anchorPoint { |
+ [self setAnchorPoint:anchorPoint]; |
+ [self showWindow:nil]; |
+} |
+ |
+- (BOOL)isHiding { |
+ InfoBubbleWindow* window = |
+ base::mac::ObjCCastStrict<InfoBubbleWindow>([self window]); |
+ return [window isClosing]; |
+} |
+ |
+ |
+- (void)performLayoutWithController: |
+ (autofill::GeneratedCreditCardBubbleController*)controller { |
+ CGFloat bubbleWidth = autofill::GeneratedCreditCardBubbleView::kContentsWidth; |
+ |
+ // Build the bubble title. |
+ NSFont* titleFont = [NSFont systemFontOfSize:15.0]; |
+ NSString* title = base::SysUTF16ToNSString(controller->TitleText()); |
+ base::scoped_nsobject<HyperlinkTextView> titleView( |
+ [[HyperlinkTextView alloc] initWithFrame:NSZeroRect]); |
+ [titleView setMessage:title |
+ withFont:titleFont |
+ messageColor:[NSColor blackColor]]; |
+ |
+ [titleView setFrame:NSMakeRect( |
+ 0, 0, |
+ CGFLOAT_MAX, CGFLOAT_MAX)]; |
+ |
+ // Now use the layout manager to compute layout. |
+ NSLayoutManager* layoutManager = [titleView layoutManager]; |
+ NSTextContainer* textContainer = [titleView textContainer]; |
+ [layoutManager ensureLayoutForTextContainer:textContainer]; |
+ NSRect titleFrame = [layoutManager usedRectForTextContainer:textContainer]; |
+ [titleView setFrame:titleFrame]; |
Scott Hess - ex-Googler
2013/10/31 20:23:19
This section of code seems to be copy/paste inheri
groby-ooo-7-16
2013/10/31 22:55:47
I can skip this since it's always going to be sing
|
+ |
+ bubbleWidth = std::max(bubbleWidth, NSWidth([titleView frame]) + 2 * kInset); |
Scott Hess - ex-Googler
2013/10/31 20:23:19
Is it certain that the title won't cause windows t
groby-ooo-7-16
2013/10/31 22:55:47
That depends - if the browser is almost completely
Scott Hess - ex-Googler
2013/10/31 23:33:38
I agree that it would be a conundrum, but just bec
|
+ |
+ // Build the contents view. |
+ base::scoped_nsobject<HyperlinkTextView> contentsView( |
+ [[HyperlinkTextView alloc] initWithFrame:NSZeroRect]); |
+ |
+ [contentsView setEditable:NO]; |
+ [contentsView setDelegate:self]; |
+ |
+ NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSize]]; |
+ NSFont* boldFont = [NSFont boldSystemFontOfSize:[NSFont systemFontSize]]; |
Scott Hess - ex-Googler
2013/10/31 20:23:19
Could as easily store the @{} in an NSDictionary*
groby-ooo-7-16
2013/10/31 22:55:47
You mean the font? Yes, but HyperlinkTextView does
Scott Hess - ex-Googler
2013/10/31 23:33:38
|boldFont| is being applied to non-link sections t
|
+ [contentsView setMessage:base::SysUTF16ToNSString(controller->ContentsText()) |
+ withFont:font |
+ messageColor:[NSColor blackColor]]; |
+ |
+ const std::vector<autofill::TextRange>& text_ranges = |
+ controller->ContentsTextRanges(); |
+ for (size_t i = 0; i < text_ranges.size(); ++i) { |
+ NSRange range = text_ranges[i].range.ToNSRange(); |
+ if (text_ranges[i].is_link) { |
+ [contentsView addLinkRange:range |
+ withName:@(i) |
+ linkColor:[NSColor blueColor]]; |
+ } else { |
+ [[contentsView textStorage] |
+ addAttributes:@{ NSFontAttributeName : boldFont } |
+ range:range]; |
+ } |
+ } |
+ |
+ // There's no direct API to compute desired sizes - use layouting instead. |
+ // Layout in a rect with fixed width and "infinite" height. |
+ [contentsView setFrame:NSMakeRect( |
+ 0, 0, |
+ bubbleWidth - 2 * kInset, CGFLOAT_MAX)]; |
+ |
+ // Use the layout manager to compute size. |
+ layoutManager = [contentsView layoutManager]; |
+ textContainer = [contentsView textContainer]; |
+ [layoutManager ensureLayoutForTextContainer:textContainer]; |
+ NSRect newFrame = [layoutManager usedRectForTextContainer:textContainer]; |
+ [contentsView setFrame:newFrame]; |
Scott Hess - ex-Googler
2013/10/31 20:23:19
Likewise here, seems like having contentsView expo
groby-ooo-7-16
2013/10/31 22:55:47
Technically, it belongs on NSTextView, but I'm rel
Scott Hess - ex-Googler
2013/10/31 23:33:38
I find two existing cases where we're doing this b
|
+ |
+ // Sizes are computed, now lay out the individual parts. |
+ CGFloat bubbleHeight = NSHeight([titleView frame]) + |
+ kTitlePadding + |
+ 2 * kInset + |
+ NSHeight([contentsView frame]); |
+ [titleView setFrameOrigin: |
+ NSMakePoint(kInset, bubbleHeight - kInset - NSHeight([titleView frame]))]; |
Scott Hess - ex-Googler
2013/10/31 20:23:19
Rather than constructing bubbleHeight and then bac
groby-ooo-7-16
2013/10/31 22:55:47
Done.
|
+ [contentsView setFrameOrigin:NSMakePoint(kInset, kInset)]; |
Scott Hess - ex-Googler
2013/10/31 20:23:19
Though another option would be to construct additi
groby-ooo-7-16
2013/10/31 22:55:47
Done.
|
+ [[[self window] contentView] setSubviews:@[ contentsView, titleView ]]; |
+ |
+ // Update window frame. |
+ NSRect windowFrame = [[self window] frame]; |
+ windowFrame.size = NSMakeSize(bubbleWidth, bubbleHeight); |
+ [[self window] setFrame:windowFrame display:YES]; |
+} |
+ |
+@end |
+ |
+ |
+namespace autofill { |
+ |
+// static |
+base::WeakPtr<GeneratedCreditCardBubbleView> |
+ GeneratedCreditCardBubbleView::Create( |
+ const base::WeakPtr<GeneratedCreditCardBubbleController>& controller) { |
+ return (new GeneratedCreditCardBubbleCocoa(controller))->weak_ptr_factory_. |
+ GetWeakPtr(); |
+} |
+ |
+GeneratedCreditCardBubbleCocoa::GeneratedCreditCardBubbleCocoa( |
+ const base::WeakPtr<GeneratedCreditCardBubbleController>& controller) |
+ : bubbleController_(NULL), |
+ controller_(controller), |
+ weak_ptr_factory_(this) { |
+} |
+ |
+GeneratedCreditCardBubbleCocoa::~GeneratedCreditCardBubbleCocoa() {} |
+ |
+void GeneratedCreditCardBubbleCocoa::Show() { |
+ NSView* browser_view = |
+ controller_->web_contents()->GetView()->GetNativeView(); |
Scott Hess - ex-Googler
2013/10/31 20:23:19
AFAICT, your code never checks whether the weak po
groby-ooo-7-16
2013/10/31 22:55:47
It does check in the observer blocks. Show is inde
|
+ NSWindow* parent_window = [browser_view window]; |
+ LocationBarViewMac* location_bar = |
+ [[parent_window windowController] locationBarBridge]; |
+ |
+ // |location_bar| can be NULL during initialization stages. |
+ if (!location_bar) |
+ return; |
+ |
+ if (!bubbleController_) { |
+ CloseObserver observer = ^(GeneratedCreditCardBubbleControllerCocoa*) { |
+ bubbleController_ = nil; |
+ }; |
+ ClickObserver clickObserver = ^(int index) { |
+ if (controller_) |
+ controller_->OnLinkClicked(); |
+ }; |
+ bubbleController_ = [[GeneratedCreditCardBubbleControllerCocoa alloc] |
+ initWithParentWindow:parent_window |
+ controller:controller_.get() |
+ clickObserver:clickObserver |
+ closeObserver:observer]; |
+ } |
+ |
+ NSRect frame = NSZeroRect; |
+ frame.origin = location_bar->GetGeneratedCreditCardBubblePoint(); |
Scott Hess - ex-Googler
2013/10/31 20:23:19
Excess space.
groby-ooo-7-16
2013/10/31 22:55:47
Done.
|
+ NSPoint anchor = [parent_window convertRectToScreen:frame].origin; |
+ [bubbleController_ showAtAnchor:anchor]; |
+} |
+ |
Scott Hess - ex-Googler
2013/10/31 20:23:19
extra line.
groby-ooo-7-16
2013/10/31 22:55:47
Done.
|
+ |
+void GeneratedCreditCardBubbleCocoa::Hide() { |
+ [bubbleController_ close]; |
+} |
+ |
+bool GeneratedCreditCardBubbleCocoa::IsHiding() const { |
+ return [bubbleController_ isHiding]; |
+} |
+ |
Scott Hess - ex-Googler
2013/10/31 20:23:19
extra line
groby-ooo-7-16
2013/10/31 22:55:47
Done.
|
+ |
+} // autofill |