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 |