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

Side by Side Diff: chrome/browser/ui/cocoa/browser/password_generation_bubble_controller.mm

Issue 11416047: Add mac UI for password generation (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: comments and tests Created 8 years 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
(Empty)
1 // Copyright (c) 2012 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/browser/password_generation_bubble_controller.h "
6
7 #include "base/sys_string_conversions.h"
8 #include "base/mac/foundation_util.h"
9 #include "chrome/common/autofill_messages.h"
10 #include "chrome/browser/autofill/password_generator.h"
11 #include "chrome/browser/password_manager/password_manager.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_window.h"
14 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
15 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
16 #include "chrome/browser/ui/cocoa/key_equivalent_constants.h"
17 #import "chrome/browser/ui/cocoa/styled_text_field_cell.h"
18 #import "chrome/browser/ui/cocoa/tracking_area.h"
19 #include "content/public/browser/render_view_host.h"
20 #include "content/public/common/password_form.h"
21 #include "grit/generated_resources.h"
22 #include "grit/theme_resources.h"
23 #include "ui/base/l10n/l10n_util_mac.h"
24 #include "ui/base/resource/resource_bundle.h"
25
26 namespace {
27
28 // Size of the border in the bubble.
29 const CGFloat kBorderSize = 9.0;
30
31 // Visible size of the textfield.
32 const CGFloat kTextFieldHeight = 20.0;
33 const CGFloat kTextFieldWidth = 172.0;
34
35 // Frame padding necessary to make the textfield the correct visible size.
36 const CGFloat kTextFieldTopPadding = 3.0;
37
38 // Visible size of the button
39 const CGFloat kButtonWidth = 63.0;
40 const CGFloat kButtonHeight = 20.0;
41
42 // Padding that is added to the frame around the button to make it the
43 // correct visible size. Determined via visual inspection.
44 const CGFloat kButtonHorizontalPadding = 6.5;
45 const CGFloat kButtonVerticalPadding = 3.0;
46
47 // Visible size of the title.
48 const CGFloat kTitleWidth = 170.0;
49 const CGFloat kTitleHeight = 15.0;
50
51 // Space between the title and the textfield.
52 const CGFloat kVerticalSpacing = 13.0;
53
54 // Space between the textfield and the button.
55 const CGFloat kHorizontalSpacing = 4.0;
56
57 // We don't actually want the border to be kBorderSize on top as there is
58 // whitespace in the title text that makes it looks substantially bigger.
59 const CGFloat kTopBorderOffset = 3.0;
60
61 const CGFloat kIconSize = 26.0;
62
63 } // namespace
64
65 // Customized StyledTextFieldCell to display one button decoration that changes
66 // on hover.
67 @interface PasswordGenerationTextFieldCell : StyledTextFieldCell {
68 @private
69 PasswordGenerationBubbleController* controller_;
70 BOOL hovering_;
71 scoped_nsobject<NSImage> normalImage_;
72 scoped_nsobject<NSImage> hoverImage_;
73 }
74
75 - (void)setUpWithController:(PasswordGenerationBubbleController*)controller
76 normalImage:(NSImage*)normalImage
77 hoverImage:(NSImage*)hoverImage;
78 - (void)mouseEntered:(NSEvent*)theEvent
79 inView:(PasswordGenerationTextField*)controlView;
80 - (void)mouseExited:(NSEvent*)theEvent
81 inView:(PasswordGenerationTextField*)controlView;
82 - (BOOL)mouseDown:(NSEvent*)theEvent
83 inView:(PasswordGenerationTextField*)controlView;
84 - (void)setUpTrackingAreaInRect:(NSRect)frame
85 ofView:(PasswordGenerationTextField*)controlView;
86 // Exposed for testing.
87 - (NSRect)getIconFrame:(NSRect)cellFrame;
88 @end
89
90 @implementation PasswordGenerationTextField
91
92 + (Class)cellClass {
93 return [PasswordGenerationTextFieldCell class];
94 }
95
96 - (PasswordGenerationTextFieldCell*)cell {
97 return base::mac::ObjCCastStrict<PasswordGenerationTextFieldCell>(
98 [super cell]);
99 }
100
101 - (id)initWithFrame:(NSRect)frame
102 withController:(PasswordGenerationBubbleController*)controller
103 normalImage:(NSImage*)normalImage
104 hoverImage:(NSImage*)hoverImage {
105 self = [super initWithFrame:frame];
106 if (self) {
107 PasswordGenerationTextFieldCell* cell = [self cell];
108 [cell setUpWithController:controller
109 normalImage:normalImage
110 hoverImage:hoverImage];
111 [cell setUpTrackingAreaInRect:[self bounds] ofView:self];
112 }
113 return self;
114 }
115
116 - (void)mouseEntered:(NSEvent*)theEvent {
117 [[self cell] mouseEntered:theEvent inView:self];
118 }
119
120 - (void)mouseExited:(NSEvent*)theEvent {
121 [[self cell] mouseExited:theEvent inView:self];
122 }
123
124 - (void)mouseDown:(NSEvent*)theEvent {
Scott Hess - ex-Googler 2012/11/28 20:01:06 I'm looking at this and wondering what happens if
Garrett Casto 2012/11/30 20:56:26 Changed.
125 // Let the cell handle the click if it's in the decoration.
126 if (![[self cell] mouseDown:theEvent inView:self]) {
127 [[self currentEditor] mouseDown:theEvent];
128 }
129 }
130
131 - (NSRect)getIconFrame {
132 return [[self cell] getIconFrame:[self bounds]];
133 }
134
135 @end
136
137 @implementation PasswordGenerationTextFieldCell
138
139 - (void)setUpWithController:(PasswordGenerationBubbleController*)controller
140 normalImage:(NSImage*)normalImage
141 hoverImage:(NSImage*)hoverImage {
142 controller_ = controller;
143 hovering_ = NO;
144 normalImage_.reset([normalImage retain]);
145 hoverImage_.reset([hoverImage retain]);
146 [self setLineBreakMode:NSLineBreakByTruncatingTail];
147 [self setTruncatesLastVisibleLine:YES];
148 }
149
150 - (void)splitFrame:(NSRect*)cellFrame toIconFrame:(NSRect*)iconFrame {
151 NSDivideRect(*cellFrame, iconFrame, cellFrame,
152 kIconSize, NSMaxXEdge);
153 }
154
155 - (NSRect)getIconFrame:(NSRect)cellFrame {
156 NSRect iconFrame;
157 [self splitFrame:&cellFrame toIconFrame:&iconFrame];
158 return iconFrame;
159 }
160
161 - (NSRect)getTextFrame:(NSRect)cellFrame {
162 NSRect iconFrame;
163 [self splitFrame:&cellFrame toIconFrame:&iconFrame];
164 return cellFrame;
165 }
166
167 - (BOOL)eventIsInDecoration:(NSEvent*)theEvent
168 inView:(PasswordGenerationTextField*)controlView {
169 NSPoint mouseLocation = [controlView convertPoint:[theEvent locationInWindow]
170 fromView:nil];
171 NSRect cellFrame = [controlView bounds];
172 return NSMouseInRect(mouseLocation,
173 [self getIconFrame:cellFrame],
174 [controlView isFlipped]);
175 }
176
177 - (void)mouseEntered:(NSEvent*)theEvent
178 inView:(PasswordGenerationTextField*)controlView {
179 hovering_ = YES;
180 [controlView setNeedsDisplay:YES];
181 }
182
183 - (void)mouseExited:(NSEvent*)theEvent
184 inView:(PasswordGenerationTextField*)controlView {
185 hovering_ = NO;
186 [controlView setNeedsDisplay:YES];
187 }
188
189 - (BOOL)mouseDown:(NSEvent*)theEvent
190 inView:(PasswordGenerationTextField*)controlView {
191 if ([self eventIsInDecoration:theEvent inView:controlView]) {
192 [controller_ regeneratePassword];
193 return YES;
194 }
195 return NO;
196 }
197
198 - (NSImage*)getImage {
199 if (hovering_)
200 return hoverImage_;
201 return normalImage_;
202 }
203
204 - (NSRect)adjustFrameForFrame:(NSRect)frame {
205 // By default, there appears to be a 2 pixel gap between what is considered
206 // part of the textFrame and what is considered part of the icon.
207 // TODO(gcasto): This really should be fixed in StyledTextFieldCell, as it
208 // looks like the location bar also suffers from this issue.
209 frame.size.width += 2;
Scott Hess - ex-Googler 2012/11/28 20:01:06 Per comment I made probably against an earlier pat
Garrett Casto 2012/11/30 20:56:26 Looks right to me.
210 return frame;
211 }
212
213 - (NSRect)textFrameForFrame:(NSRect)cellFrame {
214 // Baseclass insets the rect by baselineAdjust.
215 NSRect textFrame = [super textFrameForFrame:cellFrame];
216 textFrame = [self getTextFrame:textFrame];
217 return [self adjustFrameForFrame:textFrame];
218 }
219
220 - (NSRect)textCursorFrameForFrame:(NSRect)cellFrame {
221 NSRect textFrame = [self getTextFrame:cellFrame];
222 return [self adjustFrameForFrame:textFrame];
223 }
224
225 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
226 NSImage* image = [self getImage];
227 NSRect iconFrame = [self getIconFrame:cellFrame];
228 // Center the image in the available space. At the moment the image is
229 // slightly larger than the frame so we crop it.
230 // Offset the full difference on the left hand side since the border on the
231 // right takes up some space. Offset half the vertical difference on the
232 // bottom so that the image stays vertically centered.
233 const CGFloat x_offset = [image size].width - NSWidth(iconFrame);
234 const CGFloat y_offset = ([image size].height - (NSHeight(iconFrame))) / 2.0;
Scott Hess - ex-Googler 2012/11/28 20:01:06 xOffset, yOffset.
Garrett Casto 2012/11/30 20:56:26 Done.
235 NSRect croppedRect = NSMakeRect(x_offset,
236 y_offset,
237 NSWidth(iconFrame),
238 NSHeight(iconFrame));
239
240 [image drawInRect:iconFrame
241 fromRect:croppedRect
242 operation:NSCompositeSourceOver
243 fraction:1.0
244 respectFlipped:YES
245 hints:nil];
246
247 [super drawInteriorWithFrame:cellFrame inView:controlView];
248 }
249
250 - (void)setUpTrackingAreaInRect:(NSRect)frame
251 ofView:(PasswordGenerationTextField*)view {
252 NSRect iconFrame = [self getIconFrame:frame];
253 scoped_nsobject<CrTrackingArea> area(
254 [[CrTrackingArea alloc] initWithRect:iconFrame
255 options:NSTrackingMouseEnteredAndExited |
256 NSTrackingActiveAlways
257 owner:view
258 userInfo:nil]);
259 [view addTrackingArea:area];
260 }
261
262 - (CGFloat)baselineAdjust {
263 return 1.0;
264 }
265
266 - (CGFloat)cornerRadius {
267 return 4.0;
268 }
269
270 - (BOOL)shouldDrawBezel {
271 return YES;
272 }
273
274 @end
275
276 @implementation PasswordGenerationBubbleController
277
278 - (id)initWithWindow:(NSWindow*)parentWindow
279 anchoredAt:(NSPoint)point
280 renderViewHost:(content::RenderViewHost*)renderViewHost
281 passwordManager:(PasswordManager*)passwordManager
282 usingGenerator:(autofill::PasswordGenerator*)passwordGenerator
283 forForm:(const content::PasswordForm&)form {
284 int width = (kBorderSize*2 +
285 kTextFieldWidth +
286 kHorizontalSpacing +
287 kButtonWidth);
288 int height = (kBorderSize*2 +
289 kTextFieldHeight +
290 kVerticalSpacing +
291 kTitleHeight -
292 kTopBorderOffset +
293 info_bubble::kBubbleArrowHeight);
Scott Hess - ex-Googler 2012/11/28 20:01:06 These should probably both be CGFloat.
Garrett Casto 2012/11/30 20:56:26 Done.
294 NSRect contentRect = NSMakeRect(0, 0, width, height);
295 scoped_nsobject<InfoBubbleWindow> window(
296 [[InfoBubbleWindow alloc] initWithContentRect:contentRect
297 styleMask:NSBorderlessWindowMask
298 backing:NSBackingStoreBuffered
299 defer:NO]);
300 if (self = [super initWithWindow:window
301 parentWindow:parentWindow
302 anchoredAt:point]) {
303 passwordGenerator_ = passwordGenerator;
304 renderViewHost_ = renderViewHost;
305 passwordManager_ = passwordManager;
306 form_ = form;
307 [[self bubble] setArrowLocation:info_bubble::kTopLeft];
308 [self performLayout];
309 }
310
311 return self;
312 }
313
314 - (void)performLayout {
315 NSView* contentView = [[self window] contentView];
316 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
317
318 textField_ =
319 [[PasswordGenerationTextField alloc]
320 initWithFrame:NSMakeRect(kBorderSize,
321 kBorderSize,
322 kTextFieldWidth,
323 kTextFieldHeight + kTextFieldTopPadding)
324 withController:self
325 normalImage:rb.GetNativeImageNamed(IDR_RELOAD_DIMMED).ToNSImage()
326 hoverImage:rb.GetNativeImageNamed(IDR_RELOAD).ToNSImage()];
327 gfx::Font smallBoldFont =
328 rb.GetFont(ResourceBundle::SmallFont).DeriveFont(0, gfx::Font::BOLD);
329 [textField_ setFont:smallBoldFont.GetNativeFont()];
330 [textField_
331 setStringValue:base::SysUTF8ToNSString(passwordGenerator_->Generate())];
332 [contentView addSubview:textField_];
333
334 int button_x = (kBorderSize +
335 kTextFieldWidth +
336 kHorizontalSpacing -
337 kButtonVerticalPadding);
Scott Hess - ex-Googler 2012/11/28 20:01:06 CGFloat here, too. kButtonVerticalPadding when ca
Garrett Casto 2012/11/30 20:56:26 Yep, good catch. I was trying to figure out why th
338 int button_y = kBorderSize - kButtonVerticalPadding;
339 NSButton* button =
340 [[NSButton alloc] initWithFrame:NSMakeRect(
341 button_x,
342 button_y,
343 kButtonWidth + 2 * kButtonHorizontalPadding,
344 kButtonHeight + 2 * kButtonVerticalPadding)];
345 [button setBezelStyle:NSRoundedBezelStyle];
346 [button setTitle:l10n_util::GetNSString(IDS_PASSWORD_GENERATION_BUTTON_TEXT)];
347 [button setTarget:self];
348 [button setAction:@selector(fillPassword:)];
349 [contentView addSubview:button];
350
351 NSTextField* title = [[NSTextField alloc]
352 initWithFrame:NSMakeRect(
353 kBorderSize,
354 kBorderSize + kTextFieldHeight + kVerticalSpacing,
Scott Hess - ex-Googler 2012/11/28 20:01:06 As with the earlier comment, this could be NSMaxY(
Garrett Casto 2012/11/30 20:56:26 This isn't equivalent. The way I've been trying to
355 kTitleWidth,
356 kTitleHeight)];
357 [title setEditable:NO];
358 [title setBordered:NO];
359 [title setStringValue:l10n_util::GetNSString(
360 IDS_PASSWORD_GENERATION_BUBBLE_TITLE)];
361 [contentView addSubview:title];
362 }
363
364 - (IBAction)fillPassword:(id)sender {
365 renderViewHost_->Send(
366 new AutofillMsg_GeneratedPasswordAccepted(
367 renderViewHost_->GetRoutingID(),
368 base::SysNSStringToUTF16([textField_ stringValue])));
369 passwordManager_->SetFormHasGeneratedPassword(form_);
370 [self close];
371 }
372
373 - (void)regeneratePassword {
374 [textField_
375 setStringValue:base::SysUTF8ToNSString(passwordGenerator_->Generate())];
376 }
377
378 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698