Index: chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.mm |
diff --git a/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.mm b/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.mm |
index cc8897d4e7692583d115c363dd61d96f79105a6e..542441543c31948a3d4a532c86957d4ec2d8d132 100644 |
--- a/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.mm |
+++ b/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.mm |
@@ -2,10 +2,14 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+#include "base/strings/sys_string_conversions.h" |
+#include "chrome/browser/ui/autofill/autofill_dialog_models.h" |
#include "chrome/browser/ui/autofill/card_unmask_prompt_controller.h" |
#include "chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.h" |
+#import "chrome/browser/ui/cocoa/autofill/autofill_pop_up_button.h" |
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h" |
#include "chrome/browser/ui/chrome_style.h" |
+#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_control_utils.h" |
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h" |
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_window.h" |
#import "chrome/browser/ui/cocoa/key_equivalent_constants.h" |
@@ -14,7 +18,11 @@ |
#include "ui/base/l10n/l10n_util.h" |
namespace { |
+ |
const CGFloat kButtonGap = 6.0f; |
+const CGFloat kDialogContentMinWidth = 210.0f; |
+const CGFloat kCvcInputWidth = 64.0f; |
+ |
} // namespace |
namespace autofill { |
@@ -30,12 +38,16 @@ CardUnmaskPromptView* CardUnmaskPromptView::CreateAndShow( |
CardUnmaskPromptViewBridge::CardUnmaskPromptViewBridge( |
CardUnmaskPromptController* controller) |
: controller_(controller) { |
- sheet_controller_.reset([[CardUnmaskPromptViewCocoa alloc] |
+ view_controller_.reset([[CardUnmaskPromptViewCocoa alloc] |
initWithWebContents:controller_->GetWebContents() |
bridge:this]); |
+ |
+ // Setup the constrained window that will show the view. |
+ base::scoped_nsobject<NSWindow> window([[ConstrainedWindowCustomWindow alloc] |
+ initWithContentRect:[[view_controller_ view] bounds]]); |
+ [window setContentView:[view_controller_ view]]; |
base::scoped_nsobject<CustomConstrainedWindowSheet> sheet( |
- [[CustomConstrainedWindowSheet alloc] |
- initWithCustomWindow:[sheet_controller_ window]]); |
+ [[CustomConstrainedWindowSheet alloc] initWithCustomWindow:window]); |
constrained_window_.reset( |
new ConstrainedWindowMac(this, controller_->GetWebContents(), sheet)); |
} |
@@ -44,6 +56,8 @@ CardUnmaskPromptViewBridge::~CardUnmaskPromptViewBridge() { |
} |
void CardUnmaskPromptViewBridge::ControllerGone() { |
+ controller_ = nullptr; |
+ PerformClose(); |
} |
void CardUnmaskPromptViewBridge::DisableAndWaitForVerification() { |
@@ -55,7 +69,12 @@ void CardUnmaskPromptViewBridge::GotVerificationResult(bool success) { |
void CardUnmaskPromptViewBridge::OnConstrainedWindowClosed( |
ConstrainedWindowMac* window) { |
constrained_window_.reset(); |
- controller_->OnUnmaskDialogClosed(); |
+ if (controller_) |
+ controller_->OnUnmaskDialogClosed(); |
+} |
+ |
+CardUnmaskPromptController* CardUnmaskPromptViewBridge::GetController() { |
+ return controller_; |
} |
void CardUnmaskPromptViewBridge::PerformClose() { |
@@ -68,19 +87,46 @@ void CardUnmaskPromptViewBridge::PerformClose() { |
@implementation CardUnmaskPromptViewCocoa |
++ (AutofillPopUpButton*)buildDatePopupWithModel:(ui::ComboboxModel&)model { |
+ AutofillPopUpButton* popup = |
+ [[AutofillPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO]; |
+ |
+ for (int i = 0; i < model.GetItemCount(); ++i) { |
+ [popup addItemWithTitle:base::SysUTF16ToNSString(model.GetItemAt(i))]; |
+ } |
+ [popup setDefaultValue:base::SysUTF16ToNSString( |
+ model.GetItemAt(model.GetDefaultIndex()))]; |
+ [popup sizeToFit]; |
+ return popup; |
+} |
+ |
+// Set |view|'s frame to the minimum dimensions required to contain all of its |
+// subviews. |
++ (void)sizeToFitView:(NSView*)view { |
+ NSRect frame = NSZeroRect; |
+ for (NSView* child in [view subviews]) { |
+ frame = NSUnionRect(frame, [child frame]); |
+ } |
+ [view setFrame:frame]; |
+} |
+ |
++ (void)verticallyCenterSubviewsInView:(NSView*)view { |
+ CGFloat height = NSHeight([view frame]); |
+ for (NSView* child in [view subviews]) { |
+ [child setFrameOrigin:NSMakePoint( |
+ NSMinX([child frame]), |
+ ceil((height - NSHeight([child frame])) * 0.5))]; |
+ } |
+} |
+ |
- (id)initWithWebContents:(content::WebContents*)webContents |
bridge:(autofill::CardUnmaskPromptViewBridge*)bridge { |
DCHECK(webContents); |
DCHECK(bridge); |
- NSRect frame = NSMakeRect(0, 0, 550, 600); |
- base::scoped_nsobject<ConstrainedWindowCustomWindow> window( |
- [[ConstrainedWindowCustomWindow alloc] initWithContentRect:frame]); |
- if ((self = [super initWithWindow:window])) { |
+ if ((self = [super initWithNibName:nil bundle:nil])) { |
webContents_ = webContents; |
bridge_ = bridge; |
- |
- [self buildWindowButtons]; |
} |
return self; |
} |
@@ -89,38 +135,157 @@ void CardUnmaskPromptViewBridge::PerformClose() { |
bridge_->PerformClose(); |
} |
-- (void)buildWindowButtons { |
- base::scoped_nsobject<NSView> buttonContainer( |
+- (void)loadView { |
+ autofill::CardUnmaskPromptController* controller = bridge_->GetController(); |
+ DCHECK(controller); |
+ |
+ base::scoped_nsobject<NSBox> mainView( |
+ [[NSBox alloc] initWithFrame:NSZeroRect]); |
+ [mainView setBoxType:NSBoxCustom]; |
+ [mainView setBorderType:NSNoBorder]; |
+ [mainView setTitlePosition:NSNoTitle]; |
+ [mainView |
+ setContentViewMargins:NSMakeSize(chrome_style::kHorizontalPadding, 0)]; |
+ |
+ base::scoped_nsobject<NSView> inputRowView( |
[[NSView alloc] initWithFrame:NSZeroRect]); |
+ [mainView addSubview:inputRowView]; |
+ |
+ // Title label. |
+ NSTextField* title = constrained_window::CreateLabel(); |
+ NSAttributedString* titleString = |
+ constrained_window::GetAttributedLabelString( |
+ SysUTF16ToNSString(controller->GetWindowTitle()), |
+ chrome_style::kTitleFontStyle, NSNaturalTextAlignment, |
+ NSLineBreakByWordWrapping); |
+ [title setAttributedStringValue:titleString]; |
+ [title sizeToFit]; |
+ [mainView addSubview:title]; |
+ |
+ // Instructions label. |
+ NSTextField* instructions = constrained_window::CreateLabel(); |
+ NSAttributedString* instructionsString = |
+ constrained_window::GetAttributedLabelString( |
+ SysUTF16ToNSString(controller->GetInstructionsMessage()), |
+ chrome_style::kTextFontStyle, NSNaturalTextAlignment, |
+ NSLineBreakByWordWrapping); |
+ [instructions setAttributedStringValue:instructionsString]; |
+ [mainView addSubview:instructions]; |
- base::scoped_nsobject<NSButton> button( |
+ // Expiration date. |
+ base::scoped_nsobject<NSView> expirationView; |
+ if (controller->ShouldRequestExpirationDate()) { |
+ expirationView.reset([[NSView alloc] initWithFrame:NSZeroRect]); |
+ |
+ // Month. |
+ autofill::MonthComboboxModel monthModel; |
+ base::scoped_nsobject<AutofillPopUpButton> monthPopup( |
+ [CardUnmaskPromptViewCocoa buildDatePopupWithModel:monthModel]); |
+ [expirationView addSubview:monthPopup]; |
+ |
+ // Year. |
+ autofill::YearComboboxModel yearModel; |
+ base::scoped_nsobject<AutofillPopUpButton> yearPopup( |
+ [CardUnmaskPromptViewCocoa buildDatePopupWithModel:yearModel]); |
+ [expirationView addSubview:yearPopup]; |
+ |
+ // Layout month and year within expirationView. |
+ [yearPopup |
+ setFrameOrigin:NSMakePoint(NSMaxX([monthPopup frame]) + kButtonGap, 0)]; |
+ NSRect expirationFrame = NSUnionRect([monthPopup frame], [yearPopup frame]); |
+ expirationFrame.size.width += kButtonGap; |
+ [expirationView setFrame:expirationFrame]; |
+ [inputRowView addSubview:expirationView]; |
+ } |
+ |
+ // CVC text input. |
+ base::scoped_nsobject<NSTextField> cvcInput( |
+ [[NSTextField alloc] initWithFrame:NSZeroRect]); |
+ [[cvcInput cell] |
+ setPlaceholderString:l10n_util::GetNSString( |
+ IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC)]; |
+ [[cvcInput cell] setScrollable:YES]; |
+ [cvcInput sizeToFit]; |
+ [cvcInput setFrame:NSMakeRect(NSMaxX([expirationView frame]), 0, |
+ kCvcInputWidth, NSHeight([cvcInput frame]))]; |
+ [inputRowView addSubview:cvcInput]; |
+ |
+ // CVC image. |
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
+ NSImage* cvcImage = |
+ rb.GetNativeImageNamed(controller->GetCvcImageRid()).ToNSImage(); |
+ base::scoped_nsobject<NSImageView> cvcImageView( |
+ [[NSImageView alloc] initWithFrame:NSZeroRect]); |
+ [cvcImageView setImage:cvcImage]; |
+ [cvcImageView setFrameSize:[cvcImage size]]; |
+ [cvcImageView |
+ setFrameOrigin:NSMakePoint(NSMaxX([cvcInput frame]) + kButtonGap, 0)]; |
+ [inputRowView addSubview:cvcImageView]; |
+ |
+ // Cancel button. |
+ base::scoped_nsobject<NSButton> cancelButton( |
[[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect]); |
- [button setTitle:l10n_util::GetNSStringWithFixup(IDS_CANCEL)]; |
- [button setKeyEquivalent:kKeyEquivalentEscape]; |
- [button setTarget:self]; |
- [button setAction:@selector(closeSheet:)]; |
- [button sizeToFit]; |
- [buttonContainer addSubview:button]; |
- |
- CGFloat nextX = NSMaxX([button frame]) + kButtonGap; |
- button.reset([[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect]); |
- [button setFrameOrigin:NSMakePoint(nextX, 0)]; |
- [button setTitle:l10n_util::GetNSStringWithFixup( |
- IDS_AUTOFILL_DIALOG_SUBMIT_BUTTON)]; |
- [button setKeyEquivalent:kKeyEquivalentReturn]; |
- [button setTarget:self]; |
- [button setAction:@selector(closeSheet:)]; |
- [button sizeToFit]; |
- [buttonContainer addSubview:button]; |
- |
- const CGFloat dialogOffset = NSWidth([[self window] frame]) - |
- chrome_style::kHorizontalPadding - |
- NSMaxX([button frame]); |
- [buttonContainer |
- setFrame:NSMakeRect(dialogOffset, chrome_style::kClientBottomPadding, |
- NSMaxX([button frame]), NSMaxY([button frame]))]; |
- |
- [[[self window] contentView] addSubview:buttonContainer]; |
+ [cancelButton setTitle:l10n_util::GetNSStringWithFixup(IDS_CANCEL)]; |
+ [cancelButton setKeyEquivalent:kKeyEquivalentEscape]; |
+ [cancelButton setTarget:self]; |
+ [cancelButton setAction:@selector(closeSheet:)]; |
+ [cancelButton sizeToFit]; |
+ [mainView addSubview:cancelButton]; |
+ |
+ // Verify button. |
+ base::scoped_nsobject<NSButton> verifyButton( |
+ [[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect]); |
+ // TODO(bondd): use l10n string. |
+ [verifyButton setTitle:@"Verify"]; |
+ [verifyButton setKeyEquivalent:kKeyEquivalentReturn]; |
+ [verifyButton setTarget:self]; |
+ [verifyButton setAction:@selector(closeSheet:)]; |
+ [verifyButton sizeToFit]; |
+ [mainView addSubview:verifyButton]; |
+ |
+ // Layout inputRowView. |
+ [CardUnmaskPromptViewCocoa sizeToFitView:inputRowView]; |
+ [CardUnmaskPromptViewCocoa verticallyCenterSubviewsInView:inputRowView]; |
+ |
+ // Calculate dialog content width. |
+ CGFloat contentWidth = |
+ std::max(NSWidth([title frame]), NSWidth([inputRowView frame])); |
+ contentWidth = std::max(contentWidth, kDialogContentMinWidth); |
+ |
+ // Layout mainView contents, starting at the bottom and moving up. |
+ |
+ // Verify and Cancel buttons. |
+ [verifyButton |
+ setFrameOrigin:NSMakePoint(contentWidth - NSWidth([verifyButton frame]), |
+ chrome_style::kClientBottomPadding)]; |
+ |
+ [cancelButton |
+ setFrameOrigin:NSMakePoint(NSMinX([verifyButton frame]) - kButtonGap - |
+ NSWidth([cancelButton frame]), |
+ NSMinY([verifyButton frame]))]; |
+ |
+ // Input row. |
+ [inputRowView setFrameOrigin:NSMakePoint(0, NSMaxY([cancelButton frame]) + |
+ chrome_style::kRowPadding)]; |
+ |
+ // Instruction label. |
+ [instructions setFrameOrigin:NSMakePoint(0, NSMaxY([inputRowView frame]) + |
+ chrome_style::kRowPadding)]; |
+ NSSize instructionsSize = [[instructions cell] |
+ cellSizeForBounds:NSMakeRect(0, 0, contentWidth, CGFLOAT_MAX)]; |
+ [instructions setFrameSize:instructionsSize]; |
+ |
+ // Title label. |
+ [title setFrameOrigin:NSMakePoint(0, NSMaxY([instructions frame]) + |
+ chrome_style::kRowPadding)]; |
+ |
+ // Dialog size. |
+ [mainView |
+ setFrameSize:NSMakeSize( |
+ contentWidth + [mainView contentViewMargins].width * 2.0, |
+ NSMaxY([title frame]) + chrome_style::kTitleTopPadding)]; |
+ |
+ [self setView:mainView]; |
} |
@end |