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"
7 #include "base/command_line.h"
8 #include "base/strings/string_util.h"
9 #include "base/win/windows_version.h"
10 #include "ui/app_list/app_list_constants.h"
11 #include "ui/app_list/app_list_model.h"
12 #include "ui/app_list/app_list_view_delegate.h"
13 #include "ui/app_list/pagination_model.h"
14 #include "ui/app_list/signin_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_main_view.h"
18 #include "ui/app_list/views/app_list_view_observer.h"
19 #include "ui/app_list/views/search_box_view.h"
20 #include "ui/app_list/views/signin_view.h"
21 #include "ui/app_list/views/speech_view.h"
22 #include "ui/base/ui_base_switches.h"
23 #include "ui/compositor/layer.h"
24 #include "ui/compositor/scoped_layer_animation_settings.h"
25 #include "ui/gfx/image/image_skia.h"
26 #include "ui/gfx/insets.h"
27 #include "ui/gfx/path.h"
28 #include "ui/gfx/skia_util.h"
29 #include "ui/views/bubble/bubble_frame_view.h"
30 #include "ui/views/bubble/bubble_window_targeter.h"
31 #include "ui/views/controls/textfield/textfield.h"
32 #include "ui/views/layout/fill_layout.h"
33 #include "ui/views/widget/widget.h"
36 #include "ui/aura/window.h"
37 #include "ui/aura/root_window.h"
39 #include "ui/base/win/shell.h"
41 #if !defined(OS_CHROMEOS)
42 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
44 #endif // defined(USE_AURA)
50 void (*g_next_paint_callback
)();
52 // The margin from the edge to the speech UI.
53 const int kSpeechUIMargin
= 12;
55 // The vertical position for the appearing animation of the speech UI.
56 const float kSpeechUIAppearingPosition
= 12;
58 // The distance between the arrow tip and edge of the anchor view.
59 const int kArrowOffset
= 10;
61 // Determines whether the current environment supports shadows bubble borders.
62 bool SupportsShadow() {
64 // Shadows are not supported on Windows without Aero Glass.
65 if (!ui::win::IsAeroGlassEnabled() ||
66 CommandLine::ForCurrentProcess()->HasSwitch(
67 switches::kDisableDwmComposition
)) {
70 #elif defined(OS_LINUX) && !defined(USE_ASH)
71 // Shadows are not supported on (non-ChromeOS) Linux.
79 // An animation observer to hide the view at the end of the animation.
80 class HideViewAnimationObserver
: public ui::ImplicitAnimationObserver
{
82 HideViewAnimationObserver()
87 virtual ~HideViewAnimationObserver() {
89 StopObservingImplicitAnimations();
92 void SetTarget(views::View
* target
) {
94 StopObservingImplicitAnimations();
98 void set_frame(views::BubbleFrameView
* frame
) { frame_
= frame
; }
101 // Overridden from ui::ImplicitAnimationObserver:
102 virtual void OnImplicitAnimationsCompleted() OVERRIDE
{
104 target_
->SetVisible(false);
107 // Should update the background by invoking SchedulePaint().
108 frame_
->SchedulePaint();
112 views::BubbleFrameView
* frame_
;
113 views::View
* target_
;
115 DISALLOW_COPY_AND_ASSIGN(HideViewAnimationObserver
);
118 ////////////////////////////////////////////////////////////////////////////////
121 AppListView::AppListView(AppListViewDelegate
* delegate
)
122 : delegate_(delegate
),
123 app_list_main_view_(NULL
),
126 animation_observer_(new HideViewAnimationObserver()) {
129 delegate_
->AddObserver(this);
130 delegate_
->GetSpeechUI()->AddObserver(this);
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);
141 void AppListView::InitAsBubbleAttachedToAnchor(
142 gfx::NativeView parent
,
143 PaginationModel
* pagination_model
,
145 const gfx::Vector2d
& anchor_offset
,
146 views::BubbleBorder::Arrow arrow
,
147 bool border_accepts_events
) {
148 SetAnchorView(anchor
);
149 InitAsBubbleInternal(
150 parent
, pagination_model
, arrow
, border_accepts_events
, anchor_offset
);
153 void AppListView::InitAsBubbleAtFixedLocation(
154 gfx::NativeView parent
,
155 PaginationModel
* pagination_model
,
156 const gfx::Point
& anchor_point_in_screen
,
157 views::BubbleBorder::Arrow arrow
,
158 bool border_accepts_events
) {
160 SetAnchorRect(gfx::Rect(anchor_point_in_screen
, gfx::Size()));
161 InitAsBubbleInternal(
162 parent
, pagination_model
, arrow
, border_accepts_events
, gfx::Vector2d());
165 void AppListView::SetBubbleArrow(views::BubbleBorder::Arrow arrow
) {
166 GetBubbleFrameView()->bubble_border()->set_arrow(arrow
);
167 SizeToContents(); // Recalcuates with new border.
168 GetBubbleFrameView()->SchedulePaint();
171 void AppListView::SetAnchorPoint(const gfx::Point
& anchor_point
) {
172 SetAnchorRect(gfx::Rect(anchor_point
, gfx::Size()));
175 void AppListView::SetDragAndDropHostOfCurrentAppList(
176 ApplicationDragAndDropHost
* drag_and_drop_host
) {
177 app_list_main_view_
->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host
);
180 void AppListView::ShowWhenReady() {
181 app_list_main_view_
->ShowAppListWhenReady();
184 void AppListView::Close() {
185 app_list_main_view_
->Close();
186 delegate_
->Dismiss();
189 void AppListView::UpdateBounds() {
193 gfx::Size
AppListView::GetPreferredSize() {
194 return app_list_main_view_
->GetPreferredSize();
197 void AppListView::Paint(gfx::Canvas
* canvas
) {
198 views::BubbleDelegateView::Paint(canvas
);
199 if (g_next_paint_callback
) {
200 g_next_paint_callback();
201 g_next_paint_callback
= NULL
;
205 void AppListView::OnThemeChanged() {
207 GetWidget()->Close();
211 bool AppListView::ShouldHandleSystemCommands() const {
215 void AppListView::Prerender() {
216 app_list_main_view_
->Prerender();
219 void AppListView::OnProfilesChanged() {
220 SigninDelegate
* signin_delegate
=
221 delegate_
? delegate_
->GetSigninDelegate() : NULL
;
222 bool show_signin_view
= signin_delegate
&& signin_delegate
->NeedSignin();
224 signin_view_
->SetVisible(show_signin_view
);
225 app_list_main_view_
->SetVisible(!show_signin_view
);
226 app_list_main_view_
->search_box_view()->InvalidateMenu();
229 void AppListView::SetProfileByPath(const base::FilePath
& profile_path
) {
230 delegate_
->SetProfileByPath(profile_path
);
231 app_list_main_view_
->ModelChanged();
234 void AppListView::AddObserver(AppListViewObserver
* observer
) {
235 observers_
.AddObserver(observer
);
238 void AppListView::RemoveObserver(AppListViewObserver
* observer
) {
239 observers_
.RemoveObserver(observer
);
243 void AppListView::SetNextPaintCallback(void (*callback
)()) {
244 g_next_paint_callback
= callback
;
248 HWND
AppListView::GetHWND() const {
249 #if defined(USE_AURA)
250 gfx::NativeWindow window
=
251 GetWidget()->GetTopLevelWidget()->GetNativeWindow();
252 return window
->GetDispatcher()->host()->GetAcceleratedWidget();
254 return GetWidget()->GetTopLevelWidget()->GetNativeWindow();
259 void AppListView::InitAsBubbleInternal(gfx::NativeView parent
,
260 PaginationModel
* pagination_model
,
261 views::BubbleBorder::Arrow arrow
,
262 bool border_accepts_events
,
263 const gfx::Vector2d
& anchor_offset
) {
264 app_list_main_view_
= new AppListMainView(delegate_
.get(),
267 AddChildView(app_list_main_view_
);
268 #if defined(USE_AURA)
269 app_list_main_view_
->SetPaintToLayer(true);
270 app_list_main_view_
->SetFillsBoundsOpaquely(false);
271 app_list_main_view_
->layer()->SetMasksToBounds(true);
275 new SigninView(delegate_
->GetSigninDelegate(),
276 app_list_main_view_
->GetPreferredSize().width());
277 AddChildView(signin_view_
);
279 // Speech recognition is available only when the start page exists.
280 if (delegate_
&& delegate_
->GetSpeechRecognitionContents()) {
281 speech_view_
= new SpeechView(delegate_
.get());
282 speech_view_
->SetVisible(false);
283 #if defined(USE_AURA)
284 speech_view_
->SetPaintToLayer(true);
285 speech_view_
->SetFillsBoundsOpaquely(false);
286 speech_view_
->layer()->SetOpacity(0.0f
);
288 AddChildView(speech_view_
);
292 set_color(kContentsBackgroundColor
);
293 set_margins(gfx::Insets());
294 set_move_with_anchor(true);
295 set_parent_window(parent
);
296 set_close_on_deactivate(false);
297 set_close_on_esc(false);
298 set_anchor_view_insets(gfx::Insets(kArrowOffset
+ anchor_offset
.y(),
299 kArrowOffset
+ anchor_offset
.x(),
300 kArrowOffset
- anchor_offset
.y(),
301 kArrowOffset
- anchor_offset
.x()));
302 set_border_accepts_events(border_accepts_events
);
303 set_shadow(SupportsShadow() ? views::BubbleBorder::BIG_SHADOW
304 : views::BubbleBorder::NO_SHADOW_OPAQUE_BORDER
);
305 views::BubbleDelegateView::CreateBubble(this);
306 SetBubbleArrow(arrow
);
308 #if defined(USE_AURA)
309 aura::Window
* window
= GetWidget()->GetNativeWindow();
310 window
->layer()->SetMasksToBounds(true);
311 GetBubbleFrameView()->set_background(new AppListBackground(
312 GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
313 app_list_main_view_
));
314 set_background(NULL
);
315 window
->SetEventTargeter(scoped_ptr
<ui::EventTargeter
>(
316 new views::BubbleWindowTargeter(this)));
318 set_background(new AppListBackground(
319 GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
320 app_list_main_view_
));
322 // On non-aura the bubble has two widgets, and it's possible for the border
323 // to be shown independently in odd situations. Explicitly hide the bubble
324 // widget to ensure that any WM_WINDOWPOSCHANGED messages triggered by the
325 // window manager do not have the SWP_SHOWWINDOW flag set which would cause
326 // the border to be shown. See http://crbug.com/231687 .
331 delegate_
->ViewInitialized();
334 void AppListView::OnBeforeBubbleWidgetInit(
335 views::Widget::InitParams
* params
,
336 views::Widget
* widget
) const {
337 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
338 if (delegate_
&& delegate_
->ForceNativeDesktop())
339 params
->native_widget
= new views::DesktopNativeWidgetAura(widget
);
342 // Windows 7 and higher offer pinning to the taskbar, but we need presence
343 // on the taskbar for the user to be able to pin us. So, show the window on
344 // the taskbar for these versions of Windows.
345 if (base::win::GetVersion() >= base::win::VERSION_WIN7
)
346 params
->force_show_in_taskbar
= true;
347 #elif defined(OS_LINUX)
348 // Set up a custom WM_CLASS for the app launcher window. This allows task
349 // switchers in X11 environments to distinguish it from main browser windows.
350 params
->wm_class_name
= kAppListWMClass
;
351 // Show the window in the taskbar, even though it is a bubble, which would not
352 // normally be shown.
353 params
->force_show_in_taskbar
= true;
357 views::View
* AppListView::GetInitiallyFocusedView() {
358 return app_list_main_view_
->search_box_view()->search_box();
361 gfx::ImageSkia
AppListView::GetWindowIcon() {
363 return delegate_
->GetWindowIcon();
365 return gfx::ImageSkia();
368 bool AppListView::WidgetHasHitTestMask() const {
372 void AppListView::GetWidgetHitTestMask(gfx::Path
* mask
) const {
374 mask
->addRect(gfx::RectToSkRect(
375 GetBubbleFrameView()->GetContentsBounds()));
378 bool AppListView::AcceleratorPressed(const ui::Accelerator
& accelerator
) {
379 // The accelerator is added by BubbleDelegateView.
380 if (accelerator
.key_code() == ui::VKEY_ESCAPE
) {
381 if (app_list_main_view_
->search_box_view()->HasSearch()) {
382 app_list_main_view_
->search_box_view()->ClearSearch();
384 GetWidget()->Deactivate();
393 void AppListView::Layout() {
394 const gfx::Rect contents_bounds
= GetContentsBounds();
395 app_list_main_view_
->SetBoundsRect(contents_bounds
);
396 signin_view_
->SetBoundsRect(contents_bounds
);
399 gfx::Rect speech_bounds
= contents_bounds
;
400 int preferred_height
= speech_view_
->GetPreferredSize().height();
401 speech_bounds
.Inset(kSpeechUIMargin
, kSpeechUIMargin
);
402 speech_bounds
.set_height(std::min(speech_bounds
.height(),
404 speech_bounds
.Inset(-speech_view_
->GetInsets());
405 speech_view_
->SetBoundsRect(speech_bounds
);
409 void AppListView::OnWidgetDestroying(views::Widget
* widget
) {
410 BubbleDelegateView::OnWidgetDestroying(widget
);
411 if (delegate_
&& widget
== GetWidget())
412 delegate_
->ViewClosing();
415 void AppListView::OnWidgetActivationChanged(views::Widget
* widget
,
417 // Do not called inherited function as the bubble delegate auto close
418 // functionality is not used.
419 if (widget
== GetWidget())
420 FOR_EACH_OBSERVER(AppListViewObserver
, observers_
,
421 OnActivationChanged(widget
, active
));
424 void AppListView::OnWidgetVisibilityChanged(views::Widget
* widget
,
426 BubbleDelegateView::OnWidgetVisibilityChanged(widget
, visible
);
428 if (widget
!= GetWidget())
431 // We clear the search when hiding so the next time the app list appears it is
432 // not showing search results.
434 app_list_main_view_
->search_box_view()->ClearSearch();
436 // Whether we need to signin or not may have changed since last time we were
441 void AppListView::OnSpeechRecognitionStateChanged(
442 SpeechRecognitionState new_state
) {
443 if (signin_view_
->visible() || !speech_view_
)
446 bool recognizing
= (new_state
== SPEECH_RECOGNITION_RECOGNIZING
||
447 new_state
== SPEECH_RECOGNITION_IN_SPEECH
);
448 // No change for this class.
449 if (speech_view_
->visible() == recognizing
)
453 speech_view_
->Reset();
455 #if defined(USE_AURA)
456 animation_observer_
->set_frame(GetBubbleFrameView());
457 gfx::Transform speech_transform
;
458 speech_transform
.Translate(
459 0, SkFloatToMScalar(kSpeechUIAppearingPosition
));
461 speech_view_
->layer()->SetTransform(speech_transform
);
464 ui::ScopedLayerAnimationSettings
main_settings(
465 app_list_main_view_
->layer()->GetAnimator());
467 animation_observer_
->SetTarget(app_list_main_view_
);
468 main_settings
.AddObserver(animation_observer_
.get());
470 app_list_main_view_
->layer()->SetOpacity(recognizing
? 0.0f
: 1.0f
);
474 ui::ScopedLayerAnimationSettings
speech_settings(
475 speech_view_
->layer()->GetAnimator());
477 animation_observer_
->SetTarget(speech_view_
);
478 speech_settings
.AddObserver(animation_observer_
.get());
481 speech_view_
->layer()->SetOpacity(recognizing
? 1.0f
: 0.0f
);
483 speech_view_
->layer()->SetTransform(gfx::Transform());
485 speech_view_
->layer()->SetTransform(speech_transform
);
489 speech_view_
->SetVisible(true);
491 app_list_main_view_
->SetVisible(true);
493 speech_view_
->SetVisible(recognizing
);
494 app_list_main_view_
->SetVisible(!recognizing
);
496 // Needs to schedule paint of AppListView itself, to repaint the background.
497 GetBubbleFrameView()->SchedulePaint();
501 } // namespace app_list