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_main_view.h"
20 #include "ui/app_list/views/app_list_view_observer.h"
21 #include "ui/app_list/views/search_box_view.h"
22 #include "ui/app_list/views/signin_view.h"
23 #include "ui/app_list/views/speech_view.h"
24 #include "ui/base/ui_base_switches.h"
25 #include "ui/compositor/layer.h"
26 #include "ui/compositor/layer_animation_observer.h"
27 #include "ui/compositor/scoped_layer_animation_settings.h"
28 #include "ui/gfx/image/image_skia.h"
29 #include "ui/gfx/insets.h"
30 #include "ui/gfx/path.h"
31 #include "ui/gfx/skia_util.h"
32 #include "ui/views/bubble/bubble_frame_view.h"
33 #include "ui/views/bubble/bubble_window_targeter.h"
34 #include "ui/views/controls/textfield/textfield.h"
35 #include "ui/views/layout/fill_layout.h"
36 #include "ui/views/widget/widget.h"
39 #include "ui/aura/window.h"
40 #include "ui/aura/window_tree_host.h"
42 #include "ui/base/win/shell.h"
44 #if !defined(OS_CHROMEOS)
45 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
47 #endif // defined(USE_AURA)
53 void (*g_next_paint_callback)();
55 // The margin from the edge to the speech UI.
56 const int kSpeechUIMargin = 12;
58 // The vertical position for the appearing animation of the speech UI.
59 const float kSpeechUIAppearingPosition = 12;
61 // The distance between the arrow tip and edge of the anchor view.
62 const int kArrowOffset = 10;
64 // Determines whether the current environment supports shadows bubble borders.
65 bool SupportsShadow() {
67 // Shadows are not supported on Windows without Aero Glass.
68 if (!ui::win::IsAeroGlassEnabled() ||
69 CommandLine::ForCurrentProcess()->HasSwitch(
70 switches::kDisableDwmComposition)) {
73 #elif defined(OS_LINUX) && !defined(USE_ASH)
74 // Shadows are not supported on (non-ChromeOS) Linux.
82 // An animation observer to hide the view at the end of the animation.
83 class HideViewAnimationObserver : public ui::ImplicitAnimationObserver {
85 HideViewAnimationObserver()
90 virtual ~HideViewAnimationObserver() {
92 StopObservingImplicitAnimations();
95 void SetTarget(views::View* target) {
97 StopObservingImplicitAnimations();
101 void set_frame(views::BubbleFrameView* frame) { frame_ = frame; }
104 // Overridden from ui::ImplicitAnimationObserver:
105 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
107 target_->SetVisible(false);
110 // Should update the background by invoking SchedulePaint().
111 frame_->SchedulePaint();
115 views::BubbleFrameView* frame_;
116 views::View* target_;
118 DISALLOW_COPY_AND_ASSIGN(HideViewAnimationObserver);
121 ////////////////////////////////////////////////////////////////////////////////
124 AppListView::AppListView(AppListViewDelegate* delegate)
125 : delegate_(delegate),
126 app_list_main_view_(NULL),
129 animation_observer_(new HideViewAnimationObserver()) {
132 delegate_->AddObserver(this);
133 delegate_->GetSpeechUI()->AddObserver(this);
136 AppListView::~AppListView() {
137 delegate_->GetSpeechUI()->RemoveObserver(this);
138 delegate_->RemoveObserver(this);
139 animation_observer_.reset();
140 // Remove child views first to ensure no remaining dependencies on delegate_.
141 RemoveAllChildViews(true);
144 void AppListView::InitAsBubbleAttachedToAnchor(
145 gfx::NativeView parent,
146 PaginationModel* pagination_model,
148 const gfx::Vector2d& anchor_offset,
149 views::BubbleBorder::Arrow arrow,
150 bool border_accepts_events) {
151 SetAnchorView(anchor);
152 InitAsBubbleInternal(
153 parent, pagination_model, arrow, border_accepts_events, anchor_offset);
156 void AppListView::InitAsBubbleAtFixedLocation(
157 gfx::NativeView parent,
158 PaginationModel* pagination_model,
159 const gfx::Point& anchor_point_in_screen,
160 views::BubbleBorder::Arrow arrow,
161 bool border_accepts_events) {
163 SetAnchorRect(gfx::Rect(anchor_point_in_screen, gfx::Size()));
164 InitAsBubbleInternal(
165 parent, pagination_model, arrow, border_accepts_events, gfx::Vector2d());
168 void AppListView::SetBubbleArrow(views::BubbleBorder::Arrow arrow) {
169 GetBubbleFrameView()->bubble_border()->set_arrow(arrow);
170 SizeToContents(); // Recalcuates with new border.
171 GetBubbleFrameView()->SchedulePaint();
174 void AppListView::SetAnchorPoint(const gfx::Point& anchor_point) {
175 SetAnchorRect(gfx::Rect(anchor_point, gfx::Size()));
178 void AppListView::SetDragAndDropHostOfCurrentAppList(
179 ApplicationDragAndDropHost* drag_and_drop_host) {
180 app_list_main_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
183 void AppListView::ShowWhenReady() {
184 app_list_main_view_->ShowAppListWhenReady();
187 void AppListView::Close() {
188 app_list_main_view_->Close();
189 delegate_->Dismiss();
192 void AppListView::UpdateBounds() {
196 gfx::Size AppListView::GetPreferredSize() {
197 return app_list_main_view_->GetPreferredSize();
200 void AppListView::Paint(gfx::Canvas* canvas) {
201 views::BubbleDelegateView::Paint(canvas);
202 if (g_next_paint_callback) {
203 g_next_paint_callback();
204 g_next_paint_callback = NULL;
208 void AppListView::OnThemeChanged() {
210 GetWidget()->Close();
214 bool AppListView::ShouldHandleSystemCommands() const {
218 void AppListView::Prerender() {
219 app_list_main_view_->Prerender();
222 void AppListView::OnProfilesChanged() {
223 SigninDelegate* signin_delegate =
224 delegate_ ? delegate_->GetSigninDelegate() : NULL;
225 bool show_signin_view = signin_delegate && signin_delegate->NeedSignin();
227 signin_view_->SetVisible(show_signin_view);
228 app_list_main_view_->SetVisible(!show_signin_view);
229 app_list_main_view_->search_box_view()->InvalidateMenu();
232 void AppListView::SetProfileByPath(const base::FilePath& profile_path) {
233 delegate_->SetProfileByPath(profile_path);
234 app_list_main_view_->ModelChanged();
237 void AppListView::AddObserver(AppListViewObserver* observer) {
238 observers_.AddObserver(observer);
241 void AppListView::RemoveObserver(AppListViewObserver* observer) {
242 observers_.RemoveObserver(observer);
246 void AppListView::SetNextPaintCallback(void (*callback)()) {
247 g_next_paint_callback = callback;
251 HWND AppListView::GetHWND() const {
252 #if defined(USE_AURA)
253 gfx::NativeWindow window =
254 GetWidget()->GetTopLevelWidget()->GetNativeWindow();
255 return window->GetHost()->GetAcceleratedWidget();
257 return GetWidget()->GetTopLevelWidget()->GetNativeWindow();
262 void AppListView::InitAsBubbleInternal(gfx::NativeView parent,
263 PaginationModel* pagination_model,
264 views::BubbleBorder::Arrow arrow,
265 bool border_accepts_events,
266 const gfx::Vector2d& anchor_offset) {
267 app_list_main_view_ = new AppListMainView(delegate_.get(),
270 AddChildView(app_list_main_view_);
271 #if defined(USE_AURA)
272 app_list_main_view_->SetPaintToLayer(true);
273 app_list_main_view_->SetFillsBoundsOpaquely(false);
274 app_list_main_view_->layer()->SetMasksToBounds(true);
278 new SigninView(delegate_->GetSigninDelegate(),
279 app_list_main_view_->GetPreferredSize().width());
280 AddChildView(signin_view_);
282 // Speech recognition is available only when the start page exists.
283 if (delegate_ && delegate_->GetSpeechRecognitionContents()) {
284 speech_view_ = new SpeechView(delegate_.get());
285 speech_view_->SetVisible(false);
286 #if defined(USE_AURA)
287 speech_view_->SetPaintToLayer(true);
288 speech_view_->SetFillsBoundsOpaquely(false);
289 speech_view_->layer()->SetOpacity(0.0f);
291 AddChildView(speech_view_);
295 set_color(kContentsBackgroundColor);
296 set_margins(gfx::Insets());
297 set_move_with_anchor(true);
298 set_parent_window(parent);
299 set_close_on_deactivate(false);
300 set_close_on_esc(false);
301 set_anchor_view_insets(gfx::Insets(kArrowOffset + anchor_offset.y(),
302 kArrowOffset + anchor_offset.x(),
303 kArrowOffset - anchor_offset.y(),
304 kArrowOffset - anchor_offset.x()));
305 set_border_accepts_events(border_accepts_events);
306 set_shadow(SupportsShadow() ? views::BubbleBorder::BIG_SHADOW
307 : views::BubbleBorder::NO_SHADOW_OPAQUE_BORDER);
308 views::BubbleDelegateView::CreateBubble(this);
309 SetBubbleArrow(arrow);
311 #if defined(USE_AURA)
312 aura::Window* window = GetWidget()->GetNativeWindow();
313 window->layer()->SetMasksToBounds(true);
314 GetBubbleFrameView()->set_background(new AppListBackground(
315 GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
316 app_list_main_view_));
317 set_background(NULL);
318 window->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
319 new views::BubbleWindowTargeter(this)));
321 set_background(new AppListBackground(
322 GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
323 app_list_main_view_));
325 // On non-aura the bubble has two widgets, and it's possible for the border
326 // to be shown independently in odd situations. Explicitly hide the bubble
327 // widget to ensure that any WM_WINDOWPOSCHANGED messages triggered by the
328 // window manager do not have the SWP_SHOWWINDOW flag set which would cause
329 // the border to be shown. See http://crbug.com/231687 .
333 OnSpeechRecognitionStateChanged(delegate_->GetSpeechUI()->state());
336 delegate_->ViewInitialized();
339 void AppListView::OnBeforeBubbleWidgetInit(
340 views::Widget::InitParams* params,
341 views::Widget* widget) const {
342 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
343 if (delegate_ && delegate_->ForceNativeDesktop())
344 params->native_widget = new views::DesktopNativeWidgetAura(widget);
347 // Windows 7 and higher offer pinning to the taskbar, but we need presence
348 // on the taskbar for the user to be able to pin us. So, show the window on
349 // the taskbar for these versions of Windows.
350 if (base::win::GetVersion() >= base::win::VERSION_WIN7)
351 params->force_show_in_taskbar = true;
352 #elif defined(OS_LINUX)
353 // Set up a custom WM_CLASS for the app launcher window. This allows task
354 // switchers in X11 environments to distinguish it from main browser windows.
355 params->wm_class_name = kAppListWMClass;
356 // Show the window in the taskbar, even though it is a bubble, which would not
357 // normally be shown.
358 params->force_show_in_taskbar = true;
362 views::View* AppListView::GetInitiallyFocusedView() {
363 return app_list_main_view_->search_box_view()->search_box();
366 gfx::ImageSkia AppListView::GetWindowIcon() {
368 return delegate_->GetWindowIcon();
370 return gfx::ImageSkia();
373 bool AppListView::WidgetHasHitTestMask() const {
377 void AppListView::GetWidgetHitTestMask(gfx::Path* mask) const {
379 mask->addRect(gfx::RectToSkRect(
380 GetBubbleFrameView()->GetContentsBounds()));
383 bool AppListView::AcceleratorPressed(const ui::Accelerator& accelerator) {
384 // The accelerator is added by BubbleDelegateView.
385 if (accelerator.key_code() == ui::VKEY_ESCAPE) {
386 if (app_list_main_view_->search_box_view()->HasSearch()) {
387 app_list_main_view_->search_box_view()->ClearSearch();
389 GetWidget()->Deactivate();
398 void AppListView::Layout() {
399 const gfx::Rect contents_bounds = GetContentsBounds();
400 app_list_main_view_->SetBoundsRect(contents_bounds);
401 signin_view_->SetBoundsRect(contents_bounds);
404 gfx::Rect speech_bounds = contents_bounds;
405 int preferred_height = speech_view_->GetPreferredSize().height();
406 speech_bounds.Inset(kSpeechUIMargin, kSpeechUIMargin);
407 speech_bounds.set_height(std::min(speech_bounds.height(),
409 speech_bounds.Inset(-speech_view_->GetInsets());
410 speech_view_->SetBoundsRect(speech_bounds);
414 void AppListView::SchedulePaintInRect(const gfx::Rect& rect) {
415 BubbleDelegateView::SchedulePaintInRect(rect);
416 if (GetBubbleFrameView())
417 GetBubbleFrameView()->SchedulePaint();
420 void AppListView::OnWidgetDestroying(views::Widget* widget) {
421 BubbleDelegateView::OnWidgetDestroying(widget);
422 if (delegate_ && widget == GetWidget())
423 delegate_->ViewClosing();
426 void AppListView::OnWidgetActivationChanged(views::Widget* widget,
428 // Do not called inherited function as the bubble delegate auto close
429 // functionality is not used.
430 if (widget == GetWidget())
431 FOR_EACH_OBSERVER(AppListViewObserver, observers_,
432 OnActivationChanged(widget, active));
435 void AppListView::OnWidgetVisibilityChanged(views::Widget* widget,
437 BubbleDelegateView::OnWidgetVisibilityChanged(widget, visible);
439 if (widget != GetWidget())
442 // We clear the search when hiding so the next time the app list appears it is
443 // not showing search results.
445 app_list_main_view_->search_box_view()->ClearSearch();
447 // Whether we need to signin or not may have changed since last time we were
452 void AppListView::OnSpeechRecognitionStateChanged(
453 SpeechRecognitionState new_state) {
454 if (signin_view_->visible() || !speech_view_)
457 bool recognizing = (new_state == SPEECH_RECOGNITION_RECOGNIZING ||
458 new_state == SPEECH_RECOGNITION_IN_SPEECH);
459 // No change for this class.
460 if (speech_view_->visible() == recognizing)
464 speech_view_->Reset();
466 #if defined(USE_AURA)
467 animation_observer_->set_frame(GetBubbleFrameView());
468 gfx::Transform speech_transform;
469 speech_transform.Translate(
470 0, SkFloatToMScalar(kSpeechUIAppearingPosition));
472 speech_view_->layer()->SetTransform(speech_transform);
475 ui::ScopedLayerAnimationSettings main_settings(
476 app_list_main_view_->layer()->GetAnimator());
478 animation_observer_->SetTarget(app_list_main_view_);
479 main_settings.AddObserver(animation_observer_.get());
481 app_list_main_view_->layer()->SetOpacity(recognizing ? 0.0f : 1.0f);
485 ui::ScopedLayerAnimationSettings speech_settings(
486 speech_view_->layer()->GetAnimator());
488 animation_observer_->SetTarget(speech_view_);
489 speech_settings.AddObserver(animation_observer_.get());
492 speech_view_->layer()->SetOpacity(recognizing ? 1.0f : 0.0f);
494 speech_view_->layer()->SetTransform(gfx::Transform());
496 speech_view_->layer()->SetTransform(speech_transform);
500 speech_view_->SetVisible(true);
502 app_list_main_view_->SetVisible(true);
504 speech_view_->SetVisible(recognizing);
505 app_list_main_view_->SetVisible(!recognizing);
507 // Needs to schedule paint of AppListView itself, to repaint the background.
508 GetBubbleFrameView()->SchedulePaint();
512 } // namespace app_list