Upstream version 6.35.121.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 "ui/app_list/app_list_constants.h"
13 #include "ui/app_list/app_list_model.h"
14 #include "ui/app_list/app_list_view_delegate.h"
15 #include "ui/app_list/pagination_model.h"
16 #include "ui/app_list/signin_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_main_view.h"
20 #include "ui/app_list/views/app_list_view_observer.h"
21 #include "ui/app_list/views/search_box_view.h"
22 #include "ui/app_list/views/signin_view.h"
23 #include "ui/app_list/views/speech_view.h"
24 #include "ui/base/ui_base_switches.h"
25 #include "ui/compositor/layer.h"
26 #include "ui/compositor/layer_animation_observer.h"
27 #include "ui/compositor/scoped_layer_animation_settings.h"
28 #include "ui/gfx/image/image_skia.h"
29 #include "ui/gfx/insets.h"
30 #include "ui/gfx/path.h"
31 #include "ui/gfx/skia_util.h"
32 #include "ui/views/bubble/bubble_frame_view.h"
33 #include "ui/views/bubble/bubble_window_targeter.h"
34 #include "ui/views/controls/textfield/textfield.h"
35 #include "ui/views/layout/fill_layout.h"
36 #include "ui/views/widget/widget.h"
37
38 #if defined(USE_AURA)
39 #include "ui/aura/window.h"
40 #include "ui/aura/window_tree_host.h"
41 #if defined(OS_WIN)
42 #include "ui/base/win/shell.h"
43 #endif
44 #if !defined(OS_CHROMEOS)
45 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
46 #endif
47 #endif  // defined(USE_AURA)
48
49 namespace app_list {
50
51 namespace {
52
53 void (*g_next_paint_callback)();
54
55 // The margin from the edge to the speech UI.
56 const int kSpeechUIMargin = 12;
57
58 // The vertical position for the appearing animation of the speech UI.
59 const float kSpeechUIAppearingPosition = 12;
60
61 // The distance between the arrow tip and edge of the anchor view.
62 const int kArrowOffset = 10;
63
64 // Determines whether the current environment supports shadows bubble borders.
65 bool SupportsShadow() {
66 #if defined(OS_WIN)
67   // Shadows are not supported on Windows without Aero Glass.
68   if (!ui::win::IsAeroGlassEnabled() ||
69       CommandLine::ForCurrentProcess()->HasSwitch(
70           switches::kDisableDwmComposition)) {
71     return false;
72   }
73 #elif defined(OS_LINUX) && !defined(USE_ASH)
74   // Shadows are not supported on (non-ChromeOS) Linux.
75   return false;
76 #endif
77   return true;
78 }
79
80 }  // namespace
81
82 // An animation observer to hide the view at the end of the animation.
83 class HideViewAnimationObserver : public ui::ImplicitAnimationObserver {
84  public:
85   HideViewAnimationObserver()
86       : frame_(NULL),
87         target_(NULL) {
88   }
89
90   virtual ~HideViewAnimationObserver() {
91     if (target_)
92       StopObservingImplicitAnimations();
93   }
94
95   void SetTarget(views::View* target) {
96     if (!target_)
97       StopObservingImplicitAnimations();
98     target_ = target;
99   }
100
101   void set_frame(views::BubbleFrameView* frame) { frame_ = frame; }
102
103  private:
104   // Overridden from ui::ImplicitAnimationObserver:
105   virtual void OnImplicitAnimationsCompleted() OVERRIDE {
106     if (target_) {
107       target_->SetVisible(false);
108       target_ = NULL;
109
110       // Should update the background by invoking SchedulePaint().
111       frame_->SchedulePaint();
112     }
113   }
114
115   views::BubbleFrameView* frame_;
116   views::View* target_;
117
118   DISALLOW_COPY_AND_ASSIGN(HideViewAnimationObserver);
119 };
120
121 ////////////////////////////////////////////////////////////////////////////////
122 // AppListView:
123
124 AppListView::AppListView(AppListViewDelegate* delegate)
125     : delegate_(delegate),
126       app_list_main_view_(NULL),
127       signin_view_(NULL),
128       speech_view_(NULL),
129       animation_observer_(new HideViewAnimationObserver()) {
130   CHECK(delegate);
131
132   delegate_->AddObserver(this);
133   delegate_->GetSpeechUI()->AddObserver(this);
134 }
135
136 AppListView::~AppListView() {
137   delegate_->GetSpeechUI()->RemoveObserver(this);
138   delegate_->RemoveObserver(this);
139   animation_observer_.reset();
140   // Remove child views first to ensure no remaining dependencies on delegate_.
141   RemoveAllChildViews(true);
142 }
143
144 void AppListView::InitAsBubbleAttachedToAnchor(
145     gfx::NativeView parent,
146     PaginationModel* pagination_model,
147     views::View* anchor,
148     const gfx::Vector2d& anchor_offset,
149     views::BubbleBorder::Arrow arrow,
150     bool border_accepts_events) {
151   SetAnchorView(anchor);
152   InitAsBubbleInternal(
153       parent, pagination_model, arrow, border_accepts_events, anchor_offset);
154 }
155
156 void AppListView::InitAsBubbleAtFixedLocation(
157     gfx::NativeView parent,
158     PaginationModel* pagination_model,
159     const gfx::Point& anchor_point_in_screen,
160     views::BubbleBorder::Arrow arrow,
161     bool border_accepts_events) {
162   SetAnchorView(NULL);
163   SetAnchorRect(gfx::Rect(anchor_point_in_screen, gfx::Size()));
164   InitAsBubbleInternal(
165       parent, pagination_model, arrow, border_accepts_events, gfx::Vector2d());
166 }
167
168 void AppListView::SetBubbleArrow(views::BubbleBorder::Arrow arrow) {
169   GetBubbleFrameView()->bubble_border()->set_arrow(arrow);
170   SizeToContents();  // Recalcuates with new border.
171   GetBubbleFrameView()->SchedulePaint();
172 }
173
174 void AppListView::SetAnchorPoint(const gfx::Point& anchor_point) {
175   SetAnchorRect(gfx::Rect(anchor_point, gfx::Size()));
176 }
177
178 void AppListView::SetDragAndDropHostOfCurrentAppList(
179     ApplicationDragAndDropHost* drag_and_drop_host) {
180   app_list_main_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
181 }
182
183 void AppListView::ShowWhenReady() {
184   app_list_main_view_->ShowAppListWhenReady();
185 }
186
187 void AppListView::Close() {
188   app_list_main_view_->Close();
189   delegate_->Dismiss();
190 }
191
192 void AppListView::UpdateBounds() {
193   SizeToContents();
194 }
195
196 gfx::Size AppListView::GetPreferredSize() {
197   return app_list_main_view_->GetPreferredSize();
198 }
199
200 void AppListView::Paint(gfx::Canvas* canvas) {
201   views::BubbleDelegateView::Paint(canvas);
202   if (g_next_paint_callback) {
203     g_next_paint_callback();
204     g_next_paint_callback = NULL;
205   }
206 }
207
208 void AppListView::OnThemeChanged() {
209 #if defined(OS_WIN)
210   GetWidget()->Close();
211 #endif
212 }
213
214 bool AppListView::ShouldHandleSystemCommands() const {
215   return true;
216 }
217
218 void AppListView::Prerender() {
219   app_list_main_view_->Prerender();
220 }
221
222 void AppListView::OnProfilesChanged() {
223   SigninDelegate* signin_delegate =
224       delegate_ ? delegate_->GetSigninDelegate() : NULL;
225   bool show_signin_view = signin_delegate && signin_delegate->NeedSignin();
226
227   signin_view_->SetVisible(show_signin_view);
228   app_list_main_view_->SetVisible(!show_signin_view);
229   app_list_main_view_->search_box_view()->InvalidateMenu();
230 }
231
232 void AppListView::SetProfileByPath(const base::FilePath& profile_path) {
233   delegate_->SetProfileByPath(profile_path);
234   app_list_main_view_->ModelChanged();
235 }
236
237 void AppListView::AddObserver(AppListViewObserver* observer) {
238   observers_.AddObserver(observer);
239 }
240
241 void AppListView::RemoveObserver(AppListViewObserver* observer) {
242   observers_.RemoveObserver(observer);
243 }
244
245 // static
246 void AppListView::SetNextPaintCallback(void (*callback)()) {
247   g_next_paint_callback = callback;
248 }
249
250 #if defined(OS_WIN)
251 HWND AppListView::GetHWND() const {
252 #if defined(USE_AURA)
253   gfx::NativeWindow window =
254       GetWidget()->GetTopLevelWidget()->GetNativeWindow();
255   return window->GetHost()->GetAcceleratedWidget();
256 #else
257   return GetWidget()->GetTopLevelWidget()->GetNativeWindow();
258 #endif
259 }
260 #endif
261
262 void AppListView::InitAsBubbleInternal(gfx::NativeView parent,
263                                        PaginationModel* pagination_model,
264                                        views::BubbleBorder::Arrow arrow,
265                                        bool border_accepts_events,
266                                        const gfx::Vector2d& anchor_offset) {
267   app_list_main_view_ = new AppListMainView(delegate_.get(),
268                                             pagination_model,
269                                             parent);
270   AddChildView(app_list_main_view_);
271 #if defined(USE_AURA)
272   app_list_main_view_->SetPaintToLayer(true);
273   app_list_main_view_->SetFillsBoundsOpaquely(false);
274   app_list_main_view_->layer()->SetMasksToBounds(true);
275 #endif
276
277   signin_view_ =
278       new SigninView(delegate_->GetSigninDelegate(),
279                      app_list_main_view_->GetPreferredSize().width());
280   AddChildView(signin_view_);
281
282   // Speech recognition is available only when the start page exists.
283   if (delegate_ && delegate_->GetSpeechRecognitionContents()) {
284     speech_view_ = new SpeechView(delegate_.get());
285     speech_view_->SetVisible(false);
286 #if defined(USE_AURA)
287     speech_view_->SetPaintToLayer(true);
288     speech_view_->SetFillsBoundsOpaquely(false);
289     speech_view_->layer()->SetOpacity(0.0f);
290 #endif
291     AddChildView(speech_view_);
292   }
293
294   OnProfilesChanged();
295   set_color(kContentsBackgroundColor);
296   set_margins(gfx::Insets());
297   set_move_with_anchor(true);
298   set_parent_window(parent);
299   set_close_on_deactivate(false);
300   set_close_on_esc(false);
301   set_anchor_view_insets(gfx::Insets(kArrowOffset + anchor_offset.y(),
302                                      kArrowOffset + anchor_offset.x(),
303                                      kArrowOffset - anchor_offset.y(),
304                                      kArrowOffset - anchor_offset.x()));
305   set_border_accepts_events(border_accepts_events);
306   set_shadow(SupportsShadow() ? views::BubbleBorder::BIG_SHADOW
307                               : views::BubbleBorder::NO_SHADOW_OPAQUE_BORDER);
308   views::BubbleDelegateView::CreateBubble(this);
309   SetBubbleArrow(arrow);
310
311 #if defined(USE_AURA)
312   aura::Window* window = GetWidget()->GetNativeWindow();
313   window->layer()->SetMasksToBounds(true);
314   GetBubbleFrameView()->set_background(new AppListBackground(
315       GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
316       app_list_main_view_));
317   set_background(NULL);
318   window->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
319       new views::BubbleWindowTargeter(this)));
320 #else
321   set_background(new AppListBackground(
322       GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
323       app_list_main_view_));
324
325   // On non-aura the bubble has two widgets, and it's possible for the border
326   // to be shown independently in odd situations. Explicitly hide the bubble
327   // widget to ensure that any WM_WINDOWPOSCHANGED messages triggered by the
328   // window manager do not have the SWP_SHOWWINDOW flag set which would cause
329   // the border to be shown. See http://crbug.com/231687 .
330   GetWidget()->Hide();
331 #endif
332
333   OnSpeechRecognitionStateChanged(delegate_->GetSpeechUI()->state());
334
335   if (delegate_)
336     delegate_->ViewInitialized();
337 }
338
339 void AppListView::OnBeforeBubbleWidgetInit(
340     views::Widget::InitParams* params,
341     views::Widget* widget) const {
342 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
343   if (delegate_ && delegate_->ForceNativeDesktop())
344     params->native_widget = new views::DesktopNativeWidgetAura(widget);
345 #endif
346 #if defined(OS_WIN)
347   // Windows 7 and higher offer pinning to the taskbar, but we need presence
348   // on the taskbar for the user to be able to pin us. So, show the window on
349   // the taskbar for these versions of Windows.
350   if (base::win::GetVersion() >= base::win::VERSION_WIN7)
351     params->force_show_in_taskbar = true;
352 #elif defined(OS_LINUX)
353   // Set up a custom WM_CLASS for the app launcher window. This allows task
354   // switchers in X11 environments to distinguish it from main browser windows.
355   params->wm_class_name = kAppListWMClass;
356   // Show the window in the taskbar, even though it is a bubble, which would not
357   // normally be shown.
358   params->force_show_in_taskbar = true;
359 #endif
360 }
361
362 views::View* AppListView::GetInitiallyFocusedView() {
363   return app_list_main_view_->search_box_view()->search_box();
364 }
365
366 gfx::ImageSkia AppListView::GetWindowIcon() {
367   if (delegate_)
368     return delegate_->GetWindowIcon();
369
370   return gfx::ImageSkia();
371 }
372
373 bool AppListView::WidgetHasHitTestMask() const {
374   return true;
375 }
376
377 void AppListView::GetWidgetHitTestMask(gfx::Path* mask) const {
378   DCHECK(mask);
379   mask->addRect(gfx::RectToSkRect(
380       GetBubbleFrameView()->GetContentsBounds()));
381 }
382
383 bool AppListView::AcceleratorPressed(const ui::Accelerator& accelerator) {
384   // The accelerator is added by BubbleDelegateView.
385   if (accelerator.key_code() == ui::VKEY_ESCAPE) {
386     if (app_list_main_view_->search_box_view()->HasSearch()) {
387       app_list_main_view_->search_box_view()->ClearSearch();
388     } else {
389       GetWidget()->Deactivate();
390       Close();
391     }
392     return true;
393   }
394
395   return false;
396 }
397
398 void AppListView::Layout() {
399   const gfx::Rect contents_bounds = GetContentsBounds();
400   app_list_main_view_->SetBoundsRect(contents_bounds);
401   signin_view_->SetBoundsRect(contents_bounds);
402
403   if (speech_view_) {
404     gfx::Rect speech_bounds = contents_bounds;
405     int preferred_height = speech_view_->GetPreferredSize().height();
406     speech_bounds.Inset(kSpeechUIMargin, kSpeechUIMargin);
407     speech_bounds.set_height(std::min(speech_bounds.height(),
408                                       preferred_height));
409     speech_bounds.Inset(-speech_view_->GetInsets());
410     speech_view_->SetBoundsRect(speech_bounds);
411   }
412 }
413
414 void AppListView::SchedulePaintInRect(const gfx::Rect& rect) {
415   BubbleDelegateView::SchedulePaintInRect(rect);
416   if (GetBubbleFrameView())
417     GetBubbleFrameView()->SchedulePaint();
418 }
419
420 void AppListView::OnWidgetDestroying(views::Widget* widget) {
421   BubbleDelegateView::OnWidgetDestroying(widget);
422   if (delegate_ && widget == GetWidget())
423     delegate_->ViewClosing();
424 }
425
426 void AppListView::OnWidgetActivationChanged(views::Widget* widget,
427                                             bool active) {
428   // Do not called inherited function as the bubble delegate auto close
429   // functionality is not used.
430   if (widget == GetWidget())
431     FOR_EACH_OBSERVER(AppListViewObserver, observers_,
432                       OnActivationChanged(widget, active));
433 }
434
435 void AppListView::OnWidgetVisibilityChanged(views::Widget* widget,
436                                             bool visible) {
437   BubbleDelegateView::OnWidgetVisibilityChanged(widget, visible);
438
439   if (widget != GetWidget())
440     return;
441
442   // We clear the search when hiding so the next time the app list appears it is
443   // not showing search results.
444   if (!visible)
445     app_list_main_view_->search_box_view()->ClearSearch();
446
447   // Whether we need to signin or not may have changed since last time we were
448   // shown.
449   Layout();
450 }
451
452 void AppListView::OnSpeechRecognitionStateChanged(
453     SpeechRecognitionState new_state) {
454   if (signin_view_->visible() || !speech_view_)
455     return;
456
457   bool recognizing = (new_state == SPEECH_RECOGNITION_RECOGNIZING ||
458                       new_state == SPEECH_RECOGNITION_IN_SPEECH);
459   // No change for this class.
460   if (speech_view_->visible() == recognizing)
461     return;
462
463   if (recognizing)
464     speech_view_->Reset();
465
466 #if defined(USE_AURA)
467   animation_observer_->set_frame(GetBubbleFrameView());
468   gfx::Transform speech_transform;
469   speech_transform.Translate(
470       0, SkFloatToMScalar(kSpeechUIAppearingPosition));
471   if (recognizing)
472     speech_view_->layer()->SetTransform(speech_transform);
473
474   {
475     ui::ScopedLayerAnimationSettings main_settings(
476         app_list_main_view_->layer()->GetAnimator());
477     if (recognizing) {
478       animation_observer_->SetTarget(app_list_main_view_);
479       main_settings.AddObserver(animation_observer_.get());
480     }
481     app_list_main_view_->layer()->SetOpacity(recognizing ? 0.0f : 1.0f);
482   }
483
484   {
485     ui::ScopedLayerAnimationSettings speech_settings(
486         speech_view_->layer()->GetAnimator());
487     if (!recognizing) {
488       animation_observer_->SetTarget(speech_view_);
489       speech_settings.AddObserver(animation_observer_.get());
490     }
491
492     speech_view_->layer()->SetOpacity(recognizing ? 1.0f : 0.0f);
493     if (recognizing)
494       speech_view_->layer()->SetTransform(gfx::Transform());
495     else
496       speech_view_->layer()->SetTransform(speech_transform);
497   }
498
499   if (recognizing)
500     speech_view_->SetVisible(true);
501   else
502     app_list_main_view_->SetVisible(true);
503 #else
504   speech_view_->SetVisible(recognizing);
505   app_list_main_view_->SetVisible(!recognizing);
506
507   // Needs to schedule paint of AppListView itself, to repaint the background.
508   GetBubbleFrameView()->SchedulePaint();
509 #endif
510 }
511
512 }  // namespace app_list