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/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"
44 #include "ui/aura/window.h"
45 #include "ui/aura/window_tree_host.h"
46 #include "ui/views/bubble/bubble_window_targeter.h"
48 #include "ui/base/win/shell.h"
50 #if !defined(OS_CHROMEOS)
51 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
53 #endif // defined(USE_AURA)
59 // The margin from the edge to the speech UI.
60 const int kSpeechUIMargin = 12;
62 // The vertical position for the appearing animation of the speech UI.
63 const float kSpeechUIAppearingPosition = 12;
65 // The distance between the arrow tip and edge of the anchor view.
66 const int kArrowOffset = 10;
68 // Determines whether the current environment supports shadows bubble borders.
69 bool SupportsShadow() {
71 // Shadows are not supported on Windows without Aero Glass.
72 if (!ui::win::IsAeroGlassEnabled() ||
73 CommandLine::ForCurrentProcess()->HasSwitch(
74 ::switches::kDisableDwmComposition)) {
77 #elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
78 // Shadows are not supported on (non-ChromeOS) Linux.
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 {
88 AppListOverlayBackground(int corner_radius)
89 : corner_radius_(corner_radius) {};
90 virtual ~AppListOverlayBackground() {};
92 // Overridden from views::Background:
93 virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE {
95 paint.setStyle(SkPaint::kFill_Style);
96 paint.setColor(SK_ColorWHITE);
97 canvas->DrawRoundRect(view->GetContentsBounds(), corner_radius_, paint);
101 const int corner_radius_;
103 DISALLOW_COPY_AND_ASSIGN(AppListOverlayBackground);
108 // An animation observer to hide the view at the end of the animation.
109 class HideViewAnimationObserver : public ui::ImplicitAnimationObserver {
111 HideViewAnimationObserver()
116 virtual ~HideViewAnimationObserver() {
118 StopObservingImplicitAnimations();
121 void SetTarget(views::View* target) {
123 StopObservingImplicitAnimations();
127 void set_frame(views::BubbleFrameView* frame) { frame_ = frame; }
130 // Overridden from ui::ImplicitAnimationObserver:
131 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
133 target_->SetVisible(false);
136 // Should update the background by invoking SchedulePaint().
138 frame_->SchedulePaint();
142 views::BubbleFrameView* frame_;
143 views::View* target_;
145 DISALLOW_COPY_AND_ASSIGN(HideViewAnimationObserver);
148 ////////////////////////////////////////////////////////////////////////////////
151 AppListView::AppListView(AppListViewDelegate* delegate)
152 : delegate_(delegate),
153 app_list_main_view_(NULL),
155 experimental_banner_view_(NULL),
157 animation_observer_(new HideViewAnimationObserver()) {
160 delegate_->AddObserver(this);
161 delegate_->GetSpeechUI()->AddObserver(this);
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);
172 void AppListView::InitAsBubbleAttachedToAnchor(
173 gfx::NativeView parent,
174 int initial_apps_page,
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);
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) {
191 SetAnchorRect(gfx::Rect(anchor_point_in_screen, gfx::Size()));
192 InitAsBubbleInternal(
193 parent, initial_apps_page, arrow, border_accepts_events, gfx::Vector2d());
196 void AppListView::SetBubbleArrow(views::BubbleBorder::Arrow arrow) {
197 GetBubbleFrameView()->bubble_border()->set_arrow(arrow);
198 SizeToContents(); // Recalcuates with new border.
199 GetBubbleFrameView()->SchedulePaint();
202 void AppListView::SetAnchorPoint(const gfx::Point& anchor_point) {
203 SetAnchorRect(gfx::Rect(anchor_point, gfx::Size()));
206 void AppListView::SetDragAndDropHostOfCurrentAppList(
207 ApplicationDragAndDropHost* drag_and_drop_host) {
208 app_list_main_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
211 void AppListView::ShowWhenReady() {
212 app_list_main_view_->ShowAppListWhenReady();
215 void AppListView::Close() {
216 app_list_main_view_->Close();
217 delegate_->Dismiss();
220 void AppListView::UpdateBounds() {
224 void AppListView::SetAppListOverlayVisible(bool visible) {
225 DCHECK(overlay_view_);
227 // Display the overlay immediately so we can begin the animation.
228 overlay_view_->SetVisible(true);
230 ui::ScopedLayerAnimationSettings settings(
231 overlay_view_->layer()->GetAnimator());
232 settings.SetTweenType(gfx::Tween::LINEAR);
234 // If we're dismissing the overlay, hide the view at the end of the animation.
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());
243 const float kOverlayFadeInMilliseconds = 125;
244 settings.SetTransitionDuration(
245 base::TimeDelta::FromMilliseconds(kOverlayFadeInMilliseconds));
247 const float kOverlayOpacity = 0.75f;
248 overlay_view_->layer()->SetOpacity(visible ? kOverlayOpacity : 0.0f);
251 bool AppListView::ShouldCenterWindow() const {
252 return delegate_->ShouldCenterWindow();
255 gfx::Size AppListView::GetPreferredSize() const {
256 return app_list_main_view_->GetPreferredSize();
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();
267 void AppListView::OnThemeChanged() {
269 GetWidget()->Close();
273 bool AppListView::ShouldHandleSystemCommands() const {
277 void AppListView::Prerender() {
278 app_list_main_view_->Prerender();
281 void AppListView::OnProfilesChanged() {
282 app_list_main_view_->search_box_view()->InvalidateMenu();
285 void AppListView::OnShutdown() {
286 // Nothing to do on views - the widget will soon be closed, which will tear
290 void AppListView::SetProfileByPath(const base::FilePath& profile_path) {
291 delegate_->SetProfileByPath(profile_path);
292 app_list_main_view_->ModelChanged();
295 void AppListView::AddObserver(AppListViewObserver* observer) {
296 observers_.AddObserver(observer);
299 void AppListView::RemoveObserver(AppListViewObserver* observer) {
300 observers_.RemoveObserver(observer);
304 void AppListView::SetNextPaintCallback(const base::Closure& callback) {
305 next_paint_callback_ = callback;
309 HWND AppListView::GetHWND() const {
310 gfx::NativeWindow window =
311 GetWidget()->GetTopLevelWidget()->GetNativeWindow();
312 return window->GetHost()->GetAcceleratedWidget();
316 PaginationModel* AppListView::GetAppsPaginationModel() {
317 return app_list_main_view_->contents_view()
318 ->apps_container_view()
320 ->pagination_model();
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();
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);
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_);
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_);
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);
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)));
385 set_background(new AppListBackground(
386 GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
387 app_list_main_view_));
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 .
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
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_);
417 delegate_->ViewInitialized();
419 UMA_HISTOGRAM_TIMES("Apps.AppListCreationTime",
420 base::Time::Now() - start_time);
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);
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;
446 views::View* AppListView::GetInitiallyFocusedView() {
447 return app_list::switches::IsExperimentalAppListEnabled()
448 ? app_list_main_view_->contents_view()
450 ->dummy_search_box_view()
452 : app_list_main_view_->search_box_view()->search_box();
455 gfx::ImageSkia AppListView::GetWindowIcon() {
457 return delegate_->GetWindowIcon();
459 return gfx::ImageSkia();
462 bool AppListView::WidgetHasHitTestMask() const {
466 void AppListView::GetWidgetHitTestMask(gfx::Path* mask) const {
468 mask->addRect(gfx::RectToSkRect(
469 GetBubbleFrameView()->GetContentsBounds()));
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()
486 GetWidget()->Deactivate();
495 void AppListView::Layout() {
496 const gfx::Rect contents_bounds = GetContentsBounds();
497 app_list_main_view_->SetBoundsRect(contents_bounds);
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(),
505 speech_bounds.Inset(-speech_view_->GetInsets());
506 speech_view_->SetBoundsRect(speech_bounds);
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);
519 void AppListView::SchedulePaintInRect(const gfx::Rect& rect) {
520 BubbleDelegateView::SchedulePaintInRect(rect);
521 if (GetBubbleFrameView())
522 GetBubbleFrameView()->SchedulePaint();
525 void AppListView::OnWidgetDestroying(views::Widget* widget) {
526 BubbleDelegateView::OnWidgetDestroying(widget);
527 if (delegate_ && widget == GetWidget())
528 delegate_->ViewClosing();
531 void AppListView::OnWidgetActivationChanged(views::Widget* widget,
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));
540 void AppListView::OnWidgetVisibilityChanged(views::Widget* widget,
542 BubbleDelegateView::OnWidgetVisibilityChanged(widget, visible);
544 if (widget != GetWidget())
548 app_list_main_view_->ResetForShow();
551 void AppListView::OnSpeechRecognitionStateChanged(
552 SpeechRecognitionState new_state) {
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)
564 speech_view_->Reset();
566 animation_observer_->set_frame(GetBubbleFrameView());
567 gfx::Transform speech_transform;
568 speech_transform.Translate(
569 0, SkFloatToMScalar(kSpeechUIAppearingPosition));
571 speech_view_->layer()->SetTransform(speech_transform);
574 ui::ScopedLayerAnimationSettings main_settings(
575 app_list_main_view_->layer()->GetAnimator());
577 animation_observer_->SetTarget(app_list_main_view_);
578 main_settings.AddObserver(animation_observer_.get());
580 app_list_main_view_->layer()->SetOpacity(will_appear ? 0.0f : 1.0f);
584 ui::ScopedLayerAnimationSettings speech_settings(
585 speech_view_->layer()->GetAnimator());
587 animation_observer_->SetTarget(speech_view_);
588 speech_settings.AddObserver(animation_observer_.get());
591 speech_view_->layer()->SetOpacity(will_appear ? 1.0f : 0.0f);
593 speech_view_->layer()->SetTransform(gfx::Transform());
595 speech_view_->layer()->SetTransform(speech_transform);
599 speech_view_->SetVisible(true);
601 app_list_main_view_->SetVisible(true);
604 } // namespace app_list