Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / ui / app_list / views / app_list_view.cc
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/views/app_list_view.h"
6
7 #include <algorithm>
8
9 #include "base/command_line.h"
10 #include "base/strings/string_util.h"
11 #include "base/win/windows_version.h"
12 #include "grit/ui_resources.h"
13 #include "ui/app_list/app_list_constants.h"
14 #include "ui/app_list/app_list_model.h"
15 #include "ui/app_list/app_list_switches.h"
16 #include "ui/app_list/app_list_view_delegate.h"
17 #include "ui/app_list/speech_ui_model.h"
18 #include "ui/app_list/views/app_list_background.h"
19 #include "ui/app_list/views/app_list_folder_view.h"
20 #include "ui/app_list/views/app_list_main_view.h"
21 #include "ui/app_list/views/app_list_view_observer.h"
22 #include "ui/app_list/views/apps_container_view.h"
23 #include "ui/app_list/views/contents_view.h"
24 #include "ui/app_list/views/search_box_view.h"
25 #include "ui/app_list/views/speech_view.h"
26 #include "ui/app_list/views/start_page_view.h"
27 #include "ui/base/ui_base_switches.h"
28 #include "ui/compositor/layer.h"
29 #include "ui/compositor/layer_animation_observer.h"
30 #include "ui/compositor/scoped_layer_animation_settings.h"
31 #include "ui/gfx/canvas.h"
32 #include "ui/gfx/image/image_skia.h"
33 #include "ui/gfx/insets.h"
34 #include "ui/gfx/path.h"
35 #include "ui/gfx/skia_util.h"
36 #include "ui/views/bubble/bubble_frame_view.h"
37 #include "ui/views/controls/image_view.h"
38 #include "ui/views/controls/textfield/textfield.h"
39 #include "ui/views/layout/fill_layout.h"
40 #include "ui/views/widget/widget.h"
41
42 #if defined(USE_AURA)
43 #include "ui/aura/window.h"
44 #include "ui/aura/window_tree_host.h"
45 #include "ui/views/bubble/bubble_window_targeter.h"
46 #if defined(OS_WIN)
47 #include "ui/base/win/shell.h"
48 #endif
49 #if !defined(OS_CHROMEOS)
50 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
51 #endif
52 #endif  // defined(USE_AURA)
53
54 namespace app_list {
55
56 namespace {
57
58 // The margin from the edge to the speech UI.
59 const int kSpeechUIMargin = 12;
60
61 // The vertical position for the appearing animation of the speech UI.
62 const float kSpeechUIAppearingPosition = 12;
63
64 // The distance between the arrow tip and edge of the anchor view.
65 const int kArrowOffset = 10;
66
67 // Determines whether the current environment supports shadows bubble borders.
68 bool SupportsShadow() {
69 #if defined(OS_WIN)
70   // Shadows are not supported on Windows without Aero Glass.
71   if (!ui::win::IsAeroGlassEnabled() ||
72       CommandLine::ForCurrentProcess()->HasSwitch(
73           ::switches::kDisableDwmComposition)) {
74     return false;
75   }
76 #elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
77   // Shadows are not supported on (non-ChromeOS) Linux.
78   return false;
79 #endif
80   return true;
81 }
82
83 // The background for the App List overlay, which appears as a white rounded
84 // rectangle with the given radius and the same size as the target view.
85 class AppListOverlayBackground : public views::Background {
86  public:
87   AppListOverlayBackground(int corner_radius)
88       : corner_radius_(corner_radius) {};
89   virtual ~AppListOverlayBackground() {};
90
91   // Overridden from views::Background:
92   virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE {
93     SkPaint paint;
94     paint.setStyle(SkPaint::kFill_Style);
95     paint.setColor(SK_ColorWHITE);
96     canvas->DrawRoundRect(view->GetContentsBounds(), corner_radius_, paint);
97   }
98
99  private:
100   const int corner_radius_;
101
102   DISALLOW_COPY_AND_ASSIGN(AppListOverlayBackground);
103 };
104
105 }  // namespace
106
107 // An animation observer to hide the view at the end of the animation.
108 class HideViewAnimationObserver : public ui::ImplicitAnimationObserver {
109  public:
110   HideViewAnimationObserver()
111       : frame_(NULL),
112         target_(NULL) {
113   }
114
115   virtual ~HideViewAnimationObserver() {
116     if (target_)
117       StopObservingImplicitAnimations();
118   }
119
120   void SetTarget(views::View* target) {
121     if (target_)
122       StopObservingImplicitAnimations();
123     target_ = target;
124   }
125
126   void set_frame(views::BubbleFrameView* frame) { frame_ = frame; }
127
128  private:
129   // Overridden from ui::ImplicitAnimationObserver:
130   virtual void OnImplicitAnimationsCompleted() OVERRIDE {
131     if (target_) {
132       target_->SetVisible(false);
133       target_ = NULL;
134
135       // Should update the background by invoking SchedulePaint().
136       if (frame_)
137         frame_->SchedulePaint();
138     }
139   }
140
141   views::BubbleFrameView* frame_;
142   views::View* target_;
143
144   DISALLOW_COPY_AND_ASSIGN(HideViewAnimationObserver);
145 };
146
147 ////////////////////////////////////////////////////////////////////////////////
148 // AppListView:
149
150 AppListView::AppListView(AppListViewDelegate* delegate)
151     : delegate_(delegate),
152       app_list_main_view_(NULL),
153       speech_view_(NULL),
154       experimental_banner_view_(NULL),
155       overlay_view_(NULL),
156       animation_observer_(new HideViewAnimationObserver()) {
157   CHECK(delegate);
158
159   delegate_->AddObserver(this);
160   delegate_->GetSpeechUI()->AddObserver(this);
161 }
162
163 AppListView::~AppListView() {
164   delegate_->GetSpeechUI()->RemoveObserver(this);
165   delegate_->RemoveObserver(this);
166   animation_observer_.reset();
167   // Remove child views first to ensure no remaining dependencies on delegate_.
168   RemoveAllChildViews(true);
169 }
170
171 void AppListView::InitAsBubbleAttachedToAnchor(
172     gfx::NativeView parent,
173     int initial_apps_page,
174     views::View* anchor,
175     const gfx::Vector2d& anchor_offset,
176     views::BubbleBorder::Arrow arrow,
177     bool border_accepts_events) {
178   SetAnchorView(anchor);
179   InitAsBubbleInternal(
180       parent, initial_apps_page, arrow, border_accepts_events, anchor_offset);
181 }
182
183 void AppListView::InitAsBubbleAtFixedLocation(
184     gfx::NativeView parent,
185     int initial_apps_page,
186     const gfx::Point& anchor_point_in_screen,
187     views::BubbleBorder::Arrow arrow,
188     bool border_accepts_events) {
189   SetAnchorView(NULL);
190   SetAnchorRect(gfx::Rect(anchor_point_in_screen, gfx::Size()));
191   InitAsBubbleInternal(
192       parent, initial_apps_page, arrow, border_accepts_events, gfx::Vector2d());
193 }
194
195 void AppListView::SetBubbleArrow(views::BubbleBorder::Arrow arrow) {
196   GetBubbleFrameView()->bubble_border()->set_arrow(arrow);
197   SizeToContents();  // Recalcuates with new border.
198   GetBubbleFrameView()->SchedulePaint();
199 }
200
201 void AppListView::SetAnchorPoint(const gfx::Point& anchor_point) {
202   SetAnchorRect(gfx::Rect(anchor_point, gfx::Size()));
203 }
204
205 void AppListView::SetDragAndDropHostOfCurrentAppList(
206     ApplicationDragAndDropHost* drag_and_drop_host) {
207   app_list_main_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
208 }
209
210 void AppListView::ShowWhenReady() {
211   app_list_main_view_->ShowAppListWhenReady();
212 }
213
214 void AppListView::Close() {
215   app_list_main_view_->Close();
216   delegate_->Dismiss();
217 }
218
219 void AppListView::UpdateBounds() {
220   SizeToContents();
221 }
222
223 void AppListView::SetAppListOverlayVisible(bool visible) {
224   DCHECK(overlay_view_);
225
226   // Display the overlay immediately so we can begin the animation.
227   overlay_view_->SetVisible(true);
228
229   ui::ScopedLayerAnimationSettings settings(
230       overlay_view_->layer()->GetAnimator());
231   settings.SetTweenType(gfx::Tween::LINEAR);
232
233   // If we're dismissing the overlay, hide the view at the end of the animation.
234   if (!visible) {
235     // Since only one animation is visible at a time, it's safe to re-use
236     // animation_observer_ here.
237     animation_observer_->set_frame(NULL);
238     animation_observer_->SetTarget(overlay_view_);
239     settings.AddObserver(animation_observer_.get());
240   }
241
242   const float kOverlayFadeInMilliseconds = 125;
243   settings.SetTransitionDuration(
244       base::TimeDelta::FromMilliseconds(kOverlayFadeInMilliseconds));
245
246   const float kOverlayOpacity = 0.75f;
247   overlay_view_->layer()->SetOpacity(visible ? kOverlayOpacity : 0.0f);
248 }
249
250 bool AppListView::ShouldCenterWindow() const {
251   return delegate_->ShouldCenterWindow();
252 }
253
254 gfx::Size AppListView::GetPreferredSize() const {
255   return app_list_main_view_->GetPreferredSize();
256 }
257
258 void AppListView::Paint(gfx::Canvas* canvas, const views::CullSet& cull_set) {
259   views::BubbleDelegateView::Paint(canvas, cull_set);
260   if (!next_paint_callback_.is_null()) {
261     next_paint_callback_.Run();
262     next_paint_callback_.Reset();
263   }
264 }
265
266 void AppListView::OnThemeChanged() {
267 #if defined(OS_WIN)
268   GetWidget()->Close();
269 #endif
270 }
271
272 bool AppListView::ShouldHandleSystemCommands() const {
273   return true;
274 }
275
276 void AppListView::Prerender() {
277   app_list_main_view_->Prerender();
278 }
279
280 void AppListView::OnProfilesChanged() {
281   app_list_main_view_->search_box_view()->InvalidateMenu();
282 }
283
284 void AppListView::SetProfileByPath(const base::FilePath& profile_path) {
285   delegate_->SetProfileByPath(profile_path);
286   app_list_main_view_->ModelChanged();
287 }
288
289 void AppListView::AddObserver(AppListViewObserver* observer) {
290   observers_.AddObserver(observer);
291 }
292
293 void AppListView::RemoveObserver(AppListViewObserver* observer) {
294   observers_.RemoveObserver(observer);
295 }
296
297 // static
298 void AppListView::SetNextPaintCallback(const base::Closure& callback) {
299   next_paint_callback_ = callback;
300 }
301
302 #if defined(OS_WIN)
303 HWND AppListView::GetHWND() const {
304   gfx::NativeWindow window =
305       GetWidget()->GetTopLevelWidget()->GetNativeWindow();
306   return window->GetHost()->GetAcceleratedWidget();
307 }
308 #endif
309
310 PaginationModel* AppListView::GetAppsPaginationModel() {
311   return app_list_main_view_->contents_view()
312       ->apps_container_view()
313       ->apps_grid_view()
314       ->pagination_model();
315 }
316
317 void AppListView::InitAsBubbleInternal(gfx::NativeView parent,
318                                        int initial_apps_page,
319                                        views::BubbleBorder::Arrow arrow,
320                                        bool border_accepts_events,
321                                        const gfx::Vector2d& anchor_offset) {
322   app_list_main_view_ =
323       new AppListMainView(delegate_.get(), initial_apps_page, parent);
324   AddChildView(app_list_main_view_);
325   app_list_main_view_->SetPaintToLayer(true);
326   app_list_main_view_->SetFillsBoundsOpaquely(false);
327   app_list_main_view_->layer()->SetMasksToBounds(true);
328
329   // Speech recognition is available only when the start page exists.
330   if (delegate_ && delegate_->IsSpeechRecognitionEnabled()) {
331     speech_view_ = new SpeechView(delegate_.get());
332     speech_view_->SetVisible(false);
333     speech_view_->SetPaintToLayer(true);
334     speech_view_->SetFillsBoundsOpaquely(false);
335     speech_view_->layer()->SetOpacity(0.0f);
336     AddChildView(speech_view_);
337   }
338
339   if (app_list::switches::IsExperimentalAppListEnabled()) {
340     // Draw a banner in the corner of the experimental app list.
341     experimental_banner_view_ = new views::ImageView;
342     const gfx::ImageSkia& experimental_icon =
343         *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
344             IDR_APP_LIST_EXPERIMENTAL_ICON);
345     experimental_banner_view_->SetImage(experimental_icon);
346     experimental_banner_view_->SetPaintToLayer(true);
347     experimental_banner_view_->SetFillsBoundsOpaquely(false);
348     AddChildView(experimental_banner_view_);
349   }
350
351   OnProfilesChanged();
352   set_color(kContentsBackgroundColor);
353   set_margins(gfx::Insets());
354   set_parent_window(parent);
355   set_close_on_deactivate(false);
356   set_close_on_esc(false);
357   set_anchor_view_insets(gfx::Insets(kArrowOffset + anchor_offset.y(),
358                                      kArrowOffset + anchor_offset.x(),
359                                      kArrowOffset - anchor_offset.y(),
360                                      kArrowOffset - anchor_offset.x()));
361   set_border_accepts_events(border_accepts_events);
362   set_shadow(SupportsShadow() ? views::BubbleBorder::BIG_SHADOW
363                               : views::BubbleBorder::NO_SHADOW_OPAQUE_BORDER);
364   views::BubbleDelegateView::CreateBubble(this);
365   SetBubbleArrow(arrow);
366
367 #if defined(USE_AURA)
368   aura::Window* window = GetWidget()->GetNativeWindow();
369   window->layer()->SetMasksToBounds(true);
370   GetBubbleFrameView()->set_background(new AppListBackground(
371       GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
372       app_list_main_view_));
373   set_background(NULL);
374   window->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
375       new views::BubbleWindowTargeter(this)));
376 #else
377   set_background(new AppListBackground(
378       GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
379       app_list_main_view_));
380
381   // On non-aura the bubble has two widgets, and it's possible for the border
382   // to be shown independently in odd situations. Explicitly hide the bubble
383   // widget to ensure that any WM_WINDOWPOSCHANGED messages triggered by the
384   // window manager do not have the SWP_SHOWWINDOW flag set which would cause
385   // the border to be shown. See http://crbug.com/231687 .
386   GetWidget()->Hide();
387 #endif
388
389   // To make the overlay view, construct a view with a white background, rather
390   // than a white rectangle in it. This is because we need overlay_view_ to be
391   // drawn to its own layer (so it appears correctly in the foreground).
392   overlay_view_ = new views::View();
393   overlay_view_->SetPaintToLayer(true);
394   overlay_view_->SetBoundsRect(GetContentsBounds());
395   overlay_view_->SetVisible(false);
396   overlay_view_->layer()->SetOpacity(0.0f);
397   // On platforms that don't support a shadow, the rounded border of the app
398   // list is constructed _inside_ the view, so a rectangular background goes
399   // over the border in the rounded corners. To fix this, give the background a
400   // corner radius 1px smaller than the outer border, so it just reaches but
401   // doesn't cover it.
402   const int kOverlayCornerRadius =
403       GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius();
404   overlay_view_->set_background(new AppListOverlayBackground(
405       kOverlayCornerRadius - (SupportsShadow() ? 0 : 1)));
406   AddChildView(overlay_view_);
407
408   if (delegate_)
409     delegate_->ViewInitialized();
410 }
411
412 void AppListView::OnBeforeBubbleWidgetInit(
413     views::Widget::InitParams* params,
414     views::Widget* widget) const {
415 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
416   if (delegate_ && delegate_->ForceNativeDesktop())
417     params->native_widget = new views::DesktopNativeWidgetAura(widget);
418 #endif
419 #if defined(OS_WIN)
420   // Windows 7 and higher offer pinning to the taskbar, but we need presence
421   // on the taskbar for the user to be able to pin us. So, show the window on
422   // the taskbar for these versions of Windows.
423   if (base::win::GetVersion() >= base::win::VERSION_WIN7)
424     params->force_show_in_taskbar = true;
425 #elif defined(OS_LINUX)
426   // Set up a custom WM_CLASS for the app launcher window. This allows task
427   // switchers in X11 environments to distinguish it from main browser windows.
428   params->wm_class_name = kAppListWMClass;
429   // Show the window in the taskbar, even though it is a bubble, which would not
430   // normally be shown.
431   params->force_show_in_taskbar = true;
432 #endif
433 }
434
435 views::View* AppListView::GetInitiallyFocusedView() {
436   return app_list::switches::IsExperimentalAppListEnabled()
437              ? app_list_main_view_->contents_view()
438                    ->start_page_view()
439                    ->dummy_search_box_view()
440                    ->search_box()
441              : app_list_main_view_->search_box_view()->search_box();
442 }
443
444 gfx::ImageSkia AppListView::GetWindowIcon() {
445   if (delegate_)
446     return delegate_->GetWindowIcon();
447
448   return gfx::ImageSkia();
449 }
450
451 bool AppListView::WidgetHasHitTestMask() const {
452   return true;
453 }
454
455 void AppListView::GetWidgetHitTestMask(gfx::Path* mask) const {
456   DCHECK(mask);
457   mask->addRect(gfx::RectToSkRect(
458       GetBubbleFrameView()->GetContentsBounds()));
459 }
460
461 bool AppListView::AcceleratorPressed(const ui::Accelerator& accelerator) {
462   // The accelerator is added by BubbleDelegateView.
463   if (accelerator.key_code() == ui::VKEY_ESCAPE) {
464     if (app_list_main_view_->search_box_view()->HasSearch()) {
465       app_list_main_view_->search_box_view()->ClearSearch();
466     } else if (app_list_main_view_->contents_view()
467                    ->apps_container_view()
468                    ->IsInFolderView()) {
469       app_list_main_view_->contents_view()
470           ->apps_container_view()
471           ->app_list_folder_view()
472           ->CloseFolderPage();
473       return true;
474     } else {
475       GetWidget()->Deactivate();
476       Close();
477     }
478     return true;
479   }
480
481   return false;
482 }
483
484 void AppListView::Layout() {
485   const gfx::Rect contents_bounds = GetContentsBounds();
486   app_list_main_view_->SetBoundsRect(contents_bounds);
487
488   if (speech_view_) {
489     gfx::Rect speech_bounds = contents_bounds;
490     int preferred_height = speech_view_->GetPreferredSize().height();
491     speech_bounds.Inset(kSpeechUIMargin, kSpeechUIMargin);
492     speech_bounds.set_height(std::min(speech_bounds.height(),
493                                       preferred_height));
494     speech_bounds.Inset(-speech_view_->GetInsets());
495     speech_view_->SetBoundsRect(speech_bounds);
496   }
497
498   if (experimental_banner_view_) {
499     // Position the experimental banner in the lower right corner.
500     gfx::Rect image_bounds = experimental_banner_view_->GetImageBounds();
501     image_bounds.set_origin(
502         gfx::Point(contents_bounds.width() - image_bounds.width(),
503                    contents_bounds.height() - image_bounds.height()));
504     experimental_banner_view_->SetBoundsRect(image_bounds);
505   }
506 }
507
508 void AppListView::SchedulePaintInRect(const gfx::Rect& rect) {
509   BubbleDelegateView::SchedulePaintInRect(rect);
510   if (GetBubbleFrameView())
511     GetBubbleFrameView()->SchedulePaint();
512 }
513
514 void AppListView::OnWidgetDestroying(views::Widget* widget) {
515   BubbleDelegateView::OnWidgetDestroying(widget);
516   if (delegate_ && widget == GetWidget())
517     delegate_->ViewClosing();
518 }
519
520 void AppListView::OnWidgetActivationChanged(views::Widget* widget,
521                                             bool active) {
522   // Do not called inherited function as the bubble delegate auto close
523   // functionality is not used.
524   if (widget == GetWidget())
525     FOR_EACH_OBSERVER(AppListViewObserver, observers_,
526                       OnActivationChanged(widget, active));
527 }
528
529 void AppListView::OnWidgetVisibilityChanged(views::Widget* widget,
530                                             bool visible) {
531   BubbleDelegateView::OnWidgetVisibilityChanged(widget, visible);
532
533   if (widget != GetWidget())
534     return;
535
536   if (!visible)
537     app_list_main_view_->ResetForShow();
538 }
539
540 void AppListView::OnSpeechRecognitionStateChanged(
541     SpeechRecognitionState new_state) {
542   if (!speech_view_)
543     return;
544
545   bool will_appear = (new_state == SPEECH_RECOGNITION_RECOGNIZING ||
546                       new_state == SPEECH_RECOGNITION_IN_SPEECH ||
547                       new_state == SPEECH_RECOGNITION_NETWORK_ERROR);
548   // No change for this class.
549   if (speech_view_->visible() == will_appear)
550     return;
551
552   if (will_appear)
553     speech_view_->Reset();
554
555   animation_observer_->set_frame(GetBubbleFrameView());
556   gfx::Transform speech_transform;
557   speech_transform.Translate(
558       0, SkFloatToMScalar(kSpeechUIAppearingPosition));
559   if (will_appear)
560     speech_view_->layer()->SetTransform(speech_transform);
561
562   {
563     ui::ScopedLayerAnimationSettings main_settings(
564         app_list_main_view_->layer()->GetAnimator());
565     if (will_appear) {
566       animation_observer_->SetTarget(app_list_main_view_);
567       main_settings.AddObserver(animation_observer_.get());
568     }
569     app_list_main_view_->layer()->SetOpacity(will_appear ? 0.0f : 1.0f);
570   }
571
572   {
573     ui::ScopedLayerAnimationSettings speech_settings(
574         speech_view_->layer()->GetAnimator());
575     if (!will_appear) {
576       animation_observer_->SetTarget(speech_view_);
577       speech_settings.AddObserver(animation_observer_.get());
578     }
579
580     speech_view_->layer()->SetOpacity(will_appear ? 1.0f : 0.0f);
581     if (will_appear)
582       speech_view_->layer()->SetTransform(gfx::Transform());
583     else
584       speech_view_->layer()->SetTransform(speech_transform);
585   }
586
587   if (will_appear)
588     speech_view_->SetVisible(true);
589   else
590     app_list_main_view_->SetVisible(true);
591 }
592
593 }  // namespace app_list