// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/app_list/views/app_list_view.h" #include #include "base/command_line.h" #include "base/macros.h" #include "base/metrics/histogram.h" #include "base/profiler/scoped_tracker.h" #include "base/strings/string_util.h" #include "base/win/windows_version.h" #include "build/build_config.h" #include "ui/app_list/app_list_constants.h" #include "ui/app_list/app_list_model.h" #include "ui/app_list/app_list_switches.h" #include "ui/app_list/app_list_view_delegate.h" #include "ui/app_list/speech_ui_model.h" #include "ui/app_list/views/app_list_background.h" #include "ui/app_list/views/app_list_folder_view.h" #include "ui/app_list/views/app_list_main_view.h" #include "ui/app_list/views/app_list_view_observer.h" #include "ui/app_list/views/apps_container_view.h" #include "ui/app_list/views/contents_view.h" #include "ui/app_list/views/custom_launcher_page_view.h" #include "ui/app_list/views/search_box_view.h" #include "ui/app_list/views/speech_view.h" #include "ui/app_list/views/start_page_view.h" #include "ui/base/ui_base_switches.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/path.h" #include "ui/gfx/skia_util.h" #include "ui/resources/grit/ui_resources.h" #include "ui/views/bubble/bubble_frame_view.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/views_delegate.h" #include "ui/views/widget/widget.h" #if defined(USE_AURA) #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" #include "ui/views/bubble/bubble_window_targeter.h" #include "ui/wm/core/masked_window_targeter.h" #if defined(OS_WIN) #include "ui/base/win/shell.h" #endif #if !defined(OS_CHROMEOS) #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" #endif #endif // defined(USE_AURA) namespace app_list { namespace { // The margin from the edge to the speech UI. const int kSpeechUIMargin = 12; // The vertical position for the appearing animation of the speech UI. const float kSpeechUIAppearingPosition = 12; // The distance between the arrow tip and edge of the anchor view. const int kArrowOffset = 10; // Determines whether the current environment supports shadows bubble borders. bool SupportsShadow() { #if defined(OS_WIN) // Shadows are not supported on Windows without Aero Glass. if (!ui::win::IsAeroGlassEnabled() || base::CommandLine::ForCurrentProcess()->HasSwitch( ::switches::kDisableDwmComposition)) { return false; } #elif defined(OS_LINUX) && !defined(OS_CHROMEOS) // Shadows are not supported on (non-ChromeOS) Linux. return false; #endif return true; } // This view forwards the focus to the search box widget by providing it as a // FocusTraversable when a focus search is provided. class SearchBoxFocusHost : public views::View { public: explicit SearchBoxFocusHost(views::Widget* search_box_widget) : search_box_widget_(search_box_widget) {} ~SearchBoxFocusHost() override {} views::FocusTraversable* GetFocusTraversable() override { return search_box_widget_; } private: views::Widget* search_box_widget_; DISALLOW_COPY_AND_ASSIGN(SearchBoxFocusHost); }; // The view for the App List overlay, which appears as a white rounded // rectangle with the given radius. class AppListOverlayView : public views::View { public: explicit AppListOverlayView(int corner_radius) : corner_radius_(corner_radius) { SetPaintToLayer(true); SetVisible(false); layer()->SetOpacity(0.0f); } ~AppListOverlayView() override {} // Overridden from views::View: void OnPaint(gfx::Canvas* canvas) override { SkPaint paint; paint.setStyle(SkPaint::kFill_Style); paint.setColor(SK_ColorWHITE); canvas->DrawRoundRect(GetContentsBounds(), corner_radius_, paint); } private: const int corner_radius_; DISALLOW_COPY_AND_ASSIGN(AppListOverlayView); }; #if defined(USE_AURA) // An event targeter for the search box widget which will ignore events that // are on the search box's shadow. class SearchBoxWindowTargeter : public wm::MaskedWindowTargeter { public: explicit SearchBoxWindowTargeter(views::View* search_box) : wm::MaskedWindowTargeter(search_box->GetWidget()->GetNativeWindow()), search_box_(search_box) {} ~SearchBoxWindowTargeter() override {} private: // wm::MaskedWindowTargeter: bool GetHitTestMask(aura::Window* window, gfx::Path* mask) const override { mask->addRect(gfx::RectToSkRect(search_box_->GetContentsBounds())); return true; } views::View* search_box_; DISALLOW_COPY_AND_ASSIGN(SearchBoxWindowTargeter); }; #endif } // namespace // An animation observer to hide the view at the end of the animation. class HideViewAnimationObserver : public ui::ImplicitAnimationObserver { public: HideViewAnimationObserver() : frame_(NULL), target_(NULL) { } ~HideViewAnimationObserver() override { if (target_) StopObservingImplicitAnimations(); } void SetTarget(views::View* target) { if (target_) StopObservingImplicitAnimations(); target_ = target; } void set_frame(views::BubbleFrameView* frame) { frame_ = frame; } private: // Overridden from ui::ImplicitAnimationObserver: void OnImplicitAnimationsCompleted() override { if (target_) { target_->SetVisible(false); target_ = NULL; // Should update the background by invoking SchedulePaint(). if (frame_) frame_->SchedulePaint(); } } views::BubbleFrameView* frame_; views::View* target_; DISALLOW_COPY_AND_ASSIGN(HideViewAnimationObserver); }; //////////////////////////////////////////////////////////////////////////////// // AppListView: AppListView::AppListView(AppListViewDelegate* delegate) : delegate_(delegate), app_list_main_view_(nullptr), speech_view_(nullptr), search_box_focus_host_(nullptr), search_box_widget_(nullptr), search_box_view_(nullptr), overlay_view_(nullptr), animation_observer_(new HideViewAnimationObserver()) { CHECK(delegate); delegate_->AddObserver(this); delegate_->GetSpeechUI()->AddObserver(this); } AppListView::~AppListView() { delegate_->GetSpeechUI()->RemoveObserver(this); delegate_->RemoveObserver(this); animation_observer_.reset(); // Remove child views first to ensure no remaining dependencies on delegate_. RemoveAllChildViews(true); } void AppListView::InitAsBubbleAttachedToAnchor( gfx::NativeView parent, int initial_apps_page, views::View* anchor, const gfx::Vector2d& anchor_offset, views::BubbleBorder::Arrow arrow, bool border_accepts_events) { SetAnchorView(anchor); InitAsBubbleInternal( parent, initial_apps_page, arrow, border_accepts_events, anchor_offset); } void AppListView::InitAsBubbleAtFixedLocation( gfx::NativeView parent, int initial_apps_page, const gfx::Point& anchor_point_in_screen, views::BubbleBorder::Arrow arrow, bool border_accepts_events) { SetAnchorView(NULL); SetAnchorRect(gfx::Rect(anchor_point_in_screen, gfx::Size())); InitAsBubbleInternal( parent, initial_apps_page, arrow, border_accepts_events, gfx::Vector2d()); } void AppListView::InitAsFramelessWindow(gfx::NativeView parent, int initial_apps_page, gfx::Rect bounds) { InitContents(parent, initial_apps_page); overlay_view_ = new AppListOverlayView(0 /* no corners */); AddChildView(overlay_view_); views::Widget* widget = new views::Widget(); views::Widget::InitParams params( views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); params.parent = parent; params.delegate = this; widget->Init(params); widget->SetBounds(bounds); // This needs to be set *after* Widget::Init() because BubbleDelegateView sets // its own background at OnNativeThemeChanged(), which is called in // View::AddChildView() which is called at Widget::SetContentsView() to build // the views hierarchy in the widget. set_background(new AppListBackground(0)); InitChildWidgets(); } void AppListView::SetBubbleArrow(views::BubbleBorder::Arrow arrow) { GetBubbleFrameView()->bubble_border()->set_arrow(arrow); SizeToContents(); // Recalcuates with new border. GetBubbleFrameView()->SchedulePaint(); } void AppListView::SetAnchorPoint(const gfx::Point& anchor_point) { SetAnchorRect(gfx::Rect(anchor_point, gfx::Size())); } void AppListView::SetDragAndDropHostOfCurrentAppList( ApplicationDragAndDropHost* drag_and_drop_host) { app_list_main_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host); } void AppListView::ShowWhenReady() { app_list_main_view_->ShowAppListWhenReady(); } void AppListView::Close() { app_list_main_view_->Close(); delegate_->Dismiss(); } void AppListView::UpdateBounds() { SizeToContents(); } void AppListView::SetAppListOverlayVisible(bool visible) { DCHECK(overlay_view_); // Display the overlay immediately so we can begin the animation. overlay_view_->SetVisible(true); ui::ScopedLayerAnimationSettings settings( overlay_view_->layer()->GetAnimator()); settings.SetTweenType(gfx::Tween::LINEAR); // If we're dismissing the overlay, hide the view at the end of the animation. if (!visible) { // Since only one animation is visible at a time, it's safe to re-use // animation_observer_ here. animation_observer_->set_frame(NULL); animation_observer_->SetTarget(overlay_view_); settings.AddObserver(animation_observer_.get()); } const float kOverlayFadeInMilliseconds = 125; settings.SetTransitionDuration( base::TimeDelta::FromMilliseconds(kOverlayFadeInMilliseconds)); const float kOverlayOpacity = 0.75f; overlay_view_->layer()->SetOpacity(visible ? kOverlayOpacity : 0.0f); // Create the illusion that the search box is hidden behind the app list // overlay mask by setting its opacity to the same value, and disabling it. { ui::ScopedLayerAnimationSettings settings( search_box_widget_->GetLayer()->GetAnimator()); const float kSearchBoxWidgetOpacity = 0.5f; search_box_widget_->GetLayer()->SetOpacity(visible ? kSearchBoxWidgetOpacity : 1.0f); search_box_view_->SetEnabled(!visible); if (!visible) search_box_view_->search_box()->RequestFocus(); } } bool AppListView::ShouldCenterWindow() const { return delegate_->ShouldCenterWindow(); } gfx::Size AppListView::GetPreferredSize() const { return app_list_main_view_->GetPreferredSize(); } void AppListView::OnPaint(gfx::Canvas* canvas) { views::BubbleDelegateView::OnPaint(canvas); if (!next_paint_callback_.is_null()) { next_paint_callback_.Run(); next_paint_callback_.Reset(); } } void AppListView::OnThemeChanged() { #if defined(OS_WIN) GetWidget()->Close(); #endif } bool AppListView::ShouldHandleSystemCommands() const { return true; } bool AppListView::ShouldDescendIntoChildForEventHandling( gfx::NativeView child, const gfx::Point& location) { // While on the start page, don't descend into the custom launcher page. Since // the only valid action is to open it. ContentsView* contents_view = app_list_main_view_->contents_view(); if (contents_view->custom_page_view() && contents_view->GetActiveState() == AppListModel::STATE_START) return !contents_view->custom_page_view() ->GetCollapsedLauncherPageBounds() .Contains(location); return views::BubbleDelegateView::ShouldDescendIntoChildForEventHandling( child, location); } void AppListView::Prerender() { app_list_main_view_->Prerender(); } void AppListView::OnProfilesChanged() { app_list_main_view_->search_box_view()->InvalidateMenu(); } void AppListView::OnShutdown() { // Nothing to do on views - the widget will soon be closed, which will tear // everything down. } void AppListView::SetProfileByPath(const base::FilePath& profile_path) { delegate_->SetProfileByPath(profile_path); app_list_main_view_->ModelChanged(); } void AppListView::AddObserver(AppListViewObserver* observer) { observers_.AddObserver(observer); } void AppListView::RemoveObserver(AppListViewObserver* observer) { observers_.RemoveObserver(observer); } // static void AppListView::SetNextPaintCallback(const base::Closure& callback) { next_paint_callback_ = callback; } #if defined(OS_WIN) HWND AppListView::GetHWND() const { gfx::NativeWindow window = GetWidget()->GetTopLevelWidget()->GetNativeWindow(); return window->GetHost()->GetAcceleratedWidget(); } #endif PaginationModel* AppListView::GetAppsPaginationModel() { return app_list_main_view_->contents_view() ->apps_container_view() ->apps_grid_view() ->pagination_model(); } void AppListView::InitContents(gfx::NativeView parent, int initial_apps_page) { // TODO(vadimt): Remove ScopedTracker below once crbug.com/440224 and // crbug.com/441028 are fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( "440224, 441028 AppListView::InitContents")); app_list_main_view_ = new AppListMainView(delegate_); AddChildView(app_list_main_view_); app_list_main_view_->SetPaintToLayer(true); app_list_main_view_->SetFillsBoundsOpaquely(false); app_list_main_view_->layer()->SetMasksToBounds(true); // This will be added to the |search_box_widget_| after the app list widget is // initialized. search_box_view_ = new SearchBoxView(app_list_main_view_, delegate_); search_box_view_->SetPaintToLayer(true); search_box_view_->SetFillsBoundsOpaquely(false); search_box_view_->layer()->SetMasksToBounds(true); // TODO(vadimt): Remove ScopedTracker below once crbug.com/440224 and // crbug.com/441028 are fixed. tracked_objects::ScopedTracker tracking_profile1( FROM_HERE_WITH_EXPLICIT_FUNCTION( "440224, 441028 AppListView::InitContents1")); app_list_main_view_->Init(parent, initial_apps_page, search_box_view_); // TODO(vadimt): Remove ScopedTracker below once crbug.com/440224 and // crbug.com/441028 are fixed. tracked_objects::ScopedTracker tracking_profile2( FROM_HERE_WITH_EXPLICIT_FUNCTION( "440224, 441028 AppListView::InitContents2")); // Speech recognition is available only when the start page exists. if (delegate_ && delegate_->IsSpeechRecognitionEnabled()) { speech_view_ = new SpeechView(delegate_); speech_view_->SetVisible(false); speech_view_->SetPaintToLayer(true); speech_view_->SetFillsBoundsOpaquely(false); speech_view_->layer()->SetOpacity(0.0f); AddChildView(speech_view_); } OnProfilesChanged(); } void AppListView::InitChildWidgets() { DCHECK(search_box_view_); // Create the search box widget. views::Widget::InitParams search_box_widget_params( views::Widget::InitParams::TYPE_CONTROL); search_box_widget_params.parent = GetWidget()->GetNativeView(); search_box_widget_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; // Create a widget for the SearchBoxView to live in. This allows the // SearchBoxView to be on top of the custom launcher page's WebContents // (otherwise the search box events will be captured by the WebContents). search_box_widget_ = new views::Widget; search_box_widget_->Init(search_box_widget_params); search_box_widget_->SetContentsView(search_box_view_); // The search box will not naturally receive focus by itself (because it is in // a separate widget). Create this SearchBoxFocusHost in the main widget to // forward the focus search into to the search box. search_box_focus_host_ = new SearchBoxFocusHost(search_box_widget_); AddChildView(search_box_focus_host_); search_box_widget_->SetFocusTraversableParentView(search_box_focus_host_); search_box_widget_->SetFocusTraversableParent( GetWidget()->GetFocusTraversable()); #if defined(USE_AURA) // Mouse events on the search box shadow should not be captured. aura::Window* window = search_box_widget_->GetNativeWindow(); window->SetEventTargeter(scoped_ptr( new SearchBoxWindowTargeter(search_box_view_))); #endif app_list_main_view_->contents_view()->Layout(); } void AppListView::InitAsBubbleInternal(gfx::NativeView parent, int initial_apps_page, views::BubbleBorder::Arrow arrow, bool border_accepts_events, const gfx::Vector2d& anchor_offset) { base::Time start_time = base::Time::Now(); InitContents(parent, initial_apps_page); set_color(kContentsBackgroundColor); set_margins(gfx::Insets()); set_parent_window(parent); set_close_on_deactivate(false); set_close_on_esc(false); set_anchor_view_insets(gfx::Insets(kArrowOffset + anchor_offset.y(), kArrowOffset + anchor_offset.x(), kArrowOffset - anchor_offset.y(), kArrowOffset - anchor_offset.x())); set_border_accepts_events(border_accepts_events); set_shadow(SupportsShadow() ? views::BubbleBorder::BIG_SHADOW : views::BubbleBorder::NO_SHADOW_OPAQUE_BORDER); { // TODO(tapted): Remove ScopedTracker below once crbug.com/431326 is fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( "431326 views::BubbleDelegateView::CreateBubble()")); // This creates the app list widget. (Before this, child widgets cannot be // created.) views::BubbleDelegateView::CreateBubble(this); } SetBubbleArrow(arrow); // We can now create the internal widgets. InitChildWidgets(); #if defined(USE_AURA) aura::Window* window = GetWidget()->GetNativeWindow(); window->layer()->SetMasksToBounds(true); GetBubbleFrameView()->set_background(new AppListBackground( GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius())); set_background(NULL); window->SetEventTargeter(scoped_ptr( new views::BubbleWindowTargeter(this))); #else set_background(new AppListBackground( GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius())); // On non-aura the bubble has two widgets, and it's possible for the border // to be shown independently in odd situations. Explicitly hide the bubble // widget to ensure that any WM_WINDOWPOSCHANGED messages triggered by the // window manager do not have the SWP_SHOWWINDOW flag set which would cause // the border to be shown. See http://crbug.com/231687 . GetWidget()->Hide(); #endif // On platforms that don't support a shadow, the rounded border of the app // list is constructed _inside_ the view, so a rectangular background goes // over the border in the rounded corners. To fix this, give the background a // corner radius 1px smaller than the outer border, so it just reaches but // doesn't cover it. const int kOverlayCornerRadius = GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(); overlay_view_ = new AppListOverlayView(kOverlayCornerRadius - (SupportsShadow() ? 0 : 1)); overlay_view_->SetBoundsRect(GetContentsBounds()); AddChildView(overlay_view_); if (delegate_) delegate_->ViewInitialized(); UMA_HISTOGRAM_TIMES("Apps.AppListCreationTime", base::Time::Now() - start_time); } void AppListView::OnBeforeBubbleWidgetInit( views::Widget::InitParams* params, views::Widget* widget) const { if (!params->native_widget) { views::ViewsDelegate* views_delegate = views::ViewsDelegate::GetInstance(); if (views_delegate && !views_delegate->native_widget_factory().is_null()) { params->native_widget = views_delegate->native_widget_factory().Run(*params, widget); } } #if defined(USE_AURA) && !defined(OS_CHROMEOS) if (!params->native_widget && delegate_ && delegate_->ForceNativeDesktop()) params->native_widget = new views::DesktopNativeWidgetAura(widget); #endif #if defined(OS_WIN) // Windows 7 and higher offer pinning to the taskbar, but we need presence // on the taskbar for the user to be able to pin us. So, show the window on // the taskbar for these versions of Windows. if (base::win::GetVersion() >= base::win::VERSION_WIN7) params->force_show_in_taskbar = true; #elif defined(OS_LINUX) // Set up a custom WM_CLASS for the app launcher window. This allows task // switchers in X11 environments to distinguish it from main browser windows. params->wm_class_name = kAppListWMClass; // Show the window in the taskbar, even though it is a bubble, which would not // normally be shown. params->force_show_in_taskbar = true; #endif } views::View* AppListView::GetInitiallyFocusedView() { return app_list_main_view_->search_box_view()->search_box(); } gfx::ImageSkia AppListView::GetWindowIcon() { if (delegate_) return delegate_->GetWindowIcon(); return gfx::ImageSkia(); } bool AppListView::WidgetHasHitTestMask() const { return GetBubbleFrameView() != nullptr; } void AppListView::GetWidgetHitTestMask(gfx::Path* mask) const { DCHECK(mask); DCHECK(GetBubbleFrameView()); mask->addRect(gfx::RectToSkRect( GetBubbleFrameView()->GetContentsBounds())); } bool AppListView::AcceleratorPressed(const ui::Accelerator& accelerator) { // The accelerator is added by BubbleDelegateView. if (accelerator.key_code() == ui::VKEY_ESCAPE) { if (switches::IsExperimentalAppListEnabled()) { // If the ContentsView does not handle the back action, then this is the // top level, so we close the app list. if (!app_list_main_view_->contents_view()->Back()) { GetWidget()->Deactivate(); Close(); } return true; } if (app_list_main_view_->search_box_view()->HasSearch()) { app_list_main_view_->search_box_view()->ClearSearch(); } else if (app_list_main_view_->contents_view() ->apps_container_view() ->IsInFolderView()) { app_list_main_view_->contents_view() ->apps_container_view() ->app_list_folder_view() ->CloseFolderPage(); return true; } else { GetWidget()->Deactivate(); Close(); } return true; } return false; } void AppListView::Layout() { const gfx::Rect contents_bounds = GetContentsBounds(); // Make sure to layout |app_list_main_view_| and |speech_view_| at the center // of the widget. gfx::Rect centered_bounds = contents_bounds; centered_bounds.ClampToCenteredSize(gfx::Size( app_list_main_view_->contents_view()->GetDefaultContentsBounds().width(), contents_bounds.height())); app_list_main_view_->SetBoundsRect(centered_bounds); if (speech_view_) { gfx::Rect speech_bounds = centered_bounds; int preferred_height = speech_view_->GetPreferredSize().height(); speech_bounds.Inset(kSpeechUIMargin, kSpeechUIMargin); speech_bounds.set_height(std::min(speech_bounds.height(), preferred_height)); speech_bounds.Inset(-speech_view_->GetInsets()); speech_view_->SetBoundsRect(speech_bounds); } } void AppListView::SchedulePaintInRect(const gfx::Rect& rect) { BubbleDelegateView::SchedulePaintInRect(rect); if (GetBubbleFrameView()) GetBubbleFrameView()->SchedulePaint(); } void AppListView::OnWidgetDestroying(views::Widget* widget) { BubbleDelegateView::OnWidgetDestroying(widget); if (delegate_ && widget == GetWidget()) delegate_->ViewClosing(); } void AppListView::OnWidgetActivationChanged(views::Widget* widget, bool active) { // Do not called inherited function as the bubble delegate auto close // functionality is not used. if (widget == GetWidget()) FOR_EACH_OBSERVER(AppListViewObserver, observers_, OnActivationChanged(widget, active)); } void AppListView::OnWidgetVisibilityChanged(views::Widget* widget, bool visible) { BubbleDelegateView::OnWidgetVisibilityChanged(widget, visible); if (widget != GetWidget()) return; if (!visible) app_list_main_view_->ResetForShow(); } void AppListView::OnSpeechRecognitionStateChanged( SpeechRecognitionState new_state) { if (!speech_view_) return; bool will_appear = (new_state == SPEECH_RECOGNITION_RECOGNIZING || new_state == SPEECH_RECOGNITION_IN_SPEECH || new_state == SPEECH_RECOGNITION_NETWORK_ERROR); // No change for this class. if (speech_view_->visible() == will_appear) return; if (will_appear) speech_view_->Reset(); animation_observer_->set_frame(GetBubbleFrameView()); gfx::Transform speech_transform; speech_transform.Translate( 0, SkFloatToMScalar(kSpeechUIAppearingPosition)); if (will_appear) speech_view_->layer()->SetTransform(speech_transform); { ui::ScopedLayerAnimationSettings main_settings( app_list_main_view_->layer()->GetAnimator()); if (will_appear) { animation_observer_->SetTarget(app_list_main_view_); main_settings.AddObserver(animation_observer_.get()); } app_list_main_view_->layer()->SetOpacity(will_appear ? 0.0f : 1.0f); } { ui::ScopedLayerAnimationSettings search_box_settings( search_box_widget_->GetLayer()->GetAnimator()); search_box_widget_->GetLayer()->SetOpacity(will_appear ? 0.0f : 1.0f); } { ui::ScopedLayerAnimationSettings speech_settings( speech_view_->layer()->GetAnimator()); if (!will_appear) { animation_observer_->SetTarget(speech_view_); speech_settings.AddObserver(animation_observer_.get()); } speech_view_->layer()->SetOpacity(will_appear ? 1.0f : 0.0f); if (will_appear) speech_view_->layer()->SetTransform(gfx::Transform()); else speech_view_->layer()->SetTransform(speech_transform); } // Prevent the search box from receiving events when hidden. search_box_view_->SetEnabled(!will_appear); if (will_appear) { speech_view_->SetVisible(true); } else { app_list_main_view_->SetVisible(true); // Refocus the search box. However, if the app list widget does not have // focus, it means another window has already taken focus, and we *must not* // focus the search box (or we would steal focus back into the app list). if (GetWidget()->IsActive()) search_box_view_->search_box()->RequestFocus(); } } } // namespace app_list