| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 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. | |
| 4 | |
| 5 #import "chrome/browser/ui/cocoa/autofill/autofill_main_container.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <cmath> | |
| 9 | |
| 10 #include "base/mac/foundation_util.h" | |
| 11 #include "base/strings/sys_string_conversions.h" | |
| 12 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h" | |
| 13 #include "chrome/browser/ui/chrome_style.h" | |
| 14 #import "chrome/browser/ui/cocoa/autofill/autofill_details_container.h" | |
| 15 #include "chrome/browser/ui/cocoa/autofill/autofill_dialog_constants.h" | |
| 16 #import "chrome/browser/ui/cocoa/autofill/autofill_notification_container.h" | |
| 17 #import "chrome/browser/ui/cocoa/autofill/autofill_tooltip_controller.h" | |
| 18 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h" | |
| 19 #import "chrome/browser/ui/cocoa/key_equivalent_constants.h" | |
| 20 #include "grit/components_scaled_resources.h" | |
| 21 #include "skia/ext/skia_utils_mac.h" | |
| 22 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTw
eaker.h" | |
| 23 #import "ui/base/cocoa/controls/blue_label_button.h" | |
| 24 #import "ui/base/cocoa/controls/hyperlink_text_view.h" | |
| 25 #include "ui/base/cocoa/window_size_constants.h" | |
| 26 #include "ui/gfx/range/range.h" | |
| 27 | |
| 28 namespace { | |
| 29 | |
| 30 // Padding between buttons and the last suggestion or details view. The mock | |
| 31 // has a total of 30px - but 10px are already provided by details/suggestions. | |
| 32 const CGFloat kButtonVerticalPadding = 20.0; | |
| 33 | |
| 34 } // namespace | |
| 35 | |
| 36 @interface AutofillMainContainer (Private) | |
| 37 - (void)buildWindowButtons; | |
| 38 - (void)layoutButtons; | |
| 39 - (void)updateButtons; | |
| 40 @end | |
| 41 | |
| 42 | |
| 43 @implementation AutofillMainContainer | |
| 44 | |
| 45 @synthesize target = target_; | |
| 46 | |
| 47 - (id)initWithDelegate:(autofill::AutofillDialogViewDelegate*)delegate { | |
| 48 if (self = [super init]) { | |
| 49 delegate_ = delegate; | |
| 50 } | |
| 51 return self; | |
| 52 } | |
| 53 | |
| 54 - (void)loadView { | |
| 55 [self buildWindowButtons]; | |
| 56 | |
| 57 base::scoped_nsobject<NSView> view([[NSView alloc] initWithFrame:NSZeroRect]); | |
| 58 [view setAutoresizesSubviews:YES]; | |
| 59 [view setSubviews:@[buttonContainer_]]; | |
| 60 [self setView:view]; | |
| 61 | |
| 62 // Set up "Save in Chrome" checkbox. | |
| 63 saveInChromeCheckbox_.reset([[NSButton alloc] initWithFrame:NSZeroRect]); | |
| 64 [saveInChromeCheckbox_ setButtonType:NSSwitchButton]; | |
| 65 [saveInChromeCheckbox_ setTitle: | |
| 66 base::SysUTF16ToNSString(delegate_->SaveLocallyText())]; | |
| 67 [saveInChromeCheckbox_ sizeToFit]; | |
| 68 [[self view] addSubview:saveInChromeCheckbox_]; | |
| 69 | |
| 70 saveInChromeTooltip_.reset( | |
| 71 [[AutofillTooltipController alloc] | |
| 72 initWithArrowLocation:info_bubble::kTopCenter]); | |
| 73 [saveInChromeTooltip_ setImage: | |
| 74 ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed( | |
| 75 IDR_AUTOFILL_TOOLTIP_ICON).ToNSImage()]; | |
| 76 [saveInChromeTooltip_ setMessage: | |
| 77 base::SysUTF16ToNSString(delegate_->SaveLocallyTooltip())]; | |
| 78 [[self view] addSubview:[saveInChromeTooltip_ view]]; | |
| 79 [self updateSaveInChrome]; | |
| 80 | |
| 81 detailsContainer_.reset( | |
| 82 [[AutofillDetailsContainer alloc] initWithDelegate:delegate_]); | |
| 83 NSSize frameSize = [[detailsContainer_ view] frame].size; | |
| 84 [[detailsContainer_ view] setFrameOrigin: | |
| 85 NSMakePoint(0, NSHeight([buttonContainer_ frame]))]; | |
| 86 frameSize.height += NSHeight([buttonContainer_ frame]); | |
| 87 [[self view] setFrameSize:frameSize]; | |
| 88 [[self view] addSubview:[detailsContainer_ view]]; | |
| 89 | |
| 90 notificationContainer_.reset( | |
| 91 [[AutofillNotificationContainer alloc] initWithDelegate:delegate_]); | |
| 92 [[self view] addSubview:[notificationContainer_ view]]; | |
| 93 } | |
| 94 | |
| 95 - (NSSize)decorationSizeForWidth:(CGFloat)width { | |
| 96 NSSize buttonSize = [buttonContainer_ frame].size; | |
| 97 NSSize buttonStripSize = | |
| 98 NSMakeSize(buttonSize.width + chrome_style::kHorizontalPadding, | |
| 99 buttonSize.height + kButtonVerticalPadding + | |
| 100 chrome_style::kClientBottomPadding); | |
| 101 | |
| 102 NSSize size = NSMakeSize(std::max(buttonStripSize.width, width), | |
| 103 buttonStripSize.height); | |
| 104 NSSize notificationSize = | |
| 105 [notificationContainer_ preferredSizeForWidth:width]; | |
| 106 size.height += notificationSize.height; | |
| 107 | |
| 108 return size; | |
| 109 } | |
| 110 | |
| 111 - (NSSize)preferredSize { | |
| 112 NSSize detailsSize = [detailsContainer_ preferredSize]; | |
| 113 NSSize decorationSize = [self decorationSizeForWidth:detailsSize.width]; | |
| 114 | |
| 115 NSSize size = NSMakeSize(std::max(decorationSize.width, detailsSize.width), | |
| 116 decorationSize.height + detailsSize.height); | |
| 117 size.height += autofill::kDetailVerticalPadding; | |
| 118 | |
| 119 return size; | |
| 120 } | |
| 121 | |
| 122 - (void)performLayout { | |
| 123 NSRect bounds = [[self view] bounds]; | |
| 124 | |
| 125 CGFloat currentY = 0.0; | |
| 126 NSRect buttonFrame = [buttonContainer_ frame]; | |
| 127 buttonFrame.origin.y = currentY + chrome_style::kClientBottomPadding; | |
| 128 [buttonContainer_ setFrameOrigin:buttonFrame.origin]; | |
| 129 currentY = NSMaxY(buttonFrame) + kButtonVerticalPadding; | |
| 130 | |
| 131 NSRect checkboxFrame = [saveInChromeCheckbox_ frame]; | |
| 132 [saveInChromeCheckbox_ setFrameOrigin: | |
| 133 NSMakePoint(chrome_style::kHorizontalPadding, | |
| 134 NSMidY(buttonFrame) - NSHeight(checkboxFrame) / 2.0)]; | |
| 135 | |
| 136 NSRect tooltipFrame = [[saveInChromeTooltip_ view] frame]; | |
| 137 [[saveInChromeTooltip_ view] setFrameOrigin: | |
| 138 NSMakePoint(NSMaxX([saveInChromeCheckbox_ frame]) + autofill::kButtonGap, | |
| 139 NSMidY(buttonFrame) - (NSHeight(tooltipFrame) / 2.0))]; | |
| 140 | |
| 141 NSRect notificationFrame = NSZeroRect; | |
| 142 notificationFrame.size = [notificationContainer_ preferredSizeForWidth: | |
| 143 NSWidth(bounds)]; | |
| 144 | |
| 145 // Buttons/checkbox/legal take up lower part of view, notifications the | |
| 146 // upper part. Adjust the detailsContainer to take up the remainder. | |
| 147 CGFloat remainingHeight = | |
| 148 NSHeight(bounds) - currentY - NSHeight(notificationFrame); | |
| 149 NSRect containerFrame = | |
| 150 NSMakeRect(0, currentY, NSWidth(bounds), remainingHeight); | |
| 151 [[detailsContainer_ view] setFrame:containerFrame]; | |
| 152 [detailsContainer_ performLayout]; | |
| 153 | |
| 154 notificationFrame.origin = NSMakePoint(0, NSMaxY(containerFrame)); | |
| 155 [[notificationContainer_ view] setFrame:notificationFrame]; | |
| 156 [notificationContainer_ performLayout]; | |
| 157 } | |
| 158 | |
| 159 - (void)buildWindowButtons { | |
| 160 if (buttonContainer_.get()) | |
| 161 return; | |
| 162 | |
| 163 buttonContainer_.reset([[GTMWidthBasedTweaker alloc] initWithFrame: | |
| 164 ui::kWindowSizeDeterminedLater]); | |
| 165 [buttonContainer_ | |
| 166 setAutoresizingMask:(NSViewMinXMargin | NSViewMaxYMargin)]; | |
| 167 | |
| 168 base::scoped_nsobject<NSButton> button( | |
| 169 [[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect]); | |
| 170 [button setKeyEquivalent:kKeyEquivalentEscape]; | |
| 171 [button setTarget:target_]; | |
| 172 [button setAction:@selector(cancel:)]; | |
| 173 [button sizeToFit]; | |
| 174 [buttonContainer_ addSubview:button]; | |
| 175 | |
| 176 CGFloat nextX = NSMaxX([button frame]) + autofill::kButtonGap; | |
| 177 button.reset([[BlueLabelButton alloc] initWithFrame:NSZeroRect]); | |
| 178 [button setFrameOrigin:NSMakePoint(nextX, 0)]; | |
| 179 [button setKeyEquivalent:kKeyEquivalentReturn]; | |
| 180 [button setTarget:target_]; | |
| 181 [button setAction:@selector(accept:)]; | |
| 182 [buttonContainer_ addSubview:button]; | |
| 183 [self updateButtons]; | |
| 184 | |
| 185 NSRect frame = NSMakeRect( | |
| 186 -NSMaxX([button frame]) - chrome_style::kHorizontalPadding, 0, | |
| 187 NSMaxX([button frame]), NSHeight([button frame])); | |
| 188 [buttonContainer_ setFrame:frame]; | |
| 189 } | |
| 190 | |
| 191 - (void)layoutButtons { | |
| 192 base::scoped_nsobject<GTMUILocalizerAndLayoutTweaker> layoutTweaker( | |
| 193 [[GTMUILocalizerAndLayoutTweaker alloc] init]); | |
| 194 [layoutTweaker tweakUI:buttonContainer_]; | |
| 195 | |
| 196 // Now ensure both buttons have the same height. The second button is | |
| 197 // known to be the larger one. | |
| 198 CGFloat buttonHeight = | |
| 199 NSHeight([[[buttonContainer_ subviews] objectAtIndex:1] frame]); | |
| 200 | |
| 201 // Force first button to be the same height. | |
| 202 NSView* button = [[buttonContainer_ subviews] objectAtIndex:0]; | |
| 203 NSSize buttonSize = [button frame].size; | |
| 204 buttonSize.height = buttonHeight; | |
| 205 [button setFrameSize:buttonSize]; | |
| 206 } | |
| 207 | |
| 208 - (void)updateButtons { | |
| 209 NSButton* button = base::mac::ObjCCastStrict<NSButton>( | |
| 210 [[buttonContainer_ subviews] objectAtIndex:0]); | |
| 211 [button setTitle:base::SysUTF16ToNSString(delegate_->CancelButtonText())]; | |
| 212 button = base::mac::ObjCCastStrict<NSButton>( | |
| 213 [[buttonContainer_ subviews] objectAtIndex:1]); | |
| 214 [button setTitle:base::SysUTF16ToNSString(delegate_->ConfirmButtonText())]; | |
| 215 [self layoutButtons]; | |
| 216 } | |
| 217 | |
| 218 - (AutofillSectionContainer*)sectionForId:(autofill::DialogSection)section { | |
| 219 return [detailsContainer_ sectionForId:section]; | |
| 220 } | |
| 221 | |
| 222 - (void)modelChanged { | |
| 223 [self updateSaveInChrome]; | |
| 224 [self updateButtons]; | |
| 225 [detailsContainer_ modelChanged]; | |
| 226 } | |
| 227 | |
| 228 - (BOOL)saveDetailsLocally { | |
| 229 return [saveInChromeCheckbox_ state] == NSOnState; | |
| 230 } | |
| 231 | |
| 232 - (void)updateNotificationArea { | |
| 233 [notificationContainer_ setNotifications:delegate_->CurrentNotifications()]; | |
| 234 id delegate = [[[self view] window] windowController]; | |
| 235 if ([delegate respondsToSelector:@selector(requestRelayout)]) | |
| 236 [delegate performSelector:@selector(requestRelayout)]; | |
| 237 } | |
| 238 | |
| 239 - (BOOL)validate { | |
| 240 return [detailsContainer_ validate]; | |
| 241 } | |
| 242 | |
| 243 - (void)updateSaveInChrome { | |
| 244 [saveInChromeCheckbox_ setHidden:!delegate_->ShouldOfferToSaveInChrome()]; | |
| 245 [[saveInChromeTooltip_ view] setHidden:[saveInChromeCheckbox_ isHidden]]; | |
| 246 [saveInChromeCheckbox_ setState: | |
| 247 (delegate_->ShouldSaveInChrome() ? NSOnState : NSOffState)]; | |
| 248 } | |
| 249 | |
| 250 - (void)makeFirstInvalidInputFirstResponder { | |
| 251 NSView* field = [detailsContainer_ firstInvalidField]; | |
| 252 if (!field) | |
| 253 return; | |
| 254 | |
| 255 [detailsContainer_ scrollToView:field]; | |
| 256 [[[self view] window] makeFirstResponder:field]; | |
| 257 } | |
| 258 | |
| 259 - (void)scrollInitialEditorIntoViewAndMakeFirstResponder { | |
| 260 // Try to focus on the first invalid field. If there isn't one, focus on the | |
| 261 // first editable field instead. | |
| 262 NSView* field = [detailsContainer_ firstInvalidField]; | |
| 263 if (!field) | |
| 264 field = [detailsContainer_ firstVisibleField]; | |
| 265 if (!field) | |
| 266 return; | |
| 267 | |
| 268 [detailsContainer_ scrollToView:field]; | |
| 269 [[[self view] window] makeFirstResponder:field]; | |
| 270 } | |
| 271 | |
| 272 - (void)updateErrorBubble { | |
| 273 [detailsContainer_ updateErrorBubble]; | |
| 274 } | |
| 275 | |
| 276 @end | |
| 277 | |
| 278 | |
| 279 @implementation AutofillMainContainer (Testing) | |
| 280 | |
| 281 - (NSButton*)saveInChromeCheckboxForTesting { | |
| 282 return saveInChromeCheckbox_.get(); | |
| 283 } | |
| 284 | |
| 285 - (NSButton*)saveInChromeTooltipForTesting { | |
| 286 return base::mac::ObjCCast<NSButton>([saveInChromeTooltip_ view]); | |
| 287 } | |
| 288 | |
| 289 @end | |
| OLD | NEW |