| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "chrome/browser/cocoa/location_bar/autocomplete_text_field_cell.h" | 5 #import "chrome/browser/cocoa/location_bar/autocomplete_text_field_cell.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #import "chrome/browser/cocoa/image_utils.h" | 8 #import "chrome/browser/cocoa/image_utils.h" |
| 9 #import "chrome/browser/cocoa/location_bar/autocomplete_text_field.h" |
| 9 #import "chrome/browser/cocoa/location_bar/location_bar_decoration.h" | 10 #import "chrome/browser/cocoa/location_bar/location_bar_decoration.h" |
| 10 | 11 |
| 11 @interface AutocompleteTextAttachmentCell : NSTextAttachmentCell { | |
| 12 } | |
| 13 | |
| 14 // TODO(shess): | |
| 15 // Override -cellBaselineOffset to allow the image to be shifted up or | |
| 16 // down relative to the containing text's baseline. | |
| 17 | |
| 18 // Draw the image using |DrawImageInRect()| helper function for | |
| 19 // flipped consistency with other image drawing. | |
| 20 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)aView; | |
| 21 | |
| 22 @end | |
| 23 | |
| 24 namespace { | 12 namespace { |
| 25 | 13 |
| 26 const CGFloat kBaselineAdjust = 2.0; | 14 const CGFloat kBaselineAdjust = 2.0; |
| 27 | 15 |
| 28 // Matches the clipping radius of |GradientButtonCell|. | 16 // Matches the clipping radius of |GradientButtonCell|. |
| 29 const CGFloat kCornerRadius = 4.0; | 17 const CGFloat kCornerRadius = 4.0; |
| 30 | 18 |
| 31 // How far to shift bounding box of hint down from top of field. | |
| 32 // Assumes -setFlipped:YES. | |
| 33 const NSInteger kHintYOffset = 4; | |
| 34 | |
| 35 // TODO(shess): The keyword hint image wants to sit on the baseline. | |
| 36 // This moves it down so that there is approximately as much image | |
| 37 // above the lowercase ascender as below the baseline. A better | |
| 38 // technique would be nice to have, though. | |
| 39 const NSInteger kKeywordHintImageBaseline = -6; | |
| 40 | |
| 41 // How far to shift bounding box of hint icon label down from top of field. | |
| 42 const NSInteger kIconLabelYOffset = 7; | |
| 43 | |
| 44 // How far the editor insets itself, for purposes of determining if | |
| 45 // decorations need to be trimmed. | |
| 46 const CGFloat kEditorHorizontalInset = 3.0; | |
| 47 | |
| 48 // How far to inset the left-hand decorations from the field's bounds. | 19 // How far to inset the left-hand decorations from the field's bounds. |
| 49 const CGFloat kLeftDecorationXOffset = 3.0; | 20 const CGFloat kLeftDecorationXOffset = 3.0; |
| 50 | 21 |
| 51 // How far to inset the right-hand decorations from the field's bounds. | 22 // How far to inset the right-hand decorations from the field's bounds. |
| 52 // TODO(shess): Why is this different from |kLeftDecorationXOffset|? | 23 // TODO(shess): Why is this different from |kLeftDecorationXOffset|? |
| 53 const CGFloat kRightDecorationXOffset = 4.0; | 24 const CGFloat kRightDecorationXOffset = 4.0; |
| 54 | 25 |
| 55 // The amount of padding on either side reserved for drawing | 26 // The amount of padding on either side reserved for drawing |
| 56 // decorations. [Views has |kItemPadding| == 3.] | 27 // decorations. [Views has |kItemPadding| == 3.] |
| 57 const CGFloat kDecorationHorizontalPad = 3.0; | 28 const CGFloat kDecorationHorizontalPad = 3.0; |
| 58 | 29 |
| 59 // How long to wait for mouse-up on the location icon before assuming | 30 // How long to wait for mouse-up on the location icon before assuming |
| 60 // that the user wants to drag. | 31 // that the user wants to drag. |
| 61 const NSTimeInterval kLocationIconDragTimeout = 0.25; | 32 const NSTimeInterval kLocationIconDragTimeout = 0.25; |
| 62 | 33 |
| 63 // Conveniences to centralize width+offset calculations. | |
| 64 CGFloat WidthForHint(NSAttributedString* hintString) { | |
| 65 return kRightDecorationXOffset + ceil([hintString size].width); | |
| 66 } | |
| 67 | |
| 68 // Convenience to draw |image| in the |rect| portion of |view|. | |
| 69 void DrawImageInRect(NSImage* image, NSView* view, const NSRect& rect) { | |
| 70 // If there is an image, make sure we calculated the target size | |
| 71 // correctly. | |
| 72 DCHECK(!image || NSEqualSizes([image size], rect.size)); | |
| 73 [image drawInRect:rect | |
| 74 fromRect:NSZeroRect // Entire image | |
| 75 operation:NSCompositeSourceOver | |
| 76 fraction:1.0 | |
| 77 neverFlipped:YES]; | |
| 78 } | |
| 79 | |
| 80 // Helper function to generate an attributed string containing | |
| 81 // |anImage|. If |baselineAdjustment| is 0, the image sits on the | |
| 82 // text baseline, positive values shift it up, negative values shift | |
| 83 // it down. | |
| 84 NSAttributedString* AttributedStringForImage(NSImage* anImage, | |
| 85 CGFloat baselineAdjustment) { | |
| 86 scoped_nsobject<AutocompleteTextAttachmentCell> attachmentCell( | |
| 87 [[AutocompleteTextAttachmentCell alloc] initImageCell:anImage]); | |
| 88 scoped_nsobject<NSTextAttachment> attachment( | |
| 89 [[NSTextAttachment alloc] init]); | |
| 90 [attachment setAttachmentCell:attachmentCell]; | |
| 91 | |
| 92 scoped_nsobject<NSMutableAttributedString> as( | |
| 93 [[NSAttributedString attributedStringWithAttachment:attachment] | |
| 94 mutableCopy]); | |
| 95 [as addAttribute:NSBaselineOffsetAttributeName | |
| 96 value:[NSNumber numberWithFloat:baselineAdjustment] | |
| 97 range:NSMakeRange(0, [as length])]; | |
| 98 | |
| 99 return [[as copy] autorelease]; | |
| 100 } | |
| 101 | |
| 102 // Calculate the positions for a set of decorations. |frame| is the | 34 // Calculate the positions for a set of decorations. |frame| is the |
| 103 // overall frame to do layout in, |remaining_frame| will get the | 35 // overall frame to do layout in, |remaining_frame| will get the |
| 104 // left-over space. |all_decorations| is the set of decorations to | 36 // left-over space. |all_decorations| is the set of decorations to |
| 105 // lay out, |decorations| will be set to the decorations which are | 37 // lay out, |decorations| will be set to the decorations which are |
| 106 // visible and which fit, in the same order as |all_decorations|, | 38 // visible and which fit, in the same order as |all_decorations|, |
| 107 // while |decoration_frames| will be the corresponding frames. | 39 // while |decoration_frames| will be the corresponding frames. |
| 108 // |x_edge| describes the edge to layout the decorations against | 40 // |x_edge| describes the edge to layout the decorations against |
| 109 // (|NSMinXEdge| or |NSMaxXEdge|). |initial_padding| is the padding | 41 // (|NSMinXEdge| or |NSMaxXEdge|). |initial_padding| is the padding |
| 110 // from the edge of |cell_frame| (|kDecorationHorizontalPad| is used | 42 // from the edge of |cell_frame| (|kDecorationHorizontalPad| is used |
| 111 // between decorations). | 43 // between decorations). |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 199 std::reverse(decorations->begin() + left_count, decorations->end()); | 131 std::reverse(decorations->begin() + left_count, decorations->end()); |
| 200 std::reverse(decoration_frames->begin() + left_count, | 132 std::reverse(decoration_frames->begin() + left_count, |
| 201 decoration_frames->end()); | 133 decoration_frames->end()); |
| 202 | 134 |
| 203 *remaining_frame = frame; | 135 *remaining_frame = frame; |
| 204 return left_count; | 136 return left_count; |
| 205 } | 137 } |
| 206 | 138 |
| 207 } // namespace | 139 } // namespace |
| 208 | 140 |
| 209 @implementation AutocompleteTextAttachmentCell | |
| 210 | |
| 211 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)aView { | |
| 212 // Draw image with |DrawImageInRect()| to get consistent | |
| 213 // flipped treatment. | |
| 214 DrawImageInRect([self image], aView, cellFrame); | |
| 215 } | |
| 216 | |
| 217 @end | |
| 218 | |
| 219 @implementation AutocompleteTextFieldCell | 141 @implementation AutocompleteTextFieldCell |
| 220 | 142 |
| 221 // @synthesize doesn't seem to compile for this transition. | |
| 222 - (NSAttributedString*)hintString { | |
| 223 return hintString_.get(); | |
| 224 } | |
| 225 | |
| 226 - (CGFloat)baselineAdjust { | 143 - (CGFloat)baselineAdjust { |
| 227 return kBaselineAdjust; | 144 return kBaselineAdjust; |
| 228 } | 145 } |
| 229 | 146 |
| 230 - (CGFloat)cornerRadius { | 147 - (CGFloat)cornerRadius { |
| 231 return kCornerRadius; | 148 return kCornerRadius; |
| 232 } | 149 } |
| 233 | 150 |
| 234 // Convenience for the attributes used in the right-justified info | |
| 235 // cells. | |
| 236 - (NSDictionary*)hintAttributes { | |
| 237 scoped_nsobject<NSMutableParagraphStyle> style( | |
| 238 [[NSMutableParagraphStyle alloc] init]); | |
| 239 [style setAlignment:NSRightTextAlignment]; | |
| 240 | |
| 241 return [NSDictionary dictionaryWithObjectsAndKeys: | |
| 242 [self font], NSFontAttributeName, | |
| 243 [NSColor lightGrayColor], NSForegroundColorAttributeName, | |
| 244 style.get(), NSParagraphStyleAttributeName, | |
| 245 nil]; | |
| 246 } | |
| 247 | |
| 248 - (void)setKeywordHintPrefix:(NSString*)prefixString | |
| 249 image:(NSImage*)anImage | |
| 250 suffix:(NSString*)suffixString | |
| 251 availableWidth:(CGFloat)width { | |
| 252 DCHECK(prefixString != nil); | |
| 253 DCHECK(anImage != nil); | |
| 254 DCHECK(suffixString != nil); | |
| 255 | |
| 256 // Adjust for space between editor and decorations. | |
| 257 width -= 2 * kEditorHorizontalInset; | |
| 258 | |
| 259 // If |hintString_| is now too wide, clear it so that we don't pass | |
| 260 // the equality tests. | |
| 261 if (hintString_ && WidthForHint(hintString_) > width) { | |
| 262 [self clearHint]; | |
| 263 } | |
| 264 | |
| 265 // TODO(shess): Also check the length? | |
| 266 if (![[hintString_ string] hasPrefix:prefixString] || | |
| 267 ![[hintString_ string] hasSuffix:suffixString]) { | |
| 268 | |
| 269 // Build an attributed string with the concatenation of the prefix | |
| 270 // and suffix. | |
| 271 NSString* s = [prefixString stringByAppendingString:suffixString]; | |
| 272 scoped_nsobject<NSMutableAttributedString> as( | |
| 273 [[NSMutableAttributedString alloc] | |
| 274 initWithString:s attributes:[self hintAttributes]]); | |
| 275 | |
| 276 // Build an attachment containing the hint image. | |
| 277 NSAttributedString* is = | |
| 278 AttributedStringForImage(anImage, kKeywordHintImageBaseline); | |
| 279 | |
| 280 // Stuff the image attachment between the prefix and suffix. | |
| 281 [as insertAttributedString:is atIndex:[prefixString length]]; | |
| 282 | |
| 283 // If too wide, just show the image. | |
| 284 hintString_.reset(WidthForHint(as) > width ? [is copy] : [as copy]); | |
| 285 } | |
| 286 } | |
| 287 | |
| 288 - (void)setSearchHintString:(NSString*)aString | |
| 289 availableWidth:(CGFloat)width { | |
| 290 DCHECK(aString != nil); | |
| 291 | |
| 292 // Adjust for space between editor and decorations. | |
| 293 width -= 2 * kEditorHorizontalInset; | |
| 294 | |
| 295 // If |hintString_| is now too wide, clear it so that we don't pass | |
| 296 // the equality tests. | |
| 297 if (hintString_ && WidthForHint(hintString_) > width) { | |
| 298 [self clearHint]; | |
| 299 } | |
| 300 | |
| 301 if (![[hintString_ string] isEqualToString:aString]) { | |
| 302 scoped_nsobject<NSAttributedString> as( | |
| 303 [[NSAttributedString alloc] initWithString:aString | |
| 304 attributes:[self hintAttributes]]); | |
| 305 | |
| 306 // If too wide, don't keep the hint. | |
| 307 hintString_.reset(WidthForHint(as) > width ? nil : [as copy]); | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 - (void)clearHint { | |
| 312 hintString_.reset(); | |
| 313 } | |
| 314 | |
| 315 - (void)clearDecorations { | 151 - (void)clearDecorations { |
| 316 leftDecorations_.clear(); | 152 leftDecorations_.clear(); |
| 317 rightDecorations_.clear(); | 153 rightDecorations_.clear(); |
| 318 } | 154 } |
| 319 | 155 |
| 320 - (void)addLeftDecoration:(LocationBarDecoration*)decoration { | 156 - (void)addLeftDecoration:(LocationBarDecoration*)decoration { |
| 321 leftDecorations_.push_back(decoration); | 157 leftDecorations_.push_back(decoration); |
| 322 } | 158 } |
| 323 | 159 |
| 324 - (void)addRightDecoration:(LocationBarDecoration*)decoration { | 160 - (void)addRightDecoration:(LocationBarDecoration*)decoration { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 356 return decorationFrames[index]; | 192 return decorationFrames[index]; |
| 357 } | 193 } |
| 358 | 194 |
| 359 // Decorations which are not visible should have been filtered out | 195 // Decorations which are not visible should have been filtered out |
| 360 // at the top, but return |NSZeroRect| rather than a 0-width rect | 196 // at the top, but return |NSZeroRect| rather than a 0-width rect |
| 361 // for consistency. | 197 // for consistency. |
| 362 NOTREACHED(); | 198 NOTREACHED(); |
| 363 return NSZeroRect; | 199 return NSZeroRect; |
| 364 } | 200 } |
| 365 | 201 |
| 366 // Overriden to account for the hint strings and hint icons. | 202 // Overriden to account for the decorations. |
| 367 - (NSRect)textFrameForFrame:(NSRect)cellFrame { | 203 - (NSRect)textFrameForFrame:(NSRect)cellFrame { |
| 368 // Get the frame adjusted for decorations. | 204 // Get the frame adjusted for decorations. |
| 369 std::vector<LocationBarDecoration*> decorations; | 205 std::vector<LocationBarDecoration*> decorations; |
| 370 std::vector<NSRect> decorationFrames; | 206 std::vector<NSRect> decorationFrames; |
| 371 NSRect textFrame = [super textFrameForFrame:cellFrame]; | 207 NSRect textFrame = [super textFrameForFrame:cellFrame]; |
| 372 CalculatePositionsInFrame(textFrame, leftDecorations_, rightDecorations_, | 208 CalculatePositionsInFrame(textFrame, leftDecorations_, rightDecorations_, |
| 373 &decorations, &decorationFrames, &textFrame); | 209 &decorations, &decorationFrames, &textFrame); |
| 374 | 210 |
| 375 // NOTE: This function must closely match the logic in | 211 // NOTE: This function must closely match the logic in |
| 376 // |-drawInteriorWithFrame:inView:|. | 212 // |-drawInteriorWithFrame:inView:|. |
| 377 | 213 |
| 378 // Keyword string or hint string if they fit. | |
| 379 if (hintString_) { | |
| 380 const CGFloat hintWidth(WidthForHint(hintString_)); | |
| 381 | |
| 382 // TODO(shess): This could be better. Show the hint until the | |
| 383 // non-hint text bumps against it? | |
| 384 if (hintWidth < NSWidth(textFrame)) { | |
| 385 textFrame.size.width -= hintWidth; | |
| 386 } | |
| 387 } | |
| 388 | |
| 389 return textFrame; | 214 return textFrame; |
| 390 } | 215 } |
| 391 | 216 |
| 392 - (NSRect)textCursorFrameForFrame:(NSRect)cellFrame { | 217 - (NSRect)textCursorFrameForFrame:(NSRect)cellFrame { |
| 393 std::vector<LocationBarDecoration*> decorations; | 218 std::vector<LocationBarDecoration*> decorations; |
| 394 std::vector<NSRect> decorationFrames; | 219 std::vector<NSRect> decorationFrames; |
| 395 NSRect textFrame; | 220 NSRect textFrame; |
| 396 size_t left_count = | 221 size_t left_count = |
| 397 CalculatePositionsInFrame(cellFrame, leftDecorations_, rightDecorations_, | 222 CalculatePositionsInFrame(cellFrame, leftDecorations_, rightDecorations_, |
| 398 &decorations, &decorationFrames, &textFrame); | 223 &decorations, &decorationFrames, &textFrame); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 422 maxX = NSMaxX(cellFrame); | 247 maxX = NSMaxX(cellFrame); |
| 423 } else { | 248 } else { |
| 424 maxX = NSMaxX(decorationFrames[index]) + kDecorationHorizontalPad; | 249 maxX = NSMaxX(decorationFrames[index]) + kDecorationHorizontalPad; |
| 425 } | 250 } |
| 426 } | 251 } |
| 427 | 252 |
| 428 // I-beam cursor covers left-most to right-most. | 253 // I-beam cursor covers left-most to right-most. |
| 429 return NSMakeRect(minX, NSMinY(textFrame), maxX - minX, NSHeight(textFrame)); | 254 return NSMakeRect(minX, NSMinY(textFrame), maxX - minX, NSHeight(textFrame)); |
| 430 } | 255 } |
| 431 | 256 |
| 432 - (void)drawHintWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { | |
| 433 DCHECK(hintString_); | |
| 434 | |
| 435 NSRect textFrame = [self textFrameForFrame:cellFrame]; | |
| 436 NSRect infoFrame(NSMakeRect(NSMaxX(textFrame), | |
| 437 cellFrame.origin.y + kHintYOffset, | |
| 438 ceil([hintString_ size].width), | |
| 439 cellFrame.size.height - kHintYOffset)); | |
| 440 [hintString_.get() drawInRect:infoFrame]; | |
| 441 } | |
| 442 | |
| 443 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { | 257 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { |
| 444 std::vector<LocationBarDecoration*> decorations; | 258 std::vector<LocationBarDecoration*> decorations; |
| 445 std::vector<NSRect> decorationFrames; | 259 std::vector<NSRect> decorationFrames; |
| 446 NSRect workingFrame; | 260 NSRect workingFrame; |
| 447 CalculatePositionsInFrame(cellFrame, leftDecorations_, rightDecorations_, | 261 CalculatePositionsInFrame(cellFrame, leftDecorations_, rightDecorations_, |
| 448 &decorations, &decorationFrames, &workingFrame); | 262 &decorations, &decorationFrames, &workingFrame); |
| 449 | 263 |
| 450 // Draw the decorations first. | 264 // Draw the decorations. |
| 451 for (size_t i = 0; i < decorations.size(); ++i) { | 265 for (size_t i = 0; i < decorations.size(); ++i) { |
| 452 if (decorations[i]) | 266 if (decorations[i]) |
| 453 decorations[i]->DrawInFrame(decorationFrames[i], controlView); | 267 decorations[i]->DrawInFrame(decorationFrames[i], controlView); |
| 454 } | 268 } |
| 455 | 269 |
| 456 // NOTE: This function must closely match the logic in | 270 // NOTE: This function must closely match the logic in |
| 457 // |-textFrameForFrame:|. | 271 // |-textFrameForFrame:|. |
| 458 | 272 |
| 459 // Keyword string or hint string if they fit. | |
| 460 if (hintString_) { | |
| 461 const CGFloat hintWidth(WidthForHint(hintString_)); | |
| 462 | |
| 463 // TODO(shess): This could be better. Show the hint until the | |
| 464 // non-hint text bumps against it? | |
| 465 if (hintWidth < NSWidth(workingFrame)) { | |
| 466 [self drawHintWithFrame:cellFrame inView:controlView]; | |
| 467 workingFrame.size.width -= hintWidth; | |
| 468 } | |
| 469 } | |
| 470 | |
| 471 // Superclass draws text portion WRT original |cellFrame|. | 273 // Superclass draws text portion WRT original |cellFrame|. |
| 472 [super drawInteriorWithFrame:cellFrame inView:controlView]; | 274 [super drawInteriorWithFrame:cellFrame inView:controlView]; |
| 473 } | 275 } |
| 474 | 276 |
| 475 - (LocationBarDecoration*)decorationForEvent:(NSEvent*)theEvent | 277 - (LocationBarDecoration*)decorationForEvent:(NSEvent*)theEvent |
| 476 inRect:(NSRect)cellFrame | 278 inRect:(NSRect)cellFrame |
| 477 ofView:(AutocompleteTextField*)controlView | 279 ofView:(AutocompleteTextField*)controlView |
| 478 { | 280 { |
| 479 const BOOL flipped = [controlView isFlipped]; | 281 const BOOL flipped = [controlView isFlipped]; |
| 480 const NSPoint location = | 282 const NSPoint location = |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 558 return NO; | 360 return NO; |
| 559 | 361 |
| 560 return YES; | 362 return YES; |
| 561 } | 363 } |
| 562 | 364 |
| 563 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal { | 365 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal { |
| 564 return NSDragOperationCopy; | 366 return NSDragOperationCopy; |
| 565 } | 367 } |
| 566 | 368 |
| 567 @end | 369 @end |
| OLD | NEW |