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