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 |