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.
5 #import "chrome/browser/ui/cocoa/autofill/autofill_popup_view_cocoa.h"
7 #include "base/logging.h"
8 #include "base/strings/sys_string_conversions.h"
9 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
10 #include "chrome/browser/ui/autofill/popup_constants.h"
11 #include "chrome/browser/ui/cocoa/autofill/autofill_popup_view_bridge.h"
12 #include "components/autofill/core/browser/popup_item_ids.h"
13 #include "components/autofill/core/browser/suggestion.h"
14 #include "ui/base/cocoa/window_size_constants.h"
15 #include "ui/base/resource/resource_bundle.h"
16 #include "ui/gfx/font_list.h"
17 #include "ui/gfx/geometry/point.h"
18 #include "ui/gfx/geometry/rect.h"
19 #include "ui/gfx/image/image.h"
21 using autofill::AutofillPopupView;
23 @interface AutofillPopupViewCocoa ()
26 #pragma mark Private methods
28 // Draws an Autofill suggestion in the given |bounds|, labeled with the given
29 // |name| and |subtext| hint. If the suggestion |isSelected|, then it is drawn
30 // with a highlight. |index| determines the font to use, as well as the icon,
31 // if the row requires it -- such as for credit cards. |imageFirst| indicates
32 // whether the image should be drawn before the name, and with the same
33 // alignment, or whether it should be drawn afterwards, with the opposite
35 - (void)drawSuggestionWithName:(NSString*)name
36 subtext:(NSString*)subtext
39 selected:(BOOL)isSelected
40 imageFirst:(BOOL)imageFirst
41 textYOffset:(CGFloat)textYOffset;
43 // This comment block applies to all three draw* methods that follow.
44 // If |rightAlign| == YES.
45 // Draws the widget with right border aligned to |x|.
46 // Returns the x value of left border of the widget.
47 // If |rightAlign| == NO.
48 // Draws the widget with left border aligned to |x|.
49 // Returns the x value of right border of the widget.
50 - (CGFloat)drawName:(NSString*)name
53 rightAlign:(BOOL)rightAlign
55 textYOffset:(CGFloat)textYOffset;
56 - (CGFloat)drawIconAtIndex:(size_t)index
58 rightAlign:(BOOL)rightAlign
59 bounds:(NSRect)bounds;
60 - (CGFloat)drawSubtext:(NSString*)subtext
62 rightAlign:(BOOL)rightAlign
64 textYOffset:(CGFloat)textYOffset;
66 // Returns the icon for the row with the given |index|, or |nil| if there is
68 - (NSImage*)iconAtIndex:(size_t)index;
72 @implementation AutofillPopupViewCocoa
75 #pragma mark Initialisers
77 - (id)initWithFrame:(NSRect)frame {
79 return [self initWithController:NULL frame:frame];
82 - (id)initWithController:(autofill::AutofillPopupController*)controller
84 self = [super initWithDelegate:controller frame:frame];
86 controller_ = controller;
92 #pragma mark NSView implementation:
94 - (void)drawRect:(NSRect)dirtyRect {
95 // If the view is in the process of being destroyed, don't bother drawing.
99 [self drawBackgroundAndBorder];
101 for (size_t i = 0; i < controller_->GetLineCount(); ++i) {
102 // Skip rows outside of the dirty rect.
104 NSRectFromCGRect(controller_->GetRowBounds(i).ToCGRect());
105 if (!NSIntersectsRect(rowBounds, dirtyRect))
107 const autofill::Suggestion& suggestion = controller_->GetSuggestionAt(i);
109 if (suggestion.frontend_id == autofill::POPUP_ITEM_ID_SEPARATOR) {
110 [self drawSeparatorWithBounds:rowBounds];
114 // Additional offset applied to the text in the vertical direction.
115 CGFloat textYOffset = 0;
116 BOOL imageFirst = NO;
117 if (suggestion.frontend_id == autofill::POPUP_ITEM_ID_MAC_ACCESS_CONTACTS) {
118 // Due to the weighting of the asset used for this autofill entry, the
119 // text needs to be bumped up by 1 pt to make it look vertically aligned.
124 NSString* value = SysUTF16ToNSString(controller_->GetElidedValueAt(i));
125 NSString* label = SysUTF16ToNSString(controller_->GetElidedLabelAt(i));
126 BOOL isSelected = static_cast<int>(i) == controller_->selected_line();
127 [self drawSuggestionWithName:value
132 imageFirst:imageFirst
133 textYOffset:textYOffset];
138 #pragma mark Public API:
140 - (void)controllerDestroyed {
141 // Since the |controller_| either already has been destroyed or is about to
142 // be, about the only thing we can safely do with it is to null it out.
144 [super delegateDestroyed];
147 - (void)invalidateRow:(size_t)row {
149 NSRectFromCGRect(controller_->GetRowBounds(row).ToCGRect());
150 [self setNeedsDisplayInRect:dirty_rect];
154 #pragma mark Private API:
156 - (void)drawSuggestionWithName:(NSString*)name
157 subtext:(NSString*)subtext
159 bounds:(NSRect)bounds
160 selected:(BOOL)isSelected
161 imageFirst:(BOOL)imageFirst
162 textYOffset:(CGFloat)textYOffset {
163 // If this row is selected, highlight it.
165 [[self highlightColor] set];
166 [NSBezierPath fillRect:bounds];
169 BOOL isRTL = controller_->IsRTL();
171 // The X values of the left and right borders of the autofill widget.
172 CGFloat leftX = NSMinX(bounds) + AutofillPopupView::kEndPadding;
173 CGFloat rightX = NSMaxX(bounds) - AutofillPopupView::kEndPadding;
175 // Draw left side if isRTL == NO, right side if isRTL == YES.
176 CGFloat x = isRTL ? rightX : leftX;
178 x = [self drawIconAtIndex:index atX:x rightAlign:isRTL bounds:bounds];
184 textYOffset:textYOffset];
186 // Draw right side if isRTL == NO, left side if isRTL == YES.
187 x = isRTL ? leftX : rightX;
189 x = [self drawIconAtIndex:index atX:x rightAlign:!isRTL bounds:bounds];
190 [self drawSubtext:subtext
194 textYOffset:textYOffset];
197 - (CGFloat)drawName:(NSString*)name
200 rightAlign:(BOOL)rightAlign
201 bounds:(NSRect)bounds
202 textYOffset:(CGFloat)textYOffset {
204 controller_->IsWarning(index) ? [self warningColor] : [self nameColor];
205 NSDictionary* nameAttributes =
206 [NSDictionary dictionaryWithObjectsAndKeys:
207 controller_->GetValueFontListForRow(index).GetPrimaryFont().
209 NSFontAttributeName, nameColor, NSForegroundColorAttributeName,
211 NSSize nameSize = [name sizeWithAttributes:nameAttributes];
212 x -= rightAlign ? nameSize.width : 0;
213 CGFloat y = bounds.origin.y + (bounds.size.height - nameSize.height) / 2;
216 [name drawAtPoint:NSMakePoint(x, y) withAttributes:nameAttributes];
218 x += rightAlign ? 0 : nameSize.width;
222 - (CGFloat)drawIconAtIndex:(size_t)index
224 rightAlign:(BOOL)rightAlign
225 bounds:(NSRect)bounds {
226 NSImage* icon = [self iconAtIndex:index];
229 NSSize iconSize = [icon size];
230 x -= rightAlign ? iconSize.width : 0;
231 CGFloat y = bounds.origin.y + (bounds.size.height - iconSize.height) / 2;
232 [icon drawInRect:NSMakeRect(x, y, iconSize.width, iconSize.height)
234 operation:NSCompositeSourceOver
239 x += rightAlign ? -AutofillPopupView::kIconPadding
240 : iconSize.width + AutofillPopupView::kIconPadding;
244 - (CGFloat)drawSubtext:(NSString*)subtext
246 rightAlign:(BOOL)rightAlign
247 bounds:(NSRect)bounds
248 textYOffset:(CGFloat)textYOffset {
249 NSDictionary* subtextAttributes =
250 [NSDictionary dictionaryWithObjectsAndKeys:
251 controller_->GetLabelFontList().GetPrimaryFont().GetNativeFont(),
254 NSForegroundColorAttributeName,
256 NSSize subtextSize = [subtext sizeWithAttributes:subtextAttributes];
257 x -= rightAlign ? subtextSize.width : 0;
258 CGFloat y = bounds.origin.y + (bounds.size.height - subtextSize.height) / 2;
261 [subtext drawAtPoint:NSMakePoint(x, y) withAttributes:subtextAttributes];
262 x += rightAlign ? 0 : subtextSize.width;
266 - (NSImage*)iconAtIndex:(size_t)index {
267 const base::string16& icon = controller_->GetSuggestionAt(index).icon;
271 int iconId = controller_->GetIconResourceID(icon);
272 DCHECK_NE(-1, iconId);
274 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
275 return rb.GetNativeImageNamed(iconId).ToNSImage();