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 height of the divider between the password and help sections, in pixels. |
| 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 // Lay out the password section, which includes the key icon, the title, and |
| 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 // beneath the border). |
| 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 |