1 // Copyright 2013 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.
5 #include "chrome/browser/ui/cocoa/autofill/new_credit_card_bubble_cocoa.h"
7 #include "base/i18n/rtl.h"
8 #include "base/mac/foundation_util.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "chrome/browser/ui/autofill/new_credit_card_bubble_controller.h"
11 #include "chrome/browser/ui/browser_finder.h"
12 #include "chrome/browser/ui/browser_window.h"
13 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
14 #import "chrome/browser/ui/cocoa/base_bubble_controller.h"
15 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
16 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
17 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
18 #include "content/public/browser/web_contents.h"
19 #include "content/public/browser/web_contents_view.h"
20 #include "skia/ext/skia_utils_mac.h"
21 #import "ui/base/cocoa/controls/hyperlink_button_cell.h"
22 #include "ui/base/cocoa/window_size_constants.h"
23 #include "ui/native_theme/native_theme.h"
27 const CGFloat kWrenchBubblePointOffsetY = 6;
28 const CGFloat kVerticalSpacing = 8;
29 const CGFloat kHorizontalSpacing = 4;
30 const CGFloat kInset = 20.0;
31 const CGFloat kTitleSpacing = 15;
32 const CGFloat kAnchorlessEndPadding = 20;
33 const CGFloat kAnchorlessTopPadding = 10;
37 @interface NewCreditCardBubbleControllerCocoa : BaseBubbleController {
39 // Controller that drives this bubble. Never NULL; outlives this class.
40 autofill::NewCreditCardBubbleController* controller_;
42 // Objective-C/C++ bridge. Owned by this class.
43 scoped_ptr<autofill::NewCreditCardBubbleCocoa> bridge_;
46 // Designated initializer.
47 - (id)initWithParentWindow:(NSWindow*)parentWindow
49 (autofill::NewCreditCardBubbleController*)controller
50 bridge:(autofill::NewCreditCardBubbleCocoa*)bridge;
52 // Displays the bubble anchored at the specified |anchorPoint|.
53 - (void)showAtAnchor:(NSPoint)anchorPoint;
55 // Helper function to create an uneditable text field.
56 - (base::scoped_nsobject<NSTextField>)makeTextField;
58 // Helper function to set the appropriately styled details string.
59 - (void)setDetailsStringForField:(NSTextField*)details;
61 // Create and lay out all control elements inside the bubble.
62 - (void)performLayout;
64 // Delegate interface for clicks on "Learn more".
65 - (void)onLinkClicked:(id)sender;
70 @implementation NewCreditCardBubbleControllerCocoa
72 - (id)initWithParentWindow:(NSWindow*)parentWindow
74 (autofill::NewCreditCardBubbleController*)controller
75 bridge:(autofill::NewCreditCardBubbleCocoa*)bridge {
76 base::scoped_nsobject<InfoBubbleWindow> window(
77 [[InfoBubbleWindow alloc]
78 initWithContentRect:ui::kWindowSizeDeterminedLater
79 styleMask:NSBorderlessWindowMask
80 backing:NSBackingStoreBuffered
82 bridge_.reset(bridge);
83 if ((self = [super initWithWindow:window
84 parentWindow:parentWindow
85 anchoredAt:NSZeroPoint])) {
86 controller_ = controller;
87 [window setCanBecomeKeyWindow:NO];
89 ui::NativeTheme* nativeTheme = ui::NativeTheme::instance();
90 [[self bubble] setBackgroundColor:
91 gfx::SkColorToCalibratedNSColor(nativeTheme->GetSystemColor(
92 ui::NativeTheme::kColorId_DialogBackground))];
98 - (void)showAtAnchor:(NSPoint)anchorPoint {
99 [self setAnchorPoint:anchorPoint];
100 [[self bubble] setNeedsDisplay:YES];
101 [self showWindow:nil];
104 - (base::scoped_nsobject<NSTextField>)makeTextField {
105 base::scoped_nsobject<NSTextField> textField(
106 [[NSTextField alloc] initWithFrame:NSZeroRect]);
107 [textField setEditable:NO];
108 [textField setSelectable:NO];
109 [textField setDrawsBackground:NO];
110 [textField setBezeled:NO];
115 - (void)setDetailsStringForField:(NSTextField*)details {
116 // Set linespacing for |details| to match line spacing used in the dialog.
117 base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
118 [[NSMutableParagraphStyle alloc] init]);
119 [paragraphStyle setLineSpacing:0.5 * [[details font] pointSize]];
121 NSString* description =
122 base::SysUTF16ToNSString(controller_->CardDescription().description);
123 base::scoped_nsobject<NSAttributedString> detailsString(
124 [[NSAttributedString alloc]
125 initWithString:description
126 attributes:@{ NSParagraphStyleAttributeName : paragraphStyle }]);
128 [details setAttributedStringValue:detailsString];
131 - (void)performLayout {
132 base::scoped_nsobject<NSTextField> title([self makeTextField]);
133 base::scoped_nsobject<NSImageView> icon([[NSImageView alloc]
134 initWithFrame:NSZeroRect]);
135 base::scoped_nsobject<NSTextField> name([self makeTextField]);
136 base::scoped_nsobject<NSTextField> details([self makeTextField]);
137 base::scoped_nsobject<NSButton> link([[HyperlinkButtonCell buttonWithString:
138 base::SysUTF16ToNSString(controller_->LinkText())] retain]);
140 [link setTarget:self];
141 [link setAction:@selector(onLinkClicked:)];
143 // Use uniform font across all controls (except title).
144 [details setFont:[link font]];
145 [name setFont:[link font]];
146 [title setFont:[NSFont systemFontOfSize:15.0]];
149 const autofill::CreditCardDescription& card_desc =
150 controller_->CardDescription();
151 [title setStringValue:base::SysUTF16ToNSString(controller_->TitleText())];
152 [icon setImage:card_desc.icon.AsNSImage()];
153 [name setStringValue:base::SysUTF16ToNSString(card_desc.name)];
154 [self setDetailsStringForField:details];
157 CGFloat bubbleWidth = autofill::NewCreditCardBubbleView::kContentsWidth;
159 bubbleWidth = std::max(bubbleWidth, NSWidth([title frame]) + 2 * kInset);
160 [icon setFrameSize:[[icon image] size]];
162 [details setFrameSize:[[details cell] cellSizeForBounds:
163 NSMakeRect(0, 0, bubbleWidth - 2 * kInset, CGFLOAT_MAX)]];
167 [link setFrameOrigin:NSMakePoint(kInset, kInset)];
168 [details setFrameOrigin:NSMakePoint(
169 kInset, NSMaxY([link frame]) + kVerticalSpacing)];
170 [icon setFrameOrigin:NSMakePoint(
171 kInset, NSMaxY([details frame]) + kVerticalSpacing)];
172 [name setFrameOrigin:NSMakePoint(
173 NSMaxX([icon frame]) + kHorizontalSpacing,
174 NSMidY([icon frame]) - (NSHeight([name frame]) / 2.0))];
175 [title setFrameOrigin:
176 NSMakePoint(kInset, NSMaxY([icon frame]) + kTitleSpacing)];
178 [[[self window] contentView] setSubviews:
179 @[ title, icon, name, details, link ]];
181 // Update window frame.
182 NSRect windowFrame = [[self window] frame];
183 windowFrame.size.height = NSMaxY([title frame]) + kInset;
184 windowFrame.size.width = bubbleWidth;
185 [[self window] setFrame:windowFrame display:NO];
188 - (void)onLinkClicked:(id)sender {
189 controller_->OnLinkClicked();
197 NewCreditCardBubbleCocoa::~NewCreditCardBubbleCocoa() {
198 controller_->OnBubbleDestroyed();
201 void NewCreditCardBubbleCocoa::Show() {
202 NSView* browser_view =
203 controller_->web_contents()->GetView()->GetNativeView();
204 NSWindow* parent_window = [browser_view window];
205 BrowserWindowController* bwc = [BrowserWindowController
206 browserWindowControllerForWindow:parent_window];
208 if (!bubbleController_)
209 CreateCocoaController(parent_window);
211 NSPoint anchor_point;
213 if ([bwc isTabbedWindow]) {
214 anchor_view = [[bwc toolbarController] wrenchButton];
215 anchor_point = NSMakePoint(
216 NSMidX([anchor_view bounds]),
217 NSMinY([anchor_view bounds]) + kWrenchBubblePointOffsetY);
218 [[bubbleController_ bubble] setArrowLocation:info_bubble::kTopRight];
219 [[bubbleController_ bubble] setAlignment:info_bubble::kAlignArrowToAnchor];
221 anchor_view = browser_view;
222 anchor_point = NSMakePoint(
223 NSMaxX([browser_view bounds]) - kAnchorlessEndPadding,
224 NSMaxY([browser_view bounds]) - kAnchorlessTopPadding);
225 [[bubbleController_ bubble] setArrowLocation:info_bubble::kNoArrow];
226 if (base::i18n::IsRTL()) {
227 anchor_point.x = NSMaxX([anchor_view bounds]) - anchor_point.x;
228 [[bubbleController_ bubble] setAlignment:
229 info_bubble::kAlignLeftEdgeToAnchorEdge];
231 [[bubbleController_ bubble] setAlignment:
232 info_bubble::kAlignRightEdgeToAnchorEdge];
235 if ([anchor_view isFlipped])
236 anchor_point.y = NSMaxY([anchor_view bounds]) - anchor_point.y;
237 anchor_point = [anchor_view convertPoint:anchor_point toView:nil];
239 NSRect frame = NSZeroRect;
240 frame.origin = anchor_point;
241 anchor_point = [parent_window convertBaseToScreen:anchor_point];
242 [bubbleController_ showAtAnchor:anchor_point];
245 void NewCreditCardBubbleCocoa::Hide() {
246 [bubbleController_ close];
250 base::WeakPtr<NewCreditCardBubbleView> NewCreditCardBubbleView::Create(
251 NewCreditCardBubbleController* controller) {
252 NewCreditCardBubbleCocoa* bubble = new NewCreditCardBubbleCocoa(controller);
253 return bubble->weak_ptr_factory_.GetWeakPtr();
256 NewCreditCardBubbleCocoa::NewCreditCardBubbleCocoa(
257 NewCreditCardBubbleController* controller)
258 : bubbleController_(NULL),
259 controller_(controller),
260 weak_ptr_factory_(this) {
263 void NewCreditCardBubbleCocoa::CreateCocoaController(NSWindow* parent) {
264 DCHECK(!bubbleController_);
265 bubbleController_ = [[NewCreditCardBubbleControllerCocoa alloc]
266 initWithParentWindow:parent
267 controller:controller_
271 InfoBubbleWindow* NewCreditCardBubbleCocoa::GetInfoBubbleWindow() {
272 return base::mac::ObjCCastStrict<InfoBubbleWindow>(
273 [bubbleController_ window]);
276 } // namespace autofill