OLD | NEW |
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 #import "chrome/browser/ui/cocoa/autofill/autofill_details_container.h" | 5 #import "chrome/browser/ui/cocoa/autofill/autofill_details_container.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/mac/foundation_util.h" | 9 #include "base/mac/foundation_util.h" |
10 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h" | 10 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h" |
| 11 #import "chrome/browser/ui/cocoa/autofill/autofill_error_bubble_controller.h" |
11 #import "chrome/browser/ui/cocoa/autofill/autofill_section_container.h" | 12 #import "chrome/browser/ui/cocoa/autofill/autofill_section_container.h" |
12 #import "chrome/browser/ui/cocoa/info_bubble_view.h" | |
13 #include "skia/ext/skia_utils_mac.h" | |
14 | |
15 namespace { | |
16 | |
17 // Imported constant from Views version. TODO(groby): Share. | |
18 SkColor const kWarningColor = 0xffde4932; // SkColorSetRGB(0xde, 0x49, 0x32); | |
19 | |
20 } // namespace | |
21 | |
22 @interface AutofillDetailsContainer () | |
23 // Compute infobubble origin based on anchor/view. | |
24 - (NSPoint)originFromAnchorView:(NSView*)view; | |
25 @end | |
26 | 13 |
27 @implementation AutofillDetailsContainer | 14 @implementation AutofillDetailsContainer |
28 | 15 |
29 - (id)initWithDelegate:(autofill::AutofillDialogViewDelegate*)delegate { | 16 - (id)initWithDelegate:(autofill::AutofillDialogViewDelegate*)delegate { |
30 if (self = [super init]) { | 17 if (self = [super init]) { |
31 delegate_ = delegate; | 18 delegate_ = delegate; |
32 } | 19 } |
33 return self; | 20 return self; |
34 } | 21 } |
35 | 22 |
(...skipping 18 matching lines...) Expand all Loading... |
54 [scrollView_ setHasHorizontalScroller:NO]; | 41 [scrollView_ setHasHorizontalScroller:NO]; |
55 [scrollView_ setBorderType:NSNoBorder]; | 42 [scrollView_ setBorderType:NSNoBorder]; |
56 [scrollView_ setAutohidesScrollers:YES]; | 43 [scrollView_ setAutohidesScrollers:YES]; |
57 [self setView:scrollView_]; | 44 [self setView:scrollView_]; |
58 | 45 |
59 [scrollView_ setDocumentView:[[NSView alloc] initWithFrame:NSZeroRect]]; | 46 [scrollView_ setDocumentView:[[NSView alloc] initWithFrame:NSZeroRect]]; |
60 | 47 |
61 for (AutofillSectionContainer* container in details_.get()) | 48 for (AutofillSectionContainer* container in details_.get()) |
62 [[scrollView_ documentView] addSubview:[container view]]; | 49 [[scrollView_ documentView] addSubview:[container view]]; |
63 | 50 |
64 errorBubble_.reset([[InfoBubbleView alloc] initWithFrame:NSZeroRect]); | |
65 [errorBubble_ setBackgroundColor: | |
66 gfx::SkColorToCalibratedNSColor(kWarningColor)]; | |
67 [errorBubble_ setArrowLocation:info_bubble::kTopCenter]; | |
68 [errorBubble_ setAlignment:info_bubble::kAlignArrowToAnchor]; | |
69 [errorBubble_ setHidden:YES]; | |
70 | |
71 base::scoped_nsobject<NSTextField> label([[NSTextField alloc] init]); | |
72 [label setEditable:NO]; | |
73 [label setBordered:NO]; | |
74 [label setDrawsBackground:NO]; | |
75 [label setTextColor:[NSColor whiteColor]]; | |
76 [errorBubble_ addSubview:label]; | |
77 | |
78 [[scrollView_ documentView] addSubview:errorBubble_]; | |
79 | |
80 [self performLayout]; | 51 [self performLayout]; |
81 } | 52 } |
82 | 53 |
83 - (NSSize)preferredSize { | 54 - (NSSize)preferredSize { |
84 NSSize size = NSZeroSize; | 55 NSSize size = NSZeroSize; |
85 for (AutofillSectionContainer* container in details_.get()) { | 56 for (AutofillSectionContainer* container in details_.get()) { |
86 NSSize containerSize = [container preferredSize]; | 57 NSSize containerSize = [container preferredSize]; |
87 size.height += containerSize.height; | 58 size.height += containerSize.height; |
88 size.width = std::max(containerSize.width, size.width); | 59 size.width = std::max(containerSize.width, size.width); |
89 } | 60 } |
(...skipping 30 matching lines...) Expand all Loading... |
120 - (BOOL)validate { | 91 - (BOOL)validate { |
121 bool allValid = true; | 92 bool allValid = true; |
122 for (AutofillSectionContainer* details in details_.get()) { | 93 for (AutofillSectionContainer* details in details_.get()) { |
123 if (![[details view] isHidden]) | 94 if (![[details view] isHidden]) |
124 allValid = [details validateFor:autofill::VALIDATE_FINAL] && allValid; | 95 allValid = [details validateFor:autofill::VALIDATE_FINAL] && allValid; |
125 } | 96 } |
126 return allValid; | 97 return allValid; |
127 } | 98 } |
128 | 99 |
129 - (void)updateErrorBubble { | 100 - (void)updateErrorBubble { |
130 if (!delegate_->ShouldShowErrorBubble()) | 101 if (!delegate_->ShouldShowErrorBubble()) { |
131 [errorBubble_ setHidden:YES]; | 102 [errorBubbleController_ close]; |
| 103 } |
132 } | 104 } |
133 | 105 |
134 // TODO(groby): Unify with BaseBubbleController's originFromAnchor:view:. | 106 - (NSPoint)anchorPointFromView:(NSView*)view { |
135 - (NSPoint)originFromAnchorView:(NSView*)view { | |
136 // All math done in window coordinates, since views might be flipped. | 107 // All math done in window coordinates, since views might be flipped. |
137 NSRect viewRect = [view convertRect:[view bounds] toView:nil]; | 108 NSRect viewRect = [view convertRect:[view bounds] toView:nil]; |
138 NSPoint anchorPoint = | 109 NSPoint anchorPoint = |
139 NSMakePoint(NSMidX(viewRect), NSMinY(viewRect)); | 110 NSMakePoint(NSMidX(viewRect), NSMinY(viewRect)); |
140 NSRect bubbleRect = [errorBubble_ convertRect:[errorBubble_ bounds] | 111 return [[view window] convertBaseToScreen:anchorPoint]; |
141 toView:nil]; | 112 } |
142 NSPoint bubbleOrigin = NSMakePoint(anchorPoint.x - NSWidth(bubbleRect) / 2.0, | 113 |
143 anchorPoint.y - NSHeight(bubbleRect)); | 114 - (void)errorBubbleWindowWillClose:(NSNotification*)notification { |
144 return [[errorBubble_ superview] convertPoint:bubbleOrigin fromView:nil]; | 115 DCHECK_EQ([notification object], [errorBubbleController_ window]); |
| 116 |
| 117 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; |
| 118 [center removeObserver:self |
| 119 name:NSWindowWillCloseNotification |
| 120 object:[errorBubbleController_ window]]; |
| 121 errorBubbleController_ = nil; |
| 122 } |
| 123 |
| 124 - (void)showErrorBubbleForField:(NSControl<AutofillInputField>*)field { |
| 125 DCHECK(!errorBubbleController_); |
| 126 NSWindow* parentWindow = [field window]; |
| 127 DCHECK(parentWindow); |
| 128 errorBubbleController_ = |
| 129 [[AutofillErrorBubbleController alloc] |
| 130 initWithParentWindow:parentWindow |
| 131 message:[field validityMessage]]; |
| 132 |
| 133 // Handle bubble self-deleting. |
| 134 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; |
| 135 [center addObserver:self |
| 136 selector:@selector(errorBubbleWindowWillClose:) |
| 137 name:NSWindowWillCloseNotification |
| 138 object:[errorBubbleController_ window]]; |
| 139 |
| 140 // Compute anchor point (in window coords - views might be flipped). |
| 141 NSRect viewRect = [field convertRect:[field bounds] toView:nil]; |
| 142 NSPoint anchorPoint = NSMakePoint(NSMidX(viewRect), NSMinY(viewRect)); |
| 143 [errorBubbleController_ setAnchorPoint: |
| 144 [parentWindow convertBaseToScreen:anchorPoint]]; |
| 145 |
| 146 [errorBubbleController_ showWindow:self]; |
145 } | 147 } |
146 | 148 |
147 - (void)updateMessageForField:(NSControl<AutofillInputField>*)field { | 149 - (void)updateMessageForField:(NSControl<AutofillInputField>*)field { |
148 // Ignore fields that are not first responder. Testing this is a bit | 150 // Ignore fields that are not first responder. Testing this is a bit |
149 // convoluted, since for NSTextFields with firstResponder status, the | 151 // convoluted, since for NSTextFields with firstResponder status, the |
150 // firstResponder is a subview of the NSTextField, not the field itself. | 152 // firstResponder is a subview of the NSTextField, not the field itself. |
151 NSView* firstResponderView = | 153 NSView* firstResponderView = |
152 base::mac::ObjCCast<NSView>([[field window] firstResponder]); | 154 base::mac::ObjCCast<NSView>([[field window] firstResponder]); |
153 if (![firstResponderView isDescendantOf:field]) | 155 if (![firstResponderView isDescendantOf:field]) |
154 return; | 156 return; |
155 | |
156 if (!delegate_->ShouldShowErrorBubble()) { | 157 if (!delegate_->ShouldShowErrorBubble()) { |
157 DCHECK([errorBubble_ isHidden]); | 158 DCHECK(!errorBubbleController_); |
158 return; | 159 return; |
159 } | 160 } |
160 | 161 |
161 if ([field invalid]) { | 162 if ([field invalid]) { |
162 const CGFloat labelInset = 3.0; | 163 [self showErrorBubbleForField:field]; |
163 | |
164 NSTextField* label = [[errorBubble_ subviews] objectAtIndex:0]; | |
165 [label setStringValue:[field validityMessage]]; | |
166 [label sizeToFit]; | |
167 NSSize bubbleSize = [label frame].size; | |
168 bubbleSize.width += 2 * labelInset; | |
169 bubbleSize.height += 2 * labelInset + info_bubble::kBubbleArrowHeight; | |
170 [errorBubble_ setFrameSize:bubbleSize]; | |
171 [label setFrameOrigin:NSMakePoint(labelInset, labelInset)]; | |
172 [errorBubble_ setFrameOrigin:[self originFromAnchorView:field]]; | |
173 [errorBubble_ setHidden:NO]; | |
174 } else { | 164 } else { |
175 [errorBubble_ setHidden:YES]; | 165 [errorBubbleController_ close]; |
176 } | 166 } |
177 } | 167 } |
178 | 168 |
179 @end | 169 @end |
OLD | NEW |