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.
5 #include "ui/app_list/views/app_list_view.h"
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_switches.h"
15 #include "ui/app_list/app_list_view_delegate.h"
16 #include "ui/app_list/speech_ui_model.h"
17 #include "ui/app_list/views/app_list_background.h"
18 #include "ui/app_list/views/app_list_folder_view.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/apps_container_view.h"
22 #include "ui/app_list/views/contents_view.h"
23 #include "ui/app_list/views/search_box_view.h"
24 #include "ui/app_list/views/speech_view.h"
25 #include "ui/app_list/views/start_page_view.h"
26 #include "ui/base/ui_base_switches.h"
27 #include "ui/compositor/layer.h"
28 #include "ui/compositor/layer_animation_observer.h"
29 #include "ui/compositor/scoped_layer_animation_settings.h"
30 #include "ui/gfx/canvas.h"
31 #include "ui/gfx/image/image_skia.h"
32 #include "ui/gfx/insets.h"
33 #include "ui/gfx/path.h"
34 #include "ui/gfx/skia_util.h"
35 #include "ui/resources/grit/ui_resources.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"
43 #include "ui/aura/window.h"
44 #include "ui/aura/window_tree_host.h"
45 #include "ui/views/bubble/bubble_window_targeter.h"
47 #include "ui/base/win/shell.h"
49 #if !defined(OS_CHROMEOS)
50 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
52 #endif // defined(USE_AURA)
58 // The margin from the edge to the speech UI.
59 const int kSpeechUIMargin
= 12;
61 // The vertical position for the appearing animation of the speech UI.
62 const float kSpeechUIAppearingPosition
= 12;
64 // The distance between the arrow tip and edge of the anchor view.
65 const int kArrowOffset
= 10;
67 // Determines whether the current environment supports shadows bubble borders.
68 bool SupportsShadow() {
70 // Shadows are not supported on Windows without Aero Glass.
71 if (!ui::win::IsAeroGlassEnabled() ||
72 CommandLine::ForCurrentProcess()->HasSwitch(
73 ::switches::kDisableDwmComposition
)) {
76 #elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
77 // Shadows are not supported on (non-ChromeOS) Linux.
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
{
87 AppListOverlayBackground(int corner_radius
)
88 : corner_radius_(corner_radius
) {};
89 virtual ~AppListOverlayBackground() {};
91 // Overridden from views::Background:
92 virtual void Paint(gfx::Canvas
* canvas
, views::View
* view
) const OVERRIDE
{
94 paint
.setStyle(SkPaint::kFill_Style
);
95 paint
.setColor(SK_ColorWHITE
);
96 canvas
->DrawRoundRect(view
->GetContentsBounds(), corner_radius_
, paint
);
100 const int corner_radius_
;
102 DISALLOW_COPY_AND_ASSIGN(AppListOverlayBackground
);
107 // An animation observer to hide the view at the end of the animation.
108 class HideViewAnimationObserver
: public ui::ImplicitAnimationObserver
{
110 HideViewAnimationObserver()
115 virtual ~HideViewAnimationObserver() {
117 StopObservingImplicitAnimations();
120 void SetTarget(views::View
* target
) {
122 StopObservingImplicitAnimations();
126 void set_frame(views::BubbleFrameView
* frame
) { frame_
= frame
; }
129 // Overridden from ui::ImplicitAnimationObserver:
130 virtual void OnImplicitAnimationsCompleted() OVERRIDE
{
132 target_
->SetVisible(false);
135 // Should update the background by invoking SchedulePaint().
137 frame_
->SchedulePaint();
141 views::BubbleFrameView
* frame_
;
142 views::View
* target_
;
144 DISALLOW_COPY_AND_ASSIGN(HideViewAnimationObserver
);
147 ////////////////////////////////////////////////////////////////////////////////
150 AppListView::AppListView(AppListViewDelegate
* delegate
)
151 : delegate_(delegate
),
152 app_list_main_view_(NULL
),
154 experimental_banner_view_(NULL
),
156 animation_observer_(new HideViewAnimationObserver()) {
159 delegate_
->AddObserver(this);
160 delegate_
->GetSpeechUI()->AddObserver(this);
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);
171 void AppListView::InitAsBubbleAttachedToAnchor(
172 gfx::NativeView parent
,
173 int initial_apps_page
,
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
);
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
) {
190 SetAnchorRect(gfx::Rect(anchor_point_in_screen
, gfx::Size()));
191 InitAsBubbleInternal(
192 parent
, initial_apps_page
, arrow
, border_accepts_events
, gfx::Vector2d());
195 void AppListView::SetBubbleArrow(views::BubbleBorder::Arrow arrow
) {
196 GetBubbleFrameView()->bubble_border()->set_arrow(arrow
);
197 SizeToContents(); // Recalcuates with new border.
198 GetBubbleFrameView()->SchedulePaint();
201 void AppListView::SetAnchorPoint(const gfx::Point
& anchor_point
) {
202 SetAnchorRect(gfx::Rect(anchor_point
, gfx::Size()));
205 void AppListView::SetDragAndDropHostOfCurrentAppList(
206 ApplicationDragAndDropHost
* drag_and_drop_host
) {
207 app_list_main_view_
->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host
);
210 void AppListView::ShowWhenReady() {
211 app_list_main_view_
->ShowAppListWhenReady();
214 void AppListView::Close() {
215 app_list_main_view_
->Close();
216 delegate_
->Dismiss();
219 void AppListView::UpdateBounds() {
223 void AppListView::SetAppListOverlayVisible(bool visible
) {
224 DCHECK(overlay_view_
);
226 // Display the overlay immediately so we can begin the animation.
227 overlay_view_
->SetVisible(true);
229 ui::ScopedLayerAnimationSettings
settings(
230 overlay_view_
->layer()->GetAnimator());
231 settings
.SetTweenType(gfx::Tween::LINEAR
);
233 // If we're dismissing the overlay, hide the view at the end of the animation.
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());
242 const float kOverlayFadeInMilliseconds
= 125;
243 settings
.SetTransitionDuration(
244 base::TimeDelta::FromMilliseconds(kOverlayFadeInMilliseconds
));
246 const float kOverlayOpacity
= 0.75f
;
247 overlay_view_
->layer()->SetOpacity(visible
? kOverlayOpacity
: 0.0f
);
250 bool AppListView::ShouldCenterWindow() const {
251 return delegate_
->ShouldCenterWindow();
254 gfx::Size
AppListView::GetPreferredSize() const {
255 return app_list_main_view_
->GetPreferredSize();
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();
266 void AppListView::OnThemeChanged() {
268 GetWidget()->Close();
272 bool AppListView::ShouldHandleSystemCommands() const {
276 void AppListView::Prerender() {
277 app_list_main_view_
->Prerender();
280 void AppListView::OnProfilesChanged() {
281 app_list_main_view_
->search_box_view()->InvalidateMenu();
284 void AppListView::SetProfileByPath(const base::FilePath
& profile_path
) {
285 delegate_
->SetProfileByPath(profile_path
);
286 app_list_main_view_
->ModelChanged();
289 void AppListView::AddObserver(AppListViewObserver
* observer
) {
290 observers_
.AddObserver(observer
);
293 void AppListView::RemoveObserver(AppListViewObserver
* observer
) {
294 observers_
.RemoveObserver(observer
);
298 void AppListView::SetNextPaintCallback(const base::Closure
& callback
) {
299 next_paint_callback_
= callback
;
303 HWND
AppListView::GetHWND() const {
304 gfx::NativeWindow window
=
305 GetWidget()->GetTopLevelWidget()->GetNativeWindow();
306 return window
->GetHost()->GetAcceleratedWidget();
310 PaginationModel
* AppListView::GetAppsPaginationModel() {
311 return app_list_main_view_
->contents_view()
312 ->apps_container_view()
314 ->pagination_model();
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);
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_
);
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_
);
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
);
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)));
377 set_background(new AppListBackground(
378 GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
379 app_list_main_view_
));
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 .
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
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_
);
409 delegate_
->ViewInitialized();
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
);
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;
435 views::View
* AppListView::GetInitiallyFocusedView() {
436 return app_list::switches::IsExperimentalAppListEnabled()
437 ? app_list_main_view_
->contents_view()
439 ->dummy_search_box_view()
441 : app_list_main_view_
->search_box_view()->search_box();
444 gfx::ImageSkia
AppListView::GetWindowIcon() {
446 return delegate_
->GetWindowIcon();
448 return gfx::ImageSkia();
451 bool AppListView::WidgetHasHitTestMask() const {
455 void AppListView::GetWidgetHitTestMask(gfx::Path
* mask
) const {
457 mask
->addRect(gfx::RectToSkRect(
458 GetBubbleFrameView()->GetContentsBounds()));
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()
475 GetWidget()->Deactivate();
484 void AppListView::Layout() {
485 const gfx::Rect contents_bounds
= GetContentsBounds();
486 app_list_main_view_
->SetBoundsRect(contents_bounds
);
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(),
494 speech_bounds
.Inset(-speech_view_
->GetInsets());
495 speech_view_
->SetBoundsRect(speech_bounds
);
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
);
508 void AppListView::SchedulePaintInRect(const gfx::Rect
& rect
) {
509 BubbleDelegateView::SchedulePaintInRect(rect
);
510 if (GetBubbleFrameView())
511 GetBubbleFrameView()->SchedulePaint();
514 void AppListView::OnWidgetDestroying(views::Widget
* widget
) {
515 BubbleDelegateView::OnWidgetDestroying(widget
);
516 if (delegate_
&& widget
== GetWidget())
517 delegate_
->ViewClosing();
520 void AppListView::OnWidgetActivationChanged(views::Widget
* widget
,
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
));
529 void AppListView::OnWidgetVisibilityChanged(views::Widget
* widget
,
531 BubbleDelegateView::OnWidgetVisibilityChanged(widget
, visible
);
533 if (widget
!= GetWidget())
537 app_list_main_view_
->ResetForShow();
540 void AppListView::OnSpeechRecognitionStateChanged(
541 SpeechRecognitionState new_state
) {
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
)
553 speech_view_
->Reset();
555 animation_observer_
->set_frame(GetBubbleFrameView());
556 gfx::Transform speech_transform
;
557 speech_transform
.Translate(
558 0, SkFloatToMScalar(kSpeechUIAppearingPosition
));
560 speech_view_
->layer()->SetTransform(speech_transform
);
563 ui::ScopedLayerAnimationSettings
main_settings(
564 app_list_main_view_
->layer()->GetAnimator());
566 animation_observer_
->SetTarget(app_list_main_view_
);
567 main_settings
.AddObserver(animation_observer_
.get());
569 app_list_main_view_
->layer()->SetOpacity(will_appear
? 0.0f
: 1.0f
);
573 ui::ScopedLayerAnimationSettings
speech_settings(
574 speech_view_
->layer()->GetAnimator());
576 animation_observer_
->SetTarget(speech_view_
);
577 speech_settings
.AddObserver(animation_observer_
.get());
580 speech_view_
->layer()->SetOpacity(will_appear
? 1.0f
: 0.0f
);
582 speech_view_
->layer()->SetTransform(gfx::Transform());
584 speech_view_
->layer()->SetTransform(speech_transform
);
588 speech_view_
->SetVisible(true);
590 app_list_main_view_
->SetVisible(true);
593 } // namespace app_list