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

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

Issue 173194: [Mac] Omnibox keyword, keyword hint, and search hint support.... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 11 years, 4 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 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/autocomplete_text_field_cell.h" 5 #import "chrome/browser/cocoa/autocomplete_text_field_cell.h"
6
7 #import "base/logging.h"
6 #import "third_party/GTM/AppKit/GTMTheme.h" 8 #import "third_party/GTM/AppKit/GTMTheme.h"
7 9
8 const NSInteger kBaselineOffset = 2; 10 namespace {
11
12 const NSInteger kBaselineAdjust = 2;
13
14 // How far to offset the keyword token into the field.
15 const NSInteger kKeywordXOffset = 3;
16
17 // How much width (beyond text) to add to the keyword token on each
18 // side.
19 const NSInteger kKeywordTokenInset = 3;
20
21 // Gap to leave between hint and right-hand-side of cell.
22 const NSInteger kHintXOffset = 4;
23
24 // How far to shift bounding box of hint down from top of field.
25 // Assumes -setFlipped:YES.
26 const NSInteger kHintYOffset = 4;
27
28 // How far to inset the keywork token from sides.
29 const NSInteger kKeywordYInset = 4;
30
31 // TODO(shess): The keyword hint image wants to sit on the baseline.
32 // This moves it down so that there is approximately as much image
33 // above the lowercase ascender as below the baseline. A better
34 // technique would be nice to have, though.
35 const NSInteger kKeywordHintImageBaseline = -6;
36
37 // Offset from the bottom of the field for drawing decoration text.
38 // TODO(shess): Somehow determine the baseline for the text field and
39 // use that.
40 const NSInteger kBaselineOffset = 4;
41
42 } // namespace
9 43
10 @implementation AutocompleteTextFieldCell 44 @implementation AutocompleteTextFieldCell
11 45
46 @synthesize fieldEditorNeedsReset = fieldEditorNeedsReset_;
47
48 // @synthesize doesn't seem to compile for this transition.
49 - (NSAttributedString*)keywordString {
50 return keywordString_.get();
51 }
52 - (NSAttributedString*)hintString {
53 return hintString_.get();
54 }
55
56 - (void)setKeywordString:(NSString*)aString {
57 DCHECK(aString != nil);
58 if (hintString_ || ![[keywordString_ string] isEqualToString:aString]) {
59 NSDictionary* attributes =
60 [NSDictionary dictionaryWithObjectsAndKeys:
61 [self font], NSFontAttributeName,
62 nil];
63
64 keywordString_.reset(
65 [[NSAttributedString alloc] initWithString:aString
66 attributes:attributes]);
67 hintString_.reset();
68
69 fieldEditorNeedsReset_ = YES;
70 }
71 }
72
73 - (void)setHintString:(NSAttributedString*)aString {
74 keywordString_.reset();
75 hintString_.reset([aString copy]);
76
77 fieldEditorNeedsReset_ = YES;
78 }
79
80 // Convenience for the attributes used in the right-justified info
81 // cells.
82 - (NSDictionary*)hintAttributes {
83 NSMutableParagraphStyle* style =
84 [[[NSMutableParagraphStyle alloc] init] autorelease];
85 [style setAlignment:NSRightTextAlignment];
86
87 return [NSDictionary dictionaryWithObjectsAndKeys:
88 [self font], NSFontAttributeName,
89 [NSColor lightGrayColor], NSForegroundColorAttributeName,
90 style, NSParagraphStyleAttributeName,
91 nil];
92 }
93
94 - (void)setKeywordHintPrefix:(NSString*)prefixString
95 image:(NSImage*)anImage
96 suffix:(NSString*)suffixString {
97 DCHECK(prefixString != nil);
98 DCHECK(anImage != nil);
99 DCHECK(suffixString != nil);
100
101 // TODO(shess): Also check the length?
102 if (keywordString_ ||
103 ![[hintString_ string] hasPrefix:prefixString] ||
104 ![[hintString_ string] hasSuffix:suffixString]) {
105
106 // Build an attributed string with the concatenation of the prefix
107 // and suffix.
108 NSString* s = [prefixString stringByAppendingString:suffixString];
109 NSMutableAttributedString* as =
110 [[[NSMutableAttributedString alloc]
111 initWithString:s attributes:[self hintAttributes]] autorelease];
112
113 // Build an attachment containing the hint image.
114 NSTextAttachmentCell* attachmentCell =
115 [[[NSTextAttachmentCell alloc] initImageCell:anImage] autorelease];
116 NSTextAttachment* attachment =
117 [[[NSTextAttachment alloc] init] autorelease];
118 [attachment setAttachmentCell:attachmentCell];
119
120 // The attachment's baseline needs to be adjusted so the image
121 // doesn't sit on the same baseline as the text and make
122 // everything too tall.
123 NSMutableAttributedString* is =
124 [[[NSAttributedString attributedStringWithAttachment:attachment]
125 mutableCopy] autorelease];
126 [is addAttribute:NSBaselineOffsetAttributeName
127 value:[NSNumber numberWithFloat:kKeywordHintImageBaseline]
128 range:NSMakeRange(0, [is length])];
129
130 // Stuff the image attachment between the prefix and suffix.
131 [as insertAttributedString:is atIndex:[prefixString length]];
132
133 [self setHintString:as];
134 }
135 }
136
137 - (void)setSearchHintString:(NSString*)aString {
138 DCHECK(aString != nil);
139
140 if (keywordString_ || ![[hintString_ string] isEqualToString:aString]) {
141 NSAttributedString* as =
142 [[[NSAttributedString alloc] initWithString:aString
143 attributes:[self hintAttributes]]
144 autorelease];
145
146 [self setHintString:as];
147 }
148 }
149
150 - (void)clearKeywordAndHint {
151 if (keywordString_ || hintString_) {
152 keywordString_.reset();
153 hintString_.reset();
154
155 fieldEditorNeedsReset_ = YES;
156 }
157 }
158
12 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { 159 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
160 DCHECK([controlView isFlipped]);
13 [[NSColor colorWithCalibratedWhite:1.0 alpha:0.25] set]; 161 [[NSColor colorWithCalibratedWhite:1.0 alpha:0.25] set];
14 NSFrameRectWithWidthUsingOperation(cellFrame, 1, NSCompositeSourceOver); 162 NSFrameRectWithWidthUsingOperation(cellFrame, 1, NSCompositeSourceOver);
15 163
16 NSRect frame = NSInsetRect(cellFrame, 0, 1); 164 NSRect frame = NSInsetRect(cellFrame, 0, 1);
17 [[self backgroundColor] setFill]; 165 [[self backgroundColor] setFill];
18 NSRect innerFrame = NSInsetRect(frame, 1, 1); 166 NSRect innerFrame = NSInsetRect(frame, 1, 1);
19 NSRectFill(innerFrame); 167 NSRectFill(innerFrame);
20 168
21 NSRect shadowFrame, restFrame; 169 NSRect shadowFrame, restFrame;
22 NSDivideRect(innerFrame, &shadowFrame, &restFrame, 1, NSMinYEdge); 170 NSDivideRect(innerFrame, &shadowFrame, &restFrame, 1, NSMinYEdge);
23 171
24 BOOL isMainWindow = [[controlView window] isMainWindow]; 172 BOOL isMainWindow = [[controlView window] isMainWindow];
25 GTMTheme *theme = [controlView gtm_theme]; 173 GTMTheme *theme = [controlView gtm_theme];
26 NSColor* stroke = [theme strokeColorForStyle:GTMThemeStyleToolBarButton 174 NSColor* stroke = [theme strokeColorForStyle:GTMThemeStyleToolBarButton
27 state:isMainWindow]; 175 state:isMainWindow];
28 [stroke set]; 176 [stroke set];
29 NSFrameRectWithWidthUsingOperation(frame, 1.0, NSCompositeSourceOver); 177 NSFrameRectWithWidthUsingOperation(frame, 1.0, NSCompositeSourceOver);
30 178
31 // Draw the shadow. 179 // Draw the shadow.
32 [[NSColor colorWithCalibratedWhite:0.0 alpha:0.05] setFill]; 180 [[NSColor colorWithCalibratedWhite:0.0 alpha:0.05] setFill];
33 NSRectFillUsingOperation(shadowFrame, NSCompositeSourceOver); 181 NSRectFillUsingOperation(shadowFrame, NSCompositeSourceOver);
34 182
35 if ([self showsFirstResponder]) { 183 if ([self showsFirstResponder]) {
36 [[[NSColor keyboardFocusIndicatorColor] colorWithAlphaComponent:0.5] set]; 184 [[[NSColor keyboardFocusIndicatorColor] colorWithAlphaComponent:0.5] set];
37 NSFrameRectWithWidthUsingOperation(NSInsetRect(frame, 0, 0), 2, 185 NSFrameRectWithWidthUsingOperation(NSInsetRect(frame, 0, 0), 2,
38 NSCompositeSourceOver); 186 NSCompositeSourceOver);
39 } 187 }
40 188
41 [self drawInteriorWithFrame:cellFrame 189 [self drawInteriorWithFrame:cellFrame inView:controlView];
42 inView:controlView];
43
44 } 190 }
45 191
46 - (void)drawInteriorWithFrame:(NSRect)cellFrame 192 - (NSRect)textFrameForFrame:(NSRect)cellFrame {
47 inView:(NSView*)controlView { 193 NSRect textFrame(cellFrame);
48 [super drawInteriorWithFrame:NSInsetRect(cellFrame, 0, kBaselineOffset) 194
195 if (hintString_) {
196 DCHECK(!keywordString_);
197 const CGFloat hintWidth = kHintXOffset + ceil([hintString_ size].width);
198
199 // TODO(shess): This could be better. Show the hint until the
200 // non-hint text bumps against it?
201 if (hintWidth < NSWidth(cellFrame)) {
202 textFrame.size.width -= hintWidth;
203 }
204 } else if (keywordString_) {
205 DCHECK(!hintString_);
206 const CGFloat keywordWidth = kKeywordXOffset +
207 ceil([keywordString_ size].width) + 2 * kKeywordTokenInset;
208
209 // TODO(shess): This could be better. There's support for a
210 // "short" version of the keyword string, work that in in a
211 // follow-on pass.
212 if (keywordWidth < NSWidth(cellFrame)) {
213 textFrame.origin.x += keywordWidth;
214 textFrame.size.width = NSMaxX(cellFrame) - NSMinX(textFrame);
215 }
216 }
217
218 return NSInsetRect(textFrame, 0, kBaselineAdjust);
219 }
220
221 - (void)drawHintWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
222 DCHECK(hintString_);
223
224 NSRect textFrame = [self textFrameForFrame:cellFrame];
225 NSRect infoFrame(NSMakeRect(NSMaxX(textFrame),
226 cellFrame.origin.y + kHintYOffset,
227 ceil([hintString_ size].width),
228 cellFrame.size.height - kHintYOffset));
229 [hintString_.get() drawInRect:infoFrame];
230 }
231
232 - (void)drawKeywordWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
233 DCHECK(keywordString_);
234
235 NSRect textFrame = [self textFrameForFrame:cellFrame];
236 const CGFloat x = NSMinX(cellFrame) + kKeywordXOffset;
237 NSRect infoFrame(NSMakeRect(x,
238 cellFrame.origin.y + kKeywordYInset,
239 NSMinX(textFrame) - x,
240 cellFrame.size.height - 2 * kKeywordYInset));
241
242 // Draw a token rectangle with rounded corners.
243 NSRect frame(NSInsetRect(infoFrame, 0.5, 0.5));
244 NSBezierPath* path =
245 [NSBezierPath bezierPathWithRoundedRect:frame xRadius:4.0 yRadius:4.0];
246
247 [[NSColor controlColor] set];
248 [path fill];
249
250 GTMTheme *theme = [controlView gtm_theme];
251 NSColor* stroke = [theme strokeColorForStyle:GTMThemeStyleToolBarButton
252 state:YES];
253 [stroke setStroke];
254 [path setLineWidth:1.0];
255 [path stroke];
256
257 // Draw text w/in the rectangle.
258 infoFrame.origin.x += 4.0;
259 infoFrame.origin.y += 1.0;
260 [keywordString_.get() drawInRect:infoFrame];
261 }
262
263 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
264 if (hintString_) {
265 [self drawHintWithFrame:cellFrame inView:controlView];
266 } else if (keywordString_) {
267 [self drawKeywordWithFrame:cellFrame inView:controlView];
268 }
269
270 [super drawInteriorWithFrame:[self textFrameForFrame:cellFrame]
49 inView:controlView]; 271 inView:controlView];
50 } 272 }
51 273
52 // Override these methods so that the field editor shows up in the right place 274 // Override these methods so that the field editor shows up in the right place
53 - (void)editWithFrame:(NSRect)cellFrame 275 - (void)editWithFrame:(NSRect)cellFrame
54 inView:(NSView*)controlView 276 inView:(NSView*)controlView
55 editor:(NSText*)textObj 277 editor:(NSText*)textObj
56 delegate:(id)anObject 278 delegate:(id)anObject
57 event:(NSEvent*)theEvent { 279 event:(NSEvent*)theEvent {
58 [super editWithFrame:NSInsetRect(cellFrame, 0, kBaselineOffset) 280 [super editWithFrame:[self textFrameForFrame:cellFrame]
59 inView:controlView 281 inView:controlView
60 editor:textObj 282 editor:textObj
61 delegate:anObject 283 delegate:anObject
62 event:theEvent]; 284 event:theEvent];
63 } 285 }
64 286
65
66 // Override these methods so that the field editor shows up in the right place 287 // Override these methods so that the field editor shows up in the right place
67 - (void)selectWithFrame:(NSRect)cellFrame 288 - (void)selectWithFrame:(NSRect)cellFrame
68 inView:(NSView*)controlView 289 inView:(NSView*)controlView
69 editor:(NSText*)textObj 290 editor:(NSText*)textObj
70 delegate:(id)anObject 291 delegate:(id)anObject
71 start:(NSInteger)selStart 292 start:(NSInteger)selStart
72 length:(NSInteger)selLength { 293 length:(NSInteger)selLength {
73 [super selectWithFrame:NSInsetRect(cellFrame, 0, kBaselineOffset) 294 [super selectWithFrame:[self textFrameForFrame:cellFrame]
74 inView:controlView editor:textObj 295 inView:controlView editor:textObj
75 delegate:anObject 296 delegate:anObject
76 start:selStart 297 start:selStart
77 length:selLength]; 298 length:selLength];
78 } 299 }
79 300
80 @end 301 @end
OLDNEW
« no previous file with comments | « chrome/browser/cocoa/autocomplete_text_field_cell.h ('k') | chrome/browser/cocoa/autocomplete_text_field_cell_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698