[AiS] Mac AiS on two lines
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / omnibox / omnibox_popup_matrix.mm
blobf7282457066f8dd4629f4bf2d879f880a8006886
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.h"
7 #include "base/logging.h"
8 #include "base/mac/foundation_util.h"
9 #import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h"
10 #include "chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h"
11 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h"
12 #include "components/omnibox/autocomplete_result.h"
14 namespace {
16 // NSEvent -buttonNumber for middle mouse button.
17 const NSInteger kMiddleButtonNumber = 2;
19 }  // namespace
21 @interface OmniboxPopupTableController ()
22 - (instancetype)initWithArray:(NSArray*)array;
23 @end
25 @implementation OmniboxPopupTableController
27 - (instancetype)initWithMatchResults:(const AutocompleteResult&)result
28                            tableView:(OmniboxPopupMatrix*)tableView
29                            popupView:(const OmniboxPopupViewMac&)popupView
30                          answerImage:(NSImage*)answerImage {
31   base::scoped_nsobject<NSMutableArray> array([[NSMutableArray alloc] init]);
32   CGFloat max_match_contents_width = 0.0f;
33   CGFloat contentsOffset = -1.0f;
34   for (const AutocompleteMatch& match : result) {
35     if (match.type == AutocompleteMatchType::SEARCH_SUGGEST_TAIL &&
36         contentsOffset < 0.0f)
37       contentsOffset = [OmniboxPopupCell computeContentsOffset:match];
38     base::scoped_nsobject<OmniboxPopupCellData> cellData(
39         [[OmniboxPopupCellData alloc]
40              initWithMatch:match
41             contentsOffset:contentsOffset
42                      image:popupView.ImageForMatch(match)
43                answerImage:(match.answer ? answerImage : nil)]);
44     [array addObject:cellData];
45     if (match.type == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) {
46       max_match_contents_width =
47           std::max(max_match_contents_width, [cellData getMatchContentsWidth]);
48     }
49   }
51   [tableView setMaxMatchContentsWidth:max_match_contents_width];
52   return [self initWithArray:array];
55 - (instancetype)initWithArray:(NSArray*)array {
56   if ((self = [super init])) {
57     hoveredIndex_ = -1;
58     array_.reset([array copy]);
59   }
60   return self;
63 - (NSInteger)numberOfRowsInTableView:(NSTableView*)tableView {
64   return [array_ count];
67 - (id)tableView:(NSTableView*)tableView
68     objectValueForTableColumn:(NSTableColumn*)tableColumn
69                           row:(NSInteger)rowIndex {
70   return [array_ objectAtIndex:rowIndex];
73 - (void)tableView:(NSTableView*)tableView
74     setObjectValue:(id)object
75     forTableColumn:(NSTableColumn*)tableColumn
76                row:(NSInteger)rowIndex {
77   NOTREACHED();
80 - (void)tableView:(NSTableView*)tableView
81     willDisplayCell:(id)cell
82      forTableColumn:(NSTableColumn*)tableColumn
83                 row:(NSInteger)rowIndex {
84   OmniboxPopupCell* popupCell =
85       base::mac::ObjCCastStrict<OmniboxPopupCell>(cell);
86   [popupCell
87       setState:([tableView selectedRow] == rowIndex) ? NSOnState : NSOffState];
88   [popupCell highlight:(hoveredIndex_ == rowIndex)
89              withFrame:[tableView bounds]
90                 inView:tableView];
93 - (NSInteger)highlightedRow {
94   return hoveredIndex_;
97 - (void)setHighlightedRow:(NSInteger)rowIndex {
98   hoveredIndex_ = rowIndex;
101 - (CGFloat)tableView:(NSTableView*)tableView heightOfRow:(NSInteger)row {
102   CGFloat height = kContentLineHeight;
103   if ([[array_ objectAtIndex:row] isAnswer]) {
104     OmniboxPopupMatrix* matrix =
105         base::mac::ObjCCastStrict<OmniboxPopupMatrix>(tableView);
106     height += [matrix answerLineHeight];
107   }
108   return height;
111 @end
113 @interface OmniboxPopupMatrix ()
114 - (OmniboxPopupTableController*)controller;
115 - (void)resetTrackingArea;
116 - (void)highlightRowUnder:(NSEvent*)theEvent;
117 - (BOOL)selectCellForEvent:(NSEvent*)theEvent;
118 @end
120 @implementation OmniboxPopupMatrix
122 @synthesize separator = separator_;
123 @synthesize maxMatchContentsWidth = maxMatchContentsWidth_;
124 @synthesize answerLineHeight = answerLineHeight_;
126 - (instancetype)initWithObserver:(OmniboxPopupMatrixObserver*)observer {
127   if ((self = [super initWithFrame:NSZeroRect])) {
128     observer_ = observer;
130     base::scoped_nsobject<NSTableColumn> column(
131         [[NSTableColumn alloc] initWithIdentifier:@"MainColumn"]);
132     [column setDataCell:[[[OmniboxPopupCell alloc] init] autorelease]];
133     [self addTableColumn:column];
135     // Cells pack with no spacing.
136     [self setIntercellSpacing:NSMakeSize(0.0, 0.0)];
138     [self setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleNone];
139     [self setBackgroundColor:[NSColor controlBackgroundColor]];
140     [self setAllowsEmptySelection:YES];
141     [self deselectAll:self];
143     [self resetTrackingArea];
145     base::scoped_nsobject<NSLayoutManager> layoutManager(
146         [[NSLayoutManager alloc] init]);
147     answerLineHeight_ =
148         [layoutManager defaultLineHeightForFont:OmniboxViewMac::GetLargeFont(
149                                                     gfx::Font::NORMAL)];
150   }
151   return self;
154 - (OmniboxPopupTableController*)controller {
155   return base::mac::ObjCCastStrict<OmniboxPopupTableController>(
156       [self delegate]);
159 - (void)setObserver:(OmniboxPopupMatrixObserver*)observer {
160   observer_ = observer;
163 - (void)updateTrackingAreas {
164   [self resetTrackingArea];
165   [super updateTrackingAreas];
168 // Callbacks from tracking area.
169 - (void)mouseEntered:(NSEvent*)theEvent {
170   [self highlightRowUnder:theEvent];
173 - (void)mouseMoved:(NSEvent*)theEvent {
174   [self highlightRowUnder:theEvent];
177 - (void)mouseExited:(NSEvent*)theEvent {
178   [[self controller] setHighlightedRow:-1];
181 // The tracking area events aren't forwarded during a drag, so handle
182 // highlighting manually for middle-click and middle-drag.
183 - (void)otherMouseDown:(NSEvent*)theEvent {
184   if ([theEvent buttonNumber] == kMiddleButtonNumber) {
185     [self highlightRowUnder:theEvent];
186   }
187   [super otherMouseDown:theEvent];
190 - (void)otherMouseDragged:(NSEvent*)theEvent {
191   if ([theEvent buttonNumber] == kMiddleButtonNumber) {
192     [self highlightRowUnder:theEvent];
193   }
194   [super otherMouseDragged:theEvent];
197 - (void)otherMouseUp:(NSEvent*)theEvent {
198   // Only intercept middle button.
199   if ([theEvent buttonNumber] != kMiddleButtonNumber) {
200     [super otherMouseUp:theEvent];
201     return;
202   }
204   // -otherMouseDragged: should always have been called at this location, but
205   // make sure the user is getting the right feedback.
206   [self highlightRowUnder:theEvent];
208   const NSInteger highlightedRow = [[self controller] highlightedRow];
209   if (highlightedRow != -1) {
210     DCHECK(observer_);
211     observer_->OnMatrixRowMiddleClicked(self, highlightedRow);
212   }
215 // Track the mouse until released, keeping the cell under the mouse selected.
216 // If the mouse wanders off-view, revert to the originally-selected cell. If
217 // the mouse is released over a cell, call the delegate to open the row's URL.
218 - (void)mouseDown:(NSEvent*)theEvent {
219   NSCell* selectedCell = [self selectedCell];
221   // Clear any existing highlight.
222   [[self controller] setHighlightedRow:-1];
224   do {
225     if (![self selectCellForEvent:theEvent]) {
226       [self selectCell:selectedCell];
227     }
229     const NSUInteger mask = NSLeftMouseUpMask | NSLeftMouseDraggedMask;
230     theEvent = [[self window] nextEventMatchingMask:mask];
231   } while ([theEvent type] == NSLeftMouseDragged);
233   // Do not message the delegate if released outside view.
234   if (![self selectCellForEvent:theEvent]) {
235     [self selectCell:selectedCell];
236   } else {
237     const NSInteger selectedRow = [self selectedRow];
239     // No row could be selected if the model failed to update.
240     if (selectedRow == -1) {
241       NOTREACHED();
242       return;
243     }
245     DCHECK(observer_);
246     observer_->OnMatrixRowClicked(self, selectedRow);
247   }
250 - (void)selectRowIndex:(NSInteger)rowIndex {
251   NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:rowIndex];
252   [self selectRowIndexes:indexSet byExtendingSelection:NO];
255 - (NSInteger)highlightedRow {
256   return [[self controller] highlightedRow];
259 - (void)setController:(OmniboxPopupTableController*)controller {
260   matrixController_.reset([controller retain]);
261   [self setDelegate:controller];
262   [self setDataSource:controller];
263   [self reloadData];
266 - (void)resetTrackingArea {
267   if (trackingArea_.get())
268     [self removeTrackingArea:trackingArea_.get()];
270   trackingArea_.reset([[CrTrackingArea alloc]
271       initWithRect:[self frame]
272            options:NSTrackingMouseEnteredAndExited |
273                    NSTrackingMouseMoved |
274                    NSTrackingActiveInActiveApp |
275                    NSTrackingInVisibleRect
276              owner:self
277           userInfo:nil]);
278   [self addTrackingArea:trackingArea_.get()];
281 - (void)highlightRowUnder:(NSEvent*)theEvent {
282   NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
283   NSInteger oldRow = [[self controller] highlightedRow];
284   NSInteger newRow = [self rowAtPoint:point];
285   if (oldRow != newRow) {
286     [[self controller] setHighlightedRow:newRow];
287     [self setNeedsDisplayInRect:[self rectOfRow:oldRow]];
288     [self setNeedsDisplayInRect:[self rectOfRow:newRow]];
289   }
292 - (BOOL)selectCellForEvent:(NSEvent*)theEvent {
293   NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
295   NSInteger row = [self rowAtPoint:point];
296   [self selectRowIndex:row];
297   if (row != -1) {
298     DCHECK(observer_);
299     observer_->OnMatrixRowSelected(self, row);
300     return YES;
301   }
302   return NO;
305 @end