| OLD | NEW |
| (Empty) | |
| 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. |
| 4 |
| 5 #import "chrome/browser/ui/cocoa/autofill/autofill_popup_view_cocoa.h" |
| 6 |
| 7 #include "base/i18n/rtl.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/sys_string_conversions.h" |
| 10 #include "chrome/browser/ui/autofill/autofill_popup_controller.h" |
| 11 #include "chrome/browser/ui/cocoa/autofill/autofill_popup_view_bridge.h" |
| 12 #include "grit/ui_resources.h" |
| 13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h" |
| 14 #include "ui/base/resource/resource_bundle.h" |
| 15 #include "ui/gfx/image/image.h" |
| 16 #include "ui/gfx/point.h" |
| 17 #include "ui/gfx/rect.h" |
| 18 |
| 19 namespace { |
| 20 |
| 21 NSColor* BorderColor() { |
| 22 // TODO(isherman): This color does not match the other platforms, but it |
| 23 // matches what the existing UI on Mac has as the color. The other platforms |
| 24 // have a strange color: the RGB values are almost, but not quite, identical |
| 25 // to each other. |
| 26 // TODO(isherman): Maybe use a system color for this? |
| 27 return [NSColor colorWithCalibratedRed:127 / 255.0 |
| 28 green:157 / 255.0 |
| 29 blue:185 / 255.0 |
| 30 alpha:1.0]; |
| 31 } |
| 32 |
| 33 NSColor* SeparatorColor() { |
| 34 return [NSColor colorWithCalibratedWhite:220 / 255.0 alpha:1]; |
| 35 } |
| 36 |
| 37 NSColor* HighlightColor() { |
| 38 return [NSColor colorWithCalibratedWhite:205 / 255.0 alpha:1]; |
| 39 } |
| 40 |
| 41 } // anonymous namespace |
| 42 |
| 43 #pragma mark - |
| 44 #pragma mark Private methods |
| 45 |
| 46 @interface AutofillPopupViewCocoa () |
| 47 |
| 48 // Draws a thin separator in the popup UI. |
| 49 - (void)drawSeparatorWithBounds:(NSRect)bounds; |
| 50 |
| 51 // Draws an Autofill suggestion in the given |bounds|, labeled with the given |
| 52 // |name| and |subtext| hint. If the suggestion |isSelected|, then it is drawn |
| 53 // with a highlight. Some suggestions -- such as for credit cards -- might also |
| 54 // include an |icon| -- e.g. for the card type. Finally, if |canDelete| is |
| 55 // true, a delete icon is also drawn. |
| 56 - (void)drawSuggestionWithName:(NSString*)name |
| 57 subtext:(NSString*)subtext |
| 58 icon:(NSImage*)icon |
| 59 bounds:(NSRect)bounds |
| 60 selected:(BOOL)isSelected |
| 61 canDelete:(BOOL)canDelete; |
| 62 |
| 63 // Returns the icon for the row with the given |index|, or |nil| if there is |
| 64 // none. |
| 65 - (NSImage*)iconAtIndex:(size_t)index; |
| 66 |
| 67 @end |
| 68 |
| 69 @implementation AutofillPopupViewCocoa |
| 70 |
| 71 #pragma mark - |
| 72 #pragma mark Initialisers |
| 73 |
| 74 - (id)initWithFrame:(NSRect)frame { |
| 75 NOTREACHED(); |
| 76 return [self initWithController:NULL frame:frame]; |
| 77 } |
| 78 |
| 79 - (id)initWithController:(AutofillPopupController*)controller |
| 80 frame:(NSRect)frame { |
| 81 self = [super initWithFrame:frame]; |
| 82 if (self) |
| 83 controller_ = controller; |
| 84 |
| 85 return self; |
| 86 } |
| 87 |
| 88 #pragma mark - |
| 89 #pragma mark NSView implementation: |
| 90 |
| 91 // A slight optimization for drawing: |
| 92 // https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Cocoa
ViewsGuide/Optimizing/Optimizing.html |
| 93 - (BOOL)isOpaque { |
| 94 return YES; |
| 95 } |
| 96 |
| 97 - (BOOL)isFlipped { |
| 98 // Flipped so that it's easier to share controller logic with other OSes. |
| 99 return YES; |
| 100 } |
| 101 |
| 102 - (void)drawRect:(NSRect)dirtyRect { |
| 103 // If the view is in the process of being destroyed, don't bother drawing. |
| 104 if (!controller_) |
| 105 return; |
| 106 |
| 107 // TODO(isherman): Is there a better way to leave room for the border? |
| 108 // TODO(isherman): Drawing the border as part of the content view means that |
| 109 // the rest of the content has to be careful not to overlap the border. |
| 110 // Should the border be part of the window instead? If not, should the rest |
| 111 // of the view be a subview? Or should I just draw the window content |
| 112 // carefully? |
| 113 // TODO(isherman): We should consider using asset-based drawing for the |
| 114 // border, creating simple bitmaps for the view's border and background, and |
| 115 // drawing them using NSDrawNinePartImage(). |
| 116 NSRect borderRect = NSInsetRect([self bounds], 0.5, 0.5); |
| 117 NSBezierPath* border = [NSBezierPath bezierPathWithRect:borderRect]; |
| 118 |
| 119 [[NSColor whiteColor] set]; |
| 120 [border fill]; |
| 121 |
| 122 // TODO(isherman): This color does not match the other platforms, but it |
| 123 // matches what the existing UI on Mac has as the color. The other platforms |
| 124 // have a strange color: the RGB values are almost, but not quite, identical |
| 125 // to each other. |
| 126 // TODO(isherman): Maybe use a system color for this? |
| 127 [BorderColor() set]; |
| 128 [border stroke]; |
| 129 |
| 130 for (size_t i = 0; i < controller_->names().size(); ++i) { |
| 131 // Skip rows outside of the dirty rect. |
| 132 NSRect rowBounds = |
| 133 NSRectFromCGRect(controller_->GetRowBounds(i).ToCGRect()); |
| 134 if (!NSIntersectsRect(rowBounds, dirtyRect)) |
| 135 continue; |
| 136 |
| 137 if (controller_->identifiers()[i] == |
| 138 WebKit::WebAutofillClient::MenuItemIDSeparator) { |
| 139 [self drawSeparatorWithBounds:rowBounds]; |
| 140 } else { |
| 141 NSString* name = SysUTF16ToNSString(controller_->names()[i]); |
| 142 NSString* subtext = SysUTF16ToNSString(controller_->subtexts()[i]); |
| 143 BOOL isSelected = static_cast<int>(i) == controller_->selected_line(); |
| 144 [self drawSuggestionWithName:name |
| 145 subtext:subtext |
| 146 icon:[self iconAtIndex:i] |
| 147 bounds:rowBounds |
| 148 selected:isSelected |
| 149 canDelete:controller_->CanDelete(i)]; |
| 150 } |
| 151 } |
| 152 } |
| 153 |
| 154 - (void)mouseUp:(NSEvent*)theEvent { |
| 155 // If the view is in the process of being destroyed, abort. |
| 156 if (!controller_) |
| 157 return; |
| 158 |
| 159 NSPoint location = [self convertPoint:[theEvent locationInWindow] |
| 160 fromView:nil]; |
| 161 |
| 162 if (NSPointInRect(location, [self bounds])) { |
| 163 controller_->MouseClicked(static_cast<int>(location.x), |
| 164 static_cast<int>(location.y)); |
| 165 } |
| 166 } |
| 167 |
| 168 - (void)mouseMoved:(NSEvent*)theEvent { |
| 169 // If the view is in the process of being destroyed, abort. |
| 170 if (!controller_) |
| 171 return; |
| 172 |
| 173 NSPoint location = [self convertPoint:[theEvent locationInWindow] |
| 174 fromView:nil]; |
| 175 |
| 176 controller_->MouseHovered(static_cast<int>(location.x), |
| 177 static_cast<int>(location.y)); |
| 178 } |
| 179 |
| 180 - (void)mouseDragged:(NSEvent*)theEvent { |
| 181 [self mouseMoved:theEvent]; |
| 182 } |
| 183 |
| 184 - (void)mouseExited:(NSEvent*)theEvent { |
| 185 // If the view is in the process of being destroyed, abort. |
| 186 if (!controller_) |
| 187 return; |
| 188 |
| 189 controller_->MouseExitedPopup(); |
| 190 } |
| 191 |
| 192 #pragma mark - |
| 193 #pragma mark Public API: |
| 194 |
| 195 - (void)controllerDestroyed { |
| 196 // Since the |controller_| either already has been destroyed or is about to |
| 197 // be, about the only thing we can safely do with it is to null it out. |
| 198 controller_ = NULL; |
| 199 } |
| 200 |
| 201 #pragma mark - |
| 202 #pragma mark Private API: |
| 203 |
| 204 - (void)drawSeparatorWithBounds:(NSRect)bounds { |
| 205 [SeparatorColor() set]; |
| 206 [NSBezierPath fillRect:bounds]; |
| 207 } |
| 208 |
| 209 - (void)drawSuggestionWithName:(NSString*)name |
| 210 subtext:(NSString*)subtext |
| 211 icon:(NSImage*)icon |
| 212 bounds:(NSRect)bounds |
| 213 selected:(BOOL)isSelected |
| 214 canDelete:(BOOL)canDelete { |
| 215 // If this row is selected, highlight it. |
| 216 if (isSelected) { |
| 217 // TODO(isherman): The highlight color should match the system highlight |
| 218 // color. Maybe use controlHighlightColor or selectedTextBackgroundColor |
| 219 // for this? |
| 220 [HighlightColor() set]; |
| 221 [NSBezierPath fillRect:bounds]; |
| 222 } |
| 223 |
| 224 BOOL isRTL = base::i18n::IsRTL(); |
| 225 |
| 226 // TODO(isherman): Set font, colors, and any other appropriate attributes. |
| 227 NSSize nameSize = [name sizeWithAttributes:nil]; |
| 228 CGFloat x = bounds.origin.x + |
| 229 (isRTL ? |
| 230 bounds.size.width - AutofillPopupView::kEndPadding - nameSize.width: |
| 231 AutofillPopupView::kEndPadding); |
| 232 CGFloat y = bounds.origin.y + (bounds.size.height - nameSize.height) / 2; |
| 233 |
| 234 [name drawAtPoint:NSMakePoint(x, y) withAttributes:nil]; |
| 235 |
| 236 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 237 |
| 238 // The x-coordinate will be updated as each element is drawn. |
| 239 x = bounds.origin.x + |
| 240 (isRTL ? |
| 241 AutofillPopupView::kEndPadding : |
| 242 bounds.size.width - AutofillPopupView::kEndPadding); |
| 243 |
| 244 // Draw the delete icon, if one is needed. |
| 245 if (canDelete) { |
| 246 // TODO(isherman): Refactor the cross-platform code so that the delete icon |
| 247 // is a button implemented as a subview, rather than having all its logic be |
| 248 // custom handled by the controller. |
| 249 // TODO(csharp): Create a custom resource for the delete icon. |
| 250 // http://crbug.com/131801 |
| 251 NSImage* deleteIcon; |
| 252 if (isSelected && controller_->delete_icon_hovered()) |
| 253 deleteIcon = rb.GetNativeImageNamed(IDR_CLOSE_BAR_H).ToNSImage(); |
| 254 else |
| 255 deleteIcon = rb.GetNativeImageNamed(IDR_CLOSE_BAR).ToNSImage(); |
| 256 |
| 257 NSSize iconSize = [deleteIcon size]; |
| 258 x += isRTL ? 0 : -iconSize.width; |
| 259 y = bounds.origin.y + (bounds.size.height - iconSize.height) / 2; |
| 260 [deleteIcon drawInRect:NSMakeRect(x, y, iconSize.width, iconSize.height) |
| 261 fromRect:NSZeroRect |
| 262 operation:NSCompositeSourceOver |
| 263 fraction:1.0 |
| 264 respectFlipped:YES |
| 265 hints:nil]; |
| 266 |
| 267 x += isRTL ? |
| 268 iconSize.width + AutofillPopupView::kIconPadding : |
| 269 -AutofillPopupView::kIconPadding; |
| 270 } |
| 271 |
| 272 // Draw the Autofill icon, if one exists. |
| 273 if (icon) { |
| 274 NSSize iconSize = [icon size]; |
| 275 x += isRTL ? 0 : -iconSize.width; |
| 276 y = bounds.origin.y + (bounds.size.height - iconSize.height) / 2; |
| 277 [icon drawInRect:NSMakeRect(x, y, iconSize.width, iconSize.height) |
| 278 fromRect:NSZeroRect |
| 279 operation:NSCompositeSourceOver |
| 280 fraction:1.0 |
| 281 respectFlipped:YES |
| 282 hints:nil]; |
| 283 |
| 284 x += isRTL ? |
| 285 iconSize.width + AutofillPopupView::kIconPadding : |
| 286 -AutofillPopupView::kIconPadding; |
| 287 } |
| 288 |
| 289 // Draw the subtext. |
| 290 NSSize subtextSize = [subtext sizeWithAttributes:nil]; |
| 291 x += isRTL ? 0 : -subtextSize.width; |
| 292 y = bounds.origin.y + (bounds.size.height - subtextSize.height) / 2; |
| 293 |
| 294 [subtext drawAtPoint:NSMakePoint(x, y) withAttributes:nil]; |
| 295 } |
| 296 |
| 297 - (NSImage*)iconAtIndex:(size_t)index { |
| 298 if (controller_->icons()[index].empty()) |
| 299 return nil; |
| 300 |
| 301 int iconId = controller_->GetIconResourceID(controller_->icons()[index]); |
| 302 DCHECK_NE(-1, iconId); |
| 303 |
| 304 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 305 return rb.GetNativeImageNamed(iconId).ToNSImage(); |
| 306 } |
| 307 |
| 308 @end |
| OLD | NEW |