Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(67)

Side by Side Diff: chrome/browser/ui/cocoa/autofill/autofill_details_container.mm

Issue 1931043002: Remove requestAutocomplete (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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_details_container.h"
6
7 #include <algorithm>
8
9 #include "base/mac/foundation_util.h"
10 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h"
11 #import "chrome/browser/ui/cocoa/autofill/autofill_bubble_controller.h"
12 #import "chrome/browser/ui/cocoa/autofill/autofill_section_container.h"
13 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
14 #include "ui/base/cocoa/cocoa_base_utils.h"
15
16 typedef BOOL (^FieldFilterBlock)(NSView<AutofillInputField>*);
17
18 @interface AutofillDetailsContainer ()
19
20 // Find the editable input field that is closest to the top of the dialog and
21 // matches the |predicateBlock|.
22 - (NSView*)firstEditableFieldMatchingBlock:(FieldFilterBlock)predicateBlock;
23
24 @end
25
26 @implementation AutofillDetailsContainer
27
28 - (id)initWithDelegate:(autofill::AutofillDialogViewDelegate*)delegate {
29 if (self = [super init]) {
30 delegate_ = delegate;
31 }
32 return self;
33 }
34
35 - (void)addSection:(autofill::DialogSection)section {
36 base::scoped_nsobject<AutofillSectionContainer> sectionContainer(
37 [[AutofillSectionContainer alloc] initWithDelegate:delegate_
38 forSection:section]);
39 [sectionContainer setValidationDelegate:self];
40 [details_ addObject:sectionContainer];
41 }
42
43 - (void)loadView {
44 details_.reset([[NSMutableArray alloc] init]);
45
46 [self addSection:autofill::SECTION_CC];
47 [self addSection:autofill::SECTION_BILLING];
48 [self addSection:autofill::SECTION_SHIPPING];
49
50 scrollView_.reset([[NSScrollView alloc] initWithFrame:NSZeroRect]);
51 [scrollView_ setHasVerticalScroller:YES];
52 [scrollView_ setHasHorizontalScroller:NO];
53 [scrollView_ setBorderType:NSNoBorder];
54 [scrollView_ setAutohidesScrollers:YES];
55 [self setView:scrollView_];
56
57 [scrollView_ setDocumentView:[[NSView alloc] initWithFrame:NSZeroRect]];
58
59 for (AutofillSectionContainer* container in details_.get())
60 [[scrollView_ documentView] addSubview:[container view]];
61
62 [self performLayout];
63 }
64
65 - (NSSize)preferredSize {
66 NSSize size = NSZeroSize;
67 for (AutofillSectionContainer* container in details_.get()) {
68 NSSize containerSize = [container preferredSize];
69 size.height += containerSize.height;
70 size.width = std::max(containerSize.width, size.width);
71 }
72 return size;
73 }
74
75 - (void)performLayout {
76 NSRect rect = NSZeroRect;
77 for (AutofillSectionContainer* container in
78 [details_ reverseObjectEnumerator]) {
79 if (![[container view] isHidden]) {
80 [container performLayout];
81 [[container view] setFrameOrigin:NSMakePoint(0, NSMaxY(rect))];
82 rect = NSUnionRect(rect, [[container view] frame]);
83 }
84 }
85
86 [[scrollView_ documentView] setFrameSize:[self preferredSize]];
87 }
88
89 - (AutofillSectionContainer*)sectionForId:(autofill::DialogSection)section {
90 for (AutofillSectionContainer* details in details_.get()) {
91 if ([details section] == section)
92 return details;
93 }
94 return nil;
95 }
96
97 - (void)modelChanged {
98 for (AutofillSectionContainer* details in details_.get())
99 [details modelChanged];
100 }
101
102 - (BOOL)validate {
103 // Account for a subtle timing issue. -validate is called from the dialog's
104 // -accept. -accept then hides the dialog. If the data does not validate the
105 // dialog is then reshown, focusing on the first invalid field. This happens
106 // without running the message loop, so windowWillClose has not fired when
107 // the dialog and error bubble is reshown, leading to a missing error bubble.
108 // Resetting the anchor view here forces the bubble to show.
109 errorBubbleAnchorView_ = nil;
110
111 bool allValid = true;
112 for (AutofillSectionContainer* details in details_.get()) {
113 if (![[details view] isHidden])
114 allValid = [details validateFor:autofill::VALIDATE_FINAL] && allValid;
115 }
116 return allValid;
117 }
118
119 - (NSView*)firstInvalidField {
120 return [self firstEditableFieldMatchingBlock:
121 ^BOOL (NSView<AutofillInputField>* field) {
122 return [field invalid];
123 }];
124 }
125
126 - (NSView*)firstVisibleField {
127 return [self firstEditableFieldMatchingBlock:
128 ^BOOL (NSView<AutofillInputField>* field) {
129 return YES;
130 }];
131 }
132
133 - (void)scrollToView:(NSView*)field {
134 const CGFloat bottomPadding = 5.0; // Padding below the visible field.
135
136 NSClipView* clipView = [scrollView_ contentView];
137 NSRect fieldRect = [field convertRect:[field bounds] toView:clipView];
138
139 // If the entire field is already visible, let's not scroll.
140 NSRect documentRect = [clipView documentVisibleRect];
141 documentRect = [[clipView documentView] convertRect:documentRect
142 toView:clipView];
143 if (NSContainsRect(documentRect, fieldRect))
144 return;
145
146 NSPoint scrollPoint = [clipView constrainScrollPoint:
147 NSMakePoint(0, NSMinY(fieldRect) - bottomPadding)];
148 [clipView scrollToPoint:scrollPoint];
149 [scrollView_ reflectScrolledClipView:clipView];
150 [self updateErrorBubble];
151 }
152
153 - (void)updateErrorBubble {
154 if (!delegate_->ShouldShowErrorBubble()) {
155 [errorBubbleController_ close];
156 }
157 }
158
159 - (void)errorBubbleWindowWillClose:(NSNotification*)notification {
160 DCHECK_EQ([notification object], [errorBubbleController_ window]);
161
162 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
163 [center removeObserver:self
164 name:NSWindowWillCloseNotification
165 object:[errorBubbleController_ window]];
166 errorBubbleController_ = nil;
167 errorBubbleAnchorView_ = nil;
168 }
169
170 - (void)showErrorBubbleForField:(NSControl<AutofillInputField>*)field {
171 // If there is already a bubble controller handling this field, reuse.
172 if (errorBubbleController_ && errorBubbleAnchorView_ == field) {
173 [errorBubbleController_ setMessage:[field validityMessage]];
174
175 return;
176 }
177
178 if (errorBubbleController_)
179 [errorBubbleController_ close];
180 DCHECK(!errorBubbleController_);
181 NSWindow* parentWindow = [field window];
182 DCHECK(parentWindow);
183 errorBubbleController_ =
184 [[AutofillBubbleController alloc]
185 initWithParentWindow:parentWindow
186 message:[field validityMessage]];
187
188 // Handle bubble self-deleting.
189 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
190 [center addObserver:self
191 selector:@selector(errorBubbleWindowWillClose:)
192 name:NSWindowWillCloseNotification
193 object:[errorBubbleController_ window]];
194
195 // Compute anchor point (in window coords - views might be flipped).
196 NSRect viewRect = [field convertRect:[field bounds] toView:nil];
197
198 // If a bubble at maximum size with a left-aligned edge would exceed the
199 // window width, align the right edge of bubble and view. In all other
200 // cases, align the left edge of the bubble and the view.
201 // Alignment is based on maximum width to avoid the arrow changing positions
202 // if the validation bubble stays on the same field but gets a message of
203 // differing length. (E.g. "Field is required"/"Invalid Zip Code. Please
204 // check and try again" if an empty zip field gets changed to a bad zip).
205 NSPoint anchorPoint;
206 if ((NSMinX(viewRect) + [errorBubbleController_ maxWidth]) >
207 NSWidth([parentWindow frame])) {
208 anchorPoint = NSMakePoint(NSMaxX(viewRect), NSMinY(viewRect));
209 [[errorBubbleController_ bubble] setArrowLocation:info_bubble::kTopRight];
210 [[errorBubbleController_ bubble] setAlignment:
211 info_bubble::kAlignRightEdgeToAnchorEdge];
212
213 } else {
214 anchorPoint = NSMakePoint(NSMinX(viewRect), NSMinY(viewRect));
215 [[errorBubbleController_ bubble] setArrowLocation:info_bubble::kTopLeft];
216 [[errorBubbleController_ bubble] setAlignment:
217 info_bubble::kAlignLeftEdgeToAnchorEdge];
218 }
219 [errorBubbleController_ setAnchorPoint:ui::ConvertPointFromWindowToScreen(
220 parentWindow, anchorPoint)];
221
222 errorBubbleAnchorView_ = field;
223 [errorBubbleController_ showWindow:self];
224 }
225
226 - (void)hideErrorBubble {
227 [errorBubbleController_ close];
228 }
229
230 - (void)updateMessageForField:(NSControl<AutofillInputField>*)field {
231 // Ignore fields that are not first responder. Testing this is a bit
232 // convoluted, since for NSTextFields with firstResponder status, the
233 // firstResponder is a subview of the NSTextField, not the field itself.
234 NSView* firstResponderView =
235 base::mac::ObjCCast<NSView>([[field window] firstResponder]);
236 if (![firstResponderView isDescendantOf:field])
237 return;
238 if (!delegate_->ShouldShowErrorBubble()) {
239 DCHECK(!errorBubbleController_);
240 return;
241 }
242
243 if ([field invalid]) {
244 [self showErrorBubbleForField:field];
245 } else {
246 [errorBubbleController_ close];
247 }
248 }
249
250 - (NSView*)firstEditableFieldMatchingBlock:(FieldFilterBlock)predicateBlock {
251 base::scoped_nsobject<NSMutableArray> fields([[NSMutableArray alloc] init]);
252
253 for (AutofillSectionContainer* details in details_.get()) {
254 if (![[details view] isHidden])
255 [details addInputsToArray:fields];
256 }
257
258 NSPoint selectedFieldOrigin = NSZeroPoint;
259 NSView* selectedField = nil;
260 for (NSControl<AutofillInputField>* field in fields.get()) {
261 if (!base::mac::ObjCCast<NSControl>(field))
262 continue;
263 if (![field conformsToProtocol:@protocol(AutofillInputField)])
264 continue;
265 if ([field isHiddenOrHasHiddenAncestor])
266 continue;
267 if (![field isEnabled])
268 continue;
269 if (![field canBecomeKeyView])
270 continue;
271 if (!predicateBlock(field))
272 continue;
273
274 NSPoint fieldOrigin = [field convertPoint:[field bounds].origin toView:nil];
275 if (fieldOrigin.y < selectedFieldOrigin.y)
276 continue;
277 if (fieldOrigin.y == selectedFieldOrigin.y &&
278 fieldOrigin.x > selectedFieldOrigin.x) {
279 continue;
280 }
281
282 selectedField = field;
283 selectedFieldOrigin = fieldOrigin;
284 }
285
286 return selectedField;
287 }
288
289 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698