Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(230)

Side by Side Diff: chrome/browser/cocoa/location_bar/autocomplete_text_field_cell.mm

Issue 2854051: [Mac] Convert omnibox keyword hint to decoration. (Closed) Base URL: git://codf21.jail/chromium.git
Patch Set: Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698