OLD | NEW |
| (Empty) |
1 // Copyright 2016 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. | |
4 | |
5 #include "ui/app_list/shower/app_list_shower_impl.h" | |
6 | |
7 #include "ui/app_list/app_list_constants.h" | |
8 #include "ui/app_list/app_list_switches.h" | |
9 #include "ui/app_list/pagination_model.h" | |
10 #include "ui/app_list/shower/app_list_shower_delegate_factory.h" | |
11 #include "ui/app_list/views/app_list_view.h" | |
12 #include "ui/aura/client/focus_client.h" | |
13 #include "ui/aura/window.h" | |
14 #include "ui/compositor/layer.h" | |
15 #include "ui/compositor/scoped_layer_animation_settings.h" | |
16 #include "ui/views/widget/widget.h" | |
17 | |
18 namespace app_list { | |
19 namespace { | |
20 | |
21 // Duration for show/hide animation in milliseconds. | |
22 const int kAnimationDurationMs = 200; | |
23 | |
24 // The maximum shift in pixels when over-scroll happens. | |
25 const int kMaxOverScrollShift = 48; | |
26 | |
27 ui::Layer* GetLayer(views::Widget* widget) { | |
28 return widget->GetNativeView()->layer(); | |
29 } | |
30 | |
31 } // namespace | |
32 | |
33 AppListShowerImpl::AppListShowerImpl(AppListShowerDelegateFactory* factory) | |
34 : factory_(factory) { | |
35 DCHECK(factory); | |
36 } | |
37 | |
38 AppListShowerImpl::~AppListShowerImpl() { | |
39 shower_delegate_.reset(); | |
40 // Ensures app list view goes before the controller since pagination model | |
41 // lives in the controller and app list view would access it on destruction. | |
42 if (view_) { | |
43 view_->GetAppsPaginationModel()->RemoveObserver(this); | |
44 if (view_->GetWidget()) | |
45 view_->GetWidget()->CloseNow(); | |
46 } | |
47 } | |
48 | |
49 aura::Window* AppListShowerImpl::GetWindow() { | |
50 return is_visible_ && view_ ? view_->GetWidget()->GetNativeWindow() : nullptr; | |
51 } | |
52 | |
53 void AppListShowerImpl::Show(aura::Window* window) { | |
54 if (is_visible_) | |
55 return; | |
56 | |
57 DCHECK(window); | |
58 is_visible_ = true; | |
59 aura::Window* root_window = window->GetRootWindow(); | |
60 if (view_) { | |
61 ScheduleAnimation(); | |
62 } else { | |
63 shower_delegate_ = factory_->GetDelegate(this); | |
64 AppListViewDelegate* view_delegate = shower_delegate_->GetViewDelegate(); | |
65 DCHECK(view_delegate); | |
66 // Note the AppListViewDelegate outlives the AppListView. For Ash, the view | |
67 // is destroyed when dismissed. | |
68 AppListView* view = new AppListView(view_delegate); | |
69 shower_delegate_->Init(view, root_window, current_apps_page_); | |
70 SetView(view); | |
71 } | |
72 shower_delegate_->OnShown(root_window); | |
73 } | |
74 | |
75 void AppListShowerImpl::Dismiss() { | |
76 if (!is_visible_) | |
77 return; | |
78 | |
79 // If the app list is currently visible, there should be an existing view. | |
80 DCHECK(view_); | |
81 | |
82 is_visible_ = false; | |
83 | |
84 // Our widget is currently active. When the animation completes we'll hide | |
85 // the widget, changing activation. If a menu is shown before the animation | |
86 // completes then the activation change triggers the menu to close. By | |
87 // deactivating now we ensure there is no activation change when the | |
88 // animation completes and any menus stay open. | |
89 view_->GetWidget()->Deactivate(); | |
90 | |
91 shower_delegate_->OnDismissed(); | |
92 ScheduleAnimation(); | |
93 } | |
94 | |
95 bool AppListShowerImpl::IsVisible() const { | |
96 return view_ && view_->GetWidget()->IsVisible(); | |
97 } | |
98 | |
99 bool AppListShowerImpl::GetTargetVisibility() const { | |
100 return is_visible_; | |
101 } | |
102 | |
103 //////////////////////////////////////////////////////////////////////////////// | |
104 // AppListShowerImpl, private: | |
105 | |
106 void AppListShowerImpl::SetView(AppListView* view) { | |
107 DCHECK(view_ == nullptr); | |
108 DCHECK(is_visible_); | |
109 | |
110 view_ = view; | |
111 views::Widget* widget = view_->GetWidget(); | |
112 widget->AddObserver(this); | |
113 widget->GetNativeView()->GetRootWindow()->AddObserver(this); | |
114 aura::client::GetFocusClient(widget->GetNativeView())->AddObserver(this); | |
115 view_->GetAppsPaginationModel()->AddObserver(this); | |
116 view_->ShowWhenReady(); | |
117 } | |
118 | |
119 void AppListShowerImpl::ResetView() { | |
120 if (!view_) | |
121 return; | |
122 | |
123 views::Widget* widget = view_->GetWidget(); | |
124 widget->RemoveObserver(this); | |
125 GetLayer(widget)->GetAnimator()->RemoveObserver(this); | |
126 shower_delegate_.reset(); | |
127 widget->GetNativeView()->GetRootWindow()->RemoveObserver(this); | |
128 aura::client::GetFocusClient(widget->GetNativeView())->RemoveObserver(this); | |
129 | |
130 view_->GetAppsPaginationModel()->RemoveObserver(this); | |
131 | |
132 view_ = nullptr; | |
133 } | |
134 | |
135 void AppListShowerImpl::ScheduleAnimation() { | |
136 // Stop observing previous animation. | |
137 StopObservingImplicitAnimations(); | |
138 | |
139 views::Widget* widget = view_->GetWidget(); | |
140 ui::Layer* layer = GetLayer(widget); | |
141 layer->GetAnimator()->StopAnimating(); | |
142 | |
143 gfx::Rect target_bounds; | |
144 gfx::Vector2d offset = shower_delegate_->GetVisibilityAnimationOffset( | |
145 widget->GetNativeView()->GetRootWindow()); | |
146 if (is_visible_) { | |
147 target_bounds = widget->GetWindowBoundsInScreen(); | |
148 gfx::Rect start_bounds = gfx::Rect(target_bounds); | |
149 start_bounds.Offset(offset); | |
150 widget->SetBounds(start_bounds); | |
151 } else { | |
152 target_bounds = widget->GetWindowBoundsInScreen(); | |
153 target_bounds.Offset(offset); | |
154 } | |
155 | |
156 ui::ScopedLayerAnimationSettings animation(layer->GetAnimator()); | |
157 animation.SetTransitionDuration(base::TimeDelta::FromMilliseconds( | |
158 is_visible_ ? 0 : kAnimationDurationMs)); | |
159 animation.AddObserver(this); | |
160 | |
161 layer->SetOpacity(is_visible_ ? 1.0 : 0.0); | |
162 widget->SetBounds(target_bounds); | |
163 } | |
164 | |
165 //////////////////////////////////////////////////////////////////////////////// | |
166 // AppListShowerImpl, aura::client::FocusChangeObserver implementation: | |
167 | |
168 void AppListShowerImpl::OnWindowFocused(aura::Window* gained_focus, | |
169 aura::Window* lost_focus) { | |
170 if (view_ && is_visible_) { | |
171 aura::Window* applist_window = view_->GetWidget()->GetNativeView(); | |
172 aura::Window* applist_container = applist_window->parent(); | |
173 if (applist_container->Contains(lost_focus) && | |
174 (!gained_focus || !applist_container->Contains(gained_focus)) && | |
175 !switches::ShouldNotDismissOnBlur()) { | |
176 Dismiss(); | |
177 } | |
178 } | |
179 } | |
180 | |
181 //////////////////////////////////////////////////////////////////////////////// | |
182 // AppListShowerImpl, aura::WindowObserver implementation: | |
183 void AppListShowerImpl::OnWindowBoundsChanged(aura::Window* root, | |
184 const gfx::Rect& old_bounds, | |
185 const gfx::Rect& new_bounds) { | |
186 if (shower_delegate_) | |
187 shower_delegate_->UpdateBounds(); | |
188 } | |
189 | |
190 //////////////////////////////////////////////////////////////////////////////// | |
191 // AppListShowerImpl, ui::ImplicitAnimationObserver implementation: | |
192 | |
193 void AppListShowerImpl::OnImplicitAnimationsCompleted() { | |
194 if (is_visible_) | |
195 view_->GetWidget()->Activate(); | |
196 else | |
197 view_->GetWidget()->Close(); | |
198 } | |
199 | |
200 //////////////////////////////////////////////////////////////////////////////// | |
201 // AppListShowerImpl, views::WidgetObserver implementation: | |
202 | |
203 void AppListShowerImpl::OnWidgetDestroying(views::Widget* widget) { | |
204 DCHECK(view_->GetWidget() == widget); | |
205 if (is_visible_) | |
206 Dismiss(); | |
207 ResetView(); | |
208 } | |
209 | |
210 //////////////////////////////////////////////////////////////////////////////// | |
211 // AppListShowerImpl, PaginationModelObserver implementation: | |
212 | |
213 void AppListShowerImpl::TotalPagesChanged() {} | |
214 | |
215 void AppListShowerImpl::SelectedPageChanged(int old_selected, | |
216 int new_selected) { | |
217 current_apps_page_ = new_selected; | |
218 } | |
219 | |
220 void AppListShowerImpl::TransitionStarted() {} | |
221 | |
222 void AppListShowerImpl::TransitionChanged() { | |
223 // |view_| could be NULL when app list is closed with a running transition. | |
224 if (!view_) | |
225 return; | |
226 | |
227 PaginationModel* pagination_model = view_->GetAppsPaginationModel(); | |
228 | |
229 const PaginationModel::Transition& transition = | |
230 pagination_model->transition(); | |
231 if (pagination_model->is_valid_page(transition.target_page)) | |
232 return; | |
233 | |
234 views::Widget* widget = view_->GetWidget(); | |
235 ui::LayerAnimator* widget_animator = | |
236 widget->GetNativeView()->layer()->GetAnimator(); | |
237 if (!pagination_model->IsRevertingCurrentTransition()) { | |
238 // Update cached |view_bounds_| if it is the first over-scroll move and | |
239 // widget does not have running animations. | |
240 if (!should_snap_back_ && !widget_animator->is_animating()) | |
241 view_bounds_ = widget->GetWindowBoundsInScreen(); | |
242 | |
243 const int current_page = pagination_model->selected_page(); | |
244 const int dir = transition.target_page > current_page ? -1 : 1; | |
245 | |
246 const double progress = 1.0 - pow(1.0 - transition.progress, 4); | |
247 const int shift = kMaxOverScrollShift * progress * dir; | |
248 | |
249 gfx::Rect shifted(view_bounds_); | |
250 shifted.set_x(shifted.x() + shift); | |
251 | |
252 widget->SetBounds(shifted); | |
253 | |
254 should_snap_back_ = true; | |
255 } else if (should_snap_back_) { | |
256 should_snap_back_ = false; | |
257 ui::ScopedLayerAnimationSettings animation(widget_animator); | |
258 animation.SetTransitionDuration( | |
259 base::TimeDelta::FromMilliseconds(kOverscrollPageTransitionDurationMs)); | |
260 widget->SetBounds(view_bounds_); | |
261 } | |
262 } | |
263 | |
264 } // namespace app_list | |
OLD | NEW |