OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/password_generation_popup_view_cocoa.h " | 5 #import "chrome/browser/ui/cocoa/autofill/password_generation_popup_view_cocoa.h " |
6 | 6 |
7 #include <cmath> | |
8 | |
7 #include "base/logging.h" | 9 #include "base/logging.h" |
8 #include "base/strings/sys_string_conversions.h" | 10 #include "base/strings/sys_string_conversions.h" |
9 #include "chrome/browser/ui/autofill/autofill_popup_controller.h" | 11 #include "chrome/browser/ui/autofill/autofill_popup_controller.h" |
10 #include "chrome/browser/ui/autofill/autofill_popup_view.h" | 12 #include "chrome/browser/ui/autofill/autofill_popup_view.h" |
11 #include "chrome/browser/ui/autofill/popup_constants.h" | 13 #include "chrome/browser/ui/autofill/popup_constants.h" |
12 #include "chrome/browser/ui/chrome_style.h" | 14 #include "chrome/browser/ui/chrome_style.h" |
13 #include "chrome/browser/ui/cocoa/autofill/password_generation_popup_view_bridge .h" | 15 #include "chrome/browser/ui/cocoa/autofill/password_generation_popup_view_bridge .h" |
14 #import "chrome/browser/ui/cocoa/hyperlink_text_view.h" | 16 #import "chrome/browser/ui/cocoa/hyperlink_text_view.h" |
15 #import "chrome/browser/ui/cocoa/l10n_util.h" | 17 #import "chrome/browser/ui/cocoa/l10n_util.h" |
16 #include "components/autofill/core/browser/popup_item_ids.h" | 18 #include "components/autofill/core/browser/popup_item_ids.h" |
19 #include "grit/theme_resources.h" | |
17 #include "skia/ext/skia_utils_mac.h" | 20 #include "skia/ext/skia_utils_mac.h" |
18 #include "ui/base/resource/resource_bundle.h" | 21 #include "ui/base/resource/resource_bundle.h" |
19 #include "ui/gfx/font_list.h" | 22 #include "ui/gfx/font_list.h" |
20 #include "ui/gfx/image/image.h" | 23 #include "ui/gfx/image/image.h" |
21 #include "ui/gfx/point.h" | 24 #include "ui/gfx/point.h" |
22 #include "ui/gfx/range/range.h" | 25 #include "ui/gfx/range/range.h" |
23 #include "ui/gfx/rect.h" | 26 #include "ui/gfx/rect.h" |
24 #include "ui/gfx/text_constants.h" | 27 #include "ui/gfx/text_constants.h" |
25 | 28 |
26 using autofill::AutofillPopupView; | 29 using autofill::AutofillPopupView; |
30 using autofill::PasswordGenerationPopupController; | |
27 using autofill::PasswordGenerationPopupView; | 31 using autofill::PasswordGenerationPopupView; |
28 using base::scoped_nsobject; | 32 using base::scoped_nsobject; |
29 | 33 |
30 namespace { | 34 namespace { |
31 | 35 |
36 // The width of the divider between the password and help sections, in pixels. | |
Ilya Sherman
2014/08/22 06:20:11
nit: "width" -> "height"
dconnelly
2014/08/22 11:13:21
Done.
| |
37 const CGFloat kDividerHeight = 1; | |
38 | |
39 // The amount of whitespace, in pixels, between lines of text in the password | |
40 // section. | |
41 const CGFloat kPasswordSectionVerticalSeparation = 5; | |
42 | |
32 NSColor* DividerColor() { | 43 NSColor* DividerColor() { |
33 return gfx::SkColorToCalibratedNSColor( | 44 return gfx::SkColorToCalibratedNSColor( |
34 PasswordGenerationPopupView::kDividerColor); | 45 PasswordGenerationPopupView::kDividerColor); |
35 } | 46 } |
36 | 47 |
37 NSColor* HelpTextBackgroundColor() { | 48 NSColor* HelpTextBackgroundColor() { |
38 return gfx::SkColorToCalibratedNSColor( | 49 return gfx::SkColorToCalibratedNSColor( |
39 PasswordGenerationPopupView::kExplanatoryTextBackgroundColor); | 50 PasswordGenerationPopupView::kExplanatoryTextBackgroundColor); |
40 } | 51 } |
41 | 52 |
(...skipping 16 matching lines...) Expand all Loading... | |
58 NOTREACHED(); | 69 NOTREACHED(); |
59 return nil; | 70 return nil; |
60 } | 71 } |
61 | 72 |
62 - (id)initWithController: | 73 - (id)initWithController: |
63 (autofill::PasswordGenerationPopupController*)controller | 74 (autofill::PasswordGenerationPopupController*)controller |
64 frame:(NSRect)frame { | 75 frame:(NSRect)frame { |
65 if (self = [super initWithDelegate:controller frame:frame]) { | 76 if (self = [super initWithDelegate:controller frame:frame]) { |
66 controller_ = controller; | 77 controller_ = controller; |
67 | 78 |
68 passwordField_ = [self textFieldWithText:controller_->password() | 79 passwordSection_.reset([[NSView alloc] initWithFrame:NSZeroRect]); |
69 color:[self nameColor] | 80 [self addSubview:passwordSection_]; |
70 alignment:NSLeftTextAlignment]; | |
71 [self addSubview:passwordField_]; | |
72 | 81 |
73 passwordSubtextField_ = [self textFieldWithText:controller_->SuggestedText() | 82 passwordField_.reset( |
74 color:[self subtextColor] | 83 [[self textFieldWithText:controller_->password() |
75 alignment:NSRightTextAlignment]; | 84 attributes:[self passwordAttributes]] retain]); |
76 [self addSubview:passwordSubtextField_]; | 85 [passwordSection_ addSubview:passwordField_]; |
77 | 86 |
78 scoped_nsobject<HyperlinkTextView> helpTextView( | 87 passwordTitleField_.reset( |
79 [[HyperlinkTextView alloc] initWithFrame:NSZeroRect]); | 88 [[self textFieldWithText:controller_->SuggestedText() |
80 [helpTextView setMessage:base::SysUTF16ToNSString(controller_->HelpText()) | 89 attributes:[self passwordTitleAttributes]] retain]); |
81 withFont:[self textFont] | 90 [passwordSection_ addSubview:passwordTitleField_]; |
82 messageColor:HelpTextColor()]; | 91 |
83 [helpTextView addLinkRange:controller_->HelpTextLinkRange().ToNSRange() | 92 keyIcon_.reset([[NSImageView alloc] initWithFrame:NSZeroRect]); |
84 withName:@"" | 93 NSImage* keyImage = ResourceBundle::GetSharedInstance() |
85 linkColor:HelpLinkColor()]; | 94 .GetImageNamed(IDR_GENERATE_PASSWORD_KEY) |
86 [helpTextView setDelegate:self]; | 95 .ToNSImage(); |
87 [[helpTextView textContainer] setLineFragmentPadding:0.0f]; | 96 [keyIcon_ setImage:keyImage]; |
88 [helpTextView setVerticallyResizable:YES]; | 97 [passwordSection_ addSubview:keyIcon_]; |
89 [self addSubview:helpTextView]; | 98 |
90 helpTextView_ = helpTextView.get(); | 99 divider_.reset([[NSBox alloc] initWithFrame:NSZeroRect]); |
91 } | 100 [divider_ setBoxType:NSBoxCustom]; |
101 [divider_ setBorderType:NSLineBorder]; | |
102 [divider_ setBorderColor:DividerColor()]; | |
103 [self addSubview:divider_]; | |
104 | |
105 helpTextView_.reset([[HyperlinkTextView alloc] initWithFrame:NSZeroRect]); | |
106 [helpTextView_ setMessage:base::SysUTF16ToNSString(controller_->HelpText()) | |
107 withFont:[self textFont] | |
108 messageColor:HelpTextColor()]; | |
109 [helpTextView_ addLinkRange:controller_->HelpTextLinkRange().ToNSRange() | |
110 withName:@"" | |
111 linkColor:HelpLinkColor()]; | |
112 [helpTextView_ setDelegate:self]; | |
113 [helpTextView_ setDrawsBackground:YES]; | |
114 [helpTextView_ setBackgroundColor:HelpTextBackgroundColor()]; | |
115 [helpTextView_ | |
116 setTextContainerInset:NSMakeSize(controller_->kHorizontalPadding, | |
117 controller_->kHelpVerticalPadding)]; | |
118 // Remove the underlining. | |
119 NSTextStorage* text = [helpTextView_ textStorage]; | |
120 [text addAttribute:NSUnderlineStyleAttributeName | |
121 value:@(NSUnderlineStyleNone) | |
122 range:controller_->HelpTextLinkRange().ToNSRange()]; | |
123 [self addSubview:helpTextView_]; | |
124 } | |
92 | 125 |
93 return self; | 126 return self; |
94 } | 127 } |
95 | 128 |
96 #pragma mark NSView implementation: | 129 #pragma mark NSView implementation: |
97 | 130 |
98 - (void)drawRect:(NSRect)dirtyRect { | 131 - (void)drawRect:(NSRect)dirtyRect { |
132 [super drawRect:dirtyRect]; | |
133 | |
99 // If the view is in the process of being destroyed, don't bother drawing. | 134 // If the view is in the process of being destroyed, don't bother drawing. |
100 if (!controller_) | 135 if (!controller_) |
101 return; | 136 return; |
102 | 137 |
103 [self drawBackgroundAndBorder]; | 138 [self drawBackgroundAndBorder]; |
104 | 139 |
105 if (controller_->password_selected()) { | 140 if (controller_->password_selected()) { |
106 // Draw a highlight under the suggested password. | 141 // Draw a highlight under the suggested password. |
107 NSRect highlightBounds = [self passwordBounds]; | 142 NSRect highlightBounds = [passwordSection_ frame]; |
108 [[self highlightColor] set]; | 143 [[self highlightColor] set]; |
109 [NSBezierPath fillRect:highlightBounds]; | 144 [NSBezierPath fillRect:highlightBounds]; |
110 } | 145 } |
111 | |
112 // Render the background of the help text. | |
113 [HelpTextBackgroundColor() set]; | |
114 [NSBezierPath fillRect:[self helpBounds]]; | |
115 | |
116 // Render the divider. | |
117 [DividerColor() set]; | |
118 [NSBezierPath fillRect:[self dividerBounds]]; | |
119 } | 146 } |
120 | 147 |
121 #pragma mark Public API: | 148 #pragma mark Public API: |
122 | 149 |
150 - (NSSize)preferredSize { | |
151 const NSSize passwordTitleSize = | |
152 [base::SysUTF16ToNSString(controller_->SuggestedText()) | |
153 sizeWithAttributes:@{ NSFontAttributeName : [self boldFont] }]; | |
154 const NSSize passwordSize = [base::SysUTF16ToNSString(controller_->password()) | |
155 sizeWithAttributes:@{ NSFontAttributeName : [self textFont] }]; | |
156 | |
157 CGFloat width = | |
158 autofill::kPopupBorderThickness + | |
159 controller_->kHorizontalPadding + | |
160 [[keyIcon_ image] size].width + | |
161 controller_->kHorizontalPadding + | |
162 std::max(passwordSize.width, passwordTitleSize.width) + | |
163 controller_->kHorizontalPadding + | |
164 autofill::kPopupBorderThickness; | |
165 | |
166 width = std::max(width, (CGFloat)controller_->GetMinimumWidth()); | |
167 | |
168 CGFloat height = | |
169 autofill::kPopupBorderThickness + | |
170 controller_->kHelpVerticalPadding + | |
171 [self helpSizeForPopupWidth:width].height + | |
172 controller_->kHelpVerticalPadding + | |
173 autofill::kPopupBorderThickness; | |
174 | |
175 if (controller_->display_password()) | |
176 height += controller_->kPopupPasswordSectionHeight; | |
177 | |
178 return NSMakeSize(width, height); | |
179 } | |
180 | |
123 - (void)updateBoundsAndRedrawPopup { | 181 - (void)updateBoundsAndRedrawPopup { |
124 [self positionView:passwordField_ inRect:[self passwordBounds]]; | 182 const CGFloat popupWidth = controller_->popup_bounds().width(); |
125 [self positionView:passwordSubtextField_ inRect:[self passwordBounds]]; | 183 const CGFloat contentWidth = |
126 [self positionView:helpTextView_ inRect:[self helpBounds]]; | 184 popupWidth - (2 * autofill::kPopupBorderThickness); |
185 const CGFloat contentHeight = controller_->popup_bounds().height() - | |
186 (2 * autofill::kPopupBorderThickness); | |
187 | |
188 if (controller_->display_password()) { | |
189 // The password can change while the bubble is shown: If the user has | |
190 // accepted the password and then selects the form again and starts deleting | |
191 // the password, the field will be initially invisible and then become | |
192 // visible. | |
193 [self updatePassword]; | |
194 | |
195 // Layout the password section, which includes the key icon, the title, and | |
Ilya Sherman
2014/08/22 06:20:11
nit: "Layout" -> "Lay out" (noun vs. verb)
dconnelly
2014/08/22 11:13:21
Done.
| |
196 // the suggested password. | |
197 [passwordSection_ | |
198 setFrame:NSMakeRect(autofill::kPopupBorderThickness, | |
199 autofill::kPopupBorderThickness, | |
200 contentWidth, | |
201 controller_->kPopupPasswordSectionHeight)]; | |
202 | |
203 // The key icon falls to the left of the title and password. | |
204 const NSSize imageSize = [[keyIcon_ image] size]; | |
205 const CGFloat keyX = controller_->kHorizontalPadding; | |
206 const CGFloat keyY = | |
207 std::ceil((controller_->kPopupPasswordSectionHeight / 2.0) - | |
208 (imageSize.height / 2.0)); | |
209 [keyIcon_ setFrameOrigin:NSMakePoint(keyX, keyY)]; | |
210 [keyIcon_ sizeToFit]; | |
211 | |
212 // The title and password fall to the right of the key icon and are centered | |
213 // vertically as a group with some padding in between. | |
214 [passwordTitleField_ sizeToFit]; | |
215 [passwordField_ sizeToFit]; | |
216 const CGFloat groupHeight = NSHeight([passwordField_ frame]) + | |
217 kPasswordSectionVerticalSeparation + | |
218 NSHeight([passwordTitleField_ frame]); | |
219 const CGFloat groupX = | |
220 NSMaxX([keyIcon_ frame]) + controller_->kHorizontalPadding; | |
221 const CGFloat groupY = | |
222 std::ceil((controller_->kPopupPasswordSectionHeight / 2.0) - | |
223 (groupHeight / 2.0)); | |
224 [passwordField_ setFrameOrigin:NSMakePoint(groupX, groupY)]; | |
225 const CGFloat titleY = groupY + | |
226 NSHeight([passwordField_ frame]) + | |
227 kPasswordSectionVerticalSeparation; | |
228 [passwordTitleField_ setFrameOrigin:NSMakePoint(groupX, titleY)]; | |
229 | |
230 // Layout the divider, which falls immediately below the password section. | |
231 const CGFloat dividerX = autofill::kPopupBorderThickness; | |
232 const CGFloat dividerY = NSMaxY([passwordSection_ frame]); | |
233 NSRect dividerFrame = | |
234 NSMakeRect(dividerX, dividerY, contentWidth, kDividerHeight); | |
235 [divider_ setFrame:dividerFrame]; | |
236 } | |
237 | |
238 // Layout the help section beneath the divider (if applicable, otherwise | |
239 // beneach the border). | |
Ilya Sherman
2014/08/22 06:20:11
nit: "beneach" -> "beneath"
dconnelly
2014/08/22 11:13:21
Done.
| |
240 const CGFloat helpX = autofill::kPopupBorderThickness; | |
241 const CGFloat helpY = controller_->display_password() | |
242 ? NSMaxY([divider_ frame]) | |
243 : autofill::kPopupBorderThickness; | |
244 const CGFloat helpHeight = contentHeight - | |
245 NSHeight([passwordSection_ frame]) - | |
246 NSHeight([divider_ frame]); | |
247 [helpTextView_ setFrame:NSMakeRect(helpX, helpY, contentWidth, helpHeight)]; | |
127 | 248 |
128 [super updateBoundsAndRedrawPopup]; | 249 [super updateBoundsAndRedrawPopup]; |
129 } | 250 } |
130 | 251 |
252 - (BOOL)isPointInPasswordBounds:(NSPoint)point { | |
253 return NSPointInRect(point, [passwordSection_ frame]); | |
254 } | |
255 | |
131 - (void)controllerDestroyed { | 256 - (void)controllerDestroyed { |
132 controller_ = NULL; | 257 controller_ = NULL; |
133 [super delegateDestroyed]; | 258 [super delegateDestroyed]; |
134 } | 259 } |
135 | 260 |
136 #pragma mark NSTextViewDelegate implementation: | 261 #pragma mark NSTextViewDelegate implementation: |
137 | 262 |
138 - (BOOL)textView:(NSTextView*)textView | 263 - (BOOL)textView:(NSTextView*)textView |
139 clickedOnLink:(id)link | 264 clickedOnLink:(id)link |
140 atIndex:(NSUInteger)charIndex { | 265 atIndex:(NSUInteger)charIndex { |
141 controller_->OnSavedPasswordsLinkClicked(); | 266 controller_->OnSavedPasswordsLinkClicked(); |
142 return YES; | 267 return YES; |
143 } | 268 } |
144 | 269 |
145 #pragma mark Private helpers: | 270 #pragma mark Private helpers: |
146 | 271 |
147 - (NSTextField*)textFieldWithText:(const base::string16&)text | 272 - (void)updatePassword { |
148 color:(NSColor*)color | 273 base::scoped_nsobject<NSMutableAttributedString> updatedPassword( |
149 alignment:(NSTextAlignment)alignment { | 274 [[NSMutableAttributedString alloc] |
275 initWithString:base::SysUTF16ToNSString(controller_->password()) | |
276 attributes:[self passwordAttributes]]); | |
277 [passwordField_ setAttributedStringValue:updatedPassword]; | |
278 } | |
279 | |
280 - (NSDictionary*)passwordTitleAttributes { | |
150 scoped_nsobject<NSMutableParagraphStyle> paragraphStyle( | 281 scoped_nsobject<NSMutableParagraphStyle> paragraphStyle( |
151 [[NSMutableParagraphStyle alloc] init]); | 282 [[NSMutableParagraphStyle alloc] init]); |
152 [paragraphStyle setAlignment:alignment]; | 283 [paragraphStyle setAlignment:NSLeftTextAlignment]; |
284 return @{ | |
285 NSFontAttributeName : [self boldFont], | |
286 NSForegroundColorAttributeName : [self nameColor], | |
287 NSParagraphStyleAttributeName : paragraphStyle.autorelease() | |
288 }; | |
289 } | |
153 | 290 |
154 NSDictionary* textAttributes = @{ | 291 - (NSDictionary*)passwordAttributes { |
292 scoped_nsobject<NSMutableParagraphStyle> paragraphStyle( | |
293 [[NSMutableParagraphStyle alloc] init]); | |
294 [paragraphStyle setAlignment:NSLeftTextAlignment]; | |
295 return @{ | |
155 NSFontAttributeName : [self textFont], | 296 NSFontAttributeName : [self textFont], |
156 NSForegroundColorAttributeName : color, | 297 NSForegroundColorAttributeName : [self nameColor], |
157 NSParagraphStyleAttributeName : paragraphStyle | 298 NSParagraphStyleAttributeName : paragraphStyle.autorelease() |
158 }; | 299 }; |
300 } | |
159 | 301 |
302 - (NSTextField*)textFieldWithText:(const base::string16&)text | |
303 attributes:(NSDictionary*)attributes { | |
304 NSTextField* textField = | |
305 [[[NSTextField alloc] initWithFrame:NSZeroRect] autorelease]; | |
160 scoped_nsobject<NSAttributedString> attributedString( | 306 scoped_nsobject<NSAttributedString> attributedString( |
161 [[NSAttributedString alloc] | 307 [[NSAttributedString alloc] |
162 initWithString:base::SysUTF16ToNSString(text) | 308 initWithString:base::SysUTF16ToNSString(text) |
163 attributes:textAttributes]); | 309 attributes:attributes]); |
164 | 310 [textField setAttributedStringValue:attributedString.autorelease()]; |
165 NSTextField* textField = | |
166 [[[NSTextField alloc] initWithFrame:NSZeroRect] autorelease]; | |
167 [textField setAttributedStringValue:attributedString]; | |
168 [textField setEditable:NO]; | 311 [textField setEditable:NO]; |
169 [textField setSelectable:NO]; | 312 [textField setSelectable:NO]; |
170 [textField setDrawsBackground:NO]; | 313 [textField setDrawsBackground:NO]; |
171 [textField setBezeled:NO]; | 314 [textField setBezeled:NO]; |
172 return textField; | 315 return textField; |
173 } | 316 } |
174 | 317 |
175 - (void)positionView:(NSView*)view inRect:(NSRect)bounds { | 318 - (NSSize)helpSizeForPopupWidth:(CGFloat)width { |
176 NSRect frame = NSInsetRect(bounds, controller_->kHorizontalPadding, 0); | 319 const CGFloat helpWidth = width - |
177 [view setFrame:frame]; | 320 2 * controller_->kHorizontalPadding - |
178 | 321 2 * autofill::kPopupBorderThickness; |
179 // Center the text vertically within the bounds. | 322 const NSSize size = NSMakeSize(helpWidth, MAXFLOAT); |
180 NSSize delta = cocoa_l10n_util::WrapOrSizeToFit(view); | 323 NSRect textFrame = [base::SysUTF16ToNSString(controller_->HelpText()) |
181 [view setFrameOrigin: | 324 boundingRectWithSize:size |
182 NSInsetRect(frame, 0, floor(-delta.height/2)).origin]; | 325 options:NSLineBreakByWordWrapping | |
326 NSStringDrawingUsesLineFragmentOrigin | |
327 attributes:@{ NSFontAttributeName : [self textFont] }]; | |
328 return textFrame.size; | |
183 } | 329 } |
184 | 330 |
185 - (NSRect)passwordBounds { | 331 - (NSFont*)boldFont { |
186 return NSZeroRect; | 332 return [NSFont boldSystemFontOfSize:[NSFont smallSystemFontSize]]; |
187 } | |
188 | |
189 - (NSRect)helpBounds { | |
190 return NSZeroRect; | |
191 } | |
192 | |
193 - (NSRect)dividerBounds { | |
194 return NSZeroRect; | |
195 } | 333 } |
196 | 334 |
197 - (NSFont*)textFont { | 335 - (NSFont*)textFont { |
198 return ResourceBundle::GetSharedInstance().GetFontList( | 336 return [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; |
199 ResourceBundle::SmallFont).GetPrimaryFont().GetNativeFont(); | |
200 } | 337 } |
201 | 338 |
202 @end | 339 @end |
OLD | NEW |