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