app_list.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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 <string>
6
7#include "ash/session_state_delegate.h"
8#include "ash/shell.h"
9#include "ash/shell/example_factory.h"
10#include "ash/shell/toplevel_window.h"
11#include "ash/shell_delegate.h"
12#include "base/basictypes.h"
13#include "base/callback.h"
14#include "base/files/file_path.h"
15#include "base/i18n/case_conversion.h"
16#include "base/i18n/string_search.h"
17#include "base/strings/string_util.h"
18#include "base/strings/stringprintf.h"
19#include "base/strings/utf_string_conversions.h"
20#include "ui/app_list/app_list_item.h"
21#include "ui/app_list/app_list_item_list.h"
22#include "ui/app_list/app_list_model.h"
23#include "ui/app_list/app_list_view_delegate.h"
24#include "ui/app_list/search_box_model.h"
25#include "ui/app_list/search_result.h"
26#include "ui/app_list/speech_ui_model.h"
27#include "ui/gfx/canvas.h"
28#include "ui/gfx/font_list.h"
29#include "ui/gfx/geometry/rect.h"
30#include "ui/gfx/image/image_skia.h"
31#include "ui/views/examples/examples_window_with_content.h"
32
33namespace ash {
34namespace shell {
35
36namespace {
37
38// WindowTypeShelfItem is an app item of app list. It carries a window
39// launch type and launches corresponding example window when activated.
40class WindowTypeShelfItem : public app_list::AppListItem {
41 public:
42  enum Type {
43    TOPLEVEL_WINDOW = 0,
44    NON_RESIZABLE_WINDOW,
45    LOCK_SCREEN,
46    WIDGETS_WINDOW,
47    EXAMPLES_WINDOW,
48    LAST_TYPE,
49  };
50
51  explicit WindowTypeShelfItem(const std::string& id, Type type)
52      : app_list::AppListItem(id),
53        type_(type) {
54    std::string title(GetTitle(type));
55    SetIcon(GetIcon(type), false);
56    SetName(title);
57  }
58
59  static gfx::ImageSkia GetIcon(Type type) {
60    static const SkColor kColors[] = {
61        SK_ColorRED,
62        SK_ColorGREEN,
63        SK_ColorBLUE,
64        SK_ColorYELLOW,
65        SK_ColorCYAN,
66    };
67
68    const int kIconSize = 128;
69    SkBitmap icon;
70    icon.setConfig(SkBitmap::kARGB_8888_Config, kIconSize, kIconSize);
71    icon.allocPixels();
72    icon.eraseColor(kColors[static_cast<int>(type) % arraysize(kColors)]);
73    return gfx::ImageSkia::CreateFrom1xBitmap(icon);
74  }
75
76  // The text below is not localized as this is an example code.
77  static std::string GetTitle(Type type) {
78    switch (type) {
79      case TOPLEVEL_WINDOW:
80        return "Create Window";
81      case NON_RESIZABLE_WINDOW:
82        return "Create Non-Resizable Window";
83      case LOCK_SCREEN:
84        return "Lock Screen";
85      case WIDGETS_WINDOW:
86        return "Show Example Widgets";
87      case EXAMPLES_WINDOW:
88        return "Open Views Examples Window";
89      default:
90        return "Unknown window type.";
91    }
92  }
93
94  // The text below is not localized as this is an example code.
95  static std::string GetDetails(Type type) {
96    // Assigns details only to some types so that we see both one-line
97    // and two-line results.
98    switch (type) {
99      case WIDGETS_WINDOW:
100        return "Creates a window to show example widgets";
101      case EXAMPLES_WINDOW:
102        return "Creates a window to show views example.";
103      default:
104        return std::string();
105    }
106  }
107
108  static void ActivateItem(Type type, int event_flags) {
109     switch (type) {
110      case TOPLEVEL_WINDOW: {
111        ToplevelWindow::CreateParams params;
112        params.can_resize = true;
113        ToplevelWindow::CreateToplevelWindow(params);
114        break;
115      }
116      case NON_RESIZABLE_WINDOW: {
117        ToplevelWindow::CreateToplevelWindow(ToplevelWindow::CreateParams());
118        break;
119      }
120      case LOCK_SCREEN: {
121        Shell::GetInstance()->session_state_delegate()->LockScreen();
122        break;
123      }
124      case WIDGETS_WINDOW: {
125        CreateWidgetsWindow();
126        break;
127      }
128      case EXAMPLES_WINDOW: {
129        views::examples::ShowExamplesWindowWithContent(
130            views::examples::DO_NOTHING_ON_CLOSE,
131            Shell::GetInstance()->delegate()->GetActiveBrowserContext(),
132            NULL);
133        break;
134      }
135      default:
136        break;
137    }
138  }
139
140  // AppListItem
141  virtual void Activate(int event_flags) OVERRIDE {
142    ActivateItem(type_, event_flags);
143  }
144
145 private:
146  Type type_;
147
148  DISALLOW_COPY_AND_ASSIGN(WindowTypeShelfItem);
149};
150
151// ExampleSearchResult is an app list search result. It provides what icon to
152// show, what should title and details text look like. It also carries the
153// matching window launch type so that AppListViewDelegate knows how to open
154// it.
155class ExampleSearchResult : public app_list::SearchResult {
156 public:
157  ExampleSearchResult(WindowTypeShelfItem::Type type,
158                      const base::string16& query)
159      : type_(type) {
160    SetIcon(WindowTypeShelfItem::GetIcon(type_));
161
162    base::string16 title =
163        base::UTF8ToUTF16(WindowTypeShelfItem::GetTitle(type_));
164    set_title(title);
165
166    Tags title_tags;
167    const size_t match_len = query.length();
168
169    // Highlight matching parts in title with bold.
170    // Note the following is not a proper way to handle i18n string.
171    title = base::i18n::ToLower(title);
172    size_t match_start = title.find(query);
173    while (match_start != base::string16::npos) {
174      title_tags.push_back(Tag(Tag::MATCH,
175                               match_start,
176                               match_start + match_len));
177      match_start = title.find(query, match_start + match_len);
178    }
179    set_title_tags(title_tags);
180
181    base::string16 details =
182        base::UTF8ToUTF16(WindowTypeShelfItem::GetDetails(type_));
183    set_details(details);
184    Tags details_tags;
185    details_tags.push_back(Tag(Tag::DIM, 0, details.length()));
186    set_details_tags(details_tags);
187  }
188
189  WindowTypeShelfItem::Type type() const { return type_; }
190
191 private:
192  WindowTypeShelfItem::Type type_;
193
194  DISALLOW_COPY_AND_ASSIGN(ExampleSearchResult);
195};
196
197class ExampleAppListViewDelegate : public app_list::AppListViewDelegate {
198 public:
199  ExampleAppListViewDelegate()
200      : model_(new app_list::AppListModel),
201        speech_ui_(app_list::SPEECH_RECOGNITION_OFF) {
202    PopulateApps();
203    DecorateSearchBox(model_->search_box());
204  }
205
206 private:
207  void PopulateApps() {
208    for (int i = 0; i < static_cast<int>(WindowTypeShelfItem::LAST_TYPE); ++i) {
209      WindowTypeShelfItem::Type type =
210          static_cast<WindowTypeShelfItem::Type>(i);
211      std::string id = base::StringPrintf("%d", i);
212      scoped_ptr<WindowTypeShelfItem> shelf_item(
213          new WindowTypeShelfItem(id, type));
214      model_->AddItem(shelf_item.PassAs<app_list::AppListItem>());
215    }
216  }
217
218  gfx::ImageSkia CreateSearchBoxIcon() {
219    const base::string16 icon_text = base::ASCIIToUTF16("ash");
220    const gfx::Size icon_size(32, 32);
221
222    gfx::Canvas canvas(icon_size, 1.0f, false /* is_opaque */);
223    canvas.DrawStringRectWithFlags(
224        icon_text,
225        gfx::FontList(),
226        SK_ColorBLACK,
227        gfx::Rect(icon_size),
228        gfx::Canvas::TEXT_ALIGN_CENTER | gfx::Canvas::NO_SUBPIXEL_RENDERING);
229
230    return gfx::ImageSkia(canvas.ExtractImageRep());
231  }
232
233  void DecorateSearchBox(app_list::SearchBoxModel* search_box_model) {
234    search_box_model->SetIcon(CreateSearchBoxIcon());
235    search_box_model->SetHintText(base::ASCIIToUTF16("Type to search..."));
236  }
237
238  // Overridden from app_list::AppListViewDelegate:
239  virtual bool ForceNativeDesktop() const OVERRIDE {
240    return false;
241  }
242
243  virtual void SetProfileByPath(const base::FilePath& profile_path) OVERRIDE {
244    // Nothing needs to be done.
245  }
246
247  virtual const Users& GetUsers() const OVERRIDE {
248    return users_;
249  }
250
251  virtual app_list::AppListModel* GetModel() OVERRIDE { return model_.get(); }
252
253  virtual app_list::SigninDelegate* GetSigninDelegate() OVERRIDE {
254    return NULL;
255  }
256
257  virtual app_list::SpeechUIModel* GetSpeechUI() OVERRIDE {
258    return &speech_ui_;
259  }
260
261  virtual void GetShortcutPathForApp(
262      const std::string& app_id,
263      const base::Callback<void(const base::FilePath&)>& callback) OVERRIDE {
264    callback.Run(base::FilePath());
265  }
266
267  virtual void OpenSearchResult(app_list::SearchResult* result,
268                                bool auto_launch,
269                                int event_flags) OVERRIDE {
270    const ExampleSearchResult* example_result =
271        static_cast<const ExampleSearchResult*>(result);
272    WindowTypeShelfItem::ActivateItem(example_result->type(), event_flags);
273  }
274
275  virtual void InvokeSearchResultAction(app_list::SearchResult* result,
276                                        int action_index,
277                                        int event_flags) OVERRIDE {
278    NOTIMPLEMENTED();
279  }
280
281  virtual base::TimeDelta GetAutoLaunchTimeout() OVERRIDE {
282    return base::TimeDelta();
283  }
284
285  virtual void AutoLaunchCanceled() OVERRIDE {
286  }
287
288  virtual void StartSearch() OVERRIDE {
289    base::string16 query;
290    base::TrimWhitespace(model_->search_box()->text(), base::TRIM_ALL, &query);
291    query = base::i18n::ToLower(query);
292
293    model_->results()->DeleteAll();
294    if (query.empty())
295      return;
296
297    for (int i = 0; i < static_cast<int>(WindowTypeShelfItem::LAST_TYPE); ++i) {
298      WindowTypeShelfItem::Type type =
299          static_cast<WindowTypeShelfItem::Type>(i);
300
301      base::string16 title =
302          base::UTF8ToUTF16(WindowTypeShelfItem::GetTitle(type));
303      if (base::i18n::StringSearchIgnoringCaseAndAccents(
304              query, title, NULL, NULL)) {
305        model_->results()->Add(new ExampleSearchResult(type, query));
306      }
307    }
308  }
309
310  virtual void StopSearch() OVERRIDE {
311    // Nothing needs to be done.
312  }
313
314  virtual void ViewInitialized() OVERRIDE {
315    // Nothing needs to be done.
316  }
317
318  virtual void Dismiss() OVERRIDE {
319    DCHECK(ash::Shell::HasInstance());
320    if (Shell::GetInstance()->GetAppListTargetVisibility())
321      Shell::GetInstance()->ToggleAppList(NULL);
322  }
323
324  virtual void ViewClosing() OVERRIDE {
325    // Nothing needs to be done.
326  }
327
328  virtual gfx::ImageSkia GetWindowIcon() OVERRIDE {
329    return gfx::ImageSkia();
330  }
331
332  virtual void OpenSettings() OVERRIDE {
333    // Nothing needs to be done.
334  }
335
336  virtual void OpenHelp() OVERRIDE {
337    // Nothing needs to be done.
338  }
339
340  virtual void OpenFeedback() OVERRIDE {
341    // Nothing needs to be done.
342  }
343
344  virtual void ToggleSpeechRecognition() OVERRIDE {
345    NOTIMPLEMENTED();
346  }
347
348  virtual void ShowForProfileByPath(
349      const base::FilePath& profile_path) OVERRIDE {
350    // Nothing needs to be done.
351  }
352
353  virtual content::WebContents* GetStartPageContents() OVERRIDE {
354    return NULL;
355  }
356
357  virtual content::WebContents* GetSpeechRecognitionContents() OVERRIDE {
358    return NULL;
359  }
360
361  scoped_ptr<app_list::AppListModel> model_;
362  app_list::SpeechUIModel speech_ui_;
363  Users users_;
364
365  DISALLOW_COPY_AND_ASSIGN(ExampleAppListViewDelegate);
366};
367
368}  // namespace
369
370app_list::AppListViewDelegate* CreateAppListViewDelegate() {
371  return new ExampleAppListViewDelegate;
372}
373
374}  // namespace shell
375}  // namespace ash
376