gpu: Tweak Android WebGL test expectations
[chromium-blink-merge.git] / ui / app_list / cocoa / apps_search_box_controller.mm
blob4f02a20c56bab4b6dc0aae6751a71f07411535e5
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 "ui/app_list/cocoa/apps_search_box_controller.h"
7 #include "base/mac/foundation_util.h"
8 #include "base/mac/mac_util.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "grit/ui_resources.h"
11 #import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h"
12 #include "ui/app_list/app_list_menu.h"
13 #import "ui/app_list/cocoa/current_user_menu_item_view.h"
14 #include "ui/app_list/search_box_model.h"
15 #include "ui/app_list/search_box_model_observer.h"
16 #import "ui/base/cocoa/controls/hover_image_menu_button.h"
17 #import "ui/base/cocoa/controls/hover_image_menu_button_cell.h"
18 #import "ui/base/cocoa/menu_controller.h"
19 #include "ui/base/resource/resource_bundle.h"
20 #include "ui/gfx/image/image_skia_util_mac.h"
22 namespace {
24 // Padding either side of the search icon and menu button.
25 const CGFloat kPadding = 14;
27 // Size of the search icon.
28 const CGFloat kSearchIconDimension = 32;
30 // Size of the menu button on the right.
31 const CGFloat kMenuButtonDimension = 29;
33 // Menu offset relative to the bottom-right corner of the menu button.
34 const CGFloat kMenuYOffsetFromButton = -4;
35 const CGFloat kMenuXOffsetFromButton = -7;
39 @interface AppsSearchBoxController ()
41 - (NSImageView*)searchImageView;
42 - (void)addSubviews;
44 @end
46 namespace app_list {
48 class SearchBoxModelObserverBridge : public SearchBoxModelObserver {
49  public:
50   SearchBoxModelObserverBridge(AppsSearchBoxController* parent);
51   virtual ~SearchBoxModelObserverBridge();
53   void SetSearchText(const base::string16& text);
55   virtual void IconChanged() OVERRIDE;
56   virtual void HintTextChanged() OVERRIDE;
57   virtual void SelectionModelChanged() OVERRIDE;
58   virtual void TextChanged() OVERRIDE;
60  private:
61   SearchBoxModel* GetModel();
63   AppsSearchBoxController* parent_;  // Weak. Owns us.
65   DISALLOW_COPY_AND_ASSIGN(SearchBoxModelObserverBridge);
68 SearchBoxModelObserverBridge::SearchBoxModelObserverBridge(
69     AppsSearchBoxController* parent)
70     : parent_(parent) {
71   IconChanged();
72   HintTextChanged();
73   GetModel()->AddObserver(this);
76 SearchBoxModelObserverBridge::~SearchBoxModelObserverBridge() {
77   GetModel()->RemoveObserver(this);
80 SearchBoxModel* SearchBoxModelObserverBridge::GetModel() {
81   SearchBoxModel* searchBoxModel = [[parent_ delegate] searchBoxModel];
82   DCHECK(searchBoxModel);
83   return searchBoxModel;
86 void SearchBoxModelObserverBridge::SetSearchText(const base::string16& text) {
87   SearchBoxModel* model = GetModel();
88   model->RemoveObserver(this);
89   model->SetText(text);
90   // TODO(tapted): See if this should call SetSelectionModel here.
91   model->AddObserver(this);
94 void SearchBoxModelObserverBridge::IconChanged() {
95   [[parent_ searchImageView] setImage:gfx::NSImageFromImageSkiaWithColorSpace(
96       GetModel()->icon(), base::mac::GetSRGBColorSpace())];
99 void SearchBoxModelObserverBridge::HintTextChanged() {
100   [[[parent_ searchTextField] cell] setPlaceholderString:
101       base::SysUTF16ToNSString(GetModel()->hint_text())];
104 void SearchBoxModelObserverBridge::SelectionModelChanged() {
105   // TODO(tapted): See if anything needs to be done here for RTL.
108 void SearchBoxModelObserverBridge::TextChanged() {
109   // Currently the model text is only changed when we are not observing it, or
110   // it is changed in tests to establish a particular state.
111   [[parent_ searchTextField]
112       setStringValue:base::SysUTF16ToNSString(GetModel()->text())];
115 }  // namespace app_list
117 @interface SearchTextField : NSTextField {
118  @private
119   NSRect textFrameInset_;
122 @property(readonly, nonatomic) NSRect textFrameInset;
124 - (void)setMarginsWithLeftMargin:(CGFloat)leftMargin
125                      rightMargin:(CGFloat)rightMargin;
127 @end
129 @interface AppListMenuController : MenuController {
130  @private
131   AppsSearchBoxController* searchBoxController_;  // Weak. Owns us.
134 - (id)initWithSearchBoxController:(AppsSearchBoxController*)parent;
136 @end
138 @implementation AppsSearchBoxController
140 @synthesize delegate = delegate_;
142 - (id)initWithFrame:(NSRect)frame {
143   if ((self = [super init])) {
144     base::scoped_nsobject<NSView> containerView(
145         [[NSView alloc] initWithFrame:frame]);
146     [self setView:containerView];
147     [self addSubviews];
148   }
149   return self;
152 - (void)clearSearch {
153   [searchTextField_ setStringValue:@""];
154   [self controlTextDidChange:nil];
157 - (void)rebuildMenu {
158   if (![delegate_ appListDelegate])
159     return;
161   menuController_.reset([[AppListMenuController alloc]
162       initWithSearchBoxController:self]);
163   [menuButton_ setMenu:[menuController_ menu]];  // Menu will populate here.
166 - (void)setDelegate:(id<AppsSearchBoxDelegate>)delegate {
167   [[menuButton_ menu] removeAllItems];
168   menuController_.reset();
169   appListMenu_.reset();
170   bridge_.reset();  // Ensure observers are cleared before updating |delegate_|.
171   delegate_ = delegate;
172   if (!delegate_)
173     return;
175   bridge_.reset(new app_list::SearchBoxModelObserverBridge(self));
176   if (![delegate_ appListDelegate])
177     return;
179   appListMenu_.reset(new app_list::AppListMenu([delegate_ appListDelegate]));
180   [self rebuildMenu];
183 - (NSTextField*)searchTextField {
184   return searchTextField_;
187 - (NSPopUpButton*)menuControl {
188   return menuButton_;
191 - (app_list::AppListMenu*)appListMenu {
192   return appListMenu_.get();
195 - (NSImageView*)searchImageView {
196   return searchImageView_;
199 - (void)addSubviews {
200   NSRect viewBounds = [[self view] bounds];
201   searchImageView_.reset([[NSImageView alloc] initWithFrame:NSMakeRect(
202       kPadding, 0, kSearchIconDimension, NSHeight(viewBounds))]);
204   searchTextField_.reset([[SearchTextField alloc] initWithFrame:viewBounds]);
205   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
206   [searchTextField_ setDelegate:self];
207   [searchTextField_ setFont:rb.GetFont(
208       ui::ResourceBundle::MediumFont).GetNativeFont()];
209   [searchTextField_
210       setMarginsWithLeftMargin:NSMaxX([searchImageView_ frame]) + kPadding
211                    rightMargin:kMenuButtonDimension + 2 * kPadding];
213   // Add the drop-down menu, with a custom button.
214   NSRect buttonFrame = NSMakeRect(
215       NSWidth(viewBounds) - kMenuButtonDimension - kPadding,
216       floor(NSMidY(viewBounds) - kMenuButtonDimension / 2),
217       kMenuButtonDimension,
218       kMenuButtonDimension);
219   menuButton_.reset([[HoverImageMenuButton alloc] initWithFrame:buttonFrame
220                                                       pullsDown:YES]);
221   [[menuButton_ hoverImageMenuButtonCell] setDefaultImage:
222       rb.GetNativeImageNamed(IDR_APP_LIST_TOOLS_NORMAL).AsNSImage()];
223   [[menuButton_ hoverImageMenuButtonCell] setAlternateImage:
224       rb.GetNativeImageNamed(IDR_APP_LIST_TOOLS_PRESSED).AsNSImage()];
225   [[menuButton_ hoverImageMenuButtonCell] setHoverImage:
226       rb.GetNativeImageNamed(IDR_APP_LIST_TOOLS_HOVER).AsNSImage()];
228   [[self view] addSubview:searchImageView_];
229   [[self view] addSubview:searchTextField_];
230   [[self view] addSubview:menuButton_];
233 - (BOOL)control:(NSControl*)control
234                textView:(NSTextView*)textView
235     doCommandBySelector:(SEL)command {
236   // Forward the message first, to handle grid or search results navigation.
237   BOOL handled = [delegate_ control:control
238                            textView:textView
239                 doCommandBySelector:command];
240   if (handled)
241     return YES;
243   // If the delegate did not handle the escape key, it means the window was not
244   // dismissed because there were search results. Clear them.
245   if (command == @selector(complete:)) {
246     [self clearSearch];
247     return YES;
248   }
250   return NO;
253 - (void)controlTextDidChange:(NSNotification*)notification {
254   if (bridge_) {
255     bridge_->SetSearchText(
256         base::SysNSStringToUTF16([searchTextField_ stringValue]));
257   }
259   [delegate_ modelTextDidChange];
262 @end
264 @interface SearchTextFieldCell : NSTextFieldCell;
266 - (NSRect)textFrameForFrameInternal:(NSRect)cellFrame;
268 @end
270 @implementation SearchTextField
272 @synthesize textFrameInset = textFrameInset_;
274 + (Class)cellClass {
275   return [SearchTextFieldCell class];
278 - (id)initWithFrame:(NSRect)theFrame {
279   if ((self = [super initWithFrame:theFrame])) {
280     [self setFocusRingType:NSFocusRingTypeNone];
281     [self setDrawsBackground:NO];
282     [self setBordered:NO];
283   }
284   return self;
287 - (void)setMarginsWithLeftMargin:(CGFloat)leftMargin
288                      rightMargin:(CGFloat)rightMargin {
289   // Find the preferred height for the current text properties, and center.
290   NSRect viewBounds = [self bounds];
291   [self sizeToFit];
292   NSRect textBounds = [self bounds];
293   textFrameInset_.origin.x = leftMargin;
294   textFrameInset_.origin.y = floor(NSMidY(viewBounds) - NSMidY(textBounds));
295   textFrameInset_.size.width = leftMargin + rightMargin;
296   textFrameInset_.size.height = NSHeight(viewBounds) - NSHeight(textBounds);
297   [self setFrame:viewBounds];
300 @end
302 @implementation SearchTextFieldCell
304 - (NSRect)textFrameForFrameInternal:(NSRect)cellFrame {
305   SearchTextField* searchTextField =
306       base::mac::ObjCCastStrict<SearchTextField>([self controlView]);
307   NSRect insetRect = [searchTextField textFrameInset];
308   cellFrame.origin.x += insetRect.origin.x;
309   cellFrame.origin.y += insetRect.origin.y;
310   cellFrame.size.width -= insetRect.size.width;
311   cellFrame.size.height -= insetRect.size.height;
312   return cellFrame;
315 - (NSRect)textFrameForFrame:(NSRect)cellFrame {
316   return [self textFrameForFrameInternal:cellFrame];
319 - (NSRect)textCursorFrameForFrame:(NSRect)cellFrame {
320   return [self textFrameForFrameInternal:cellFrame];
323 - (void)resetCursorRect:(NSRect)cellFrame
324                  inView:(NSView*)controlView {
325   [super resetCursorRect:[self textCursorFrameForFrame:cellFrame]
326                   inView:controlView];
329 - (NSRect)drawingRectForBounds:(NSRect)theRect {
330   return [super drawingRectForBounds:[self textFrameForFrame:theRect]];
333 - (void)editWithFrame:(NSRect)cellFrame
334                inView:(NSView*)controlView
335                editor:(NSText*)editor
336              delegate:(id)delegate
337                 event:(NSEvent*)event {
338   [super editWithFrame:[self textFrameForFrame:cellFrame]
339                 inView:controlView
340                 editor:editor
341               delegate:delegate
342                  event:event];
345 - (void)selectWithFrame:(NSRect)cellFrame
346                  inView:(NSView*)controlView
347                  editor:(NSText*)editor
348                delegate:(id)delegate
349                   start:(NSInteger)start
350                  length:(NSInteger)length {
351   [super selectWithFrame:[self textFrameForFrame:cellFrame]
352                   inView:controlView
353                   editor:editor
354                 delegate:delegate
355                    start:start
356                   length:length];
359 @end
361 @implementation AppListMenuController
363 - (id)initWithSearchBoxController:(AppsSearchBoxController*)parent {
364   // Need to initialze super with a NULL model, otherwise it will immediately
365   // try to populate, which can't be done until setting the parent.
366   if ((self = [super initWithModel:NULL
367             useWithPopUpButtonCell:YES])) {
368     searchBoxController_ = parent;
369     [super setModel:[parent appListMenu]->menu_model()];
370   }
371   return self;
374 - (void)addItemToMenu:(NSMenu*)menu
375               atIndex:(NSInteger)index
376             fromModel:(ui::MenuModel*)model {
377   [super addItemToMenu:menu
378                atIndex:index
379              fromModel:model];
380   if (model->GetCommandIdAt(index) != app_list::AppListMenu::CURRENT_USER)
381     return;
383   base::scoped_nsobject<NSView> customItemView([[CurrentUserMenuItemView alloc]
384       initWithDelegate:[[searchBoxController_ delegate] appListDelegate]]);
385   [[menu itemAtIndex:index] setView:customItemView];
388 - (NSRect)confinementRectForMenu:(NSMenu*)menu
389                         onScreen:(NSScreen*)screen {
390   NSPopUpButton* menuButton = [searchBoxController_ menuControl];
391   // Ensure the menu comes up below the menu button by trimming the window frame
392   // to a point anchored below the bottom right of the button.
393   NSRect anchorRect = [menuButton convertRect:[menuButton bounds]
394                                        toView:nil];
395   NSPoint anchorPoint = [[menuButton window] convertBaseToScreen:NSMakePoint(
396       NSMaxX(anchorRect) + kMenuXOffsetFromButton,
397       NSMinY(anchorRect) - kMenuYOffsetFromButton)];
398   NSRect confinementRect = [[menuButton window] frame];
399   confinementRect.size = NSMakeSize(anchorPoint.x - NSMinX(confinementRect),
400                                     anchorPoint.y - NSMinY(confinementRect));
401   return confinementRect;
404 @end