Chromium Code Reviews| 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 |