OLD | NEW |
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2015 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 #include "base/strings/sys_string_conversions.h" |
| 6 #include "chrome/browser/ui/autofill/autofill_dialog_models.h" |
5 #include "chrome/browser/ui/autofill/card_unmask_prompt_controller.h" | 7 #include "chrome/browser/ui/autofill/card_unmask_prompt_controller.h" |
6 #include "chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.h" | 8 #include "chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.h" |
| 9 #import "chrome/browser/ui/cocoa/autofill/autofill_pop_up_button.h" |
7 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h" | 10 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h" |
8 #include "chrome/browser/ui/chrome_style.h" | 11 #include "chrome/browser/ui/chrome_style.h" |
| 12 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_control_u
tils.h" |
9 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sh
eet.h" | 13 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sh
eet.h" |
10 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_wi
ndow.h" | 14 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_wi
ndow.h" |
11 #import "chrome/browser/ui/cocoa/key_equivalent_constants.h" | 15 #import "chrome/browser/ui/cocoa/key_equivalent_constants.h" |
12 #include "grit/generated_resources.h" | 16 #include "grit/generated_resources.h" |
13 #include "ui/base/cocoa/window_size_constants.h" | 17 #include "ui/base/cocoa/window_size_constants.h" |
14 #include "ui/base/l10n/l10n_util.h" | 18 #include "ui/base/l10n/l10n_util.h" |
15 | 19 |
16 namespace { | 20 namespace { |
| 21 |
17 const CGFloat kButtonGap = 6.0f; | 22 const CGFloat kButtonGap = 6.0f; |
| 23 const CGFloat kDialogContentMinWidth = 210.0f; |
| 24 const CGFloat kCvcInputWidth = 64.0f; |
| 25 |
18 } // namespace | 26 } // namespace |
19 | 27 |
20 namespace autofill { | 28 namespace autofill { |
21 | 29 |
22 // static | 30 // static |
23 CardUnmaskPromptView* CardUnmaskPromptView::CreateAndShow( | 31 CardUnmaskPromptView* CardUnmaskPromptView::CreateAndShow( |
24 CardUnmaskPromptController* controller) { | 32 CardUnmaskPromptController* controller) { |
25 return new CardUnmaskPromptViewBridge(controller); | 33 return new CardUnmaskPromptViewBridge(controller); |
26 } | 34 } |
27 | 35 |
28 #pragma mark CardUnmaskPromptViewBridge | 36 #pragma mark CardUnmaskPromptViewBridge |
29 | 37 |
30 CardUnmaskPromptViewBridge::CardUnmaskPromptViewBridge( | 38 CardUnmaskPromptViewBridge::CardUnmaskPromptViewBridge( |
31 CardUnmaskPromptController* controller) | 39 CardUnmaskPromptController* controller) |
32 : controller_(controller) { | 40 : controller_(controller) { |
33 sheet_controller_.reset([[CardUnmaskPromptViewCocoa alloc] | 41 view_controller_.reset([[CardUnmaskPromptViewCocoa alloc] |
34 initWithWebContents:controller_->GetWebContents() | 42 initWithWebContents:controller_->GetWebContents() |
35 bridge:this]); | 43 bridge:this]); |
| 44 |
| 45 // Setup the constrained window that will show the view. |
| 46 base::scoped_nsobject<NSWindow> window([[ConstrainedWindowCustomWindow alloc] |
| 47 initWithContentRect:[[view_controller_ view] bounds]]); |
| 48 [window setContentView:[view_controller_ view]]; |
36 base::scoped_nsobject<CustomConstrainedWindowSheet> sheet( | 49 base::scoped_nsobject<CustomConstrainedWindowSheet> sheet( |
37 [[CustomConstrainedWindowSheet alloc] | 50 [[CustomConstrainedWindowSheet alloc] initWithCustomWindow:window]); |
38 initWithCustomWindow:[sheet_controller_ window]]); | |
39 constrained_window_.reset( | 51 constrained_window_.reset( |
40 new ConstrainedWindowMac(this, controller_->GetWebContents(), sheet)); | 52 new ConstrainedWindowMac(this, controller_->GetWebContents(), sheet)); |
41 } | 53 } |
42 | 54 |
43 CardUnmaskPromptViewBridge::~CardUnmaskPromptViewBridge() { | 55 CardUnmaskPromptViewBridge::~CardUnmaskPromptViewBridge() { |
44 } | 56 } |
45 | 57 |
46 void CardUnmaskPromptViewBridge::ControllerGone() { | 58 void CardUnmaskPromptViewBridge::ControllerGone() { |
| 59 controller_ = nullptr; |
| 60 PerformClose(); |
47 } | 61 } |
48 | 62 |
49 void CardUnmaskPromptViewBridge::DisableAndWaitForVerification() { | 63 void CardUnmaskPromptViewBridge::DisableAndWaitForVerification() { |
50 } | 64 } |
51 | 65 |
52 void CardUnmaskPromptViewBridge::GotVerificationResult(bool success) { | 66 void CardUnmaskPromptViewBridge::GotVerificationResult(bool success) { |
53 } | 67 } |
54 | 68 |
55 void CardUnmaskPromptViewBridge::OnConstrainedWindowClosed( | 69 void CardUnmaskPromptViewBridge::OnConstrainedWindowClosed( |
56 ConstrainedWindowMac* window) { | 70 ConstrainedWindowMac* window) { |
57 constrained_window_.reset(); | 71 constrained_window_.reset(); |
58 controller_->OnUnmaskDialogClosed(); | 72 if (controller_) |
| 73 controller_->OnUnmaskDialogClosed(); |
| 74 } |
| 75 |
| 76 CardUnmaskPromptController* CardUnmaskPromptViewBridge::GetController() { |
| 77 return controller_; |
59 } | 78 } |
60 | 79 |
61 void CardUnmaskPromptViewBridge::PerformClose() { | 80 void CardUnmaskPromptViewBridge::PerformClose() { |
62 constrained_window_->CloseWebContentsModalDialog(); | 81 constrained_window_->CloseWebContentsModalDialog(); |
63 } | 82 } |
64 | 83 |
65 } // autofill | 84 } // autofill |
66 | 85 |
67 #pragma mark CardUnmaskPromptViewCocoa | 86 #pragma mark CardUnmaskPromptViewCocoa |
68 | 87 |
69 @implementation CardUnmaskPromptViewCocoa | 88 @implementation CardUnmaskPromptViewCocoa |
70 | 89 |
| 90 + (AutofillPopUpButton*)buildDatePopupWithModel:(ui::ComboboxModel&)model { |
| 91 AutofillPopUpButton* popup = |
| 92 [[AutofillPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO]; |
| 93 |
| 94 for (int i = 0; i < model.GetItemCount(); ++i) { |
| 95 [popup addItemWithTitle:base::SysUTF16ToNSString(model.GetItemAt(i))]; |
| 96 } |
| 97 [popup setDefaultValue:base::SysUTF16ToNSString( |
| 98 model.GetItemAt(model.GetDefaultIndex()))]; |
| 99 [popup sizeToFit]; |
| 100 return popup; |
| 101 } |
| 102 |
| 103 // Set |view|'s frame to the minimum dimensions required to contain all of its |
| 104 // subviews. |
| 105 + (void)sizeToFitView:(NSView*)view { |
| 106 NSRect frame = NSZeroRect; |
| 107 for (NSView* child in [view subviews]) { |
| 108 frame = NSUnionRect(frame, [child frame]); |
| 109 } |
| 110 [view setFrame:frame]; |
| 111 } |
| 112 |
| 113 + (void)verticallyCenterSubviewsInView:(NSView*)view { |
| 114 CGFloat height = NSHeight([view frame]); |
| 115 for (NSView* child in [view subviews]) { |
| 116 [child setFrameOrigin:NSMakePoint( |
| 117 NSMinX([child frame]), |
| 118 ceil((height - NSHeight([child frame])) * 0.5))]; |
| 119 } |
| 120 } |
| 121 |
71 - (id)initWithWebContents:(content::WebContents*)webContents | 122 - (id)initWithWebContents:(content::WebContents*)webContents |
72 bridge:(autofill::CardUnmaskPromptViewBridge*)bridge { | 123 bridge:(autofill::CardUnmaskPromptViewBridge*)bridge { |
73 DCHECK(webContents); | 124 DCHECK(webContents); |
74 DCHECK(bridge); | 125 DCHECK(bridge); |
75 | 126 |
76 NSRect frame = NSMakeRect(0, 0, 550, 600); | 127 if ((self = [super initWithNibName:nil bundle:nil])) { |
77 base::scoped_nsobject<ConstrainedWindowCustomWindow> window( | |
78 [[ConstrainedWindowCustomWindow alloc] initWithContentRect:frame]); | |
79 if ((self = [super initWithWindow:window])) { | |
80 webContents_ = webContents; | 128 webContents_ = webContents; |
81 bridge_ = bridge; | 129 bridge_ = bridge; |
82 | |
83 [self buildWindowButtons]; | |
84 } | 130 } |
85 return self; | 131 return self; |
86 } | 132 } |
87 | 133 |
88 - (IBAction)closeSheet:(id)sender { | 134 - (IBAction)closeSheet:(id)sender { |
89 bridge_->PerformClose(); | 135 bridge_->PerformClose(); |
90 } | 136 } |
91 | 137 |
92 - (void)buildWindowButtons { | 138 - (void)loadView { |
93 base::scoped_nsobject<NSView> buttonContainer( | 139 autofill::CardUnmaskPromptController* controller = bridge_->GetController(); |
| 140 DCHECK(controller); |
| 141 |
| 142 base::scoped_nsobject<NSBox> mainView( |
| 143 [[NSBox alloc] initWithFrame:NSZeroRect]); |
| 144 [mainView setBoxType:NSBoxCustom]; |
| 145 [mainView setBorderType:NSNoBorder]; |
| 146 [mainView setTitlePosition:NSNoTitle]; |
| 147 [mainView |
| 148 setContentViewMargins:NSMakeSize(chrome_style::kHorizontalPadding, 0)]; |
| 149 |
| 150 base::scoped_nsobject<NSView> inputRowView( |
94 [[NSView alloc] initWithFrame:NSZeroRect]); | 151 [[NSView alloc] initWithFrame:NSZeroRect]); |
| 152 [mainView addSubview:inputRowView]; |
95 | 153 |
96 base::scoped_nsobject<NSButton> button( | 154 // Title label. |
| 155 NSTextField* title = constrained_window::CreateLabel(); |
| 156 NSAttributedString* titleString = |
| 157 constrained_window::GetAttributedLabelString( |
| 158 SysUTF16ToNSString(controller->GetWindowTitle()), |
| 159 chrome_style::kTitleFontStyle, NSNaturalTextAlignment, |
| 160 NSLineBreakByWordWrapping); |
| 161 [title setAttributedStringValue:titleString]; |
| 162 [title sizeToFit]; |
| 163 [mainView addSubview:title]; |
| 164 |
| 165 // Instructions label. |
| 166 NSTextField* instructions = constrained_window::CreateLabel(); |
| 167 NSAttributedString* instructionsString = |
| 168 constrained_window::GetAttributedLabelString( |
| 169 SysUTF16ToNSString(controller->GetInstructionsMessage()), |
| 170 chrome_style::kTextFontStyle, NSNaturalTextAlignment, |
| 171 NSLineBreakByWordWrapping); |
| 172 [instructions setAttributedStringValue:instructionsString]; |
| 173 [mainView addSubview:instructions]; |
| 174 |
| 175 // Expiration date. |
| 176 base::scoped_nsobject<NSView> expirationView; |
| 177 if (controller->ShouldRequestExpirationDate()) { |
| 178 expirationView.reset([[NSView alloc] initWithFrame:NSZeroRect]); |
| 179 |
| 180 // Month. |
| 181 autofill::MonthComboboxModel monthModel; |
| 182 base::scoped_nsobject<AutofillPopUpButton> monthPopup( |
| 183 [CardUnmaskPromptViewCocoa buildDatePopupWithModel:monthModel]); |
| 184 [expirationView addSubview:monthPopup]; |
| 185 |
| 186 // Year. |
| 187 autofill::YearComboboxModel yearModel; |
| 188 base::scoped_nsobject<AutofillPopUpButton> yearPopup( |
| 189 [CardUnmaskPromptViewCocoa buildDatePopupWithModel:yearModel]); |
| 190 [expirationView addSubview:yearPopup]; |
| 191 |
| 192 // Layout month and year within expirationView. |
| 193 [yearPopup |
| 194 setFrameOrigin:NSMakePoint(NSMaxX([monthPopup frame]) + kButtonGap, 0)]; |
| 195 NSRect expirationFrame = NSUnionRect([monthPopup frame], [yearPopup frame]); |
| 196 expirationFrame.size.width += kButtonGap; |
| 197 [expirationView setFrame:expirationFrame]; |
| 198 [inputRowView addSubview:expirationView]; |
| 199 } |
| 200 |
| 201 // CVC text input. |
| 202 base::scoped_nsobject<NSTextField> cvcInput( |
| 203 [[NSTextField alloc] initWithFrame:NSZeroRect]); |
| 204 [[cvcInput cell] |
| 205 setPlaceholderString:l10n_util::GetNSString( |
| 206 IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC)]; |
| 207 [[cvcInput cell] setScrollable:YES]; |
| 208 [cvcInput sizeToFit]; |
| 209 [cvcInput setFrame:NSMakeRect(NSMaxX([expirationView frame]), 0, |
| 210 kCvcInputWidth, NSHeight([cvcInput frame]))]; |
| 211 [inputRowView addSubview:cvcInput]; |
| 212 |
| 213 // CVC image. |
| 214 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 215 NSImage* cvcImage = |
| 216 rb.GetNativeImageNamed(controller->GetCvcImageRid()).ToNSImage(); |
| 217 base::scoped_nsobject<NSImageView> cvcImageView( |
| 218 [[NSImageView alloc] initWithFrame:NSZeroRect]); |
| 219 [cvcImageView setImage:cvcImage]; |
| 220 [cvcImageView setFrameSize:[cvcImage size]]; |
| 221 [cvcImageView |
| 222 setFrameOrigin:NSMakePoint(NSMaxX([cvcInput frame]) + kButtonGap, 0)]; |
| 223 [inputRowView addSubview:cvcImageView]; |
| 224 |
| 225 // Cancel button. |
| 226 base::scoped_nsobject<NSButton> cancelButton( |
97 [[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect]); | 227 [[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect]); |
98 [button setTitle:l10n_util::GetNSStringWithFixup(IDS_CANCEL)]; | 228 [cancelButton setTitle:l10n_util::GetNSStringWithFixup(IDS_CANCEL)]; |
99 [button setKeyEquivalent:kKeyEquivalentEscape]; | 229 [cancelButton setKeyEquivalent:kKeyEquivalentEscape]; |
100 [button setTarget:self]; | 230 [cancelButton setTarget:self]; |
101 [button setAction:@selector(closeSheet:)]; | 231 [cancelButton setAction:@selector(closeSheet:)]; |
102 [button sizeToFit]; | 232 [cancelButton sizeToFit]; |
103 [buttonContainer addSubview:button]; | 233 [mainView addSubview:cancelButton]; |
104 | 234 |
105 CGFloat nextX = NSMaxX([button frame]) + kButtonGap; | 235 // Verify button. |
106 button.reset([[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect]); | 236 base::scoped_nsobject<NSButton> verifyButton( |
107 [button setFrameOrigin:NSMakePoint(nextX, 0)]; | 237 [[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect]); |
108 [button setTitle:l10n_util::GetNSStringWithFixup( | 238 // TODO(bondd): use l10n string. |
109 IDS_AUTOFILL_DIALOG_SUBMIT_BUTTON)]; | 239 [verifyButton setTitle:@"Verify"]; |
110 [button setKeyEquivalent:kKeyEquivalentReturn]; | 240 [verifyButton setKeyEquivalent:kKeyEquivalentReturn]; |
111 [button setTarget:self]; | 241 [verifyButton setTarget:self]; |
112 [button setAction:@selector(closeSheet:)]; | 242 [verifyButton setAction:@selector(closeSheet:)]; |
113 [button sizeToFit]; | 243 [verifyButton sizeToFit]; |
114 [buttonContainer addSubview:button]; | 244 [mainView addSubview:verifyButton]; |
115 | 245 |
116 const CGFloat dialogOffset = NSWidth([[self window] frame]) - | 246 // Layout inputRowView. |
117 chrome_style::kHorizontalPadding - | 247 [CardUnmaskPromptViewCocoa sizeToFitView:inputRowView]; |
118 NSMaxX([button frame]); | 248 [CardUnmaskPromptViewCocoa verticallyCenterSubviewsInView:inputRowView]; |
119 [buttonContainer | |
120 setFrame:NSMakeRect(dialogOffset, chrome_style::kClientBottomPadding, | |
121 NSMaxX([button frame]), NSMaxY([button frame]))]; | |
122 | 249 |
123 [[[self window] contentView] addSubview:buttonContainer]; | 250 // Calculate dialog content width. |
| 251 CGFloat contentWidth = |
| 252 std::max(NSWidth([title frame]), NSWidth([inputRowView frame])); |
| 253 contentWidth = std::max(contentWidth, kDialogContentMinWidth); |
| 254 |
| 255 // Layout mainView contents, starting at the bottom and moving up. |
| 256 |
| 257 // Verify and Cancel buttons. |
| 258 [verifyButton |
| 259 setFrameOrigin:NSMakePoint(contentWidth - NSWidth([verifyButton frame]), |
| 260 chrome_style::kClientBottomPadding)]; |
| 261 |
| 262 [cancelButton |
| 263 setFrameOrigin:NSMakePoint(NSMinX([verifyButton frame]) - kButtonGap - |
| 264 NSWidth([cancelButton frame]), |
| 265 NSMinY([verifyButton frame]))]; |
| 266 |
| 267 // Input row. |
| 268 [inputRowView setFrameOrigin:NSMakePoint(0, NSMaxY([cancelButton frame]) + |
| 269 chrome_style::kRowPadding)]; |
| 270 |
| 271 // Instruction label. |
| 272 [instructions setFrameOrigin:NSMakePoint(0, NSMaxY([inputRowView frame]) + |
| 273 chrome_style::kRowPadding)]; |
| 274 NSSize instructionsSize = [[instructions cell] |
| 275 cellSizeForBounds:NSMakeRect(0, 0, contentWidth, CGFLOAT_MAX)]; |
| 276 [instructions setFrameSize:instructionsSize]; |
| 277 |
| 278 // Title label. |
| 279 [title setFrameOrigin:NSMakePoint(0, NSMaxY([instructions frame]) + |
| 280 chrome_style::kRowPadding)]; |
| 281 |
| 282 // Dialog size. |
| 283 [mainView |
| 284 setFrameSize:NSMakeSize( |
| 285 contentWidth + [mainView contentViewMargins].width * 2.0, |
| 286 NSMaxY([title frame]) + chrome_style::kTitleTopPadding)]; |
| 287 |
| 288 [self setView:mainView]; |
124 } | 289 } |
125 | 290 |
126 @end | 291 @end |
OLD | NEW |