summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa/autocomplete_text_field_cell.mm
diff options
context:
space:
mode:
authorrohitrao@chromium.org <rohitrao@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-21 16:23:40 +0000
committerrohitrao@chromium.org <rohitrao@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-21 16:23:40 +0000
commit534c59086c951a0687fce53c2a8de49a2100867e (patch)
tree22db8743b0b3726e6451822fcdd9c10950193b48 /chrome/browser/cocoa/autocomplete_text_field_cell.mm
parent7edcd61631c5c8a9c55945eded3a5bc48cc3e350 (diff)
downloadchromium_src-534c59086c951a0687fce53c2a8de49a2100867e.zip
chromium_src-534c59086c951a0687fce53c2a8de49a2100867e.tar.gz
chromium_src-534c59086c951a0687fce53c2a8de49a2100867e.tar.bz2
[Mac] Omnibox keyword, keyword hint, and search hint support.
BUG=http://crbug.com/10944 BUG=http://crbug.com/10943 BUG=http://crbug.com/12285 TEST=With empty field, should see "Type to search" in light text on the RHS of the omnibox. Type "google" and should see "Press [Tab] to search Google" in light text on the RHS. Hit TAB, should see a rounded [Search Google:] item on the left and the cursor is now right of that. Popup should show "Keyword:" in some entries. Patch by shess@chromium.org. Review URL: http://codereview.chromium.org/173194 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@23964 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/cocoa/autocomplete_text_field_cell.mm')
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field_cell.mm239
1 files changed, 230 insertions, 9 deletions
diff --git a/chrome/browser/cocoa/autocomplete_text_field_cell.mm b/chrome/browser/cocoa/autocomplete_text_field_cell.mm
index ff6d4b3..b52d9d8 100644
--- a/chrome/browser/cocoa/autocomplete_text_field_cell.mm
+++ b/chrome/browser/cocoa/autocomplete_text_field_cell.mm
@@ -3,13 +3,161 @@
// found in the LICENSE file.
#import "chrome/browser/cocoa/autocomplete_text_field_cell.h"
+
+#import "base/logging.h"
#import "third_party/GTM/AppKit/GTMTheme.h"
-const NSInteger kBaselineOffset = 2;
+namespace {
+
+const NSInteger kBaselineAdjust = 2;
+
+// How far to offset the keyword token into the field.
+const NSInteger kKeywordXOffset = 3;
+
+// How much width (beyond text) to add to the keyword token on each
+// side.
+const NSInteger kKeywordTokenInset = 3;
+
+// Gap to leave between hint and right-hand-side of cell.
+const NSInteger kHintXOffset = 4;
+
+// How far to shift bounding box of hint down from top of field.
+// Assumes -setFlipped:YES.
+const NSInteger kHintYOffset = 4;
+
+// How far to inset the keywork token from sides.
+const NSInteger kKeywordYInset = 4;
+
+// TODO(shess): The keyword hint image wants to sit on the baseline.
+// This moves it down so that there is approximately as much image
+// above the lowercase ascender as below the baseline. A better
+// technique would be nice to have, though.
+const NSInteger kKeywordHintImageBaseline = -6;
+
+// Offset from the bottom of the field for drawing decoration text.
+// TODO(shess): Somehow determine the baseline for the text field and
+// use that.
+const NSInteger kBaselineOffset = 4;
+
+} // namespace
@implementation AutocompleteTextFieldCell
+@synthesize fieldEditorNeedsReset = fieldEditorNeedsReset_;
+
+// @synthesize doesn't seem to compile for this transition.
+- (NSAttributedString*)keywordString {
+ return keywordString_.get();
+}
+- (NSAttributedString*)hintString {
+ return hintString_.get();
+}
+
+- (void)setKeywordString:(NSString*)aString {
+ DCHECK(aString != nil);
+ if (hintString_ || ![[keywordString_ string] isEqualToString:aString]) {
+ NSDictionary* attributes =
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ [self font], NSFontAttributeName,
+ nil];
+
+ keywordString_.reset(
+ [[NSAttributedString alloc] initWithString:aString
+ attributes:attributes]);
+ hintString_.reset();
+
+ fieldEditorNeedsReset_ = YES;
+ }
+}
+
+- (void)setHintString:(NSAttributedString*)aString {
+ keywordString_.reset();
+ hintString_.reset([aString copy]);
+
+ fieldEditorNeedsReset_ = YES;
+}
+
+// Convenience for the attributes used in the right-justified info
+// cells.
+- (NSDictionary*)hintAttributes {
+ NSMutableParagraphStyle* style =
+ [[[NSMutableParagraphStyle alloc] init] autorelease];
+ [style setAlignment:NSRightTextAlignment];
+
+ return [NSDictionary dictionaryWithObjectsAndKeys:
+ [self font], NSFontAttributeName,
+ [NSColor lightGrayColor], NSForegroundColorAttributeName,
+ style, NSParagraphStyleAttributeName,
+ nil];
+}
+
+- (void)setKeywordHintPrefix:(NSString*)prefixString
+ image:(NSImage*)anImage
+ suffix:(NSString*)suffixString {
+ DCHECK(prefixString != nil);
+ DCHECK(anImage != nil);
+ DCHECK(suffixString != nil);
+
+ // TODO(shess): Also check the length?
+ if (keywordString_ ||
+ ![[hintString_ string] hasPrefix:prefixString] ||
+ ![[hintString_ string] hasSuffix:suffixString]) {
+
+ // Build an attributed string with the concatenation of the prefix
+ // and suffix.
+ NSString* s = [prefixString stringByAppendingString:suffixString];
+ NSMutableAttributedString* as =
+ [[[NSMutableAttributedString alloc]
+ initWithString:s attributes:[self hintAttributes]] autorelease];
+
+ // Build an attachment containing the hint image.
+ NSTextAttachmentCell* attachmentCell =
+ [[[NSTextAttachmentCell alloc] initImageCell:anImage] autorelease];
+ NSTextAttachment* attachment =
+ [[[NSTextAttachment alloc] init] autorelease];
+ [attachment setAttachmentCell:attachmentCell];
+
+ // The attachment's baseline needs to be adjusted so the image
+ // doesn't sit on the same baseline as the text and make
+ // everything too tall.
+ NSMutableAttributedString* is =
+ [[[NSAttributedString attributedStringWithAttachment:attachment]
+ mutableCopy] autorelease];
+ [is addAttribute:NSBaselineOffsetAttributeName
+ value:[NSNumber numberWithFloat:kKeywordHintImageBaseline]
+ range:NSMakeRange(0, [is length])];
+
+ // Stuff the image attachment between the prefix and suffix.
+ [as insertAttributedString:is atIndex:[prefixString length]];
+
+ [self setHintString:as];
+ }
+}
+
+- (void)setSearchHintString:(NSString*)aString {
+ DCHECK(aString != nil);
+
+ if (keywordString_ || ![[hintString_ string] isEqualToString:aString]) {
+ NSAttributedString* as =
+ [[[NSAttributedString alloc] initWithString:aString
+ attributes:[self hintAttributes]]
+ autorelease];
+
+ [self setHintString:as];
+ }
+}
+
+- (void)clearKeywordAndHint {
+ if (keywordString_ || hintString_) {
+ keywordString_.reset();
+ hintString_.reset();
+
+ fieldEditorNeedsReset_ = YES;
+ }
+}
+
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
+ DCHECK([controlView isFlipped]);
[[NSColor colorWithCalibratedWhite:1.0 alpha:0.25] set];
NSFrameRectWithWidthUsingOperation(cellFrame, 1, NSCompositeSourceOver);
@@ -38,14 +186,88 @@ const NSInteger kBaselineOffset = 2;
NSCompositeSourceOver);
}
- [self drawInteriorWithFrame:cellFrame
- inView:controlView];
+ [self drawInteriorWithFrame:cellFrame inView:controlView];
+}
+
+- (NSRect)textFrameForFrame:(NSRect)cellFrame {
+ NSRect textFrame(cellFrame);
+
+ if (hintString_) {
+ DCHECK(!keywordString_);
+ const CGFloat hintWidth = kHintXOffset + ceil([hintString_ size].width);
+
+ // TODO(shess): This could be better. Show the hint until the
+ // non-hint text bumps against it?
+ if (hintWidth < NSWidth(cellFrame)) {
+ textFrame.size.width -= hintWidth;
+ }
+ } else if (keywordString_) {
+ DCHECK(!hintString_);
+ const CGFloat keywordWidth = kKeywordXOffset +
+ ceil([keywordString_ size].width) + 2 * kKeywordTokenInset;
+
+ // TODO(shess): This could be better. There's support for a
+ // "short" version of the keyword string, work that in in a
+ // follow-on pass.
+ if (keywordWidth < NSWidth(cellFrame)) {
+ textFrame.origin.x += keywordWidth;
+ textFrame.size.width = NSMaxX(cellFrame) - NSMinX(textFrame);
+ }
+ }
+
+ return NSInsetRect(textFrame, 0, kBaselineAdjust);
+}
+
+- (void)drawHintWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
+ DCHECK(hintString_);
+
+ NSRect textFrame = [self textFrameForFrame:cellFrame];
+ NSRect infoFrame(NSMakeRect(NSMaxX(textFrame),
+ cellFrame.origin.y + kHintYOffset,
+ ceil([hintString_ size].width),
+ cellFrame.size.height - kHintYOffset));
+ [hintString_.get() drawInRect:infoFrame];
+}
+
+- (void)drawKeywordWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
+ DCHECK(keywordString_);
+
+ NSRect textFrame = [self textFrameForFrame:cellFrame];
+ const CGFloat x = NSMinX(cellFrame) + kKeywordXOffset;
+ NSRect infoFrame(NSMakeRect(x,
+ cellFrame.origin.y + kKeywordYInset,
+ NSMinX(textFrame) - x,
+ cellFrame.size.height - 2 * kKeywordYInset));
+ // Draw a token rectangle with rounded corners.
+ NSRect frame(NSInsetRect(infoFrame, 0.5, 0.5));
+ NSBezierPath* path =
+ [NSBezierPath bezierPathWithRoundedRect:frame xRadius:4.0 yRadius:4.0];
+
+ [[NSColor controlColor] set];
+ [path fill];
+
+ GTMTheme *theme = [controlView gtm_theme];
+ NSColor* stroke = [theme strokeColorForStyle:GTMThemeStyleToolBarButton
+ state:YES];
+ [stroke setStroke];
+ [path setLineWidth:1.0];
+ [path stroke];
+
+ // Draw text w/in the rectangle.
+ infoFrame.origin.x += 4.0;
+ infoFrame.origin.y += 1.0;
+ [keywordString_.get() drawInRect:infoFrame];
}
-- (void)drawInteriorWithFrame:(NSRect)cellFrame
- inView:(NSView*)controlView {
- [super drawInteriorWithFrame:NSInsetRect(cellFrame, 0, kBaselineOffset)
+- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
+ if (hintString_) {
+ [self drawHintWithFrame:cellFrame inView:controlView];
+ } else if (keywordString_) {
+ [self drawKeywordWithFrame:cellFrame inView:controlView];
+ }
+
+ [super drawInteriorWithFrame:[self textFrameForFrame:cellFrame]
inView:controlView];
}
@@ -55,14 +277,13 @@ const NSInteger kBaselineOffset = 2;
editor:(NSText*)textObj
delegate:(id)anObject
event:(NSEvent*)theEvent {
- [super editWithFrame:NSInsetRect(cellFrame, 0, kBaselineOffset)
+ [super editWithFrame:[self textFrameForFrame:cellFrame]
inView:controlView
editor:textObj
delegate:anObject
event:theEvent];
}
-
// Override these methods so that the field editor shows up in the right place
- (void)selectWithFrame:(NSRect)cellFrame
inView:(NSView*)controlView
@@ -70,7 +291,7 @@ const NSInteger kBaselineOffset = 2;
delegate:(id)anObject
start:(NSInteger)selStart
length:(NSInteger)selLength {
- [super selectWithFrame:NSInsetRect(cellFrame, 0, kBaselineOffset)
+ [super selectWithFrame:[self textFrameForFrame:cellFrame]
inView:controlView editor:textObj
delegate:anObject
start:selStart