Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 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 "ui/app_list/cocoa/apps_search_box_controller.h" | 5 #import "ui/app_list/cocoa/apps_search_box_controller.h" |
| 6 | 6 |
| 7 #include "base/strings/sys_string_conversions.h" | 7 #include "base/strings/sys_string_conversions.h" |
| 8 #include "grit/ui_resources.h" | |
| 9 #include "ui/app_list/app_list_menu.h" | |
| 10 #import "ui/app_list/cocoa/current_user_menu_item_view.h" | |
| 8 #include "ui/app_list/search_box_model.h" | 11 #include "ui/app_list/search_box_model.h" |
| 9 #include "ui/app_list/search_box_model_observer.h" | 12 #include "ui/app_list/search_box_model_observer.h" |
| 13 #import "ui/base/cocoa/controls/hover_image_menu_button.h" | |
| 10 #include "ui/base/resource/resource_bundle.h" | 14 #include "ui/base/resource/resource_bundle.h" |
| 11 #include "ui/gfx/image/image_skia_util_mac.h" | 15 #include "ui/gfx/image/image_skia_util_mac.h" |
| 12 | 16 |
| 13 namespace { | 17 namespace { |
| 14 | 18 |
| 15 // Padding either side of the search icon. | 19 // Padding either side of the search icon and menu button. |
| 16 const CGFloat kPadding = 14; | 20 const CGFloat kPadding = 14; |
| 17 | 21 |
| 18 // Size of the search icon. | 22 // Size of the search icon. |
| 19 const CGFloat kSearchIconDimension = 32; | 23 const CGFloat kSearchIconDimension = 32; |
| 20 | 24 |
| 25 // Size of the menu button on the right. | |
| 26 const CGFloat kMenuButtonDimension = 29; | |
| 27 | |
| 28 // Vertical offset that the menu should appear below the menu button. | |
| 29 const CGFloat kMenuOffsetFromButton = 2; | |
| 30 | |
| 21 } | 31 } |
| 22 | 32 |
| 23 @interface AppsSearchBoxController () | 33 @interface AppsSearchBoxController () |
| 24 | 34 |
| 25 - (NSImageView*)searchImage; | 35 - (NSImageView*)searchImage; |
| 26 - (void)addSubviews; | 36 - (void)addSubviews; |
| 37 - (void)menuItemSelected:(NSMenuItem*)sender; | |
| 38 | |
| 39 - (void)addItemToMenu:(NSMenu*)menu | |
| 40 forIndex:(int)index; | |
| 27 | 41 |
| 28 @end | 42 @end |
| 29 | 43 |
| 30 namespace app_list { | 44 namespace app_list { |
| 31 | 45 |
| 32 class SearchBoxModelObserverBridge : public SearchBoxModelObserver { | 46 class SearchBoxModelObserverBridge : public SearchBoxModelObserver { |
| 33 public: | 47 public: |
| 34 SearchBoxModelObserverBridge(AppsSearchBoxController* parent, | 48 SearchBoxModelObserverBridge(AppsSearchBoxController* parent, |
| 35 SearchBoxModel* model); | 49 SearchBoxModel* model); |
| 36 virtual ~SearchBoxModelObserverBridge(); | 50 virtual ~SearchBoxModelObserverBridge(); |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 102 return self; | 116 return self; |
| 103 } | 117 } |
| 104 | 118 |
| 105 - (void)clearSearch { | 119 - (void)clearSearch { |
| 106 [searchInput_ setStringValue:[NSString string]]; | 120 [searchInput_ setStringValue:[NSString string]]; |
| 107 [self controlTextDidChange:nil]; | 121 [self controlTextDidChange:nil]; |
| 108 } | 122 } |
| 109 | 123 |
| 110 - (void)setDelegate:(id<AppsSearchBoxDelegate>)delegate { | 124 - (void)setDelegate:(id<AppsSearchBoxDelegate>)delegate { |
| 111 delegate_ = delegate; | 125 delegate_ = delegate; |
| 126 app_list::AppListViewDelegate* appListDelegate = [delegate appListDelegate]; | |
| 112 app_list::SearchBoxModel* searchBoxModel = [delegate searchBoxModel]; | 127 app_list::SearchBoxModel* searchBoxModel = [delegate searchBoxModel]; |
| 128 appListMenu_.reset(appListDelegate ? | |
| 129 new app_list::AppListMenu(appListDelegate) : NULL); | |
| 113 bridge_.reset(searchBoxModel ? | 130 bridge_.reset(searchBoxModel ? |
| 114 new app_list::SearchBoxModelObserverBridge(self, searchBoxModel) : NULL); | 131 new app_list::SearchBoxModelObserverBridge(self, searchBoxModel) : NULL); |
| 115 } | 132 } |
| 116 | 133 |
| 117 - (NSTextField*)textField { | 134 - (NSTextField*)textField { |
| 118 return searchInput_; | 135 return searchInput_; |
| 119 } | 136 } |
| 120 | 137 |
| 138 - (NSPopUpButton*)menuControl { | |
| 139 return menuButton_; | |
| 140 } | |
| 141 | |
| 142 - (app_list::AppListMenu*)appListMenu { | |
| 143 return appListMenu_.get(); | |
| 144 } | |
| 145 | |
| 121 - (NSImageView*)searchImage { | 146 - (NSImageView*)searchImage { |
| 122 return searchImage_; | 147 return searchImage_; |
| 123 } | 148 } |
| 124 | 149 |
| 125 - (void)addSubviews { | 150 - (void)addSubviews { |
| 126 NSSize viewSize = [[self view] bounds].size; | 151 NSSize viewSize = [[self view] bounds].size; |
| 127 searchImage_.reset([[NSImageView alloc] initWithFrame:NSMakeRect( | 152 searchImage_.reset([[NSImageView alloc] initWithFrame:NSMakeRect( |
| 128 kPadding, 0, kSearchIconDimension, viewSize.height)]); | 153 kPadding, 0, kSearchIconDimension, viewSize.height)]); |
| 129 | 154 |
| 155 ui::ResourceBundle& resourceBundle = ui::ResourceBundle::GetSharedInstance(); | |
| 130 searchInput_.reset([[NSTextField alloc] initWithFrame:NSZeroRect]); | 156 searchInput_.reset([[NSTextField alloc] initWithFrame:NSZeroRect]); |
| 131 [searchInput_ setFocusRingType:NSFocusRingTypeNone]; | 157 [searchInput_ setFocusRingType:NSFocusRingTypeNone]; |
| 132 [searchInput_ setDrawsBackground:NO]; | 158 [searchInput_ setDrawsBackground:NO]; |
| 133 [searchInput_ setBordered:NO]; | 159 [searchInput_ setBordered:NO]; |
| 134 [searchInput_ setDelegate:self]; | 160 [searchInput_ setDelegate:self]; |
| 135 [searchInput_ setFont:ui::ResourceBundle::GetSharedInstance().GetFont( | 161 [searchInput_ setFont:resourceBundle.GetFont( |
| 136 ui::ResourceBundle::MediumFont).GetNativeFont()]; | 162 ui::ResourceBundle::MediumFont).GetNativeFont()]; |
| 137 | 163 |
| 138 // Find the preferred height for those text properties, and center. | 164 // Find the preferred height for those text properties, and center. |
| 139 [searchInput_ sizeToFit]; | 165 [searchInput_ sizeToFit]; |
| 140 CGFloat inputXOffset = kSearchIconDimension + 2 * kPadding; | 166 CGFloat inputXOffset = kSearchIconDimension + 2 * kPadding; |
| 141 CGFloat inputHeight = NSHeight([searchInput_ bounds]); | 167 CGFloat inputHeight = NSHeight([searchInput_ bounds]); |
| 142 [searchInput_ setFrame:NSMakeRect( | 168 [searchInput_ setFrame:NSMakeRect( |
| 143 inputXOffset, | 169 inputXOffset, |
| 144 floor(viewSize.height / 2 - inputHeight / 2), | 170 floor(viewSize.height / 2 - inputHeight / 2), |
| 145 viewSize.width - inputXOffset - kPadding, | 171 viewSize.width - inputXOffset - kMenuButtonDimension - 2 * kPadding, |
| 146 inputHeight)]; | 172 inputHeight)]; |
| 147 | 173 |
| 174 // Add the drop-down menu, with a custom button. | |
| 175 NSRect buttonFrame = NSMakeRect( | |
| 176 NSMaxX([searchInput_ frame]) + kPadding, | |
| 177 floor(viewSize.height / 2 - kMenuButtonDimension / 2), | |
| 178 kMenuButtonDimension, | |
| 179 kMenuButtonDimension); | |
| 180 menuButton_.reset([[HoverImageMenuButton alloc] initWithFrame:buttonFrame | |
| 181 pullsDown:YES]); | |
| 182 [[menuButton_ menu] setDelegate:self]; | |
| 183 [[menuButton_ cell] setImage:resourceBundle.GetNativeImageNamed( | |
|
sail
2013/05/31 19:07:03
use hoverImageMenuButtonCell accessor instead?
tapted
2013/06/03 12:49:18
Done.
| |
| 184 IDR_APP_LIST_TOOLS_NORMAL).AsNSImage()]; | |
| 185 [[menuButton_ cell] setAlternateImage:resourceBundle.GetNativeImageNamed( | |
| 186 IDR_APP_LIST_TOOLS_PRESSED).AsNSImage()]; | |
| 187 [[menuButton_ cell] setHoverImage:resourceBundle.GetNativeImageNamed( | |
| 188 IDR_APP_LIST_TOOLS_HOVER).AsNSImage()]; | |
| 189 | |
| 148 [[self view] addSubview:searchImage_]; | 190 [[self view] addSubview:searchImage_]; |
| 149 [[self view] addSubview:searchInput_]; | 191 [[self view] addSubview:searchInput_]; |
| 192 [[self view] addSubview:menuButton_]; | |
| 193 } | |
| 194 | |
| 195 - (void)menuItemSelected:(NSMenuItem*)sender { | |
| 196 if (!appListMenu_) | |
|
sail
2013/05/31 19:07:03
is this possible?
tapted
2013/06/03 12:49:18
Initially I wasn't sure, but I realised the menu i
| |
| 197 return; | |
| 198 | |
| 199 appListMenu_->menu_model()->ActivatedAt([sender tag]); | |
| 200 } | |
| 201 | |
| 202 - (void)menuNeedsUpdate:(NSMenu*)menu { | |
| 203 if (!appListMenu_ || [menu numberOfItems] != 1) | |
| 204 return; | |
| 205 | |
| 206 int itemCount = appListMenu_->menu_model()->GetItemCount(); | |
|
sail
2013/05/31 19:07:03
do this in addSubviews instead?
tapted
2013/06/03 12:49:18
I initially did it here because it means the creat
| |
| 207 for (int i = 0; i < itemCount; ++i) { | |
| 208 [self addItemToMenu:menu | |
| 209 forIndex:i]; | |
| 210 } | |
| 211 } | |
| 212 | |
| 213 - (void)addItemToMenu:(NSMenu*)menu | |
|
Robert Sesek
2013/05/31 19:16:09
Have you considered using MenuController? https://
tapted
2013/06/03 12:54:24
I have! But, it would need further refactoring to
| |
| 214 forIndex:(int)index { | |
| 215 DCHECK(appListMenu_); | |
| 216 ui::MenuModel* menuModel = appListMenu_->menu_model(); | |
| 217 if (menuModel->GetTypeAt(index) == ui::MenuModel::TYPE_SEPARATOR) { | |
| 218 [menu addItem:[NSMenuItem separatorItem]]; | |
| 219 return; | |
| 220 } | |
| 221 | |
| 222 string16 labelText = menuModel->GetLabelAt(index); | |
| 223 scoped_nsobject<NSMenuItem> item( | |
| 224 [[NSMenuItem alloc] initWithTitle:base::SysUTF16ToNSString(labelText) | |
| 225 action:@selector(menuItemSelected:) | |
| 226 keyEquivalent:[NSString string]]); | |
| 227 [item setTag:index]; | |
| 228 [item setTarget:self]; | |
| 229 | |
| 230 if (menuModel->GetCommandIdAt(index) == app_list::AppListMenu::CURRENT_USER) { | |
| 231 scoped_nsobject<NSView> customItemView([[CurrentUserMenuItemView alloc] | |
| 232 initWithDelegate:[delegate_ appListDelegate] | |
| 233 usingFont:[menu font]]); | |
| 234 | |
| 235 [item setView:customItemView]; | |
| 236 } | |
| 237 | |
| 238 [menu addItem:item]; | |
| 239 } | |
| 240 | |
| 241 - (NSRect)confinementRectForMenu:(NSMenu*)menu | |
| 242 onScreen:(NSScreen*)screen { | |
| 243 // Ensure the menu comes up below the menu button by trimming the window frame | |
| 244 // to a point anchored below the bottom right of the button. | |
| 245 NSPoint anchor = NSMakePoint(kMenuButtonDimension, | |
| 246 kMenuButtonDimension + kMenuOffsetFromButton); | |
| 247 anchor = [menuButton_ convertPoint:anchor | |
| 248 toView:nil]; | |
| 249 anchor = [[menuButton_ window] convertBaseToScreen:anchor]; | |
| 250 NSRect rect = [[menuButton_ window] frame]; | |
| 251 rect.size = NSMakeSize(anchor.x - NSMinX(rect), anchor.y - NSMinY(rect)); | |
|
sail
2013/05/31 19:07:03
how about just converting [menuButton_ bounds] to
tapted
2013/06/03 12:49:18
Sadly, NSWindow convertRectToScreen: is OSX 10.7+
| |
| 252 return rect; | |
| 150 } | 253 } |
| 151 | 254 |
| 152 - (BOOL)control:(NSControl*)control | 255 - (BOOL)control:(NSControl*)control |
| 153 textView:(NSTextView*)textView | 256 textView:(NSTextView*)textView |
| 154 doCommandBySelector:(SEL)command { | 257 doCommandBySelector:(SEL)command { |
| 155 // Forward the message first, to handle grid or search results navigation. | 258 // Forward the message first, to handle grid or search results navigation. |
| 156 BOOL handled = [delegate_ control:control | 259 BOOL handled = [delegate_ control:control |
| 157 textView:textView | 260 textView:textView |
| 158 doCommandBySelector:command]; | 261 doCommandBySelector:command]; |
| 159 if (handled) | 262 if (handled) |
| 160 return YES; | 263 return YES; |
| 161 | 264 |
| 162 // Escape when there is text clears the search input. | 265 // Escape when there is text clears the search input. |
| 163 if (command == @selector(complete:)) { | 266 if (command == @selector(complete:)) { |
| 164 [self clearSearch]; | 267 [self clearSearch]; |
| 165 return YES; | 268 return YES; |
| 166 } | 269 } |
| 167 | 270 |
| 168 return NO; | 271 return NO; |
| 169 } | 272 } |
| 170 | 273 |
| 171 - (void)controlTextDidChange:(NSNotification*)notification { | 274 - (void)controlTextDidChange:(NSNotification*)notification { |
| 172 if (bridge_) | 275 if (bridge_) |
| 173 bridge_->updateModel(base::SysNSStringToUTF16([searchInput_ stringValue])); | 276 bridge_->updateModel(base::SysNSStringToUTF16([searchInput_ stringValue])); |
| 174 | 277 |
| 175 [delegate_ modelTextDidChange]; | 278 [delegate_ modelTextDidChange]; |
| 176 } | 279 } |
| 177 | 280 |
| 178 @end | 281 @end |
| OLD | NEW |