1 // Copyright (c) 2013 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.
5 #import "chrome/browser/ui/cocoa/autofill/autofill_textfield.h"
10 #include "base/logging.h"
11 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
15 const CGFloat kGap = 6.0; // gap between icon and text.
16 const CGFloat kMinimumHeight = 27.0; // Enforced minimum height for text cells.
20 @interface AutofillTextFieldCell (Internal)
22 - (NSRect)textFrameForFrame:(NSRect)frame;
26 @implementation AutofillTextField
28 @synthesize inputDelegate = inputDelegate_;
31 return [AutofillTextFieldCell class];
34 - (id)initWithFrame:(NSRect)frame {
35 if (self = [super initWithFrame:frame])
36 [super setDelegate:self];
40 - (BOOL)becomeFirstResponder {
41 BOOL result = [super becomeFirstResponder];
42 if (result && inputDelegate_) {
43 [inputDelegate_ fieldBecameFirstResponder:self];
44 shouldFilterClick_ = YES;
49 - (void)onEditorMouseDown:(id)sender {
50 // Since the dialog does not care about clicks that gave firstResponder
51 // status, swallow those.
52 if (!handlingFirstClick_)
53 [inputDelegate_ onMouseDown: self];
56 - (NSRect)decorationFrame {
57 return [[self cell] decorationFrameForFrame:[self frame]];
60 - (void)mouseDown:(NSEvent*)theEvent {
61 // mouseDown: is only invoked for a click that actually gave firstResponder
62 // status to the NSTextField, and clicks to the border area. Further clicks
63 // into the content are are handled by the field editor instead.
64 handlingFirstClick_ = shouldFilterClick_;
65 [super mouseDown:theEvent];
66 handlingFirstClick_ = NO;
67 shouldFilterClick_ = NO;
70 - (void)controlTextDidEndEditing:(NSNotification*)notification {
72 [inputDelegate_ didEndEditing:self];
75 - (void)controlTextDidChange:(NSNotification*)aNotification {
77 [inputDelegate_ didChange:self];
80 - (NSString*)fieldValue {
81 return [[self cell] fieldValue];
84 - (void)setFieldValue:(NSString*)fieldValue {
85 [[self cell] setFieldValue:fieldValue];
88 - (NSString*)defaultValue {
89 return [[self cell] defaultValue];
92 - (void)setDefaultValue:(NSString*)defaultValue {
93 [[self cell] setDefaultValue:defaultValue];
97 return [[[self cell] fieldValue] isEqualToString:[[self cell] defaultValue]];
100 - (NSString*)validityMessage {
101 return validityMessage_;
104 - (void)setValidityMessage:(NSString*)validityMessage {
105 validityMessage_.reset([validityMessage copy]);
106 [[self cell] setInvalid:[self invalid]];
110 return [validityMessage_ length] != 0;
116 @implementation AutofillTextFieldCell
118 @synthesize invalid = invalid_;
119 @synthesize defaultValue = defaultValue_;
120 @synthesize decorationSize = decorationSize_;
122 - (void)setInvalid:(BOOL)invalid {
124 [[self controlView] setNeedsDisplay:YES];
131 - (void)setIcon:(NSImage*)icon {
132 icon_.reset([icon retain]);
133 [self setDecorationSize:[icon_ size]];
134 [[self controlView] setNeedsDisplay:YES];
137 - (NSString*)fieldValue {
138 return [self stringValue];
141 - (void)setFieldValue:(NSString*)fieldValue {
142 [self setStringValue:fieldValue];
145 - (NSRect)textFrameForFrame:(NSRect)frame {
146 // Ensure text height is original cell height, and the text frame is centered
147 // vertically in the cell frame.
148 NSSize originalSize = [super cellSize];
149 if (originalSize.height < NSHeight(frame)) {
150 CGFloat delta = NSHeight(frame) - originalSize.height;
151 frame.origin.y += std::floor(delta / 2.0);
152 frame.size.height -= delta;
154 DCHECK_EQ(originalSize.height, NSHeight(frame));
156 if (decorationSize_.width > 0) {
157 NSRect textFrame, decorationFrame;
158 NSDivideRect(frame, &decorationFrame, &textFrame,
159 kGap + decorationSize_.width, NSMaxXEdge);
165 - (NSRect)decorationFrameForFrame:(NSRect)frame {
166 NSRect decorationFrame;
167 if (decorationSize_.width > 0) {
169 NSDivideRect(frame, &decorationFrame, &textFrame,
170 kGap + decorationSize_.width, NSMaxXEdge);
171 decorationFrame.size = decorationSize_;
172 decorationFrame.origin.y +=
173 roundf((NSHeight(frame) - NSHeight(decorationFrame)) / 2.0);
175 return decorationFrame;
179 NSSize cellSize = [super cellSize];
181 if (decorationSize_.width > 0) {
182 cellSize.width += kGap + decorationSize_.width;
183 cellSize.height = std::max(cellSize.height, decorationSize_.height);
185 cellSize.height = std::max(cellSize.height, kMinimumHeight);
189 - (void)editWithFrame:(NSRect)cellFrame
190 inView:(NSView *)controlView
191 editor:(NSText *)editor
192 delegate:(id)delegate
193 event:(NSEvent *)event {
194 [super editWithFrame:[self textFrameForFrame:cellFrame]
201 - (void)selectWithFrame:(NSRect)cellFrame
202 inView:(NSView *)controlView
203 editor:(NSText *)editor
204 delegate:(id)delegate
205 start:(NSInteger)start
206 length:(NSInteger)length {
207 [super selectWithFrame:[self textFrameForFrame:cellFrame]
215 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
216 NSRect textFrame = [self textFrameForFrame:cellFrame];
217 [super drawInteriorWithFrame:textFrame inView:controlView];
220 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
221 [super drawWithFrame:cellFrame inView:controlView];
224 NSRect iconFrame = [self decorationFrameForFrame:cellFrame];
225 [icon_ drawInRect:iconFrame
227 operation:NSCompositeSourceOver
234 gfx::ScopedNSGraphicsContextSaveGState state;
236 // Render red border for invalid fields.
237 [[NSColor colorWithDeviceRed:1.0 green:0.0 blue:0.0 alpha:1.0] setStroke];
238 [[NSBezierPath bezierPathWithRect:NSInsetRect(cellFrame, 0.5, 0.5)] stroke];