OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "ash/app_list/app_list_view.h" | |
6 | |
7 #include "ash/app_list/app_list.h" | |
8 #include "ash/app_list/app_list_bubble_border.h" | |
9 #include "ash/app_list/app_list_item_view.h" | |
10 #include "ash/app_list/app_list_model.h" | |
11 #include "ash/app_list/app_list_model_view.h" | |
12 #include "ash/app_list/app_list_view_delegate.h" | |
13 #include "ash/app_list/page_switcher.h" | |
14 #include "ash/app_list/pagination_model.h" | |
15 #include "ash/launcher/launcher.h" | |
16 #include "ash/screen_ash.h" | |
17 #include "ash/shell.h" | |
18 #include "ash/shell_window_ids.h" | |
19 #include "ash/wm/shelf_layout_manager.h" | |
20 #include "ui/compositor/layer.h" | |
21 #include "ui/compositor/scoped_layer_animation_settings.h" | |
22 #include "ui/gfx/screen.h" | |
23 #include "ui/gfx/transform_util.h" | |
24 #include "ui/views/background.h" | |
25 #include "ui/views/bubble/bubble_frame_view.h" | |
26 #include "ui/views/layout/box_layout.h" | |
27 #include "ui/views/widget/widget.h" | |
28 | |
29 namespace ash { | |
30 | |
31 namespace { | |
32 | |
33 // 0.2 black | |
34 const SkColor kWidgetBackgroundColor = SkColorSetARGB(0x33, 0, 0, 0); | |
35 | |
36 const float kModelViewAnimationScaleFactor = 0.9f; | |
37 | |
38 const int kPreferredIconDimension = 48; | |
39 const int kPreferredCols = 4; | |
40 const int kPreferredRows = 4; | |
41 // Padding space in pixels between model view and page switcher footer. | |
42 const int kModelViewFooterPadding = 10; | |
43 | |
44 ui::Transform GetScaleTransform(AppListModelView* model_view) { | |
45 gfx::Rect pixel_bounds = model_view->GetLayerBoundsInPixel(); | |
46 gfx::Point center(pixel_bounds.width() / 2, pixel_bounds.height() / 2); | |
47 return ui::GetScaleTransform(center, kModelViewAnimationScaleFactor); | |
48 } | |
49 | |
50 // Bounds returned is used for full screen mode. Use full monitor rect so that | |
51 // the app list shade goes behind the launcher. | |
52 gfx::Rect GetFullScreenBounds() { | |
53 gfx::Point cursor = gfx::Screen::GetCursorScreenPoint(); | |
54 return gfx::Screen::GetMonitorNearestPoint(cursor).bounds(); | |
55 } | |
56 | |
57 } // namespace | |
58 | |
59 //////////////////////////////////////////////////////////////////////////////// | |
60 // AppListView: | |
61 | |
62 AppListView::AppListView(AppListViewDelegate* delegate) | |
63 : delegate_(delegate), | |
64 pagination_model_(new PaginationModel), | |
65 bubble_style_(false), | |
66 bubble_border_(NULL), | |
67 model_view_(NULL) { | |
68 if (internal::AppList::UseAppListV2()) | |
69 InitAsBubble(); | |
70 else | |
71 InitAsFullscreenWidget(); | |
72 } | |
73 | |
74 AppListView::~AppListView() { | |
75 // Model is going away, so set to NULL before superclass deletes child views. | |
76 if (model_view_) | |
77 model_view_->SetModel(NULL); | |
78 } | |
79 | |
80 void AppListView::AnimateShow(int duration_ms) { | |
81 if (bubble_style_) | |
82 return; | |
83 | |
84 ui::Layer* layer = model_view_->layer(); | |
85 ui::ScopedLayerAnimationSettings animation(layer->GetAnimator()); | |
86 animation.SetTransitionDuration( | |
87 base::TimeDelta::FromMilliseconds(duration_ms)); | |
88 animation.SetTweenType(ui::Tween::EASE_OUT); | |
89 model_view_->SetTransform(ui::Transform()); | |
90 } | |
91 | |
92 void AppListView::AnimateHide(int duration_ms) { | |
93 if (bubble_style_) | |
94 return; | |
95 | |
96 ui::Layer* layer = model_view_->layer(); | |
97 ui::ScopedLayerAnimationSettings animation(layer->GetAnimator()); | |
98 animation.SetTransitionDuration( | |
99 base::TimeDelta::FromMilliseconds(duration_ms)); | |
100 animation.SetTweenType(ui::Tween::EASE_IN); | |
101 model_view_->SetTransform(GetScaleTransform(model_view_)); | |
102 } | |
103 | |
104 void AppListView::Close() { | |
105 if (GetWidget()->IsVisible()) | |
106 Shell::GetInstance()->ToggleAppList(); | |
107 } | |
108 | |
109 void AppListView::UpdateBounds() { | |
110 if (bubble_style_) | |
111 SizeToContents(); | |
112 else | |
113 GetWidget()->SetBounds(GetFullScreenBounds()); | |
114 } | |
115 | |
116 void AppListView::InitAsFullscreenWidget() { | |
117 bubble_style_ = false; | |
118 set_background(views::Background::CreateSolidBackground( | |
119 kWidgetBackgroundColor)); | |
120 | |
121 model_view_ = new AppListModelView(this, pagination_model_.get()); | |
122 model_view_->SetPaintToLayer(true); | |
123 model_view_->layer()->SetFillsBoundsOpaquely(false); | |
124 AddChildView(model_view_); | |
125 | |
126 views::Widget::InitParams widget_params( | |
127 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); | |
128 widget_params.delegate = this; | |
129 widget_params.transparent = true; | |
130 widget_params.parent = Shell::GetInstance()->GetContainer( | |
131 internal::kShellWindowId_AppListContainer); | |
132 | |
133 views::Widget* widget = new views::Widget; | |
134 widget->Init(widget_params); | |
135 widget->SetContentsView(this); | |
136 | |
137 widget->SetBounds(GetFullScreenBounds()); | |
138 | |
139 // Turns off default animation. | |
140 widget->SetVisibilityChangedAnimationsEnabled(false); | |
141 | |
142 // Sets initial transform. AnimateShow changes it back to identity transform. | |
143 model_view_->SetTransform(GetScaleTransform(model_view_)); | |
144 UpdateModel(); | |
145 } | |
146 | |
147 void AppListView::InitAsBubble() { | |
148 bubble_style_ = true; | |
149 set_background(NULL); | |
150 | |
151 SetLayoutManager(new views::BoxLayout( | |
152 views::BoxLayout::kVertical, 0, 0, kModelViewFooterPadding)); | |
153 | |
154 model_view_ = new AppListModelView(this, pagination_model_.get()); | |
155 model_view_->SetLayout(kPreferredIconDimension, | |
156 kPreferredCols, | |
157 kPreferredRows); | |
158 AddChildView(model_view_); | |
159 | |
160 PageSwitcher* page_switcher = new PageSwitcher(pagination_model_.get()); | |
161 AddChildView(page_switcher); | |
162 | |
163 set_anchor_view(Shell::GetInstance()->launcher()->GetAppListButtonView()); | |
164 set_parent_window(Shell::GetInstance()->GetContainer( | |
165 internal::kShellWindowId_AppListContainer)); | |
166 set_close_on_deactivate(false); | |
167 views::BubbleDelegateView::CreateBubble(this); | |
168 | |
169 // Resets default background since AppListBubbleBorder paints background. | |
170 GetBubbleFrameView()->set_background(NULL); | |
171 | |
172 // Overrides border with AppListBubbleBorder. | |
173 bubble_border_ = new AppListBubbleBorder(this); | |
174 GetBubbleFrameView()->SetBubbleBorder(bubble_border_); | |
175 SizeToContents(); // Recalcuates with new border. | |
176 | |
177 UpdateModel(); | |
178 } | |
179 | |
180 void AppListView::UpdateModel() { | |
181 if (delegate_.get()) { | |
182 scoped_ptr<AppListModel> new_model(new AppListModel); | |
183 delegate_->SetModel(new_model.get()); | |
184 delegate_->UpdateModel(std::string()); | |
185 model_view_->SetModel(new_model.get()); | |
186 model_.reset(new_model.release()); | |
187 } | |
188 } | |
189 | |
190 views::View* AppListView::GetInitiallyFocusedView() { | |
191 return model_view_; | |
192 } | |
193 | |
194 void AppListView::Layout() { | |
195 gfx::Rect rect(GetContentsBounds()); | |
196 if (rect.IsEmpty()) | |
197 return; | |
198 | |
199 if (bubble_style_) { | |
200 views::View::Layout(); | |
201 } else { | |
202 // Gets work area rect, which is in screen coordinates. | |
203 gfx::Rect workarea = Shell::GetInstance()->shelf()->IsVisible() ? | |
204 ScreenAsh::GetUnmaximizedWorkAreaBounds(GetWidget()->GetNativeView()) : | |
205 gfx::Screen::GetMonitorNearestWindow( | |
206 GetWidget()->GetNativeView()).work_area(); | |
207 | |
208 // Converts |workarea| into view's coordinates. | |
209 gfx::Point origin(workarea.origin()); | |
210 views::View::ConvertPointFromScreen(this, &origin); | |
211 workarea.Offset(-origin.x(), -origin.y()); | |
212 | |
213 rect = rect.Intersect(workarea); | |
214 model_view_->SetBoundsRect(rect); | |
215 } | |
216 } | |
217 | |
218 bool AppListView::OnKeyPressed(const views::KeyEvent& event) { | |
219 if (event.key_code() == ui::VKEY_ESCAPE) { | |
220 Close(); | |
221 return true; | |
222 } | |
223 | |
224 return false; | |
225 } | |
226 | |
227 bool AppListView::OnMousePressed(const views::MouseEvent& event) { | |
228 // For full screen mode, if mouse click reaches us, this means user clicks | |
229 // on blank area. So close. | |
230 if (!bubble_style_) | |
231 Close(); | |
232 | |
233 return true; | |
234 } | |
235 | |
236 void AppListView::ButtonPressed(views::Button* sender, | |
237 const views::Event& event) { | |
238 if (sender->GetClassName() != AppListItemView::kViewClassName) | |
239 return; | |
240 | |
241 if (delegate_.get()) { | |
242 delegate_->OnAppListItemActivated( | |
243 static_cast<AppListItemView*>(sender)->model(), | |
244 event.flags()); | |
245 } | |
246 Close(); | |
247 } | |
248 | |
249 gfx::Rect AppListView::GetBubbleBounds() { | |
250 // This happens before replacing the default border. | |
251 if (!bubble_border_) | |
252 return views::BubbleDelegateView::GetBubbleBounds(); | |
253 | |
254 const int old_arrow_offset = bubble_border_->arrow_offset(); | |
255 const gfx::Rect anchor_rect = GetAnchorRect(); | |
256 | |
257 bubble_border_->set_arrow_offset(0); | |
258 gfx::Rect bubble_rect = GetBubbleFrameView()->GetUpdatedWindowBounds( | |
259 anchor_rect, | |
260 GetPreferredSize(), | |
261 false /* try_mirroring_arrow */); | |
262 | |
263 gfx::Rect monitor_rect = gfx::Screen::GetMonitorNearestPoint( | |
264 anchor_rect.CenterPoint()).work_area(); | |
265 if (monitor_rect.IsEmpty() || monitor_rect.Contains(bubble_rect)) | |
266 return bubble_rect; | |
267 | |
268 int offset = 0; | |
269 if (bubble_rect.x() < monitor_rect.x()) | |
270 offset = monitor_rect.x() - bubble_rect.x(); | |
271 else if (bubble_rect.right() > monitor_rect.right()) | |
272 offset = monitor_rect.right() - bubble_rect.right(); | |
273 | |
274 bubble_rect.set_x(bubble_rect.x() + offset); | |
275 | |
276 // Moves bubble arrow in the opposite direction. i.e. If bubble bounds is | |
277 // moved to right (positive offset), we need to move arrow to left so that | |
278 // it points to the same position before the move. | |
279 bubble_border_->set_arrow_offset(-offset); | |
280 | |
281 // Repaints border if arrow offset is changed. | |
282 if (bubble_border_->arrow_offset() != old_arrow_offset) | |
283 GetBubbleFrameView()->SchedulePaint(); | |
284 | |
285 return bubble_rect; | |
286 } | |
287 | |
288 } // namespace ash | |
OLD | NEW |