1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
|
// 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.
#import "chrome/browser/ui/cocoa/autofill/autofill_popup_view_cocoa.h"
#include "base/logging.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/browser/ui/autofill/autofill_popup_controller.h"
#include "chrome/browser/ui/autofill/popup_constants.h"
#include "chrome/browser/ui/cocoa/autofill/autofill_popup_view_bridge.h"
#include "components/autofill/core/browser/popup_item_ids.h"
#include "components/autofill/core/browser/suggestion.h"
#include "ui/base/cocoa/window_size_constants.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/image/image.h"
using autofill::AutofillPopupView;
@interface AutofillPopupViewCocoa ()
#pragma mark -
#pragma mark Private methods
// Draws an Autofill suggestion in the given |bounds|, labeled with the given
// |name| and |subtext| hint. If the suggestion |isSelected|, then it is drawn
// with a highlight. |index| determines the font to use, as well as the icon,
// if the row requires it -- such as for credit cards. |imageFirst| indicates
// whether the image should be drawn before the name, and with the same
// alignment, or whether it should be drawn afterwards, with the opposite
// alignment.
- (void)drawSuggestionWithName:(NSString*)name
subtext:(NSString*)subtext
index:(size_t)index
bounds:(NSRect)bounds
selected:(BOOL)isSelected
imageFirst:(BOOL)imageFirst
textYOffset:(CGFloat)textYOffset;
// This comment block applies to all three draw* methods that follow.
// If |rightAlign| == YES.
// Draws the widget with right border aligned to |x|.
// Returns the x value of left border of the widget.
// If |rightAlign| == NO.
// Draws the widget with left border aligned to |x|.
// Returns the x value of right border of the widget.
- (CGFloat)drawName:(NSString*)name
atX:(CGFloat)x
index:(size_t)index
rightAlign:(BOOL)rightAlign
bounds:(NSRect)bounds
textYOffset:(CGFloat)textYOffset;
- (CGFloat)drawIconAtIndex:(size_t)index
atX:(CGFloat)x
rightAlign:(BOOL)rightAlign
bounds:(NSRect)bounds;
- (CGFloat)drawSubtext:(NSString*)subtext
atX:(CGFloat)x
rightAlign:(BOOL)rightAlign
bounds:(NSRect)bounds
textYOffset:(CGFloat)textYOffset;
// Returns the icon for the row with the given |index|, or |nil| if there is
// none.
- (NSImage*)iconAtIndex:(size_t)index;
@end
@implementation AutofillPopupViewCocoa
#pragma mark -
#pragma mark Initialisers
- (id)initWithFrame:(NSRect)frame {
NOTREACHED();
return [self initWithController:NULL frame:frame];
}
- (id)initWithController:(autofill::AutofillPopupController*)controller
frame:(NSRect)frame {
self = [super initWithDelegate:controller frame:frame];
if (self)
controller_ = controller;
return self;
}
#pragma mark -
#pragma mark NSView implementation:
- (void)drawRect:(NSRect)dirtyRect {
// If the view is in the process of being destroyed, don't bother drawing.
if (!controller_)
return;
[self drawBackgroundAndBorder];
for (size_t i = 0; i < controller_->GetLineCount(); ++i) {
// Skip rows outside of the dirty rect.
NSRect rowBounds =
NSRectFromCGRect(controller_->GetRowBounds(i).ToCGRect());
if (!NSIntersectsRect(rowBounds, dirtyRect))
continue;
const autofill::Suggestion& suggestion = controller_->GetSuggestionAt(i);
if (suggestion.frontend_id == autofill::POPUP_ITEM_ID_SEPARATOR) {
[self drawSeparatorWithBounds:rowBounds];
continue;
}
// Additional offset applied to the text in the vertical direction.
CGFloat textYOffset = 0;
BOOL imageFirst = NO;
if (suggestion.frontend_id == autofill::POPUP_ITEM_ID_MAC_ACCESS_CONTACTS) {
// Due to the weighting of the asset used for this autofill entry, the
// text needs to be bumped up by 1 pt to make it look vertically aligned.
textYOffset = -1;
imageFirst = YES;
}
NSString* value = SysUTF16ToNSString(controller_->GetElidedValueAt(i));
NSString* label = SysUTF16ToNSString(controller_->GetElidedLabelAt(i));
BOOL isSelected = static_cast<int>(i) == controller_->selected_line();
[self drawSuggestionWithName:value
subtext:label
index:i
bounds:rowBounds
selected:isSelected
imageFirst:imageFirst
textYOffset:textYOffset];
}
}
#pragma mark -
#pragma mark Public API:
- (void)controllerDestroyed {
// Since the |controller_| either already has been destroyed or is about to
// be, about the only thing we can safely do with it is to null it out.
controller_ = NULL;
[super delegateDestroyed];
}
- (void)invalidateRow:(size_t)row {
NSRect dirty_rect =
NSRectFromCGRect(controller_->GetRowBounds(row).ToCGRect());
[self setNeedsDisplayInRect:dirty_rect];
}
#pragma mark -
#pragma mark Private API:
- (void)drawSuggestionWithName:(NSString*)name
subtext:(NSString*)subtext
index:(size_t)index
bounds:(NSRect)bounds
selected:(BOOL)isSelected
imageFirst:(BOOL)imageFirst
textYOffset:(CGFloat)textYOffset {
// If this row is selected, highlight it.
if (isSelected) {
[[self highlightColor] set];
[NSBezierPath fillRect:bounds];
}
BOOL isRTL = controller_->IsRTL();
// The X values of the left and right borders of the autofill widget.
CGFloat leftX = NSMinX(bounds) + AutofillPopupView::kEndPadding;
CGFloat rightX = NSMaxX(bounds) - AutofillPopupView::kEndPadding;
// Draw left side if isRTL == NO, right side if isRTL == YES.
CGFloat x = isRTL ? rightX : leftX;
if (imageFirst)
x = [self drawIconAtIndex:index atX:x rightAlign:isRTL bounds:bounds];
[self drawName:name
atX:x
index:index
rightAlign:isRTL
bounds:bounds
textYOffset:textYOffset];
// Draw right side if isRTL == NO, left side if isRTL == YES.
x = isRTL ? leftX : rightX;
if (!imageFirst)
x = [self drawIconAtIndex:index atX:x rightAlign:!isRTL bounds:bounds];
[self drawSubtext:subtext
atX:x
rightAlign:!isRTL
bounds:bounds
textYOffset:textYOffset];
}
- (CGFloat)drawName:(NSString*)name
atX:(CGFloat)x
index:(size_t)index
rightAlign:(BOOL)rightAlign
bounds:(NSRect)bounds
textYOffset:(CGFloat)textYOffset {
NSColor* nameColor =
controller_->IsWarning(index) ? [self warningColor] : [self nameColor];
NSDictionary* nameAttributes =
[NSDictionary dictionaryWithObjectsAndKeys:
controller_->GetValueFontListForRow(index).GetPrimaryFont().
GetNativeFont(),
NSFontAttributeName, nameColor, NSForegroundColorAttributeName,
nil];
NSSize nameSize = [name sizeWithAttributes:nameAttributes];
x -= rightAlign ? nameSize.width : 0;
CGFloat y = bounds.origin.y + (bounds.size.height - nameSize.height) / 2;
y += textYOffset;
[name drawAtPoint:NSMakePoint(x, y) withAttributes:nameAttributes];
x += rightAlign ? 0 : nameSize.width;
return x;
}
- (CGFloat)drawIconAtIndex:(size_t)index
atX:(CGFloat)x
rightAlign:(BOOL)rightAlign
bounds:(NSRect)bounds {
NSImage* icon = [self iconAtIndex:index];
if (!icon)
return x;
NSSize iconSize = [icon size];
x -= rightAlign ? iconSize.width : 0;
CGFloat y = bounds.origin.y + (bounds.size.height - iconSize.height) / 2;
[icon drawInRect:NSMakeRect(x, y, iconSize.width, iconSize.height)
fromRect:NSZeroRect
operation:NSCompositeSourceOver
fraction:1.0
respectFlipped:YES
hints:nil];
x += rightAlign ? -AutofillPopupView::kIconPadding
: iconSize.width + AutofillPopupView::kIconPadding;
return x;
}
- (CGFloat)drawSubtext:(NSString*)subtext
atX:(CGFloat)x
rightAlign:(BOOL)rightAlign
bounds:(NSRect)bounds
textYOffset:(CGFloat)textYOffset {
NSDictionary* subtextAttributes =
[NSDictionary dictionaryWithObjectsAndKeys:
controller_->GetLabelFontList().GetPrimaryFont().GetNativeFont(),
NSFontAttributeName,
[self subtextColor],
NSForegroundColorAttributeName,
nil];
NSSize subtextSize = [subtext sizeWithAttributes:subtextAttributes];
x -= rightAlign ? subtextSize.width : 0;
CGFloat y = bounds.origin.y + (bounds.size.height - subtextSize.height) / 2;
y += textYOffset;
[subtext drawAtPoint:NSMakePoint(x, y) withAttributes:subtextAttributes];
x += rightAlign ? 0 : subtextSize.width;
return x;
}
- (NSImage*)iconAtIndex:(size_t)index {
const base::string16& icon = controller_->GetSuggestionAt(index).icon;
if (icon.empty())
return nil;
int iconId = controller_->GetIconResourceID(icon);
DCHECK_NE(-1, iconId);
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
return rb.GetNativeImageNamed(iconId).ToNSImage();
}
@end
|