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_results_model_bridge.h" | 5 #import "ui/app_list/cocoa/apps_search_results_model_bridge.h" |
6 | 6 |
7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
8 | 8 |
9 #include "base/strings/sys_string_conversions.h" | 9 #include "base/strings/sys_string_conversions.h" |
10 #include "ui/app_list/app_list_model.h" | 10 #include "ui/app_list/app_list_model.h" |
11 #import "ui/app_list/cocoa/apps_search_results_controller.h" | 11 #import "ui/app_list/cocoa/apps_search_results_controller.h" |
12 #include "ui/app_list/search_result.h" | 12 #include "ui/app_list/search_result.h" |
13 #include "ui/app_list/search_result_observer.h" | 13 #include "ui/app_list/search_result_observer.h" |
| 14 #import "ui/base/cocoa/controls/blue_label_button.h" |
14 #import "ui/base/cocoa/menu_controller.h" | 15 #import "ui/base/cocoa/menu_controller.h" |
15 | 16 |
| 17 namespace { |
| 18 |
| 19 // Padding on the left and right of action buttons. |
| 20 const CGFloat kActionButtonPadding = 10; |
| 21 |
| 22 } |
| 23 |
| 24 // Action target for all the action buttons on a result row. |
| 25 @interface ActionButtonTarget : NSObject { |
| 26 @private |
| 27 app_list::AppsSearchResultsModelBridge::ItemObserver* parent_; |
| 28 } |
| 29 |
| 30 // Designated initializer. |
| 31 - (id)initWithParent: |
| 32 (app_list::AppsSearchResultsModelBridge::ItemObserver*)parent; |
| 33 |
| 34 // |sender| is the NSButton for the action. |
| 35 - (void)onActionButtonClicked:(id)sender; |
| 36 |
| 37 @end |
| 38 |
16 namespace app_list { | 39 namespace app_list { |
17 | 40 |
18 class AppsSearchResultsModelBridge::ItemObserver : public SearchResultObserver { | 41 class AppsSearchResultsModelBridge::ItemObserver : public SearchResultObserver { |
19 public: | 42 public: |
20 ItemObserver(AppsSearchResultsModelBridge* bridge, size_t index) | 43 ItemObserver(AppsSearchResultsModelBridge* bridge, size_t index) |
21 : bridge_(bridge), row_in_view_(index) { | 44 : bridge_(bridge), row_in_view_(index) { |
22 // Cache the result, because the results array is updated before notifying | 45 // Cache the result, because the results array is updated before notifying |
23 // observers (which happens before deleting the SearchResult). | 46 // observers (which happens before deleting the SearchResult). |
24 result_ = [bridge_->parent_ results]->GetItemAt(index); | 47 result_ = [bridge_->parent_ results]->GetItemAt(index); |
| 48 OnActionsChanged(); |
25 result_->AddObserver(this); | 49 result_->AddObserver(this); |
26 } | 50 } |
27 | 51 |
28 virtual ~ItemObserver() { | 52 virtual ~ItemObserver() { |
29 result_->RemoveObserver(this); | 53 result_->RemoveObserver(this); |
| 54 [action_button_ removeFromSuperview]; |
| 55 [action_button_ setTarget:nil]; |
30 } | 56 } |
31 | 57 |
32 NSMenu* GetContextMenu() { | 58 NSMenu* GetContextMenu() { |
33 if (!context_menu_controller_) { | 59 if (!context_menu_controller_) { |
34 ui::MenuModel* menu_model = result_->GetContextMenuModel(); | 60 ui::MenuModel* menu_model = result_->GetContextMenuModel(); |
35 if (!menu_model) | 61 if (!menu_model) |
36 return nil; | 62 return nil; |
37 | 63 |
38 context_menu_controller_.reset( | 64 context_menu_controller_.reset( |
39 [[MenuController alloc] initWithModel:menu_model | 65 [[MenuController alloc] initWithModel:menu_model |
40 useWithPopUpButtonCell:NO]); | 66 useWithPopUpButtonCell:NO]); |
41 } | 67 } |
42 return [context_menu_controller_ menu]; | 68 return [context_menu_controller_ menu]; |
43 } | 69 } |
44 | 70 |
| 71 CGFloat GetActionButtonWidth() { |
| 72 CGFloat width = NSWidth([action_button_ bounds]); |
| 73 if (width != 0) |
| 74 width += kActionButtonPadding; |
| 75 return width; |
| 76 } |
| 77 |
| 78 void InvokeActionAtIndex(int action_index) { |
| 79 [[bridge_->parent_ delegate] invokeResultAction:result_ |
| 80 atIndex:action_index]; |
| 81 } |
| 82 |
45 // SearchResultObserver overrides: | 83 // SearchResultObserver overrides: |
46 virtual void OnIconChanged() OVERRIDE { | 84 virtual void OnIconChanged() OVERRIDE { |
47 bridge_->ReloadDataForItems(row_in_view_, 1); | 85 bridge_->ReloadDataForItems(row_in_view_, 1); |
48 } | 86 } |
49 virtual void OnActionsChanged() OVERRIDE {} | 87 virtual void OnActionsChanged() OVERRIDE; |
50 virtual void OnIsInstallingChanged() OVERRIDE {} | 88 virtual void OnIsInstallingChanged() OVERRIDE {} |
51 virtual void OnPercentDownloadedChanged() OVERRIDE {} | 89 virtual void OnPercentDownloadedChanged() OVERRIDE {} |
52 virtual void OnItemInstalled() OVERRIDE {} | 90 virtual void OnItemInstalled() OVERRIDE {} |
53 virtual void OnItemUninstalled() OVERRIDE; | 91 virtual void OnItemUninstalled() OVERRIDE; |
54 | 92 |
55 private: | 93 private: |
56 AppsSearchResultsModelBridge* bridge_; // Weak. Owns us. | 94 AppsSearchResultsModelBridge* bridge_; // Weak. Owns us. |
57 SearchResult* result_; // Weak. Owned by AppListModel::SearchResults. | 95 SearchResult* result_; // Weak. Owned by AppListModel::SearchResults. |
58 size_t row_in_view_; | 96 size_t row_in_view_; |
| 97 base::scoped_nsobject<ActionButtonTarget> action_target_; |
| 98 base::scoped_nsobject<NSButton> action_button_; |
59 base::scoped_nsobject<MenuController> context_menu_controller_; | 99 base::scoped_nsobject<MenuController> context_menu_controller_; |
60 | 100 |
61 DISALLOW_COPY_AND_ASSIGN(ItemObserver); | 101 DISALLOW_COPY_AND_ASSIGN(ItemObserver); |
62 }; | 102 }; |
63 | 103 |
64 void AppsSearchResultsModelBridge::ItemObserver::OnItemUninstalled() { | 104 void AppsSearchResultsModelBridge::ItemObserver::OnItemUninstalled() { |
65 // Performing the search again will destroy |this|, so post a task. This also | 105 // Performing the search again will destroy |this|, so post a task. This also |
66 // ensures that the AppSearchProvider has observed the uninstall before | 106 // ensures that the AppSearchProvider has observed the uninstall before |
67 // performing the search again, otherwise it will provide a NULL result. | 107 // performing the search again, otherwise it will provide a NULL result. |
68 [[bridge_->parent_ delegate] performSelector:@selector(redoSearch) | 108 [[bridge_->parent_ delegate] performSelector:@selector(redoSearch) |
69 withObject:nil | 109 withObject:nil |
70 afterDelay:0]; | 110 afterDelay:0]; |
71 } | 111 } |
72 | 112 |
| 113 void AppsSearchResultsModelBridge::ItemObserver::OnActionsChanged() { |
| 114 [action_button_ removeFromSuperview]; |
| 115 action_button_.reset(); |
| 116 const SearchResult::Actions& actions = result_->actions(); |
| 117 if (actions.empty()) |
| 118 return; |
| 119 |
| 120 size_t action_index = 0; |
| 121 const SearchResult::Action& action = actions[action_index]; |
| 122 if (action.label_text.empty() || actions.size() != 1) { |
| 123 // TODO(tapted) Support multiple action buttons. |
| 124 NOTIMPLEMENTED(); |
| 125 return; |
| 126 } |
| 127 |
| 128 if (!action_target_) |
| 129 action_target_.reset([[ActionButtonTarget alloc] initWithParent:this]); |
| 130 |
| 131 action_button_.reset([[BlueLabelButton alloc] initWithFrame:NSZeroRect]); |
| 132 [action_button_ setTitle:base::SysUTF16ToNSString(action.label_text)]; |
| 133 [action_button_ setTarget:action_target_]; |
| 134 [action_button_ setAction:@selector(onActionButtonClicked:)]; |
| 135 [action_button_ setTag:action_index]; |
| 136 [action_button_ sizeToFit]; |
| 137 NSRect button_frame = [action_button_ frame]; |
| 138 NSRect cell_frame = |
| 139 [[bridge_->parent_ tableView] frameOfCellAtColumn:0 |
| 140 row:row_in_view_]; |
| 141 NSPoint origin = NSMakePoint( |
| 142 NSWidth(cell_frame) - NSWidth(button_frame) - kActionButtonPadding, |
| 143 ceil(NSMidY(cell_frame) - NSMidY(button_frame))); |
| 144 [action_button_ setFrameOrigin:origin]; |
| 145 [[bridge_->parent_ tableView] addSubview:action_button_]; |
| 146 } |
| 147 |
73 AppsSearchResultsModelBridge::AppsSearchResultsModelBridge( | 148 AppsSearchResultsModelBridge::AppsSearchResultsModelBridge( |
74 AppsSearchResultsController* results_controller) | 149 AppsSearchResultsController* results_controller) |
75 : parent_(results_controller) { | 150 : parent_(results_controller) { |
76 UpdateItemObservers(); | 151 UpdateItemObservers(); |
77 [parent_ results]->AddObserver(this); | 152 [parent_ results]->AddObserver(this); |
78 } | 153 } |
79 | 154 |
80 AppsSearchResultsModelBridge::~AppsSearchResultsModelBridge() { | 155 AppsSearchResultsModelBridge::~AppsSearchResultsModelBridge() { |
81 [parent_ results]->RemoveObserver(this); | 156 [parent_ results]->RemoveObserver(this); |
82 } | 157 } |
83 | 158 |
84 NSMenu* AppsSearchResultsModelBridge::MenuForItem(size_t index) { | 159 NSMenu* AppsSearchResultsModelBridge::MenuForItem(size_t index) { |
85 DCHECK_LT(index, item_observers_.size()); | 160 DCHECK_LT(index, item_observers_.size()); |
86 return item_observers_[index]->GetContextMenu(); | 161 return item_observers_[index]->GetContextMenu(); |
87 } | 162 } |
88 | 163 |
| 164 CGFloat AppsSearchResultsModelBridge::ActionButtonWidthForItem(size_t index) { |
| 165 DCHECK_LT(index, item_observers_.size()); |
| 166 return item_observers_[index]->GetActionButtonWidth(); |
| 167 } |
| 168 |
89 void AppsSearchResultsModelBridge::UpdateItemObservers() { | 169 void AppsSearchResultsModelBridge::UpdateItemObservers() { |
90 DCHECK(item_observers_.empty()); | 170 DCHECK(item_observers_.empty()); |
91 const size_t itemCount = [parent_ results]->item_count(); | 171 const size_t itemCount = [parent_ results]->item_count(); |
92 for (size_t i = 0 ; i < itemCount; ++i) | 172 for (size_t i = 0 ; i < itemCount; ++i) |
93 item_observers_.push_back(new ItemObserver(this, i)); | 173 item_observers_.push_back(new ItemObserver(this, i)); |
94 } | 174 } |
95 | 175 |
96 void AppsSearchResultsModelBridge::ReloadDataForItems( | 176 void AppsSearchResultsModelBridge::ReloadDataForItems( |
97 size_t start, size_t count) const { | 177 size_t start, size_t count) const { |
98 NSIndexSet* column = [NSIndexSet indexSetWithIndex:0]; | 178 NSIndexSet* column = [NSIndexSet indexSetWithIndex:0]; |
(...skipping 29 matching lines...) Expand all Loading... |
128 } | 208 } |
129 | 209 |
130 void AppsSearchResultsModelBridge::ListItemsChanged( | 210 void AppsSearchResultsModelBridge::ListItemsChanged( |
131 size_t start, size_t count) { | 211 size_t start, size_t count) { |
132 item_observers_.clear(); | 212 item_observers_.clear(); |
133 ReloadDataForItems(start, count); | 213 ReloadDataForItems(start, count); |
134 UpdateItemObservers(); | 214 UpdateItemObservers(); |
135 } | 215 } |
136 | 216 |
137 } // namespace app_list | 217 } // namespace app_list |
| 218 |
| 219 @implementation ActionButtonTarget |
| 220 |
| 221 - (id)initWithParent: |
| 222 (app_list::AppsSearchResultsModelBridge::ItemObserver*)parent { |
| 223 if ((self = [super init])) { |
| 224 parent_ = parent; |
| 225 } |
| 226 return self; |
| 227 } |
| 228 |
| 229 - (void)onActionButtonClicked:(id)sender { |
| 230 parent_->InvokeActionAtIndex([sender tag]); |
| 231 } |
| 232 |
| 233 @end |
OLD | NEW |