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 |