Chromium Code Reviews| Index: chrome/browser/ui/cocoa/browser/password_generation_bubble_controller.mm |
| diff --git a/chrome/browser/ui/cocoa/browser/password_generation_bubble_controller.mm b/chrome/browser/ui/cocoa/browser/password_generation_bubble_controller.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b881de59bc667eefc4496addd82d6138c96b1509 |
| --- /dev/null |
| +++ b/chrome/browser/ui/cocoa/browser/password_generation_bubble_controller.mm |
| @@ -0,0 +1,295 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include <cmath> |
| + |
| +#import "chrome/browser/ui/cocoa/browser/password_generation_bubble_controller.h" |
| + |
| +#include "base/sys_string_conversions.h" |
| +#include "chrome/common/autofill_messages.h" |
| +#include "chrome/browser/autofill/password_generator.h" |
| +#include "chrome/browser/password_manager/password_manager.h" |
| +#include "chrome/browser/ui/browser.h" |
| +#include "chrome/browser/ui/browser_window.h" |
| +#import "chrome/browser/ui/cocoa/info_bubble_view.h" |
| +#import "chrome/browser/ui/cocoa/info_bubble_window.h" |
| +#import "chrome/browser/ui/cocoa/styled_text_field_cell.h" |
| +#import "chrome/browser/ui/cocoa/tracking_area.h" |
| +#include "content/public/browser/render_view_host.h" |
| +#include "content/public/common/password_form.h" |
| +#include "grit/generated_resources.h" |
| +#include "grit/theme_resources.h" |
| +#include "ui/base/resource/resource_bundle.h" |
| + |
| +namespace { |
|
Scott Hess - ex-Googler
2012/11/17 01:22:41
Line of whitespace before each comment and between
Garrett Casto
2012/11/19 22:17:42
Done.
Garrett Casto
2012/11/19 22:17:42
Done.
|
| +// We make the icon smaller by this decrease some of the whitespace. |
| +const int kIconWidthInset = 3; |
| +// This helps center the image to deal with the fact that there is a border |
| +// on the right side of the textfield. |
| +const int kIconOffset = 2; |
| +} // namespace |
| + |
| +// Customized StyledTextFieldCell to display one button decoration that changes |
| +// on hover. |
| +@interface PasswordGenerationTextFieldCell : StyledTextFieldCell { |
| + @private |
| + PasswordGenerationBubbleController* controller_; |
| + BOOL hovering_; |
| + scoped_nsobject<NSImage> normalImage_; |
| + scoped_nsobject<NSImage> hoverImage_; |
| +} |
| + |
| +- (void)setUpWithController:(PasswordGenerationBubbleController*)controller |
| + normalImage:(NSImage*)normalImage |
| + hoverImage:(NSImage*)hoverImage; |
| +- (void)mouseEntered:(NSEvent*)theEvent |
| + inView:(PasswordGenerationTextField*)controlView; |
| +- (void)mouseExited:(NSEvent*)theEvent |
| + inView:(PasswordGenerationTextField*)controlView; |
| +- (BOOL)mouseDown:(NSEvent*)theEvent |
| + inView:(PasswordGenerationTextField*)controlView; |
| +- (void)setUpTrackingAreaInRect:(NSRect)frame |
| + ofView:(PasswordGenerationTextField*)controlView; |
| +@end |
| + |
| +@implementation PasswordGenerationTextField |
| + |
| ++ (Class)cellClass { |
| + return [PasswordGenerationTextFieldCell class]; |
| +} |
| + |
| +- (id)initWithFrame:(NSRect)frame |
| + withController:(PasswordGenerationBubbleController*)controller |
| + normalImage:(NSImage*)normalImage |
| + hoverImage:(NSImage*)hoverImage { |
| + self = [super initWithFrame:frame]; |
| + if (self) { |
| + DCHECK([[self cell] isKindOfClass:[PasswordGenerationTextFieldCell class]]); |
| + [[self cell] setUpWithController:controller |
| + normalImage:normalImage |
| + hoverImage:hoverImage]; |
| + [[self cell] setLineBreakMode:NSLineBreakByTruncatingTail]; |
| + [[self cell] setTruncatesLastVisibleLine:YES]; |
|
Scott Hess - ex-Googler
2012/11/17 01:22:41
These two look like attributes of the cell, not dy
Garrett Casto
2012/11/19 22:17:42
AutocompleteTextField does this in awakeFromNib, b
Scott Hess - ex-Googler
2012/11/19 23:20:35
Generally the rule is that less-specific init meth
Garrett Casto
2012/11/27 07:59:49
Done.
|
| + [[self cell] setUpTrackingAreaInRect:[self bounds] ofView:self]; |
|
Scott Hess - ex-Googler
2012/11/17 01:22:41
In Objective-C, all of the [self cell] are dynamic
Garrett Casto
2012/11/19 22:17:42
I didn't notice anything obvious in base/mac/... I
Scott Hess - ex-Googler
2012/11/19 23:20:35
Basically like:
- (PGTFC*)cell {
return base::m
Garrett Casto
2012/11/27 07:59:49
Done, though as someone new to objective C, can yo
sail
2012/11/28 06:47:16
The reason there isn't strict type checking here i
|
| + } |
| + return self; |
| +} |
| + |
| +- (void)mouseEntered:(NSEvent*)theEvent { |
| + [[self cell] mouseEntered:theEvent inView:self]; |
| +} |
| + |
| +- (void)mouseExited:(NSEvent*)theEvent { |
| + [[self cell] mouseExited:theEvent inView:self]; |
| +} |
| + |
| +- (void)mouseDown:(NSEvent*)theEvent { |
| + // Let the cell handle the click if it's in the decoration. |
| + if (![[self cell] mouseDown:theEvent inView:self]) { |
| + [[self currentEditor] mouseDown:theEvent]; |
| + } |
| +} |
| + |
| +@end |
| + |
| +@implementation PasswordGenerationTextFieldCell |
| + |
| +- (void)setUpWithController:(PasswordGenerationBubbleController*)controller |
| + normalImage:(NSImage*)normalImage |
| + hoverImage:(NSImage*)hoverImage { |
| + controller_ = controller; |
| + hovering_ = NO; |
| + normalImage_.reset([normalImage retain]); |
| + hoverImage_.reset([hoverImage retain]); |
| +} |
| + |
| +- (NSRect)getFrameForIcon:(NSRect*)cellFrame { |
|
Scott Hess - ex-Googler
2012/11/17 01:22:41
In all but one case, you use either the return val
Garrett Casto
2012/11/19 22:17:42
Done.
|
| + NSRect iconFrame; |
| + NSDivideRect(*cellFrame, &iconFrame, cellFrame, |
| + [normalImage_ size].width - kIconWidthInset, NSMaxXEdge); |
|
Scott Hess - ex-Googler
2012/11/17 01:22:41
I'm not entirely following how you're using this.
Garrett Casto
2012/11/19 22:17:42
I'm using the same icon on both Windows and Mac (a
Scott Hess - ex-Googler
2012/11/19 23:20:35
Still not sure I understand where you're going, he
Garrett Casto
2012/11/27 07:59:49
I changed this to just crop the image, which hopef
Scott Hess - ex-Googler
2012/11/28 20:01:05
Sounds like it should.
|
| + return iconFrame; |
| +} |
| + |
| +- (BOOL)eventIsInDecoration:(NSEvent*)theEvent |
| + inView:(PasswordGenerationTextField*)controlView { |
| + NSPoint mouseLocation = [controlView convertPoint:[theEvent locationInWindow] |
| + fromView:nil]; |
| + NSRect cellFrame = [controlView bounds]; |
| + return NSMouseInRect(mouseLocation, |
| + [self getFrameForIcon:&cellFrame], |
| + [controlView isFlipped]); |
| +} |
| + |
| +- (void)mouseEntered:(NSEvent*)theEvent |
| + inView:(PasswordGenerationTextField*)controlView { |
| + hovering_ = YES; |
| + [controlView setNeedsDisplay:YES]; |
| +} |
| + |
| +- (void)mouseExited:(NSEvent*)theEvent |
| + inView:(PasswordGenerationTextField*)controlView { |
| + hovering_ = NO; |
| + [controlView setNeedsDisplay:YES]; |
| +} |
| + |
| +- (BOOL)mouseDown:(NSEvent*)theEvent |
| + inView:(PasswordGenerationTextField*)controlView { |
| + if ([self eventIsInDecoration:theEvent |
| + inView:controlView]) { |
|
Scott Hess - ex-Googler
2012/11/17 01:22:41
This shouldn't need to wrap.
Garrett Casto
2012/11/19 22:17:42
Done.
|
| + [controller_ regeneratePassword]; |
| + return YES; |
| + } |
| + return NO; |
| +} |
| + |
| +- (NSImage*)getImage { |
| + if (hovering_) |
| + return hoverImage_; |
| + return normalImage_; |
| +} |
| + |
| +- (NSRect)adjustFrameForFrame:(NSRect)frame { |
| + // By default, there appears to be a 2 pixel gap between what is considered |
| + // part of the textFrame and what is considered part of the icon. I'm guessing |
| + // it has to do with inclusivity of the end points. |
| + return NSMakeRect(NSMinX(frame), |
| + NSMinY(frame), |
| + NSWidth(frame) + 2, |
| + NSHeight(frame)); |
|
Scott Hess - ex-Googler
2012/11/17 01:22:41
This seems sketchy. Is there something drawing in
Garrett Casto
2012/11/19 22:17:42
Looking at this some more, it looks like this migh
Scott Hess - ex-Googler
2012/11/28 20:01:05
I'm not sure I see the issue you mention clicking
|
| +} |
| + |
| +- (NSRect)textFrameForFrame:(NSRect)cellFrame { |
| + // Baseclass insets the rect by baselineAdjust. |
| + NSRect textFrame = [super textFrameForFrame:cellFrame]; |
| + [self getFrameForIcon:&textFrame]; |
| + return [self adjustFrameForFrame:textFrame]; |
| +} |
| + |
| +- (NSRect)textCursorFrameForFrame:(NSRect)cellFrame { |
| + [self getFrameForIcon:&cellFrame]; |
| + return [self adjustFrameForFrame:cellFrame]; |
| +} |
| + |
| +- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { |
| + NSImage* image = [self getImage]; |
| + NSRect iconFrame = [self getFrameForIcon:&cellFrame]; |
| + // Center the image in the available space |
| + const CGFloat x_inset = |
| + std::floor((NSWidth(iconFrame) - [image size].width) / 2.0); |
| + const CGFloat y_inset = |
| + std::floor((NSHeight(iconFrame) - [image size].height) / 2.0); |
| + NSRect centeredFrame = NSInsetRect(iconFrame, x_inset, y_inset); |
|
Scott Hess - ex-Googler
2012/11/17 01:22:41
This all might be cleaner using NSMidX(), NSMidY()
Garrett Casto
2012/11/19 22:17:42
Personally I find the current implementation sligh
Scott Hess - ex-Googler
2012/11/19 23:20:35
NSImage can draw at a point, instead of in a rect.
|
| + |
| + // Shift the image over to help even out the white space on both sides (since |
| + // there is a border taking up some space on the right). |
| + [image drawInRect:NSOffsetRect(centeredFrame, -kIconOffset, 0) |
| + fromRect:NSZeroRect // Entire image |
| + operation:NSCompositeSourceOver |
| + fraction:1.0 |
| + respectFlipped:YES |
| + hints:nil]; |
| + |
| + [super drawInteriorWithFrame:cellFrame inView:controlView]; |
| +} |
| + |
| +-(void) setUpTrackingAreaInRect:(NSRect)frame |
|
Scott Hess - ex-Googler
2012/11/17 01:22:41
space before (void), not after.
Garrett Casto
2012/11/19 22:17:42
Done.
|
| + ofView:(PasswordGenerationTextField*)view { |
| + NSRect iconFrame = [self getFrameForIcon:&frame]; |
| + scoped_nsobject<CrTrackingArea> area( |
| + [[CrTrackingArea alloc] initWithRect:iconFrame |
| + options:NSTrackingMouseEnteredAndExited | |
| + NSTrackingActiveAlways |
| + owner:view |
| + userInfo:nil]); |
| + [view addTrackingArea:area]; |
| +} |
| + |
| +- (CGFloat)baselineAdjust { |
| + return 1.0; |
| +} |
| + |
| +- (CGFloat)cornerRadius { |
| + return 4.0; |
| +} |
| + |
| +- (BOOL)shouldDrawBezel { |
| + return YES; |
| +} |
| + |
| +@end |
| + |
| +@implementation PasswordGenerationBubbleController |
| + |
| +- (id)initWithBrowser:(Browser*)parentBrowser |
| + anchoredAt:(NSPoint)point |
| + renderViewHost:(content::RenderViewHost*)renderViewHost |
| + passwordManager:(PasswordManager*)passwordManager |
| + usingGenerator:(autofill::PasswordGenerator*)passwordGenerator |
| + forForm:(const content::PasswordForm&)form { |
| + NSRect contentRect = NSMakeRect(0, 0, 260, 71); |
|
Scott Hess - ex-Googler
2012/11/17 01:22:41
That seems pretty unlikely to me.
Garrett Casto
2012/11/19 22:17:42
? An issue with hard coding number?
Scott Hess - ex-Googler
2012/11/19 23:20:35
Yeah. I have no idea what it means. I can see wh
|
| + scoped_nsobject<InfoBubbleWindow> window( |
| + [[InfoBubbleWindow alloc] initWithContentRect:contentRect |
| + styleMask:NSBorderlessWindowMask |
| + backing:NSBackingStoreBuffered |
| + defer:NO]); |
| + if (self = [super initWithWindow:window |
| + parentWindow:parentBrowser->window()->GetNativeWindow() |
| + anchoredAt:point]) { |
| + passwordGenerator_ = passwordGenerator; |
| + renderViewHost_ = renderViewHost; |
| + passwordManager_ = passwordManager; |
| + form_ = form; |
| + [[self bubble] setArrowLocation:info_bubble::kTopLeft]; |
| + } |
| + |
| + [self performLayout]; |
|
Scott Hess - ex-Googler
2012/11/17 01:22:41
Should be in the if() clause.
Garrett Casto
2012/11/19 22:17:42
Done.
|
| + return self; |
| +} |
| + |
| +- (void)performLayout { |
| + NSView* contentView = [[self window] contentView]; |
| + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| + |
| + textField_ = |
| + [[PasswordGenerationTextField alloc] |
| + initWithFrame:NSMakeRect(9, 9, 172, 23) |
| + withController:self |
| + normalImage:rb.GetNativeImageNamed(IDR_RELOAD_DIMMED).ToNSImage() |
| + hoverImage:rb.GetNativeImageNamed(IDR_RELOAD).ToNSImage()]; |
| + [textField_ setFont:[NSFont boldSystemFontOfSize:11]]; |
|
Scott Hess - ex-Googler
2012/11/17 01:22:41
This seems like it should be using some sort of gf
Garrett Casto
2012/11/19 22:17:42
Done.
|
| + [textField_ |
| + setStringValue:base::SysUTF8ToNSString(passwordGenerator_->Generate())]; |
| + [contentView addSubview:textField_]; |
|
Scott Hess - ex-Googler
2012/11/19 23:20:35
Is this field still editable? If so, do you want
Garrett Casto
2012/11/27 07:59:49
I think that I like this better than hooking up th
Scott Hess - ex-Googler
2012/11/28 20:01:05
nack, that's not what I meant. I meant:
[textFie
|
| + |
| + NSButton* button = [[NSButton alloc] |
| + initWithFrame:NSMakeRect(181, 6, 76, 25)]; |
| + [button setBezelStyle:NSRoundedBezelStyle]; |
| + [button setTitle:@"Try it"]; |
| + [button setTarget:self]; |
| + [button setAction:@selector(fillPassword:)]; |
| + [contentView addSubview:button]; |
| + |
| + NSTextField* title = [[NSTextField alloc] |
| + initWithFrame:NSMakeRect(8, 42, 138, 17)]; |
| + [title setEditable:NO]; |
| + [title setBordered:NO]; |
| + [title setStringValue:@"Password Suggestion"]; |
| + [contentView addSubview:title]; |
| +} |
|
Scott Hess - ex-Googler
2012/11/17 01:22:41
Sorry, but all of this hard-coded positioning is j
Garrett Casto
2012/11/19 22:17:42
So what's the right way of doing this then? I want
Scott Hess - ex-Googler
2012/11/19 23:20:35
At minimum, you could be laying things out relativ
Garrett Casto
2012/11/27 07:59:49
Done. I guess the issue I was having is that there
Scott Hess - ex-Googler
2012/11/28 20:01:05
You can punt on the actual localization, but unles
Scott Hess - ex-Googler
2012/11/28 20:01:05
Oh, it's worse than that - when you hard-code the
|
| + |
| +- (IBAction)fillPassword:(id)sender { |
| + renderViewHost_->Send( |
| + new AutofillMsg_GeneratedPasswordAccepted( |
| + renderViewHost_->GetRoutingID(), |
| + base::SysNSStringToUTF16([textField_ stringValue]))); |
| + passwordManager_->SetFormHasGeneratedPassword(form_); |
| + [self close]; |
| +} |
| + |
| +- (void)regeneratePassword { |
| + [textField_ |
| + setStringValue:base::SysUTF8ToNSString(passwordGenerator_->Generate())]; |
| +} |
| + |
| +@end |