| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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. | |
| 4 | |
| 5 #include "ui/app_list/app_list_view.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/string_util.h" | |
| 10 #include "ui/app_list/app_list_background.h" | |
| 11 #include "ui/app_list/app_list_constants.h" | |
| 12 #include "ui/app_list/app_list_item_model.h" | |
| 13 #include "ui/app_list/app_list_item_view.h" | |
| 14 #include "ui/app_list/app_list_model.h" | |
| 15 #include "ui/app_list/app_list_view_delegate.h" | |
| 16 #include "ui/app_list/contents_view.h" | |
| 17 #include "ui/app_list/pagination_model.h" | |
| 18 #include "ui/app_list/search_box_model.h" | |
| 19 #include "ui/app_list/search_box_view.h" | |
| 20 #include "ui/base/events/event.h" | |
| 21 #include "ui/gfx/insets.h" | |
| 22 #include "ui/gfx/path.h" | |
| 23 #include "ui/gfx/skia_util.h" | |
| 24 #include "ui/views/bubble/bubble_frame_view.h" | |
| 25 #include "ui/views/controls/textfield/textfield.h" | |
| 26 #include "ui/views/layout/box_layout.h" | |
| 27 #include "ui/views/widget/widget.h" | |
| 28 | |
| 29 namespace app_list { | |
| 30 | |
| 31 namespace { | |
| 32 | |
| 33 // Inner padding space in pixels of bubble contents. | |
| 34 const int kInnerPadding = 1; | |
| 35 | |
| 36 // The distance between the arrow tip and edge of the anchor view. | |
| 37 const int kArrowOffset = 10; | |
| 38 | |
| 39 // The maximum allowed time to wait for icon loading in milliseconds. | |
| 40 const int kMaxIconLoadingWaitTimeInMs = 50; | |
| 41 | |
| 42 } // namespace | |
| 43 | |
| 44 //////////////////////////////////////////////////////////////////////////////// | |
| 45 // AppListView::IconLoader | |
| 46 | |
| 47 class AppListView::IconLoader : public AppListItemModelObserver { | |
| 48 public: | |
| 49 IconLoader(AppListView* owner, | |
| 50 AppListItemModel* item, | |
| 51 ui::ScaleFactor scale_factor) | |
| 52 : owner_(owner), | |
| 53 item_(item) { | |
| 54 item_->AddObserver(this); | |
| 55 | |
| 56 // Triggers icon loading for given |scale_factor|. | |
| 57 item_->icon().GetRepresentation(scale_factor); | |
| 58 } | |
| 59 | |
| 60 virtual ~IconLoader() { | |
| 61 item_->RemoveObserver(this); | |
| 62 } | |
| 63 | |
| 64 private: | |
| 65 // AppListItemModelObserver overrides: | |
| 66 virtual void ItemIconChanged() OVERRIDE { | |
| 67 owner_->OnItemIconLoaded(this); | |
| 68 // Note that IconLoader is released here. | |
| 69 } | |
| 70 virtual void ItemTitleChanged() OVERRIDE {} | |
| 71 virtual void ItemHighlightedChanged() OVERRIDE {} | |
| 72 | |
| 73 AppListView* owner_; | |
| 74 AppListItemModel* item_; | |
| 75 | |
| 76 DISALLOW_COPY_AND_ASSIGN(IconLoader); | |
| 77 }; | |
| 78 | |
| 79 //////////////////////////////////////////////////////////////////////////////// | |
| 80 // AppListView: | |
| 81 | |
| 82 AppListView::AppListView(AppListViewDelegate* delegate) | |
| 83 : model_(new AppListModel), | |
| 84 delegate_(delegate), | |
| 85 search_box_view_(NULL), | |
| 86 contents_view_(NULL) { | |
| 87 if (delegate_) | |
| 88 delegate_->SetModel(model_.get()); | |
| 89 } | |
| 90 | |
| 91 AppListView::~AppListView() { | |
| 92 // Models are going away, ensure their references are cleared. | |
| 93 RemoveAllChildViews(true); | |
| 94 pending_icon_loaders_.clear(); | |
| 95 } | |
| 96 | |
| 97 void AppListView::InitAsBubble( | |
| 98 gfx::NativeView parent, | |
| 99 PaginationModel* pagination_model, | |
| 100 views::View* anchor, | |
| 101 const gfx::Point& anchor_point, | |
| 102 views::BubbleBorder::ArrowLocation arrow_location) { | |
| 103 // Starts icon loading early. | |
| 104 PreloadIcons(pagination_model, anchor); | |
| 105 | |
| 106 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, | |
| 107 kInnerPadding, | |
| 108 kInnerPadding, | |
| 109 kInnerPadding)); | |
| 110 | |
| 111 search_box_view_ = new SearchBoxView(this); | |
| 112 AddChildView(search_box_view_); | |
| 113 | |
| 114 contents_view_ = new ContentsView(this, pagination_model); | |
| 115 AddChildView(contents_view_); | |
| 116 | |
| 117 search_box_view_->set_contents_view(contents_view_); | |
| 118 | |
| 119 set_anchor_view(anchor); | |
| 120 set_anchor_point(anchor_point); | |
| 121 set_color(kContentsBackgroundColor); | |
| 122 set_margins(gfx::Insets()); | |
| 123 set_move_with_anchor(true); | |
| 124 set_parent_window(parent); | |
| 125 set_close_on_deactivate(false); | |
| 126 set_close_on_esc(false); | |
| 127 set_anchor_insets(gfx::Insets(kArrowOffset, kArrowOffset, | |
| 128 kArrowOffset, kArrowOffset)); | |
| 129 set_shadow(views::BubbleBorder::BIG_SHADOW); | |
| 130 views::BubbleDelegateView::CreateBubble(this); | |
| 131 SetBubbleArrowLocation(arrow_location); | |
| 132 | |
| 133 #if defined(USE_AURA) | |
| 134 GetBubbleFrameView()->set_background(new AppListBackground( | |
| 135 GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(), | |
| 136 search_box_view_)); | |
| 137 | |
| 138 contents_view_->SetPaintToLayer(true); | |
| 139 contents_view_->SetFillsBoundsOpaquely(false); | |
| 140 contents_view_->layer()->SetMasksToBounds(true); | |
| 141 set_background(NULL); | |
| 142 #else | |
| 143 set_background(new AppListBackground( | |
| 144 GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(), | |
| 145 search_box_view_)); | |
| 146 #endif | |
| 147 | |
| 148 search_box_view_->SetModel(model_->search_box()); | |
| 149 contents_view_->SetModel(model_.get()); | |
| 150 } | |
| 151 | |
| 152 void AppListView::SetBubbleArrowLocation( | |
| 153 views::BubbleBorder::ArrowLocation arrow_location) { | |
| 154 GetBubbleFrameView()->bubble_border()->set_arrow_location(arrow_location); | |
| 155 SizeToContents(); // Recalcuates with new border. | |
| 156 GetBubbleFrameView()->SchedulePaint(); | |
| 157 } | |
| 158 | |
| 159 void AppListView::SetAnchorPoint(const gfx::Point& anchor_point) { | |
| 160 set_anchor_point(anchor_point); | |
| 161 SizeToContents(); // Repositions view relative to the anchor. | |
| 162 } | |
| 163 | |
| 164 void AppListView::ShowWhenReady() { | |
| 165 if (pending_icon_loaders_.empty()) { | |
| 166 icon_loading_wait_timer_.Stop(); | |
| 167 GetWidget()->Show(); | |
| 168 return; | |
| 169 } | |
| 170 | |
| 171 if (icon_loading_wait_timer_.IsRunning()) | |
| 172 return; | |
| 173 | |
| 174 icon_loading_wait_timer_.Start( | |
| 175 FROM_HERE, | |
| 176 base::TimeDelta::FromMilliseconds(kMaxIconLoadingWaitTimeInMs), | |
| 177 this, &AppListView::OnIconLoadingWaitTimer); | |
| 178 } | |
| 179 | |
| 180 void AppListView::Close() { | |
| 181 icon_loading_wait_timer_.Stop(); | |
| 182 | |
| 183 if (delegate_.get()) | |
| 184 delegate_->Dismiss(); | |
| 185 else | |
| 186 GetWidget()->Close(); | |
| 187 } | |
| 188 | |
| 189 void AppListView::UpdateBounds() { | |
| 190 SizeToContents(); | |
| 191 } | |
| 192 | |
| 193 void AppListView::PreloadIcons(PaginationModel* pagination_model, | |
| 194 views::View* anchor) { | |
| 195 ui::ScaleFactor scale_factor = ui::SCALE_FACTOR_100P; | |
| 196 if (anchor && anchor->GetWidget()) { | |
| 197 scale_factor = ui::GetScaleFactorForNativeView( | |
| 198 anchor->GetWidget()->GetNativeView()); | |
| 199 } | |
| 200 | |
| 201 // |pagination_model| could have -1 as the initial selected page and | |
| 202 // assumes first page (i.e. index 0) will be used in this case. | |
| 203 const int selected_page = std::max(0, pagination_model->selected_page()); | |
| 204 | |
| 205 const int tiles_per_page = kPreferredCols * kPreferredRows; | |
| 206 const int start_model_index = selected_page * tiles_per_page; | |
| 207 const int end_model_index = std::min( | |
| 208 static_cast<int>(model_->apps()->item_count()), | |
| 209 start_model_index + tiles_per_page); | |
| 210 | |
| 211 pending_icon_loaders_.clear(); | |
| 212 for (int i = start_model_index; i < end_model_index; ++i) { | |
| 213 AppListItemModel* item = model_->apps()->GetItemAt(i); | |
| 214 if (item->icon().HasRepresentation(scale_factor)) | |
| 215 continue; | |
| 216 | |
| 217 pending_icon_loaders_.push_back(new IconLoader(this, item, scale_factor)); | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 void AppListView::OnIconLoadingWaitTimer() { | |
| 222 GetWidget()->Show(); | |
| 223 } | |
| 224 | |
| 225 void AppListView::OnItemIconLoaded(IconLoader* loader) { | |
| 226 ScopedVector<IconLoader>::iterator it = std::find( | |
| 227 pending_icon_loaders_.begin(), pending_icon_loaders_.end(), loader); | |
| 228 DCHECK(it != pending_icon_loaders_.end()); | |
| 229 pending_icon_loaders_.erase(it); | |
| 230 | |
| 231 if (pending_icon_loaders_.empty() && icon_loading_wait_timer_.IsRunning()) { | |
| 232 icon_loading_wait_timer_.Stop(); | |
| 233 GetWidget()->Show(); | |
| 234 } | |
| 235 } | |
| 236 | |
| 237 views::View* AppListView::GetInitiallyFocusedView() { | |
| 238 return search_box_view_->search_box(); | |
| 239 } | |
| 240 | |
| 241 bool AppListView::WidgetHasHitTestMask() const { | |
| 242 return true; | |
| 243 } | |
| 244 | |
| 245 void AppListView::GetWidgetHitTestMask(gfx::Path* mask) const { | |
| 246 DCHECK(mask); | |
| 247 mask->addRect(gfx::RectToSkRect( | |
| 248 GetBubbleFrameView()->GetContentsBounds())); | |
| 249 } | |
| 250 | |
| 251 bool AppListView::AcceleratorPressed(const ui::Accelerator& accelerator) { | |
| 252 // The accelerator is added by BubbleDelegateView. | |
| 253 if (accelerator.key_code() == ui::VKEY_ESCAPE) { | |
| 254 Close(); | |
| 255 return true; | |
| 256 } | |
| 257 | |
| 258 return false; | |
| 259 } | |
| 260 | |
| 261 void AppListView::ActivateApp(AppListItemModel* item, int event_flags) { | |
| 262 if (delegate_.get()) | |
| 263 delegate_->ActivateAppListItem(item, event_flags); | |
| 264 } | |
| 265 | |
| 266 void AppListView::QueryChanged(SearchBoxView* sender) { | |
| 267 string16 query; | |
| 268 TrimWhitespace(model_->search_box()->text(), TRIM_ALL, &query); | |
| 269 bool should_show_search = !query.empty(); | |
| 270 contents_view_->ShowSearchResults(should_show_search); | |
| 271 | |
| 272 if (delegate_.get()) { | |
| 273 if (should_show_search) | |
| 274 delegate_->StartSearch(); | |
| 275 else | |
| 276 delegate_->StopSearch(); | |
| 277 } | |
| 278 } | |
| 279 | |
| 280 void AppListView::OpenResult(const SearchResult& result, int event_flags) { | |
| 281 if (delegate_.get()) | |
| 282 delegate_->OpenSearchResult(result, event_flags); | |
| 283 } | |
| 284 | |
| 285 void AppListView::InvokeResultAction(const SearchResult& result, | |
| 286 int action_index, | |
| 287 int event_flags) { | |
| 288 if (delegate_.get()) | |
| 289 delegate_->InvokeSearchResultAction(result, action_index, event_flags); | |
| 290 } | |
| 291 | |
| 292 void AppListView::OnWidgetClosing(views::Widget* widget) { | |
| 293 BubbleDelegateView::OnWidgetClosing(widget); | |
| 294 if (delegate_.get() && widget == GetWidget()) | |
| 295 delegate_->ViewClosing(); | |
| 296 } | |
| 297 | |
| 298 void AppListView::OnWidgetActivationChanged(views::Widget* widget, | |
| 299 bool active) { | |
| 300 // Do not called inherited function as the bubble delegate auto close | |
| 301 // functionality is not used. | |
| 302 if (delegate_.get() && widget == GetWidget()) | |
| 303 delegate_->ViewActivationChanged(active); | |
| 304 } | |
| 305 | |
| 306 } // namespace app_list | |
| OLD | NEW |