diff options
author | shess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-05 19:33:39 +0000 |
---|---|---|
committer | shess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-05 19:33:39 +0000 |
commit | 0ccc17a9164d917a2ea16610d38189c63a816f4f (patch) | |
tree | 6d44925f201757aa9b25e24d139667813d0d5aa5 | |
parent | 2584f41e80ac96ccd58293acd9187169054e8bee (diff) | |
download | chromium_src-0ccc17a9164d917a2ea16610d38189c63a816f4f.zip chromium_src-0ccc17a9164d917a2ea16610d38189c63a816f4f.tar.gz chromium_src-0ccc17a9164d917a2ea16610d38189c63a816f4f.tar.bz2 |
Convert autocomplete to use a custom matrix and button cell.
On the user-visible side this change enables mouse hover (the item
under the mouse is highlighted), and lays out the text omnibox v2
style. Only hover really needed the control change, but I have
another change queued up to style the popup text. It uses different
colors and shades, and really looked horrible with the NSTableView
highlighting. [Which is why this part of the CL is coming first.]
TEST=Omnibox popup works and shows highlighting under the mouse in addition to the keyboard-selected item.
Review URL: http://codereview.chromium.org/99310
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15321 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/autocomplete/autocomplete_popup_view_mac.h | 15 | ||||
-rw-r--r-- | chrome/browser/autocomplete/autocomplete_popup_view_mac.mm | 336 | ||||
-rw-r--r-- | chrome/chrome.gyp | 5 |
3 files changed, 226 insertions, 130 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_popup_view_mac.h b/chrome/browser/autocomplete/autocomplete_popup_view_mac.h index 6639c0f..858e2fe 100644 --- a/chrome/browser/autocomplete/autocomplete_popup_view_mac.h +++ b/chrome/browser/autocomplete/autocomplete_popup_view_mac.h @@ -19,7 +19,7 @@ class AutocompletePopupModel; class AutocompleteEditModel; class AutocompleteEditViewMac; -@class AutocompleteTableTarget; +@class AutocompleteMatrixTarget; class Profile; // Implements AutocompletePopupView using a raw NSWindow containing an @@ -57,7 +57,7 @@ class AutocompletePopupViewMac : public AutocompletePopupView { } virtual void UpdatePopupAppearance(); virtual void OnHoverEnabledOrDisabled(bool disabled) { NOTIMPLEMENTED(); } - + // This is only called by model in SetSelectedLine() after updating // everything. Popup should already be visible. virtual void PaintUpdatesNow(); @@ -65,12 +65,9 @@ class AutocompletePopupViewMac : public AutocompletePopupView { // Returns the popup's model. virtual AutocompletePopupModel* GetModel(); - // Helpers which forward to model_, otherwise our Objective-C helper - // object would need model_ to be public:. - size_t ResultRowCount(); - const std::wstring& ResultContentsAt(size_t i); - bool ResultStarredAt(size_t i); - const std::wstring& ResultDescriptionAt(size_t i); + // Updates model_'s sense of selected line from the UI before + // calling edit_view_'s AcceptInput(). Used by internal Objective-C + // helper object. void AcceptInput(); private: @@ -82,7 +79,7 @@ class AutocompletePopupViewMac : public AutocompletePopupView { NSTextField* field_; // owned by tab controller - scoped_nsobject<AutocompleteTableTarget> table_target_; + scoped_nsobject<AutocompleteMatrixTarget> matrix_target_; // TODO(shess): Before checkin review implementation to make sure // that popup_'s object hierarchy doesn't keep references to // destructed objects. diff --git a/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm b/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm index 8ec24e0..244936b 100644 --- a/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm +++ b/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm @@ -9,11 +9,95 @@ #include "chrome/browser/autocomplete/autocomplete_edit_view_mac.h" #include "chrome/browser/autocomplete/autocomplete_popup_model.h" -// Thin Obj-C bridge class between the target and data source of the -// popup window's NSTableView and the AutocompletePopupView -// implementation. +namespace { -@interface AutocompleteTableTarget : NSObject { +// Background colors for different states of the popup elements. +NSColor* BackgroundColor() { + return [NSColor controlBackgroundColor]; +} +NSColor* SelectedBackgroundColor() { + return [NSColor selectedControlColor]; +} +NSColor* HoveredBackgroundColor() { + return [NSColor controlColor]; +} + +// Return the appropriate icon for the given match. Derived from the +// gtk code. +NSImage* MatchIcon(const AutocompleteMatch& match) { + if (match.starred) { + return [NSImage imageNamed:@"o2_star.png"]; + } + + switch (match.type) { + case AutocompleteMatch::URL_WHAT_YOU_TYPED: + case AutocompleteMatch::NAVSUGGEST: { + return [NSImage imageNamed:@"o2_globe.png"]; + } + case AutocompleteMatch::HISTORY_URL: + case AutocompleteMatch::HISTORY_TITLE: + case AutocompleteMatch::HISTORY_BODY: + case AutocompleteMatch::HISTORY_KEYWORD: { + return [NSImage imageNamed:@"o2_history.png"]; + } + case AutocompleteMatch::SEARCH_WHAT_YOU_TYPED: + case AutocompleteMatch::SEARCH_HISTORY: + case AutocompleteMatch::SEARCH_SUGGEST: + case AutocompleteMatch::SEARCH_OTHER_ENGINE: { + return [NSImage imageNamed:@"o2_search.png"]; + } + case AutocompleteMatch::OPEN_HISTORY_PAGE: { + return [NSImage imageNamed:@"o2_more.png"]; + } + default: + NOTREACHED(); + break; + } + + return nil; +} + +// Return the text to show for the match, based on the match's +// contents and description. +// TODO(shess): Style the runs within the text. +NSString* MatchText(const AutocompleteMatch& match) { + NSString* s = base::SysWideToNSString(match.contents); + + if (!match.description.empty()) { + NSString* description = base::SysWideToNSString(match.description); + + // Append an em dash (U-2014) and description. + s = [s stringByAppendingFormat:@" %C %@", 0x2014, description]; + } + + return s; +} + +} // namespace + +// AutocompleteButtonCell overrides how backgrounds are displayed to +// handle hover versus selected. So long as we're in there, it also +// provides some default initialization. + +@interface AutocompleteButtonCell : NSButtonCell { +} +@end + +// AutocompleteMatrix sets up a tracking area to implement hover by +// highlighting the cell the mouse is over. + +@interface AutocompleteMatrix : NSMatrix { +} +@end + +// Thin Obj-C bridge class between the target of the popup window's +// AutocompleteMatrix and the AutocompletePopupView implementation. + +// TODO(shess): Now that I'm using AutocompleteMatrix, I could instead +// subvert the target/action stuff and have it message popup_view_ +// directly. + +@interface AutocompleteMatrixTarget : NSObject { @private AutocompletePopupViewMac* popup_view_; // weak, owns us. } @@ -21,16 +105,6 @@ // Tell popup model via popup_view_ about the selected row. - (void)select:sender; - -// NSTableDataSource methods, filled from data returned by -// the popup model via popup_view_. -- (NSInteger)numberOfRowsInTableView:(NSTableView*)aTableView; -- (id)tableView:(NSTableView*)aTableView -objectValueForTableColumn:(NSTableColumn*)aTableColumn - row:(int)ri; - -// Placeholder for finding the star image. -- (NSImage*)starImage; @end AutocompletePopupViewMac::AutocompletePopupViewMac( @@ -41,7 +115,7 @@ AutocompletePopupViewMac::AutocompletePopupViewMac( : model_(new AutocompletePopupModel(this, edit_model, profile)), edit_view_(edit_view), field_(field), - table_target_([[AutocompleteTableTarget alloc] initWithPopupView:this]), + matrix_target_([[AutocompleteMatrixTarget alloc] initWithPopupView:this]), popup_(nil) { DCHECK(edit_view); DCHECK(edit_model); @@ -57,28 +131,15 @@ AutocompletePopupViewMac::~AutocompletePopupViewMac() { // it can call back to us in the destructor. model_.reset(); - // Break references to table_target_ before it is released. - NSTableView* table = [popup_ contentView]; - [table setTarget:nil]; - [table setDataSource:nil]; + // Break references to matrix_target_ before it is released. + NSMatrix* matrix = [popup_ contentView]; + [matrix setTarget:nil]; } bool AutocompletePopupViewMac::IsOpen() const { return [popup_ isVisible] ? true : false; } -static NSTableColumn* MakeTableColumn(int tag, Class field_class) { - NSNumber* id = [NSNumber numberWithInt:tag]; - NSTableColumn* col = - [[[NSTableColumn alloc] initWithIdentifier:id] autorelease]; - - [col setEditable:NO]; - [col setResizingMask:NSTableColumnNoResizing]; - [col setDataCell:[[[field_class alloc] init] autorelease]]; - - return col; -} - void AutocompletePopupViewMac::CreatePopupIfNeeded() { if (!popup_) { popup_.reset([[NSWindow alloc] initWithContentRect:NSZeroRect @@ -89,20 +150,12 @@ void AutocompletePopupViewMac::CreatePopupIfNeeded() { [popup_ setOpaque:YES]; [popup_ setHasShadow:YES]; [popup_ setLevel:NSNormalWindowLevel]; - - NSTableView* table = - [[[NSTableView alloc] initWithFrame:NSZeroRect] autorelease]; - [popup_ setContentView:table]; - - [table setTarget:table_target_]; - [table setAction:@selector(select:)]; - [table setHeaderView:nil]; - [table setDataSource:table_target_]; - [table setIntercellSpacing:NSMakeSize(1.0, 0.0)]; - - [table addTableColumn:MakeTableColumn(0, [NSTextFieldCell class])]; - [table addTableColumn:MakeTableColumn(1, [NSImageCell class])]; - [table addTableColumn:MakeTableColumn(2, [NSTextFieldCell class])]; + + AutocompleteMatrix* matrix = + [[[AutocompleteMatrix alloc] initWithFrame:NSZeroRect] autorelease]; + [matrix setTarget:matrix_target_]; + [matrix setAction:@selector(select:)]; + [popup_ setContentView:matrix]; } } @@ -112,10 +165,9 @@ void AutocompletePopupViewMac::UpdatePopupAppearance() { [[popup_ parentWindow] removeChildWindow:popup_]; [popup_ orderOut:nil]; - // Break references to table_target_ releasing popup_. - NSTableView* table = [popup_ contentView]; - [table setTarget:nil]; - [table setDataSource:nil]; + // Break references to matrix_target_ before releasing popup_. + NSMatrix* matrix = [popup_ contentView]; + [matrix setTarget:nil]; popup_.reset(nil); @@ -132,21 +184,24 @@ void AutocompletePopupViewMac::UpdatePopupAppearance() { NSRect r = [field_ bounds]; r = [field_ convertRectToBase:r]; r.origin = [[field_ window] convertBaseToScreen:r.origin]; - - // TODO(shess): Derive this from the actual image size, once the - // image is in the project. - static const int kStarWidth = 25; - - NSArray* cols = [[popup_ contentView] tableColumns]; - [[cols objectAtIndex:0] setWidth:floor((r.size.width - kStarWidth) / 2)]; - [[cols objectAtIndex:1] setWidth:kStarWidth]; - [[cols objectAtIndex:2] setWidth:ceil((r.size.width - kStarWidth) / 2)]; - - [[popup_ contentView] reloadData]; - [[popup_ contentView] tile]; - r.size.height = [[popup_ contentView] frame].size.height; + + AutocompleteMatrix* matrix = [popup_ contentView]; + [matrix setCellSize:NSMakeSize(r.size.width, [matrix cellSize].height)]; + [matrix setFrameSize:NSMakeSize(r.size.width, [matrix frame].size.height)]; + + size_t rows = model_->result().size(); + [matrix renewRows:rows columns:1]; + [matrix sizeToCells]; + r.size.height = [matrix frame].size.height; r.origin.y -= r.size.height + 2; + for (size_t ii = 0; ii < rows; ++ii) { + AutocompleteButtonCell* cell = [matrix cellAtRow:ii column:0]; + const AutocompleteMatch& match = model_->result().match_at(ii); + [cell setImage:MatchIcon(match)]; + [cell setTitle:MatchText(match)]; + } + // Update the selection. PaintUpdatesNow(); @@ -160,96 +215,135 @@ void AutocompletePopupViewMac::UpdatePopupAppearance() { // This is only called by model in SetSelectedLine() after updating // everything. Popup should already be visible. void AutocompletePopupViewMac::PaintUpdatesNow() { - NSIndexSet* set = [NSIndexSet indexSetWithIndex:model_->selected_line()]; - NSTableView* table = [popup_ contentView]; - [table selectRowIndexes:set byExtendingSelection:NO]; + AutocompleteMatrix* matrix = [popup_ contentView]; + [matrix selectCellAtRow:model_->selected_line() column:0]; } AutocompletePopupModel* AutocompletePopupViewMac::GetModel() { return model_.get(); } -size_t AutocompletePopupViewMac::ResultRowCount() { - return model_->result().size(); +void AutocompletePopupViewMac::AcceptInput() { + AutocompleteMatrix* matrix = [popup_ contentView]; + model_->SetSelectedLine([matrix selectedRow], false); + edit_view_->AcceptInput(CURRENT_TAB, false); } -const std::wstring& AutocompletePopupViewMac::ResultContentsAt(size_t i) { - return model_->result().match_at(i).contents; -} +@implementation AutocompleteButtonCell -bool AutocompletePopupViewMac::ResultStarredAt(size_t i) { - return model_->result().match_at(i).starred; -} +- init { + self = [super init]; + if (self) { + [self setImagePosition:NSImageLeft]; + [self setBordered:NO]; + [self setButtonType:NSRadioButton]; -const std::wstring& AutocompletePopupViewMac::ResultDescriptionAt(size_t i) { - return model_->result().match_at(i).description; + // Without this highlighting messes up white areas of images. + [self setHighlightsBy:NSNoCellMask]; + } + return self; } -void AutocompletePopupViewMac::AcceptInput() { - NSTableView* table = [popup_ contentView]; - model_->SetSelectedLine([table selectedRow], false); - edit_view_->AcceptInput(CURRENT_TAB, false); +- (NSColor*)backgroundColor { + if ([self state] == NSOnState) { + return SelectedBackgroundColor(); + } else if ([self isHighlighted]) { + return HoveredBackgroundColor(); + } + return BackgroundColor(); } -@implementation AutocompleteTableTarget +@end -- initWithPopupView:(AutocompletePopupViewMac*)view { - self = [super init]; - if (self) { - popup_view_ = view; +@implementation AutocompleteMatrix + +// Remove all tracking areas and initialize the one we want. Removing +// all might be overkill, but it's unclear why there would be others +// for the popup window. +- (void)resetTrackingArea { + for (NSTrackingArea* trackingArea in [self trackingAreas]) { + [self removeTrackingArea:trackingArea]; } - return self; + + // TODO(shess): Consider overriding -acceptsFirstMouse: and changing + // NSTrackingActiveInActiveApp to NSTrackingActiveAlways. + NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited; + options |= NSTrackingMouseMoved; + options |= NSTrackingActiveInActiveApp; + options |= NSTrackingInVisibleRect; + + NSTrackingArea* trackingArea = + [[[NSTrackingArea alloc] initWithRect:[self frame] + options:options + owner:self + userInfo:nil] autorelease]; + [self addTrackingArea:trackingArea]; } -- (NSImage*)starImage { - // TODO(shess): Figure out a way to share this image with the - // toolbar controller. - return [NSImage imageNamed:@"starred"]; +- (void)updateTrackingAreas { + [self resetTrackingArea]; + [super updateTrackingAreas]; } -- (NSInteger)numberOfRowsInTableView:(NSTableView*)aTableView { - DCHECK(popup_view_); - return static_cast<NSInteger>(popup_view_->ResultRowCount()); +- initWithFrame:(NSRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self setCellClass:[AutocompleteButtonCell class]]; + + [self setIntercellSpacing:NSMakeSize(1.0, 1.0)]; + [self setDrawsBackground:YES]; + [self setBackgroundColor:BackgroundColor()]; + [self renewRows:0 columns:1]; + [self setAllowsEmptySelection:YES]; + [self setMode:NSRadioModeMatrix]; + [self deselectAllCells]; + + [self resetTrackingArea]; + } + return self; } -- (id)tableView:(NSTableView*)aTableView -objectValueForTableColumn:(NSTableColumn*)aTableColumn - row:(int)ri { - int columnIndex = [[aTableColumn identifier] integerValue]; - size_t rowIndex = static_cast<size_t>(ri); - DCHECK(popup_view_); - DCHECK_LT(rowIndex, popup_view_->ResultRowCount()); - DCHECK_LT(columnIndex, 3); +- (void)highlightRowAt:(NSInteger)rowIndex { + // highlightCell will be nil if rowIndex is out of range, so no cell + // will be highlighted. + NSCell* highlightCell = [self cellAtRow:rowIndex column:0]; - if (columnIndex == 1) { - if (popup_view_->ResultStarredAt(rowIndex)) { - return [self starImage]; - } - return nil; + for (NSCell* cell in [self cells]) { + [cell setHighlighted:(cell == highlightCell)]; } +} - NSString* s; - if (columnIndex == 0) { - s = base::SysWideToNSString(popup_view_->ResultContentsAt(rowIndex)); +- (void)highlightRowUnder:(NSEvent*)theEvent { + NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil]; + NSInteger row, column; + if ([self getRow:&row column:&column forPoint:point]) { + [self highlightRowAt:row]; } else { - s = base::SysWideToNSString(popup_view_->ResultDescriptionAt(rowIndex)); + [self highlightRowAt:-1]; } +} - NSMutableParagraphStyle* style = - [[[NSMutableParagraphStyle alloc] init] autorelease]; - [style setLineBreakMode:NSLineBreakByTruncatingTail]; +// Callbacks from NSTrackingArea. +- (void)mouseEntered:(NSEvent*)theEvent { + [self highlightRowUnder:theEvent]; +} +- (void)mouseMoved:(NSEvent*)theEvent { + [self highlightRowUnder:theEvent]; +} +- (void)mouseExited:(NSEvent*)theEvent { + [self highlightRowAt:-1]; +} - NSMutableAttributedString* as = - [[[NSMutableAttributedString alloc] initWithString:s] autorelease]; - [as addAttribute:NSParagraphStyleAttributeName value:style - range:NSMakeRange(0, [s length])]; +@end - // TODO(shess): There is a ton more styling to be done, here, for - // instance URLs different from search suggestions different from secure - // URLs, etc. [See AutocompletePopupViewMac::UpdateAndStyleText().] - // Deferring in the interests of getting a minimal implementation in. +@implementation AutocompleteMatrixTarget - return as; +- initWithPopupView:(AutocompletePopupViewMac*)view { + self = [super init]; + if (self) { + popup_view_ = view; + } + return self; } - (void)select:sender { diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 7430149..8804cf7 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -1814,6 +1814,11 @@ 'app/theme/grow_box.png', 'app/theme/nav.pdf', 'app/theme/newtab.pdf', + 'app/theme/o2_globe.png', + 'app/theme/o2_history.png', + 'app/theme/o2_more.png', + 'app/theme/o2_search.png', + 'app/theme/o2_star.png', 'app/theme/reload.pdf', 'app/theme/sadtab.png', 'app/theme/star.pdf', |