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

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

Issue 73293002: [rAC, OSX] Cleanup: Separate out WindowController (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 1 month 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 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 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 "chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h" 5 #include "chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/mac/bundle_locations.h"
9 #include "base/mac/foundation_util.h"
10 #include "base/mac/scoped_nsobject.h" 8 #include "base/mac/scoped_nsobject.h"
11 #include "base/message_loop/message_loop.h" 9 #include "base/message_loop/message_loop.h"
12 #include "base/strings/sys_string_conversions.h" 10 #include "base/strings/sys_string_conversions.h"
13 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h" 11 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h"
14 #include "chrome/browser/ui/chrome_style.h"
15 #import "chrome/browser/ui/cocoa/autofill/autofill_account_chooser.h"
16 #import "chrome/browser/ui/cocoa/autofill/autofill_details_container.h" 12 #import "chrome/browser/ui/cocoa/autofill/autofill_details_container.h"
17 #include "chrome/browser/ui/cocoa/autofill/autofill_dialog_constants.h" 13 #import "chrome/browser/ui/cocoa/autofill/autofill_dialog_window_controller.h"
18 #import "chrome/browser/ui/cocoa/autofill/autofill_input_field.h"
19 #import "chrome/browser/ui/cocoa/autofill/autofill_main_container.h"
20 #import "chrome/browser/ui/cocoa/autofill/autofill_overlay_controller.h"
21 #import "chrome/browser/ui/cocoa/autofill/autofill_section_container.h"
22 #import "chrome/browser/ui/cocoa/autofill/autofill_sign_in_container.h"
23 #import "chrome/browser/ui/cocoa/autofill/autofill_textfield.h"
24 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sh eet.h" 14 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sh eet.h"
25 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_wi ndow.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/browser/web_contents_view.h"
28 #include "grit/generated_resources.h"
29 #import "ui/base/cocoa/flipped_view.h"
30 #include "ui/base/cocoa/window_size_constants.h"
31 #include "ui/base/l10n/l10n_util.h"
32 #include "ui/gfx/platform_font.h"
33
34 const CGFloat kAccountChooserHeight = 20.0;
35 const CGFloat kMinimumContentsHeight = 101;
36
37 // Height of all decorations & paddings on main dialog together.
38 const CGFloat kDecorationHeight = kAccountChooserHeight +
39 autofill::kDetailVerticalPadding +
40 chrome_style::kClientBottomPadding +
41 chrome_style::kTitleTopPadding;
42 15
43 namespace autofill { 16 namespace autofill {
44 17
45 // static 18 // static
46 AutofillDialogView* AutofillDialogView::Create( 19 AutofillDialogView* AutofillDialogView::Create(
47 AutofillDialogViewDelegate* delegate) { 20 AutofillDialogViewDelegate* delegate) {
48 return new AutofillDialogCocoa(delegate); 21 return new AutofillDialogCocoa(delegate);
49 } 22 }
50 23
51 AutofillDialogCocoa::AutofillDialogCocoa(AutofillDialogViewDelegate* delegate) 24 AutofillDialogCocoa::AutofillDialogCocoa(AutofillDialogViewDelegate* delegate)
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
225 } 198 }
226 199
227 void AutofillDialogCocoa::OnConstrainedWindowClosed( 200 void AutofillDialogCocoa::OnConstrainedWindowClosed(
228 ConstrainedWindowMac* window) { 201 ConstrainedWindowMac* window) {
229 constrained_window_.reset(); 202 constrained_window_.reset();
230 // |this| belongs to |delegate_|, so no self-destruction here. 203 // |this| belongs to |delegate_|, so no self-destruction here.
231 delegate_->ViewClosed(); 204 delegate_->ViewClosed();
232 } 205 }
233 206
234 } // autofill 207 } // autofill
235
236 #pragma mark "Loading" Shield
237
238 @interface AutofillOpaqueView : NSView
239 @end
240
241 @implementation AutofillOpaqueView
242
243 - (BOOL)isOpaque {
244 return YES;
245 }
246
247 - (void)drawRect:(NSRect)dirtyRect {
248 [[[self window] backgroundColor] setFill];
249 [NSBezierPath fillRect:[self bounds]];
250 }
251
252 @end
253
254
255 #pragma mark Field Editor
256
257 @interface AutofillDialogFieldEditor : NSTextView
258 @end
259
260
261 @implementation AutofillDialogFieldEditor
262
263 - (void)mouseDown:(NSEvent*)event {
264 // Delegate _must_ be notified before mouseDown is complete, since it needs
265 // to distinguish between mouseDown for already focused fields, and fields
266 // that will receive focus as part of the mouseDown.
267 AutofillTextField* textfield =
268 base::mac::ObjCCastStrict<AutofillTextField>([self delegate]);
269 [textfield onEditorMouseDown:self];
270 [super mouseDown:event];
271 }
272
273 @end
274
275
276 #pragma mark Window Controller
277
278 @interface AutofillDialogWindowController ()
279
280 // Compute maximum allowed height for the dialog.
281 - (CGFloat)maxHeight;
282
283 // Update size constraints on sign-in container.
284 - (void)updateSignInSizeConstraints;
285
286 // Notification that the WebContent's view frame has changed.
287 - (void)onContentViewFrameDidChange:(NSNotification*)notification;
288
289 @end
290
291
292 @implementation AutofillDialogWindowController (NSWindowDelegate)
293
294 - (id)windowWillReturnFieldEditor:(NSWindow*)window toObject:(id)client {
295 AutofillTextField* textfield = base::mac::ObjCCast<AutofillTextField>(client);
296 if (!textfield)
297 return nil;
298
299 if (!fieldEditor_) {
300 fieldEditor_.reset([[AutofillDialogFieldEditor alloc] init]);
301 [fieldEditor_ setFieldEditor:YES];
302 }
303 return fieldEditor_.get();
304 }
305
306 @end
307
308
309 @implementation AutofillDialogWindowController
310
311 - (id)initWithWebContents:(content::WebContents*)webContents
312 autofillDialog:(autofill::AutofillDialogCocoa*)autofillDialog {
313 DCHECK(webContents);
314
315 base::scoped_nsobject<ConstrainedWindowCustomWindow> window(
316 [[ConstrainedWindowCustomWindow alloc]
317 initWithContentRect:ui::kWindowSizeDeterminedLater]);
318
319 if ((self = [super initWithWindow:window])) {
320 [window setDelegate:self];
321 webContents_ = webContents;
322 autofillDialog_ = autofillDialog;
323
324 mainContainer_.reset([[AutofillMainContainer alloc]
325 initWithDelegate:autofillDialog->delegate()]);
326 [mainContainer_ setTarget:self];
327
328 signInContainer_.reset(
329 [[AutofillSignInContainer alloc] initWithDialog:autofillDialog]);
330 [[signInContainer_ view] setHidden:YES];
331
332 NSRect clientRect = [[mainContainer_ view] frame];
333 clientRect.origin = NSMakePoint(chrome_style::kClientBottomPadding,
334 chrome_style::kHorizontalPadding);
335 [[mainContainer_ view] setFrame:clientRect];
336 [[signInContainer_ view] setFrame:clientRect];
337
338 // Set dialog title.
339 titleTextField_.reset([[NSTextField alloc] initWithFrame:NSZeroRect]);
340 [titleTextField_ setEditable:NO];
341 [titleTextField_ setBordered:NO];
342 [titleTextField_ setDrawsBackground:NO];
343 [titleTextField_ setFont:[NSFont systemFontOfSize:15.0]];
344 [titleTextField_ setStringValue:
345 base::SysUTF16ToNSString(autofillDialog->delegate()->DialogTitle())];
346 [titleTextField_ sizeToFit];
347
348 NSRect headerRect = clientRect;
349 headerRect.size.height = kAccountChooserHeight;
350 headerRect.origin.y = NSMaxY(clientRect);
351 accountChooser_.reset([[AutofillAccountChooser alloc]
352 initWithFrame:headerRect
353 delegate:autofillDialog->delegate()]);
354
355 loadingShieldTextField_.reset(
356 [[NSTextField alloc] initWithFrame:NSZeroRect]);
357 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
358 NSFont* loadingFont = rb.GetFont(
359 ui::ResourceBundle::BaseFont).DeriveFont(15).GetNativeFont();
360 [loadingShieldTextField_ setFont:loadingFont];
361 [loadingShieldTextField_ setEditable:NO];
362 [loadingShieldTextField_ setBordered:NO];
363 [loadingShieldTextField_ setDrawsBackground:NO];
364
365 base::scoped_nsobject<AutofillOpaqueView> loadingShieldView(
366 [[AutofillOpaqueView alloc] initWithFrame:NSZeroRect]);
367 [loadingShieldView setHidden:YES];
368 [loadingShieldView addSubview:loadingShieldTextField_];
369
370 overlayController_.reset(
371 [[AutofillOverlayController alloc] initWithDelegate:
372 autofillDialog->delegate()]);
373 [[overlayController_ view] setHidden:YES];
374
375 // This needs a flipped content view because otherwise the size
376 // animation looks odd. However, replacing the contentView for constrained
377 // windows does not work - it does custom rendering.
378 base::scoped_nsobject<NSView> flippedContentView(
379 [[FlippedView alloc] initWithFrame:
380 [[[self window] contentView] frame]]);
381 [flippedContentView setSubviews:
382 @[accountChooser_,
383 titleTextField_,
384 [mainContainer_ view],
385 [signInContainer_ view],
386 loadingShieldView,
387 [overlayController_ view]]];
388 [flippedContentView setAutoresizingMask:
389 (NSViewWidthSizable | NSViewHeightSizable)];
390 [[[self window] contentView] addSubview:flippedContentView];
391 [mainContainer_ setAnchorView:[[accountChooser_ subviews] objectAtIndex:1]];
392 }
393 return self;
394 }
395
396 - (void)dealloc {
397 [[NSNotificationCenter defaultCenter] removeObserver:self];
398 [super dealloc];
399 }
400
401 - (CGFloat)maxHeight {
402 NSRect dialogFrameRect = [[self window] frame];
403 NSRect browserFrameRect =
404 [webContents_->GetView()->GetTopLevelNativeWindow() frame];
405 dialogFrameRect.size.height =
406 NSMaxY(dialogFrameRect) - NSMinY(browserFrameRect);
407 dialogFrameRect = [[self window] contentRectForFrameRect:dialogFrameRect];
408 return NSHeight(dialogFrameRect);
409 }
410
411 - (void)updateSignInSizeConstraints {
412 // Adjust for the size of all decorations and paddings outside main content.
413 CGFloat minHeight = kMinimumContentsHeight - kDecorationHeight;
414 CGFloat maxHeight = std::max([self maxHeight] - kDecorationHeight, minHeight);
415 CGFloat width = NSWidth([[[self window] contentView] frame]);
416
417 [signInContainer_ constrainSizeToMinimum:NSMakeSize(width, minHeight)
418 maximum:NSMakeSize(width, maxHeight)];
419 }
420
421 - (void)onContentViewFrameDidChange:(NSNotification*)notification {
422 [self updateSignInSizeConstraints];
423 if ([[signInContainer_ view] isHidden])
424 [self requestRelayout];
425 }
426
427 - (void)cancelRelayout {
428 [NSObject cancelPreviousPerformRequestsWithTarget:self
429 selector:@selector(performLayout)
430 object:nil];
431 }
432
433 - (void)requestRelayout {
434 [self cancelRelayout];
435 [self performSelector:@selector(performLayout) withObject:nil afterDelay:0.0];
436 }
437
438 - (NSSize)preferredSize {
439 NSSize size;
440
441 // Overall size is determined by either main container or sign in view.
442 if ([[signInContainer_ view] isHidden])
443 size = [mainContainer_ preferredSize];
444 else
445 size = [signInContainer_ preferredSize];
446
447 // Always make room for the header.
448 size.height += kDecorationHeight;
449
450 if (![[overlayController_ view] isHidden]) {
451 CGFloat height = [overlayController_ heightForWidth:size.width];
452 if (height != 0.0)
453 size.height = height;
454 }
455
456 // Show as much of the main view as is possible without going past the
457 // bottom of the browser window.
458 size.height = std::min(size.height, [self maxHeight]);
459
460 return size;
461 }
462
463 - (void)performLayout {
464 NSRect contentRect = NSZeroRect;
465 contentRect.size = [self preferredSize];
466 NSRect clientRect = contentRect;
467 clientRect.origin.y = chrome_style::kClientBottomPadding;
468 clientRect.size.height -= chrome_style::kTitleTopPadding +
469 chrome_style::kClientBottomPadding;
470
471 [titleTextField_ setStringValue:
472 base::SysUTF16ToNSString(autofillDialog_->delegate()->DialogTitle())];
473 [titleTextField_ sizeToFit];
474
475 NSRect headerRect, mainRect, titleRect, dummyRect;
476 NSDivideRect(clientRect, &headerRect, &mainRect,
477 kAccountChooserHeight, NSMinYEdge);
478 NSDivideRect(mainRect, &dummyRect, &mainRect,
479 autofill::kDetailVerticalPadding, NSMinYEdge);
480 headerRect = NSInsetRect(headerRect, chrome_style::kHorizontalPadding, 0);
481 NSDivideRect(headerRect, &titleRect, &headerRect,
482 NSWidth([titleTextField_ frame]), NSMinXEdge);
483
484 // Align baseline of title with bottom of accountChooser.
485 base::scoped_nsobject<NSLayoutManager> layout_manager(
486 [[NSLayoutManager alloc] init]);
487 NSFont* titleFont = [titleTextField_ font];
488 titleRect.origin.y += NSHeight(titleRect) -
489 [layout_manager defaultBaselineOffsetForFont:titleFont];
490 [titleTextField_ setFrame:titleRect];
491
492 [accountChooser_ setFrame:headerRect];
493 [accountChooser_ performLayout];
494 if ([[signInContainer_ view] isHidden]) {
495 [[mainContainer_ view] setFrame:mainRect];
496 [mainContainer_ performLayout];
497 } else {
498 [[signInContainer_ view] setFrame:mainRect];
499 }
500
501 // Loading shield has text centered in the content rect.
502 NSRect textFrame = [loadingShieldTextField_ frame];
503 textFrame.origin.x =
504 std::ceil((NSWidth(contentRect) - NSWidth(textFrame)) / 2.0);
505 textFrame.origin.y =
506 std::ceil((NSHeight(contentRect) - NSHeight(textFrame)) / 2.0);
507 [loadingShieldTextField_ setFrame:textFrame];
508 [[loadingShieldTextField_ superview] setFrame:contentRect];
509
510 [[overlayController_ view] setFrame:contentRect];
511 [overlayController_ performLayout];
512
513 NSRect frameRect = [[self window] frameRectForContentRect:contentRect];
514 [[self window] setFrame:frameRect display:YES];
515 [[self window] recalculateKeyViewLoop];
516 }
517
518 - (IBAction)accept:(id)sender {
519 if ([mainContainer_ validate])
520 autofillDialog_->delegate()->OnAccept();
521 else
522 [mainContainer_ makeFirstInvalidInputFirstResponder];
523 }
524
525 - (IBAction)cancel:(id)sender {
526 autofillDialog_->delegate()->OnCancel();
527 autofillDialog_->PerformClose();
528 }
529
530 - (void)show {
531 // Resizing the browser causes the ConstrainedWindow to move.
532 // Observe that to allow resizes based on browser size.
533 // NOTE: This MUST come last after all initial setup is done, because there
534 // is an immediate notification post registration.
535 DCHECK([self window]);
536 [[NSNotificationCenter defaultCenter]
537 addObserver:self
538 selector:@selector(onContentViewFrameDidChange:)
539 name:NSWindowDidMoveNotification
540 object:[self window]];
541
542 [self updateAccountChooser];
543 [self updateNotificationArea];
544 [self requestRelayout];
545 }
546
547 - (void)hide {
548 autofillDialog_->delegate()->OnCancel();
549 autofillDialog_->PerformClose();
550 }
551
552 - (void)updateNotificationArea {
553 [mainContainer_ updateNotificationArea];
554 }
555
556 - (void)updateAccountChooser {
557 [accountChooser_ update];
558 [mainContainer_ updateLegalDocuments];
559
560 NSString* newLoadingMessage = @"";
561 if (autofillDialog_->delegate()->ShouldShowSpinner())
562 newLoadingMessage = l10n_util::GetNSStringWithFixup(IDS_TAB_LOADING_TITLE);
563 if (![newLoadingMessage isEqualToString:
564 [loadingShieldTextField_ stringValue]]) {
565 NSView* loadingShieldView = [loadingShieldTextField_ superview];
566 [loadingShieldTextField_ setStringValue:newLoadingMessage];
567 [loadingShieldTextField_ sizeToFit];
568
569 BOOL showShield = ([newLoadingMessage length] != 0);
570
571 // For the duration of the loading shield, hide the main contents.
572 // This prevents the currently focused text field "shining through".
573 // No need to remember previous state, because the loading shield
574 // always flows through to the main container.
575 [[mainContainer_ view] setHidden:showShield];
576 [loadingShieldView setHidden:!showShield];
577 [self requestRelayout];
578 }
579 }
580
581 - (void)updateButtonStrip {
582 [overlayController_ updateState];
583 }
584
585 - (void)updateSection:(autofill::DialogSection)section {
586 [[mainContainer_ sectionForId:section] update];
587 [mainContainer_ updateSaveInChrome];
588 }
589
590 - (void)fillSection:(autofill::DialogSection)section
591 forInput:(const autofill::DetailInput&)input {
592 [[mainContainer_ sectionForId:section] fillForInput:input];
593 [mainContainer_ updateSaveInChrome];
594 }
595
596 - (content::NavigationController*)showSignIn {
597 [self updateSignInSizeConstraints];
598 [signInContainer_ loadSignInPage];
599 [[mainContainer_ view] setHidden:YES];
600 [[signInContainer_ view] setHidden:NO];
601 [self requestRelayout];
602
603 return [signInContainer_ navigationController];
604 }
605
606 - (void)getInputs:(autofill::DetailOutputMap*)output
607 forSection:(autofill::DialogSection)section {
608 [[mainContainer_ sectionForId:section] getInputs:output];
609 }
610
611 - (NSString*)getCvc {
612 autofill::DialogSection section = autofill::SECTION_CC;
613 NSString* value = [[mainContainer_ sectionForId:section] suggestionText];
614 if (!value) {
615 section = autofill::SECTION_CC_BILLING;
616 value = [[mainContainer_ sectionForId:section] suggestionText];
617 }
618 return value;
619 }
620
621 - (BOOL)saveDetailsLocally {
622 return [mainContainer_ saveDetailsLocally];
623 }
624
625 - (void)hideSignIn {
626 [[signInContainer_ view] setHidden:YES];
627 [[mainContainer_ view] setHidden:NO];
628 [self requestRelayout];
629 }
630
631 - (void)modelChanged {
632 [mainContainer_ modelChanged];
633 }
634
635 - (void)updateErrorBubble {
636 [mainContainer_ updateErrorBubble];
637 }
638
639 - (void)onSignInResize:(NSSize)size {
640 [signInContainer_ setPreferredSize:size];
641 [self requestRelayout];
642 }
643
644 @end
645
646
647 @implementation AutofillDialogWindowController (TestableAutofillDialogView)
648
649 - (void)setTextContents:(NSString*)text
650 forInput:(const autofill::DetailInput&)input {
651 for (size_t i = autofill::SECTION_MIN; i <= autofill::SECTION_MAX; ++i) {
652 autofill::DialogSection section = static_cast<autofill::DialogSection>(i);
653 // TODO(groby): Need to find the section for an input directly - wasteful.
654 [[mainContainer_ sectionForId:section] setFieldValue:text forInput:input];
655 }
656 }
657
658 - (void)setTextContents:(NSString*)text
659 ofSuggestionForSection:(autofill::DialogSection)section {
660 [[mainContainer_ sectionForId:section] setSuggestionFieldValue:text];
661 }
662
663 - (void)activateFieldForInput:(const autofill::DetailInput&)input {
664 for (size_t i = autofill::SECTION_MIN; i <= autofill::SECTION_MAX; ++i) {
665 autofill::DialogSection section = static_cast<autofill::DialogSection>(i);
666 [[mainContainer_ sectionForId:section] activateFieldForInput:input];
667 }
668 }
669
670 - (content::WebContents*)getSignInWebContents {
671 return [signInContainer_ webContents];
672 }
673
674 - (BOOL)IsShowingOverlay {
675 return ![[overlayController_ view] isHidden];
676 }
677
678 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698