1 // Copyright 2013 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_folder_view.h"
9 #include "ui/accessibility/ax_view_state.h"
10 #include "ui/app_list/app_list_constants.h"
11 #include "ui/app_list/app_list_folder_item.h"
12 #include "ui/app_list/app_list_model.h"
13 #include "ui/app_list/views/app_list_item_view.h"
14 #include "ui/app_list/views/app_list_main_view.h"
15 #include "ui/app_list/views/apps_container_view.h"
16 #include "ui/app_list/views/apps_grid_view.h"
17 #include "ui/app_list/views/contents_view.h"
18 #include "ui/app_list/views/folder_background_view.h"
19 #include "ui/app_list/views/folder_header_view.h"
20 #include "ui/app_list/views/search_box_view.h"
21 #include "ui/compositor/scoped_layer_animation_settings.h"
22 #include "ui/events/event.h"
23 #include "ui/gfx/geometry/rect_conversions.h"
24 #include "ui/strings/grit/ui_strings.h"
25 #include "ui/views/controls/textfield/textfield.h"
26 #include "ui/views/view_model.h"
27 #include "ui/views/view_model_utils.h"
33 // Indexes of interesting views in ViewModel of AppListFolderView.
34 const int kIndexFolderHeader
= 0;
35 const int kIndexChildItems
= 1;
37 // Threshold for the distance from the center of the item to the circle of the
38 // folder container ink bubble, beyond which, the item is considered dragged
39 // out of the folder boundary.
40 const int kOutOfFolderContainerBubbleDelta
= 30;
44 AppListFolderView::AppListFolderView(AppsContainerView
* container_view
,
46 AppListMainView
* app_list_main_view
)
47 : container_view_(container_view
),
48 app_list_main_view_(app_list_main_view
),
49 folder_header_view_(new FolderHeaderView(this)),
50 view_model_(new views::ViewModel
),
53 hide_for_reparent_(false) {
54 AddChildView(folder_header_view_
);
55 view_model_
->Add(folder_header_view_
, kIndexFolderHeader
);
57 items_grid_view_
= new AppsGridView(app_list_main_view_
);
58 items_grid_view_
->set_folder_delegate(this);
59 items_grid_view_
->SetLayout(
60 container_view
->apps_grid_view()->cols(),
61 container_view
->apps_grid_view()->rows_per_page());
62 items_grid_view_
->SetModel(model
);
63 AddChildView(items_grid_view_
);
64 view_model_
->Add(items_grid_view_
, kIndexChildItems
);
66 SetPaintToLayer(true);
67 SetFillsBoundsOpaquely(false);
69 model_
->AddObserver(this);
72 AppListFolderView::~AppListFolderView() {
73 model_
->RemoveObserver(this);
75 // This prevents the AppsGridView's destructor from calling the now-deleted
76 // AppListFolderView's methods if a drag is in progress at the time.
77 items_grid_view_
->set_folder_delegate(nullptr);
80 void AppListFolderView::SetAppListFolderItem(AppListFolderItem
* folder
) {
81 accessible_name_
= ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
82 IDS_APP_LIST_FOLDER_OPEN_FOLDER_ACCESSIBILE_NAME
);
83 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT
, true);
85 folder_item_
= folder
;
86 items_grid_view_
->SetItemList(folder_item_
->item_list());
87 folder_header_view_
->SetFolderItem(folder_item_
);
90 void AppListFolderView::ScheduleShowHideAnimation(bool show
,
91 bool hide_for_reparent
) {
92 hide_for_reparent_
= hide_for_reparent
;
94 // Stop any previous animation.
95 layer()->GetAnimator()->StopAnimating();
97 // Hide the top items temporarily if showing the view for opening the folder.
99 items_grid_view_
->SetTopItemViewsVisible(false);
101 // Set initial state.
102 layer()->SetOpacity(show
? 0.0f
: 1.0f
);
104 UpdateFolderNameVisibility(true);
106 ui::ScopedLayerAnimationSettings
animation(layer()->GetAnimator());
107 animation
.SetTweenType(
108 show
? kFolderFadeInTweenType
: kFolderFadeOutTweenType
);
109 animation
.AddObserver(this);
110 animation
.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
111 show
? kFolderTransitionInDurationMs
: kFolderTransitionOutDurationMs
));
113 layer()->SetOpacity(show
? 1.0f
: 0.0f
);
116 gfx::Size
AppListFolderView::GetPreferredSize() const {
117 const gfx::Size header_size
= folder_header_view_
->GetPreferredSize();
118 const gfx::Size grid_size
= items_grid_view_
->GetPreferredSize();
119 int width
= std::max(header_size
.width(), grid_size
.width());
120 int height
= header_size
.height() + grid_size
.height();
121 return gfx::Size(width
, height
);
124 void AppListFolderView::Layout() {
125 CalculateIdealBounds();
126 views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_
);
129 bool AppListFolderView::OnKeyPressed(const ui::KeyEvent
& event
) {
130 // Process TAB if focus should go to header; otherwise, AppsGridView will do
132 if (event
.key_code() == ui::VKEY_TAB
) {
133 if (items_grid_view_
->has_selected_view() == event
.IsShiftDown() &&
134 !folder_header_view_
->HasTextFocus()) {
135 folder_header_view_
->SetTextFocus();
136 items_grid_view_
->ClearAnySelectedView();
139 GiveBackFocusToSearchBox();
143 // This will select an app in the list, so we need to relinquish focus.
144 if (event
.key_code() == ui::VKEY_DOWN
)
145 GiveBackFocusToSearchBox();
147 return items_grid_view_
->OnKeyPressed(event
);
150 void AppListFolderView::OnAppListItemWillBeDeleted(AppListItem
* item
) {
151 if (item
== folder_item_
) {
152 items_grid_view_
->OnFolderItemRemoved();
153 folder_header_view_
->OnFolderItemRemoved();
156 // Do not change state if it is hidden.
157 if (hide_for_reparent_
|| layer()->opacity() == 0.0f
)
160 // If the folder item associated with this view is removed from the model,
161 // (e.g. the last item in the folder was deleted), reset the view and signal
162 // the container view to show the app list instead.
163 // Pass NULL to ShowApps() to avoid triggering animation from the deleted
165 container_view_
->ShowApps(NULL
);
169 void AppListFolderView::OnImplicitAnimationsCompleted() {
170 // Show the top items when the opening folder animation is done.
171 if (layer()->opacity() == 1.0f
)
172 items_grid_view_
->SetTopItemViewsVisible(true);
174 // If the view is hidden for reparenting a folder item, it has to be visible,
175 // so that drag_view_ can keep receiving mouse events.
176 if (layer()->opacity() == 0.0f
&& !hide_for_reparent_
)
179 // Set the view bounds to a small rect, so that it won't overlap the root
180 // level apps grid view during folder item reprenting transitional period.
181 if (hide_for_reparent_
)
182 SetBoundsRect(gfx::Rect(bounds().x(), bounds().y(), 1, 1));
185 void AppListFolderView::CalculateIdealBounds() {
186 gfx::Rect
rect(GetContentsBounds());
190 gfx::Rect
header_frame(rect
);
191 gfx::Size size
= folder_header_view_
->GetPreferredSize();
192 header_frame
.set_height(size
.height());
193 view_model_
->set_ideal_bounds(kIndexFolderHeader
, header_frame
);
195 gfx::Rect
grid_frame(rect
);
196 grid_frame
.Subtract(header_frame
);
197 view_model_
->set_ideal_bounds(kIndexChildItems
, grid_frame
);
200 void AppListFolderView::StartSetupDragInRootLevelAppsGridView(
201 AppListItemView
* original_drag_view
,
202 const gfx::Point
& drag_point_in_root_grid
,
203 bool has_native_drag
) {
204 // Converts the original_drag_view's bounds to the coordinate system of
205 // root level grid view.
206 gfx::RectF
rect_f(original_drag_view
->bounds());
207 views::View::ConvertRectToTarget(items_grid_view_
,
208 container_view_
->apps_grid_view(),
210 gfx::Rect rect_in_root_grid_view
= gfx::ToEnclosingRect(rect_f
);
212 container_view_
->apps_grid_view()
213 ->InitiateDragFromReparentItemInRootLevelGridView(original_drag_view
,
214 rect_in_root_grid_view
,
215 drag_point_in_root_grid
,
219 gfx::Rect
AppListFolderView::GetItemIconBoundsAt(int index
) {
220 AppListItemView
* item_view
= items_grid_view_
->GetItemViewAt(index
);
221 // Icon bounds relative to AppListItemView.
222 const gfx::Rect icon_bounds
= item_view
->GetIconBounds();
223 gfx::Rect to_apps_grid_view
= item_view
->ConvertRectToParent(icon_bounds
);
224 gfx::Rect to_folder
=
225 items_grid_view_
->ConvertRectToParent(to_apps_grid_view
);
227 // Get the icon image's bound.
228 to_folder
.ClampToCenteredSize(
229 gfx::Size(kGridIconDimension
, kGridIconDimension
));
234 void AppListFolderView::UpdateFolderViewBackground(bool show_bubble
) {
235 if (hide_for_reparent_
)
238 // Before showing the folder container inking bubble, hide the folder name.
240 UpdateFolderNameVisibility(false);
242 container_view_
->folder_background_view()->UpdateFolderContainerBubble(
243 show_bubble
? FolderBackgroundView::SHOW_BUBBLE
:
244 FolderBackgroundView::HIDE_BUBBLE
);
247 void AppListFolderView::UpdateFolderNameVisibility(bool visible
) {
248 folder_header_view_
->UpdateFolderNameVisibility(visible
);
251 void AppListFolderView::SetBackButtonLabel(bool folder
) {
252 app_list_main_view_
->search_box_view()->SetBackButtonLabel(folder
);
255 bool AppListFolderView::IsPointOutsideOfFolderBoundary(
256 const gfx::Point
& point
) {
257 if (!GetLocalBounds().Contains(point
))
260 gfx::Point center
= GetLocalBounds().CenterPoint();
261 float delta
= (point
- center
).Length();
262 return delta
> container_view_
->folder_background_view()->
263 GetFolderContainerBubbleRadius() + kOutOfFolderContainerBubbleDelta
;
266 // When user drags a folder item out of the folder boundary ink bubble, the
267 // folder view UI will be hidden, and switch back to top level AppsGridView.
268 // The dragged item will seamlessly move on the top level AppsGridView.
269 // In order to achieve the above, we keep the folder view and its child grid
270 // view visible with opacity 0, so that the drag_view_ on the hidden grid view
271 // will keep receiving mouse event. At the same time, we initiated a new
272 // drag_view_ in the top level grid view, and keep it moving with the hidden
273 // grid view's drag_view_, so that the dragged item can be engaged in drag and
274 // drop flow in the top level grid view. During the reparenting process, the
275 // drag_view_ in hidden grid view will dispatch the drag and drop event to
276 // the top level grid view, until the drag ends.
277 void AppListFolderView::ReparentItem(
278 AppListItemView
* original_drag_view
,
279 const gfx::Point
& drag_point_in_folder_grid
,
280 bool has_native_drag
) {
281 // Convert the drag point relative to the root level AppsGridView.
282 gfx::Point to_root_level_grid
= drag_point_in_folder_grid
;
283 ConvertPointToTarget(items_grid_view_
,
284 container_view_
->apps_grid_view(),
285 &to_root_level_grid
);
286 StartSetupDragInRootLevelAppsGridView(
287 original_drag_view
, to_root_level_grid
, has_native_drag
);
288 container_view_
->ReparentFolderItemTransit(folder_item_
);
291 void AppListFolderView::DispatchDragEventForReparent(
292 AppsGridView::Pointer pointer
,
293 const gfx::Point
& drag_point_in_folder_grid
) {
294 AppsGridView
* root_grid
= container_view_
->apps_grid_view();
295 gfx::Point drag_point_in_root_grid
= drag_point_in_folder_grid
;
296 ConvertPointToTarget(items_grid_view_
, root_grid
, &drag_point_in_root_grid
);
297 root_grid
->UpdateDragFromReparentItem(pointer
, drag_point_in_folder_grid
);
300 void AppListFolderView::DispatchEndDragEventForReparent(
301 bool events_forwarded_to_drag_drop_host
,
303 container_view_
->apps_grid_view()->EndDragFromReparentItemInRootLevel(
304 events_forwarded_to_drag_drop_host
, cancel_drag
);
305 container_view_
->ReparentDragEnded();
308 void AppListFolderView::HideViewImmediately() {
310 hide_for_reparent_
= false;
313 void AppListFolderView::CloseFolderPage() {
314 accessible_name_
= ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
315 IDS_APP_LIST_FOLDER_CLOSE_FOLDER_ACCESSIBILE_NAME
);
316 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT
, true);
318 GiveBackFocusToSearchBox();
319 if (items_grid_view()->dragging())
320 items_grid_view()->EndDrag(true);
321 items_grid_view()->ClearAnySelectedView();
322 container_view_
->ShowApps(folder_item_
);
325 bool AppListFolderView::IsOEMFolder() const {
326 return folder_item_
->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM
;
329 void AppListFolderView::SetRootLevelDragViewVisible(bool visible
) {
330 container_view_
->apps_grid_view()->SetDragViewVisible(visible
);
333 void AppListFolderView::GetAccessibleState(ui::AXViewState
* state
) {
334 state
->role
= ui::AX_ROLE_BUTTON
;
335 state
->name
= accessible_name_
;
338 void AppListFolderView::NavigateBack(AppListFolderItem
* item
,
339 const ui::Event
& event_flags
) {
343 void AppListFolderView::GiveBackFocusToSearchBox() {
344 app_list_main_view_
->search_box_view()->search_box()->RequestFocus();
347 void AppListFolderView::SetItemName(AppListFolderItem
* item
,
348 const std::string
& name
) {
349 model_
->SetItemName(item
, name
);
352 } // namespace app_list