Views Omnibox: tolerate minor click-to-select-all dragging.
[chromium-blink-merge.git] / ui / app_list / views / app_list_view.cc
blob21163f47c0ae3c4a733a85ea5c3fd1d821d7055b
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"
35 #if defined(USE_AURA)
36 #include "ui/aura/window.h"
37 #include "ui/aura/root_window.h"
38 #if defined(OS_WIN)
39 #include "ui/base/win/shell.h"
40 #endif
41 #if !defined(OS_CHROMEOS)
42 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
43 #endif
44 #endif // defined(USE_AURA)
46 namespace app_list {
48 namespace {
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() {
63 #if defined(OS_WIN)
64 // Shadows are not supported on Windows without Aero Glass.
65 if (!ui::win::IsAeroGlassEnabled() ||
66 CommandLine::ForCurrentProcess()->HasSwitch(
67 switches::kDisableDwmComposition)) {
68 return false;
70 #elif defined(OS_LINUX) && !defined(USE_ASH)
71 // Shadows are not supported on (non-ChromeOS) Linux.
72 return false;
73 #endif
74 return true;
77 } // namespace
79 // An animation observer to hide the view at the end of the animation.
80 class HideViewAnimationObserver : public ui::ImplicitAnimationObserver {
81 public:
82 HideViewAnimationObserver()
83 : frame_(NULL),
84 target_(NULL) {
87 virtual ~HideViewAnimationObserver() {
88 if (target_)
89 StopObservingImplicitAnimations();
92 void SetTarget(views::View* target) {
93 if (!target_)
94 StopObservingImplicitAnimations();
95 target_ = target;
98 void set_frame(views::BubbleFrameView* frame) { frame_ = frame; }
100 private:
101 // Overridden from ui::ImplicitAnimationObserver:
102 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
103 if (target_) {
104 target_->SetVisible(false);
105 target_ = NULL;
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 ////////////////////////////////////////////////////////////////////////////////
119 // AppListView:
121 AppListView::AppListView(AppListViewDelegate* delegate)
122 : delegate_(delegate),
123 app_list_main_view_(NULL),
124 signin_view_(NULL),
125 speech_view_(NULL),
126 animation_observer_(new HideViewAnimationObserver()) {
127 CHECK(delegate);
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,
144 views::View* anchor,
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) {
159 SetAnchorView(NULL);
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() {
190 SizeToContents();
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() {
206 #if defined(OS_WIN)
207 GetWidget()->Close();
208 #endif
211 bool AppListView::ShouldHandleSystemCommands() const {
212 return true;
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);
242 // static
243 void AppListView::SetNextPaintCallback(void (*callback)()) {
244 g_next_paint_callback = callback;
247 #if defined(OS_WIN)
248 HWND AppListView::GetHWND() const {
249 #if defined(USE_AURA)
250 gfx::NativeWindow window =
251 GetWidget()->GetTopLevelWidget()->GetNativeWindow();
252 return window->GetDispatcher()->host()->GetAcceleratedWidget();
253 #else
254 return GetWidget()->GetTopLevelWidget()->GetNativeWindow();
255 #endif
257 #endif
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(),
265 pagination_model,
266 parent);
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);
272 #endif
274 signin_view_ =
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);
287 #endif
288 AddChildView(speech_view_);
291 OnProfilesChanged();
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)));
317 #else
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 .
327 GetWidget()->Hide();
328 #endif
330 if (delegate_)
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);
340 #endif
341 #if defined(OS_WIN)
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;
354 #endif
357 views::View* AppListView::GetInitiallyFocusedView() {
358 return app_list_main_view_->search_box_view()->search_box();
361 gfx::ImageSkia AppListView::GetWindowIcon() {
362 if (delegate_)
363 return delegate_->GetWindowIcon();
365 return gfx::ImageSkia();
368 bool AppListView::WidgetHasHitTestMask() const {
369 return true;
372 void AppListView::GetWidgetHitTestMask(gfx::Path* mask) const {
373 DCHECK(mask);
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();
383 } else {
384 GetWidget()->Deactivate();
385 Close();
387 return true;
390 return false;
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);
398 if (speech_view_) {
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(),
403 preferred_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,
416 bool active) {
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,
425 bool visible) {
426 BubbleDelegateView::OnWidgetVisibilityChanged(widget, visible);
428 if (widget != GetWidget())
429 return;
431 // We clear the search when hiding so the next time the app list appears it is
432 // not showing search results.
433 if (!visible)
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
437 // shown.
438 Layout();
441 void AppListView::OnSpeechRecognitionStateChanged(
442 SpeechRecognitionState new_state) {
443 if (signin_view_->visible() || !speech_view_)
444 return;
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)
450 return;
452 if (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));
460 if (recognizing)
461 speech_view_->layer()->SetTransform(speech_transform);
464 ui::ScopedLayerAnimationSettings main_settings(
465 app_list_main_view_->layer()->GetAnimator());
466 if (recognizing) {
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());
476 if (!recognizing) {
477 animation_observer_->SetTarget(speech_view_);
478 speech_settings.AddObserver(animation_observer_.get());
481 speech_view_->layer()->SetOpacity(recognizing ? 1.0f : 0.0f);
482 if (recognizing)
483 speech_view_->layer()->SetTransform(gfx::Transform());
484 else
485 speech_view_->layer()->SetTransform(speech_transform);
488 if (recognizing)
489 speech_view_->SetVisible(true);
490 else
491 app_list_main_view_->SetVisible(true);
492 #else
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();
498 #endif
501 } // namespace app_list