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_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_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/signin_view.h"
26 #include "ui/app_list/views/speech_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/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/views/bubble/bubble_frame_view.h"
36 #include "ui/views/bubble/bubble_window_targeter.h"
37 #include "ui/views/controls/textfield/textfield.h"
38 #include "ui/views/layout/fill_layout.h"
39 #include "ui/views/widget/widget.h"
42 #include "ui/aura/window.h"
43 #include "ui/aura/window_tree_host.h"
45 #include "ui/base/win/shell.h"
47 #if !defined(OS_CHROMEOS)
48 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
50 #endif // defined(USE_AURA)
56 void (*g_next_paint_callback)();
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(USE_ASH)
77 // Shadows are not supported on (non-ChromeOS) Linux.
85 // An animation observer to hide the view at the end of the animation.
86 class HideViewAnimationObserver : public ui::ImplicitAnimationObserver {
88 HideViewAnimationObserver()
93 virtual ~HideViewAnimationObserver() {
95 StopObservingImplicitAnimations();
98 void SetTarget(views::View* target) {
100 StopObservingImplicitAnimations();
104 void set_frame(views::BubbleFrameView* frame) { frame_ = frame; }
107 // Overridden from ui::ImplicitAnimationObserver:
108 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
110 target_->SetVisible(false);
113 // Should update the background by invoking SchedulePaint().
114 frame_->SchedulePaint();
118 views::BubbleFrameView* frame_;
119 views::View* target_;
121 DISALLOW_COPY_AND_ASSIGN(HideViewAnimationObserver);
124 ////////////////////////////////////////////////////////////////////////////////
127 AppListView::AppListView(AppListViewDelegate* delegate)
128 : delegate_(delegate),
129 app_list_main_view_(NULL),
132 animation_observer_(new HideViewAnimationObserver()) {
135 delegate_->AddObserver(this);
136 delegate_->GetSpeechUI()->AddObserver(this);
139 AppListView::~AppListView() {
140 delegate_->GetSpeechUI()->RemoveObserver(this);
141 delegate_->RemoveObserver(this);
142 animation_observer_.reset();
143 // Remove child views first to ensure no remaining dependencies on delegate_.
144 RemoveAllChildViews(true);
147 void AppListView::InitAsBubbleAttachedToAnchor(
148 gfx::NativeView parent,
149 PaginationModel* pagination_model,
151 const gfx::Vector2d& anchor_offset,
152 views::BubbleBorder::Arrow arrow,
153 bool border_accepts_events) {
154 SetAnchorView(anchor);
155 InitAsBubbleInternal(
156 parent, pagination_model, arrow, border_accepts_events, anchor_offset);
159 void AppListView::InitAsBubbleAtFixedLocation(
160 gfx::NativeView parent,
161 PaginationModel* pagination_model,
162 const gfx::Point& anchor_point_in_screen,
163 views::BubbleBorder::Arrow arrow,
164 bool border_accepts_events) {
166 SetAnchorRect(gfx::Rect(anchor_point_in_screen, gfx::Size()));
167 InitAsBubbleInternal(
168 parent, pagination_model, arrow, border_accepts_events, gfx::Vector2d());
171 void AppListView::SetBubbleArrow(views::BubbleBorder::Arrow arrow) {
172 GetBubbleFrameView()->bubble_border()->set_arrow(arrow);
173 SizeToContents(); // Recalcuates with new border.
174 GetBubbleFrameView()->SchedulePaint();
177 void AppListView::SetAnchorPoint(const gfx::Point& anchor_point) {
178 SetAnchorRect(gfx::Rect(anchor_point, gfx::Size()));
181 void AppListView::SetDragAndDropHostOfCurrentAppList(
182 ApplicationDragAndDropHost* drag_and_drop_host) {
183 app_list_main_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
186 void AppListView::ShowWhenReady() {
187 app_list_main_view_->ShowAppListWhenReady();
190 void AppListView::Close() {
191 app_list_main_view_->Close();
192 delegate_->Dismiss();
195 void AppListView::UpdateBounds() {
199 bool AppListView::ShouldCenterWindow() const {
200 return delegate_->ShouldCenterWindow();
203 gfx::Size AppListView::GetPreferredSize() {
204 return app_list_main_view_->GetPreferredSize();
207 void AppListView::Paint(gfx::Canvas* canvas) {
208 views::BubbleDelegateView::Paint(canvas);
209 if (g_next_paint_callback) {
210 g_next_paint_callback();
211 g_next_paint_callback = NULL;
215 void AppListView::OnThemeChanged() {
217 GetWidget()->Close();
221 bool AppListView::ShouldHandleSystemCommands() const {
225 void AppListView::Prerender() {
226 app_list_main_view_->Prerender();
229 void AppListView::OnProfilesChanged() {
230 SigninDelegate* signin_delegate =
231 delegate_ ? delegate_->GetSigninDelegate() : NULL;
232 bool show_signin_view = signin_delegate && signin_delegate->NeedSignin();
234 signin_view_->SetVisible(show_signin_view);
235 app_list_main_view_->SetVisible(!show_signin_view);
236 app_list_main_view_->search_box_view()->InvalidateMenu();
239 void AppListView::SetProfileByPath(const base::FilePath& profile_path) {
240 delegate_->SetProfileByPath(profile_path);
241 app_list_main_view_->ModelChanged();
244 void AppListView::AddObserver(AppListViewObserver* observer) {
245 observers_.AddObserver(observer);
248 void AppListView::RemoveObserver(AppListViewObserver* observer) {
249 observers_.RemoveObserver(observer);
253 void AppListView::SetNextPaintCallback(void (*callback)()) {
254 g_next_paint_callback = callback;
258 HWND AppListView::GetHWND() const {
259 #if defined(USE_AURA)
260 gfx::NativeWindow window =
261 GetWidget()->GetTopLevelWidget()->GetNativeWindow();
262 return window->GetHost()->GetAcceleratedWidget();
264 return GetWidget()->GetTopLevelWidget()->GetNativeWindow();
269 void AppListView::InitAsBubbleInternal(gfx::NativeView parent,
270 PaginationModel* pagination_model,
271 views::BubbleBorder::Arrow arrow,
272 bool border_accepts_events,
273 const gfx::Vector2d& anchor_offset) {
274 app_list_main_view_ = new AppListMainView(delegate_.get(),
277 AddChildView(app_list_main_view_);
278 #if defined(USE_AURA)
279 app_list_main_view_->SetPaintToLayer(true);
280 app_list_main_view_->SetFillsBoundsOpaquely(false);
281 app_list_main_view_->layer()->SetMasksToBounds(true);
285 new SigninView(delegate_->GetSigninDelegate(),
286 app_list_main_view_->GetPreferredSize().width());
287 AddChildView(signin_view_);
289 // Speech recognition is available only when the start page exists.
290 if (delegate_ && delegate_->GetSpeechRecognitionContents()) {
291 speech_view_ = new SpeechView(delegate_.get());
292 speech_view_->SetVisible(false);
293 #if defined(USE_AURA)
294 speech_view_->SetPaintToLayer(true);
295 speech_view_->SetFillsBoundsOpaquely(false);
296 speech_view_->layer()->SetOpacity(0.0f);
298 AddChildView(speech_view_);
302 set_color(kContentsBackgroundColor);
303 set_margins(gfx::Insets());
304 set_parent_window(parent);
305 set_close_on_deactivate(false);
306 set_close_on_esc(false);
307 set_anchor_view_insets(gfx::Insets(kArrowOffset + anchor_offset.y(),
308 kArrowOffset + anchor_offset.x(),
309 kArrowOffset - anchor_offset.y(),
310 kArrowOffset - anchor_offset.x()));
311 set_border_accepts_events(border_accepts_events);
312 set_shadow(SupportsShadow() ? views::BubbleBorder::BIG_SHADOW
313 : views::BubbleBorder::NO_SHADOW_OPAQUE_BORDER);
314 views::BubbleDelegateView::CreateBubble(this);
315 SetBubbleArrow(arrow);
317 #if defined(USE_AURA)
318 aura::Window* window = GetWidget()->GetNativeWindow();
319 window->layer()->SetMasksToBounds(true);
320 GetBubbleFrameView()->set_background(new AppListBackground(
321 GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
322 app_list_main_view_));
323 set_background(NULL);
324 window->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
325 new views::BubbleWindowTargeter(this)));
327 set_background(new AppListBackground(
328 GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
329 app_list_main_view_));
331 // On non-aura the bubble has two widgets, and it's possible for the border
332 // to be shown independently in odd situations. Explicitly hide the bubble
333 // widget to ensure that any WM_WINDOWPOSCHANGED messages triggered by the
334 // window manager do not have the SWP_SHOWWINDOW flag set which would cause
335 // the border to be shown. See http://crbug.com/231687 .
340 delegate_->ViewInitialized();
343 void AppListView::OnBeforeBubbleWidgetInit(
344 views::Widget::InitParams* params,
345 views::Widget* widget) const {
346 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
347 if (delegate_ && delegate_->ForceNativeDesktop())
348 params->native_widget = new views::DesktopNativeWidgetAura(widget);
351 // Windows 7 and higher offer pinning to the taskbar, but we need presence
352 // on the taskbar for the user to be able to pin us. So, show the window on
353 // the taskbar for these versions of Windows.
354 if (base::win::GetVersion() >= base::win::VERSION_WIN7)
355 params->force_show_in_taskbar = true;
356 #elif defined(OS_LINUX)
357 // Set up a custom WM_CLASS for the app launcher window. This allows task
358 // switchers in X11 environments to distinguish it from main browser windows.
359 params->wm_class_name = kAppListWMClass;
360 // Show the window in the taskbar, even though it is a bubble, which would not
361 // normally be shown.
362 params->force_show_in_taskbar = true;
366 views::View* AppListView::GetInitiallyFocusedView() {
367 return app_list_main_view_->search_box_view()->search_box();
370 gfx::ImageSkia AppListView::GetWindowIcon() {
372 return delegate_->GetWindowIcon();
374 return gfx::ImageSkia();
377 bool AppListView::WidgetHasHitTestMask() const {
381 void AppListView::GetWidgetHitTestMask(gfx::Path* mask) const {
383 mask->addRect(gfx::RectToSkRect(
384 GetBubbleFrameView()->GetContentsBounds()));
387 bool AppListView::AcceleratorPressed(const ui::Accelerator& accelerator) {
388 // The accelerator is added by BubbleDelegateView.
389 if (accelerator.key_code() == ui::VKEY_ESCAPE) {
390 if (app_list_main_view_->search_box_view()->HasSearch()) {
391 app_list_main_view_->search_box_view()->ClearSearch();
392 } else if (app_list_main_view_->contents_view()
393 ->apps_container_view()
394 ->IsInFolderView()) {
395 app_list_main_view_->contents_view()
396 ->apps_container_view()
397 ->app_list_folder_view()
401 GetWidget()->Deactivate();
410 void AppListView::Layout() {
411 const gfx::Rect contents_bounds = GetContentsBounds();
412 app_list_main_view_->SetBoundsRect(contents_bounds);
413 signin_view_->SetBoundsRect(contents_bounds);
416 gfx::Rect speech_bounds = contents_bounds;
417 int preferred_height = speech_view_->GetPreferredSize().height();
418 speech_bounds.Inset(kSpeechUIMargin, kSpeechUIMargin);
419 speech_bounds.set_height(std::min(speech_bounds.height(),
421 speech_bounds.Inset(-speech_view_->GetInsets());
422 speech_view_->SetBoundsRect(speech_bounds);
426 void AppListView::SchedulePaintInRect(const gfx::Rect& rect) {
427 BubbleDelegateView::SchedulePaintInRect(rect);
428 if (GetBubbleFrameView())
429 GetBubbleFrameView()->SchedulePaint();
432 void AppListView::OnWidgetDestroying(views::Widget* widget) {
433 BubbleDelegateView::OnWidgetDestroying(widget);
434 if (delegate_ && widget == GetWidget())
435 delegate_->ViewClosing();
438 void AppListView::OnWidgetActivationChanged(views::Widget* widget,
440 // Do not called inherited function as the bubble delegate auto close
441 // functionality is not used.
442 if (widget == GetWidget())
443 FOR_EACH_OBSERVER(AppListViewObserver, observers_,
444 OnActivationChanged(widget, active));
447 void AppListView::OnWidgetVisibilityChanged(views::Widget* widget,
449 BubbleDelegateView::OnWidgetVisibilityChanged(widget, visible);
451 if (widget != GetWidget())
455 app_list_main_view_->ResetForShow();
457 // Whether we need to signin or not may have changed since last time we were
462 void AppListView::OnSpeechRecognitionStateChanged(
463 SpeechRecognitionState new_state) {
464 if (signin_view_->visible() || !speech_view_)
467 bool recognizing = (new_state == SPEECH_RECOGNITION_RECOGNIZING ||
468 new_state == SPEECH_RECOGNITION_IN_SPEECH);
469 // No change for this class.
470 if (speech_view_->visible() == recognizing)
474 speech_view_->Reset();
476 #if defined(USE_AURA)
477 animation_observer_->set_frame(GetBubbleFrameView());
478 gfx::Transform speech_transform;
479 speech_transform.Translate(
480 0, SkFloatToMScalar(kSpeechUIAppearingPosition));
482 speech_view_->layer()->SetTransform(speech_transform);
485 ui::ScopedLayerAnimationSettings main_settings(
486 app_list_main_view_->layer()->GetAnimator());
488 animation_observer_->SetTarget(app_list_main_view_);
489 main_settings.AddObserver(animation_observer_.get());
491 app_list_main_view_->layer()->SetOpacity(recognizing ? 0.0f : 1.0f);
495 ui::ScopedLayerAnimationSettings speech_settings(
496 speech_view_->layer()->GetAnimator());
498 animation_observer_->SetTarget(speech_view_);
499 speech_settings.AddObserver(animation_observer_.get());
502 speech_view_->layer()->SetOpacity(recognizing ? 1.0f : 0.0f);
504 speech_view_->layer()->SetTransform(gfx::Transform());
506 speech_view_->layer()->SetTransform(speech_transform);
510 speech_view_->SetVisible(true);
512 app_list_main_view_->SetVisible(true);
514 speech_view_->SetVisible(recognizing);
515 app_list_main_view_->SetVisible(!recognizing);
517 // Needs to schedule paint of AppListView itself, to repaint the background.
518 GetBubbleFrameView()->SchedulePaint();
522 } // namespace app_list